如何清洗 Git Repo 代码仓库

相信不少团队的代码仓库 Git Repo 变得越来越大。除了代码的提交外,时常有人会把二进制文件比如 Jar 包或者不小心把不改提交到代码库的文件提交到代码库中,比如用户名密码之类的保密信息。如何清洗代码仓库 Git Repo,彻底从历史中删除此类文件呢?

手动清理

如果你们的代码仓库问题比较少,只有几个不该提交的文件,可以参考 Atlassian 的一篇关于维护 Git Repo 的文章(见文章最后的链接)。

大致过程如下:

首先进行 Git 垃圾回收:

git gc --auto

其次查看 Git 仓库占用空间:

$ du -hs .git/objects
45M .git/objects

然后找出历史中超过一定大小的文件,最后在历史中删除并且提交。如果感兴趣手动处理这个过程可以参照文章后边的链接。

相关的几个命令:

清理历史中的文件:

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch ****/nohup.out' --prune-empty --tag-name-filter cat -- --all
git filter-branch --index-filter 'git rm --cached --ignore-unmatch ****/nohup.out' HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

强制提交覆盖:

git reflog expire --expire=now --all
git gc --prune=now
git push --all --force
git push --all --tags --force

但是这个方案有 2 个问题:1. 处理速度慢,尝试清理 2 G 大小的代码库,用了 1 晚上还没跑完。2. 只能按文件名清理,如果不同的路径有同样的文件名就无法处理了,可能误删文件或者忽略某些文件。当然有个非常好的解决方案完美解决了这个问题。

自动清理

答案就是 bfg-repo-cleaner,这是一个 Java 写的清理工具,多线程处理清理过程,命令很简单,只需要几分钟就清理了之前 1 晚上都跑不完的任务:

java -jar bfg-1.11.7.jar --delete-files *.zip myrepo.git
java -jar bfg-1.11.7.jar --delete-files *.log myrepo.git
java -jar bfg-1.11.7.jar --delete-files *.out myrepo.git
java -jar bfg-1.11.7.jar --strip-blobs-bigger-than 1M myrepo.git

附上几个常用的但又不常见的 git 小技巧:

复制代码仓库:

git clone --bare /var/www/html/myrepo.git

Git 后悔药,覆盖最后一次修改:

git add .
git commit --amend
git push origin master -f

Git 放弃本地修改:

git checkout .

Git 销毁最后一次提交:

git reset --hard HEAD^
git push -f origin HEAD^:master

打包时候嵌入版本号:

git rev-parse HEAD > version.txt

文章中涉及的链接:

  • https://confluence.atlassian.com/display/BITBUCKET/Maintaining+a+Git+Repository
  • http://rtyley.github.io/bfg-repo-cleaner/
  • http://www.d-wood.com/blog/2014/10/03_6965.html
  • 最近 3 个月变化很多,离开呆了 5 年多的北京,开始英国的工作和生活。到这之后基本在做系统向亚马逊云平台的迁移,踩了不少坑,收获也很多。由于系统的迁移涉及各个常见的架构组件,边边角角的细节很多。和大部分系统一样,长时间野蛮成长积累了很多问题。这样的老系统迁移到新平台意味着你需要处理所有之前埋下的问题。公司之前聘请了亚马逊推荐的第三方咨询服务工作在做迁移,但是由于问题太多,拖了很长时间没有完成。

    成熟老系统常见的问题:

    1. 缺乏文档

    这应该是大小公司都存在的问题。文档会极大降低开发效率,并且互联网项目的特点是易变和追求速度,详细文档不是很好的方案。这就要求方案和细节设计上的合理性和不要做 “精巧”方案。结构化设计,不要零散的组成,这样其他人即使没有文档也可以理解。

    2. 项目中临时方案太多

    导致后来看起来很别扭而且不容易理解;半截工程。系统中存在大量“精巧”的设计,导致后来者难以理解。这也告诉我们做设计的时候尽量简单通俗易懂,项目设计的可沟通性也是很重要的一方面。某位工程师说自己花了 1 周的时间才搞明白 Postfix 的收邮件并自动解析的过程是怎么运行的。

    3. 代码质量参差不齐

    代码质量问题每个大点的团队都没法保证,保持代码库的干净很重要。

    4. 繁杂的业务

    5. 代码的 BUG 和代码对环境的兼容性

    之前的系统使用配置文件做主从读写分离,配置文件由其他系统控制。但是配置文件确保留在代码库中,这意味着假如代码回滚或者 check 分支出错,配置文件会发生改变。不该发生的全会发生,这样的事情确实发生了。导致部分操作写入从库,从库与主库同步失败,典型的脑裂问题。最后只好花了很长时间重做从库的同步。这样的问题处理并不复杂,复杂的在于如何发现这个问题的原因。业务系统各种奇怪的表现,有时候很难想到问题的根源。

    迁移过程需要考虑的问题:

    1. 完善测试

    性能测试可以采取流量镜像复制,读操作有很多简单可靠的流量复制工具,有时候根本不需要一个高大上的流量复制系统。并且大部分系统都是读多写少,测试不是什么难题。

    功能性测试只能尽量做足,让熟悉系统的用户进行。

    2. 无缝迁移

    整个过程基本实现了平滑无缝迁移,系统的没有停止 1 分钟运行。由于项目的特点,比较少写操作,重点是读,暂停写操作后作将 HaProxy 后端逐步指向新集群,等全部流量导入新集群后修改 DNS 指向新集群。这里还涉及到 DNS TTL 从长变短再变长的修改过程。

    缓存预热很重要,尤其是数据库的预热,这就要求新集群流量导入逐步进行,防止对整站延迟的影响。

    3. 回退方案

    由于暂时停止写操作,即使流量导入到新集群后测试发现问题仍然可以指回旧集群。

    4. 改进还是保持原状

    由于架构组件的选择余地很大,之前的各个组件的配置是否合理需要很长时间 Review。这里就要权衡保持原状还是一次性做好优化。比较好的方案是如果不是 BUG 则保持原状,等系统完成迁移再进行改进。

    5. 性能的持续监控和对比测试

    性能监控工具已经非常成熟了,比如 AppNeta 和 New Relic , 基本可以把控各个组件的性能。在迁移之前也可以进行镜像流量复制对比测试新旧集群的性能。

    迁移带来的收益

    1. 重新设计的发布自动化

    业务代码、系统配置、云架构配置的分离,任何操作的版本化,可回退。

    2. 弹性扩展,总体成本的降低

    迁移到亚马逊的主要原因就是高低峰流量差异很大。迁移后低峰期可以节约 1 半的机器成本。

    3. 跨区域容灾,无单点故障

    实现了 Multi-AZ,任意单点故障不影响业务运行。Web 前端服务器可以随手关掉,数据库的升级,配置改动也无任何影响,当然这归功于 RDS Multi-AZ 功能。

    4. 运维难度的降低,无需运维

    系统会自动根据负载进行增减机器,所以无需担心压力大把机器打垮,单机器的各种故障也无需人工处理。

    关于亚马逊云平台的特点和各个组件以后还会有单独的文章详细解释。