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

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

a2f1f9a3abc46988d7151d5b491ce2e3.gif

要等很久。

当然,还有更慢的项目。

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

git clone --depth 1 https://github.com/facebook/react
d0e7df19ac2a6b81dcb25e6ec3b5f358.gif

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

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

9a45be7b3e7d7ec0e28b8bde581d100f.png dd6fc289711788e6e2ec96ab7a66f92f.png

那这样代码还是全的么?

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

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

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

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

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

  • glob 对象存储文件内容

  • tree 对象存储文件路径

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

b8a6e9a533d9b24444c904c6b8eff877.png

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

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

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

7a0a7355c35399f94f8f2cb56ad34a73.png

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

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

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

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

83aff53d73f916a9c0016f7fdaa22857.png

你可以 git reset 切到任意 commit:

比如:

git reset --hard 4dda96a40
4d441ddccd41061f8e720bdf08064302.png

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

75da88e781c36d8da78d6fb0e20b9565.png 12c17dd58d03e067e2ed7ea7e1779509.png
image.png

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

48cd9406366ff61543657b289513d5ce.png

就很尴尬。

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

d35138c2994001a2ab2e854f5de97456.png

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

1be86bdb9e080be486ba74603c212e23.png

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

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

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

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

正常项目是这样的:

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

b972e288379d2d8b6c1ec50675856fe3.png

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

5ca66754939a329804a52660d5f805e4.png

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

ea794dde3872b87549912c318288edc0.png

只有一个 main。

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

太天真了。

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

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

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

c012a7199932a5245916ccf08e9a8b9d.png

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

02125ca1033d13b9dfc206814202a060.png

这是因为有个 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"
7e4e5acae0937e9351ebb71b4ddb59c8.png

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

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

61fd9580be9b1c11e61551e8d4d4ef48.png

接下来再改为 * 试试:

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

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

8f8f4f056680aa8ec9211d4767be1c5f.png

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

bd21bed4fe5e9d3c14eda3074b87f38d.png 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 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

869755f2825db3b961f0a1328286dba0.png

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值