背景介绍

在使用GitHub的过程中,假如某次提交代码时不小心将密码或SSH-key提交进了公共仓库。当然,希望这种事情永远也不会发生,但是如果真遇到了,该怎么办呢?

如果发现得及时,本地提交后还没有推送到GitHub远程仓库的话,这种情况还好处理,直接修改代码后通过git commit --amend即可。

但如果发现时已经推送到了GitHub远程仓库,或者已过了许久,后续有了很多新的commits,这种情况就会比较复杂了。

错误的方式是,直接在当前代码中去除敏感信息,然后再提交到代码仓库中。这样的做法只能在最新的代码中去除了敏感信息,在git历史记录中仍然保存着敏感信息。

当然,也可以选择直接将整个仓库删除了。不过,看着昔日精心提交的代码记录,实在是难以下手。

要是可以只删除敏感信息部分,而不影响到其它提交记录就好了。事实上,GIT的确支持这种操作。

处理方式

实现的方式有两种,一是通过git filter-branch命令,另一种是采用一款开源的工具,BFG Repo-Cleaner。前者是GIT官方的实现方法,后者是一款采用Scala编写的工具,号称比git filter-branch更简单、更快捷。

git filter-branch

先来看下git filter-branch这种方式。假设要在所有历史提交记录中删除文件PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA,那么就可以采用如下命令:

1
2
3
4
5
6
$ git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' \
--prune-empty --tag-name-filter cat -- --all

> Rewrite 48dc599c80e20527ed902928085e7861e6b3cbe6 (266/266)
> Ref 'refs/heads/master' was rewritten

正常情况下,通过执行上面的命令,就可以在所有历史提交记录中彻底删除指定文件。如果要进一步确定的话,可以在.git目录中进行全局搜索,确保已彻底清理干净。

然后,就可以通过如下命令将本地代码推送到GitHub上,并强制覆盖掉所有历史记录。

1
$ git push origin --force --all

可以看出,采用git filter-branch的操作命令十分复杂(复杂到我也不想理会每个参数的具体含义),这还只是选择粗暴地将整个文件进行删除的情况。如果不想删除文件,而是单独修改特定文件特定内容的话,操作会更加复杂,如有兴趣可查看git官方文档

BFG Repo-Cleaner

估计也是因为官方的git filter-branch太过复杂,于是Roberto Tyley开发了BFG Repo-Cleaner这款工具。该工具是专门针对移除历史记录的需求而产生的,这可以从其简介中看出来。

Removes large or troublesome blobs like git-filter-branch does, but faster. And written in Scala

使用BFG Repo-Cleaner之前,需要先下载BFG's jar(requires Java 7 or above)。

如果想实现前面例子中同样的功能,删除文件PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA,可以通过如下命令实现:

1
$ java -jar bfg.jar --delete-files PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA my-repo.git

如果不想删除文件,而是单独修改特定文件特定内容的话,就可以通过如下命令实现:

1
$ java -jar bfg.jar --replace-text replacements.txt my-repo.git

replacements.txt文件中,应包含所有需要替换的内容,格式如下(不包含注释内容):

1
2
3
4
5
PASSWORD1                       # Replace with '***REMOVED***' (default)
PASSWORD2==>examplePass         # replace with 'examplePass' instead
PASSWORD3==>                    # replace with the empty string
regex:password=\w+==>password=  # Replace, using a regex
regex:\r(\n)==>$1               # Replace Windows newlines with Unix newlines

通过执行上述命令,BFG Repo-Cleaner就会扫描代码仓库的所有历史提交记录,并按照replacements.txt文件中的映射进行替换操作。

通过对比可以看到,BFG Repo-Cleaner的确是更加简洁,这也是GitHub官方推荐的方式。不过,在BFG Repo-Cleaner的介绍文档中也说了,该工具的优势在于简单和快捷,从功能强大的角度来讲,它是比不上git-filter-branch的,有些操作也只能通过git-filter-branch完成。

如需了解BFG Repo-Cleaner的更多用法,可详细阅读其文档

写在末尾

不要问我为啥写了这么一篇博客,让我再哭一会儿。我也真心地希望大家永远不会用到这些工具。

小心驶得万年船,共勉!

阅读更多