记一次Git仓库瘦身

网上同类文章比较多,不过正好有个项目自己需要动手实践一把,记录之。纸上得来终觉浅,绝知此事要躬行。

问题背景

对于Pods目录是否应该纳入版本控制,CocoaPods官网并未给出明确结论。官方原文如下:

Whether or not you check in your Pods folder is up to you, as workflows vary from project to project. We recommend that you keep the Pods directory under source control, and don’t add it to your .gitignore. But ultimately this decision is up to you:

Benefits of checking in the Pods directory

  • After cloning the repo, the project can immediately build and run, even without having CocoaPods installed on the machine. There is no need to run pod install, and no Internet connection is necessary.
  • The Pod artifacts (code/libraries) are always available, even if the source of a Pod (e.g. GitHub) were to go down.
  • The Pod artifacts are guaranteed to be identical to those in the original installation after cloning the repo.

Benefits of ignoring the Pods directory

  • The source control repo will be smaller and take up less space.
  • As long as the sources (e.g. GitHub) for all Pods are available, CocoaPods is generally able to recreate the same installation. (Technically there is no guarantee that running pod install will fetch and recreate identical artifacts when not using a commit SHA in the Podfile. This is especially true when using zip files in the Podfile.)
  • There won’t be any conflicts to deal with when performing source control operations, such as merging branches with different Pod versions.

Whether or not you check in the Pods directory, the Podfile and Podfile.lock should always be kept under version control.

简单来说就是:

  • 官方建议将Pods目录纳入版本控制,不建议将其添加到.gitignore
  • Pods目录纳入版本控制的好处
    • 无需pod install(甚至无需安装CocoaPods)就能运行项目
    • 永远可用的Pod artifacts
    • 一致的Pod artifacts
  • Pods目录不纳入版本控制的好处
    • 较小的Git仓库
    • 无需处理Pod artifacts相关的代码冲突

值得一提的是,PodfilePodfile.lock必须纳入版本控制。

我们按官方建议将Pods目录纳入版本控制。早期Git仓库体积不是问题,两年后的今天仓库体积大小增加到3GB。

-w980

仓库体积过大带来一系列问题:

  • 影响CI性能(代码都拉不下来,C什么I?)
  • 切换git分支速度缓慢
  • 无法使用git branch作为Pod依赖

解决方案

GitLab官方建议优先考虑使用 git-filter-repo,其次才是 git filter-branchBFG。不过我们的场景其实特别简单,即删除Git仓库中的Pods目录以减少仓库大小,所以使用 bfg 完全就够了。

使用git filter-branch删除Pods目录:

1
2
3
4
5
git filter-branch --force --index-filter 
\ 'git rm -r --cached --ignore-unmatch <path-to-pods>'
\ --prune-empty --tag-name-filter cat -- --all

git reflog expire --expire=now --all && git gc --prune=now --aggressive

使用bgf删除Pods目录:

1
2
3
4
5
java -jar ~/Downloads/bfg-1.14.0.jar 
\ --delete-folders Pods --no-blob-protection
\ <path-to-repo>

git reflog expire --expire=now --all && git gc --prune=now --aggressive

实施过程

先说主要步骤。

  • 第一步,备份Git仓库
  • 第二步,删除大文件
  • 第三步,更新.gitignore
  • 第四步,推送Git仓库

备份Git仓库

有不同的方式来备份Git仓库。

一种方式是通过Gitlab的仓库导出功能来备份数据。这种方式的好处是导出的数据比较全,包括Issues、Merge Requests以及工程配置等

另一种方式是git clone --bare

我采用第二种方式,操作比较简单。

删除大文件

使用 bgf 删除 Pods 目录:

1
2
3
4
5
6
# 删除所有的Pods目录
java -jar ~/Downloads/bfg-1.14.0.jar
\ --delete-folders Pods --no-blob-protection
\ <path-to-repo>
# 修改git历史记录
git reflog expire --expire=now --all && git gc --prune=now --aggressive

上述操作完成后,可以发现两个变化:

  • 一个变化是原先的各个 Pods 目录没有了
  • 另一个变化是仓库大小大大减小。

git count-objects -vH查看瘦身前后的仓库大小。从原来的2.04G减少到现在的93M,优化效果非常明显。

更新.gitignore

为了防止 Pods 目录再次被提交到仓库。我们在 .gitignore 文件中添加新的配置项:

1
**/Pods/

我经常不太记得住 .gitignore 的写法。这里提供几个常见示例,备忘。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#注释           .gitignore的注释
*.txt 忽略所有 .txt 后缀的文件
!src.a 忽略除 src.a 外的其他文件
/todo 仅忽略项目根目录下的 todo 文件,不包括 src/todo
build/ 忽略 build/目录下的所有文件,过滤整个build文件夹;
doc/*.txt 忽略doc目录下所有 .txt 后缀的文件,但不包括doc子目录的 .txt 的文件

bin/: 忽略当前路径下的 bin 文件夹,该文件夹下的所有内容都会被忽略,不忽略 bin 文件
/bin: 忽略根目录下的 bin 文件
/*.c: 忽略 cat.c,不忽略 build/cat.c
debug/*.obj: 忽略debug/io.obj,不忽略 debug/common/io.obj和tools/debug/io.obj
**/foo: 忽略/foo, a/foo, a/b/foo等
a/**/b: 忽略a/b, a/x/b, a/x/y/b等
!/bin/run.sh 不忽略bin目录下的run.sh文件
*.log: 忽略所有 .log 文件
config.js: 忽略当前路径的 config.js 文件

/mtk/ 忽略整个文件夹
*.zip 忽略所有.zip文件
/mtk/do.c 忽略某个具体文件

推送Git仓库

将修改历史记录后的Git库推送到原仓库还是比较麻烦的。我们直接新建一个空仓库,然后推送到新仓库:

1
git push --mirror <新仓库>

优化效果

不得不说瘦身后的仓库用起来清爽多了!

问题讨论

参考