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

时间:2009-05-16 14:49:18

标签: git git-commit git-filter-branch git-rewrite-history

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

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

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

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

10 个答案:

答案 0 :(得分:394)

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


有了这个,这是如何解决它。 GitHub answered exactly that question as an FAQ

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中“从上游重新恢复”中的说明进行操作。


将来,如果您不小心使用敏感信息提交了一些更改,但是您注意之前推送到远程存储库,则可以使用一些更简单的修复程序。如果您上次提交是添加敏感信息的人,则只需删除敏感信息,然后运行:

git commit -a --amend

这将使用您所做的任何新更改修改先前的提交,包括使用git rm完成的整个文件删除。如果更改在历史记录中进一步返回但仍未推送到远程存储库,则可以执行交互式rebase:

git rebase -i origin/master

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

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

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

答案 1 :(得分:76)

更改密码是一个不错的主意,但是对于从您的repo历史记录中删除密码的过程,我建议使用BFG Repo-Cleaner,这是git-filter-branch的一种更快,更简单的替代方法,专门用于删除私人数据Git回购。

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

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

将扫描您的仓库历史记录中阈值大小(默认为1MB)的所有文件,并且任何匹配的字符串(不在最新提交中)将替换为字符串“ *** *** REMOVED”。然后,您可以使用git gc清除死数据:

$ git gc --prune=now --aggressive

BFG通常比运行git-filter-branch快10-50倍,并且针对这两种常见用例简化和定制选项:

  • 删除疯狂大文件
  • 删除密码,凭据&amp;其他私人数据

完全披露:我是BFG Repo-Cleaner的作者。

答案 2 :(得分:18)

我推荐David Underhill的this script,对我来说就像一个魅力。

除了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, e.g., 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

答案 3 :(得分:14)

如果你推到GitHub,即使你在一秒钟之后强行将它推开也为时已晚

为了测试这一点,我创建了一个回购: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消失并提供4​​04,例如https://api.github.com/repos/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3b2b2d653f7a3824即使您重新创建另一个具有相同名称的存储库,这也会有效。

所以我推荐的行动方案是:

  • 更改您的凭据

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

    • 您是否拥有有价值的问题数据?

      • 否:删除存储库
      • 是:联系支持

答案 4 :(得分:9)

要明确:接受的答案是正确的。先试试吧。但是,对于某些用例,它可能会不必要地复杂,特别是如果您遇到令人讨厌的错误,例如“致命错误:错误修订 - 出现空白”,或者真的不关心您的回购历史。

另一种选择是:

  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/
  6. 这当然会删除所有提交历史记录分支,以及来自github仓库和本地git仓库的问题。如果这是不可接受的,您将不得不使用替代方法。

    称之为核选项。

答案 5 :(得分:6)

使用 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

答案 6 :(得分:5)

您可以使用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,标签等中的所有提交中消失

我时不时地遇到同样的问题,每次我都要回到这篇文章和其他人那里,这就是为什么我要自动化这个过程。

来自Stack Overflow的贡献者的信用,这让我可以把它放在一起

答案 7 :(得分:3)

所以,它看起来像这样:

git rm --cached /config/deploy.rb
echo /config/deploy.rb >> .gitignore
  

从git中删除跟踪文件的缓存,并将该文件添加到.gitignore列表

答案 8 :(得分:3)

到目前为止,我已经做过几次这样的事了。请注意,这一次仅适用于1个文件。

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

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

  2. 要从历史记录中删除文件,请使用第一个提交sha1和上一个命令中的文件路径,并将它们填入此命令:

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

答案 9 :(得分:1)

在我的Android项目中,我在 app / src / main / res / values / 文件夹中将 admob_keys.xml 作为单独的xml文件。为了删除此敏感文件,我使用了以下脚本并使其正常工作。

git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch  app/src/main/res/values/admob_keys.xml' \
--prune-empty --tag-name-filter cat -- --all