忽略已提交文件并从历史记录删除

在 Android 的一个项目里需要加载 gradle 的配置文件,文件中有一些安全等级较高的信息,不想把这个文件同步到 git 里,但是等我发现的时候它已经在版本库里了,心塞。我知道如果是还没有被 git 管理的文件可以在 .gitignore 文件里添加忽略规则来忽略一些不必要的文件。但是如果已经添加到了版本库的文件又该如何删除并且忽略呢。

这时候我假设已经知道了哪个文件是不应该被提交的,这里的不该提交是指始终不应该被提交,而不是暂时性的不提交(暂时性的不提交可以使用 git update-index --assume-unchanged 来暂停提交,使用 git update-index --no-assume-unchanged 来恢复提交)。就比如一些私密的配置文件,而 git update-index 的使用场景是某一段时间内无需提交,之后又需要恢复提交的文件,比如某个比较大暂时又没什么用的文件。

从暂存区删除

那么正确的做法是什么呢?

  • 首先需要从暂存区里删除目标文件,注意是暂存区,不是工作区,也就是:
    • git rm --cached filepath 一定要记得加 --cache
  • 更新 .gitignore 规则,忽略目标文件
    • 因为这个时候目标文件已经重新属于 untracked files 了。
  • 正常提交即可

从历史记录删除

到这里这个文件就不会再被提交到仓库了。但是如果你以为这样就万事大吉那就太天真了。之前使用 git rm --cached filepath 删除了暂存区的目标文件,但也只是删除了本次提交之前的暂存区中的目标文件。之前的提交记录中仍然存在这个危险的文件。别人只要翻翻你的提交记录就能轻易的找到这个目标文件。所以还需要从所有的提交中删除这个文件的历史记录。

  • git filter-branch –force –index-filter ‘git rm –cached –ignore-unmatch filepath’ –prune-empty –tag-name-filter cat – –all
    • 从所有的分支和提交中找出文件并删除
  • git push origin –force –all
    • 将本地仓库强制覆盖远程仓库
  • git push origin –force –tags
    • 从标记版本里移除目标文件
  • 一段时间以后你确定上面几部的操作没有带来任何非预期的影响,那么就删除这些操作的反向引用和产生的垃圾(git 1.8.5+)
    • git for-each-ref –format=’delete %(refname)’ refs/original | git update-ref –stdin
    • git reflog expire –expire=now –all
    • git gc –prune=now

参考:https://help.github.com/articles/remove-sensitive-data/

建议

  • 养成在项目开始的时候就设置 .gitignore 忽略规则的习惯
  • 最好在每次 commit 之前检查本次提交的文件是否涉及隐私、安全性或者不必要性