加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?

我们经常会用 git clone 来下载项目,但遇到大项目的时候,clone 就很慢,比如 react:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第1张图片

要等很久。

当然,还有更慢的项目。

这类项目可以通过 --depth 1 来加速:

git clone --depth 1 https://github.com/facebook/react
加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第2张图片

这速度快了有几十倍吧!越大的项目加速效果越明显。

原因就是下载的内容更少了。

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第3张图片 加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第4张图片

那这样代码还是全的么?

当然,代码是最新的完整代码。

那为啥下载的内容少了呢?少了哪一部分呢?

很容易想到,就是历史 commit。

这里要涉及一点 git 的实现原理了:

git 中文件是通过 object 存储不同数据的:

  • glob 对象存储文件内容

  • tree 对象存储文件路径

  • commit 对象存储 commit 信息,关联多个 tree

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第5张图片

然后 HEAD、branch、tag 等是指向具体 commit 的指针,可以在 .git/refs 下看到

所以说,每个版本的代码都是从 commit 对象作为入口关联起来的。

指定了 depth 1 的时候,就是只保留了最新的入口,历史入口就没下载了。

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第6张图片

这样自然快很多,代码也是完整的。

但这么好的事情也是有代价的,它有一些后遗症。

最容易想到的就是切不到历史 commit。

正常下载的项目的 git log 是这样的:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第7张图片

你可以 git reset 切到任意 commit:

比如:

git reset --hard 4dda96a40
加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第8张图片

但是 depth 1 下载的项目就不可以,因为本地没有这个 commit 可以切:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第9张图片 加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第10张图片 image.png

你再 git pull 的时候,也下载不了历史 commit 的代码:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第11张图片

就很尴尬。

git 团队自然也想到了这点,于是提供了一个 unshallow 的选项:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第12张图片

加上 --unshallow 再 pull 的时候也会同时拉取历史 commit。(默认没开这个是为了性能)

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第13张图片

你完全可以用 depth 1 下载的项目来开发,正常的 pull、push 都没问题,因为都是基于最新 commit 创建的更新的 commit。

当你有一天需要历史 commit 的时候再 pull --unshallow 也不迟。

这样下载项目快,后面也能恢复成完整版代码库,何乐而不为呢?

但 depth 1 还有一个问题,就是切换不了其他 branch。

正常项目是这样的:

git branch -r 可以查看远程分支:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第14张图片

git branch -a 可以查看本地和远程的分支:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第15张图片

但你 depth 1 下载的项目是没有的:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第16张图片

只有一个 main。

有的同学说,fetch 一下就好了呀。

太天真了。

git fetch 的作用是把远程分支的新 commit 下载到本地。

默认下载所有远程分支的新 commit。

也可以单独指定某个分支:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第17张图片

但你会发现 git fetch 了这个分支的代码,也不能看到和切换到它:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第18张图片

这是因为有个 remote.origin.fetch 的配置。

正常下载的项目的 fetch 配置是这样的:

88d40137aea72761445d091dba8f1fda.png

把 remote 的所有分支下载到本地的所有分支。

而 depth 1 下载的项目的 fetch 配置是这样的:

467abf3b226fe762e358ec8b3b999305.png

fetch 只会下载 main 分支。

就算你手动 fetch 了其他分支的代码也不会处理。

所以我们可以改下这个配置,我们先指定一个 0.3-stable 分支看看:

git config remote.origin.fetch "+refs/heads/0.3-stable:refs/remotes/origin/0.3-stable"
加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第19张图片

可以看到 pull 的时候就拉取到新分支了,而且 branch -r 可以看到这个分支了。

这里 pull 或者 fetch 都行。pull 就相当于 git fetch + git merge,把代码下载下来,然后 merge 到本地。fetch 是 pull 的第一步:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第20张图片

接下来再改为 * 试试:

git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"

再执行 git fetch 或者 git pull,就会拉取全部分支的 commit:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第21张图片

这时候就可以切换到这些分支了:

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第22张图片 6f490907d5b3b54d0fc7363aef9dd712.png

这样就解决了 --depth 1 的第二个问题。

总结

当 git clone 下载大项目的时候,加个 --depth 1 可以提速几十倍。

下载下来的项目也可以正常的 pull 和 push。

这是因为 git 是通过 commit、tree、blob 的对象存储的,每个 commit 是关联这些对象的入口。

depth 1 只会下载最后一个 commit 关联的 object,下载内容更少,所以速度快很多。

但这种方式有两个问题:

  • 切换不到历史 commit

  • 切换不到别的分支

没有历史 commit 可以通过 git pull --unshallow 解决。

切不到别的分支是因为 fetch 配置导致的,配置成 +refs/heads/*:refs/remotes/origin/* 也就可以了,也就是拉取远程所有分支代码到本地。

这样再 fetch 和 pull 就会拉取所有分支的新 commit,也可以正常的切分支。

--depth 1 在下载大项目的时候,或者 build 时下载代码的时候,都很有意义。它提高下载速度导致的俩后遗症也都可以解决。

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

加速几十倍 git clone 速度的 --depth 1,它的后遗症怎么解决?_第23张图片

你可能感兴趣的:(git,github)