从Git历史logging中删除敏感文件及其提交

我想在GitHub上放一个Git项目,但是它包含了某些带有敏感数据的文件(用户名和密码,例如capistrano的/config/deploy.rb)。

我知道我可以将这些文件名添加到.gitignore ,但是这不会在Git中删除它们的历史logging。

我也不想重新开始删除/.git目录。

有没有办法删除您的Git历史logging中的特定文件的所有痕迹?

出于所有实际的目的,你应该担心的第一件事是改变你的密码! 从你的问题不清楚你的git仓库是完全本地的还是你在其他地方有一个远程仓库, 如果它是远程的,而不是从别人的安全,你有一个问题。 如果有人在解决这个问题之前已经克隆了这个存储库,那么他们将在本地机器上拥有一个你的密码的副本,并且没有办法强制他们更新到你的“固定”版本,而不是从历史logging中删除。 你可以做的唯一安全的事情就是把你的密码更改为你使用过的任何地方。


有了这个,这里是如何解决它。 GitHub正是将这个问题作为一个常见问题回答 :

注意Windows用户 :在此命令中使用双引号(“)而不是单数

git filter-branch --index-filter \ 'git update-index --remove filename' <introduction-revision-sha1>..HEAD git push --force --verbose --dry-run git push --force 

请记住,一旦将这些代码推送到像GitHub这样的远程存储库,而其他人已经克隆了这个远程存储库,那么现在就是在重写历史的情况下。 当其他人尝试在此之后拉下最新的更改时,他们会收到一条消息,指出这些更改无法应用,因为这不是快进。

为了解决这个问题,他们必须删除他们现有的版本库并重新克隆它,或者按照git-rebase manpage中的“RECOVERING FROM UPSTREAM REBASE”中的指示进行操作。


在将来,如果您意外地对敏感信息进行了某些更改,但是推送到远程存储库之前您已经注意到,则有一些更简单的修复方法。 如果您最后一次提交是添加敏感信息的提交,您可以简单地删除敏感信息,然后运行:

 git commit -a --amend 

这将修改你以前做过的任何新的修改,包括用git rm完成的整个文件删除。 如果更改进一步回溯到历史logging中,但仍未推送到远程存储库,则可以执行交互式重定位:

 git rebase -i origin/master 

这将打开一个编辑器,其中包含自从上一个远程存储库的共同祖先以来所做的提交。 在代表提交敏感信息的任何行上将“pick”更改为“edit”,然后保存并退出。 Git将会通过这些变化,将您留在一个地方,您可以:

 $EDITOR file-to-fix git commit -a --amend git rebase --continue 

对于每个敏感信息的更改。 最终,你会最终回到你的分支,你可以安全地推动新的变化。

更改你的密码是一个好主意,但是为了从你的repo的历史logging中删除密码,我build议使用BFG Repo-Cleaner ,这是一个更快,更简单的git-filter-branch替代scheme,专门用于从Git仓库删除私人数据。

创build一个private.txt文件,列出要删除的密码等(每行一个条目),然后运行以下命令:

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

扫描仓库历史logging中所有超过阈值大小(默认为1MB)的文件,并将任何匹配的string(不在最近的提交中)replace为string“*** REMOVED ***”。 然后你可以使用git gc清理死亡数据:

 $ git gc --prune=now --aggressive 

BFG的运行速度通常比运行git-filter-branch快10-50倍,并且这些选项被简化和定制在这两个常见的用例中:

  • 删除疯狂的大文件
  • 删除密码,证件和其他私人数据

充分披露:我是BFG Repo-Cleaner的作者。

我推荐David Underhill的这个剧本 ,对我来说就像一个魅力。

除了natacado的filter-branch之外,它还增加了这些命令来清除掉它留下的混乱:

 rm -rf .git/refs/original/ git reflog expire --all git gc --aggressive --prune 

完整的脚本(所有功劳都归功于David Underhill)

 #!/bin/bash set -o errexit # Author: David Underhill # Script to permanently delete files/folders from your git repository. To use # it, cd to your repository's root and then run the script with a list of paths # you want to delete, eg, git-delete-history path1 path2 if [ $# -eq 0 ]; then exit 0 fi # make sure we're at the root of git repo if [ ! -d .git ]; then echo "Error: must run this script from the root of a git repository" exit 1 fi # remove all paths passed as arguments from the history of the repo files=$@ git filter-branch --index-filter \ "git rm -rf --cached --ignore-unmatch $files" HEAD # remove the temporary history git-filter-branch # otherwise leaves behind for a long time rm -rf .git/refs/original/ && \ git reflog expire --all && \ git gc --aggressive --prune 

如果更改为以下内容,最后两个命令可能会更好:

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

要清楚:接受的答案是正确的。 先试一试。 但是,对于某些使用情况来说,这可能不必要的复杂,特别是在遇到“致命的:错误的修改 – 空的”或者实际上并不关心回购的历史等令人讨厌的错误时。

另一种select是:

  1. cd到项目的基地分支
  2. 删除敏感的代码/文件
  3. rm -rf .git /#从代码中删除所有的git信息
  4. 转到github并删除您的存储库
  5. 按照本指南将代码推送到一个新的存储库,正如您通常会 – https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/

这当然会删除所有提交历史分支,并从您的github回购和您当地的git回购问题。 如果这是不可接受的,你将不得不使用另一种方法。

把这称为核选项。

如果您已经推送到GitHub,即使您在一秒钟之后强行推送数据,数据也会受到影响

  • GitHub保持长时间的悬挂提交。

    目前还不清楚是否永远,也不知道如果他们有一个标准的手动程序,可以通过联系支持。 请参阅: 如何从GitHub中删除一个悬而未决的提交?

    悬挂的承诺可以通过以下方式看到:

    • 提交Web UI: https : //github.com/cirosantilli/test-dangling/commit/53df36c09f092bbb59f2faa34eba15cd89ef8e83 ( Wayback机器 )
    • API: https : //api.github.com/repos/cirosantilli/test-dangling/commits/53df36c09f092bbb59f2faa34eba15cd89ef8e83 ( Wayback机器 )
  • 你所做的每一个提交事件的API都显示为type": "PushEvent" ,所以可以通过这里获得它的SHA。例如我的: https type": "PushEvent" Wayback机器 )

  • 有像http://ghtorrent.org/和https://www.githubarchive.org/这样的抓手,定期汇集GitHub数据并将其存储在别处&#x3002;

    我无法find他们是否真的提交差异,但这在技术上是可行的。

为了testing这个,我创build了一个回购: https : //github.com/cirosantilli/test-dangling并完成:

 git init git remote add origin git@github.com:cirosantilli/test-dangling.git touch a git add . git commit -m 0 git push touch b git add . git commit -m 1 git push touch c git rm b git add . git commit --amend --no-edit git push -f 

如果你删除版本库 ,提交立即从API中消失,并给404,例如https://api.github.com/repos/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3b2b2d653f7a3824即使你重新创build另一个存储库具有相同的名称&#x3002;

所以我build议的做法是:

  • 改变你的凭据

  • 如果这还不够(例如裸照):

    • 删除存储库
    • 联系支持

这是我的解决scheme在Windows中

git filter-branch –tree-filter“rm -f'filedir / filename'”HEAD

git push –force

确保path是正确的,否则将无法正常工作

我希望它有帮助

你可以使用git forget-blob

这个用法很简单,就是git forget-blob file-to-forget 。 你可以在这里获得更多的信息

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

它会从你的历史中的所有提交中消失,reflog,标签等等

我偶尔遇到同样的问题,每次我都要回到这个post和其他人,这就是为什么我会自动化这个过程。

积分给Stack Overflow的贡献者,让我把它放在一起

我不得不这样做几次到目前为止。 请注意,这只能在一个文件一次。

  1. 获取修改文件的所有提交列表。 底部的那个将是第一次提交:

    git log --pretty=oneline --branches -- pathToFile

  2. 要从历史logging中删除文件,请使用前一个命令的第一个提交sha1和文件path,并将其填充到以下命令中:

    git filter-branch --index-filter 'git rm --cached --ignore-unmatch <path-to-file>' -- <sha1-where-the-file-was-first-added>..

使用filter-branch

 git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *file_path_relative_to_git_repo*' --prune-empty --tag-name-filter cat -- --all git push origin *branch_name* -f 

我想你正在寻找这个:

  1. 使用git rm --cached命令从git中删除你的caching历史logging。 这个命令会把你的文件保存在本地目录下,但是从你的仓库中删除文件。

所以,看起来像这样:

 git rm --cached /config/deploy.rb 

现在,在你的.gitignore文件中添加/config/deploy.rb 。 而你的文件不会被git跟踪。