ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

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


B
Black

出于所有实际目的,您首先应该担心的是更改密码!从您的问题中不清楚您的 git 存储库是完全本地的还是您在其他地方是否有远程存储库;如果它是远程的并且不受其他人的保护,那么您就有问题了。如果有人在您修复此问题之前克隆了该存储库,他们将在他们的本地计算机上拥有您的密码副本,并且您无法强制他们更新到您的“修复”版本,因为它已从历史记录中消失。您可以做的唯一安全的事情是在您使用过的任何地方将密码更改为其他密码。

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

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

git filter-branch --index-filter \
'git update-index --remove PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' <introduction-revision-sha1>..HEAD
git push --force --verbose --dry-run
git push --force

2019 年更新:

这是常见问题解答中的当前代码:

  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
  git push --force --verbose --dry-run
  git push --force

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

要解决此问题,他们必须删除现有的存储库并重新克隆它,或者按照 git-rebase manpage 中“从上游重新定位中恢复”下的说明进行操作。

提示:执行 git rebase --interactive

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

git commit -a --amend

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

git rebase -i origin/master

这将打开一个编辑器,其中包含自您与远程存储库的最后一个共同祖先以来所做的提交。在代表带有敏感信息的提交的任何行上将“pick”更改为“edit”,然后保存并退出。 Git 将遍历更改,并将您留在一个可以执行以下操作的位置:

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

对于包含敏感信息的每个更改。最终,您将回到您的分支,并且可以安全地推送新的更改。


[git filter-branch --index-filter 'git update-index --remove filename' ..HEAD] 运行这个并没有重写提交历史,在运行 'git log' 时仍然提交历史存在。有什么 spl 需要检查的吗?
得到这个工作。我迷失在翻译中。我在这里使用了链接而不是命令。此外,Windows 命令最终需要双引号作为 ripper234 提及,完整路径,如 MigDus 建议的那样,并且不包括链接粘贴为换行指示符的“\”字符。最终命令类似于: git filter-branch --force --index-filter "git rm --cached --ignore-unmatch src[Project][File].[ext]" --prune-empty --tag-名称过滤猫--全部
您的 filter-branch 代码与您链接到的 github 页面中的代码之间似乎存在一些实质性差异。例如他们的第 3 行 --prune-empty --tag-name-filter cat -- --all。解决方案是否已更改或我遗漏了什么?
这个解决方案看起来相当不错,但是如果我在初始提交中引入了要删除的文件 <introduction-revision-sha1>..HEAD 将不起作用。它只会从第二次提交开始删除文件。 (如何将初始提交包含在提交范围中?)这里指出了保存方式:help.github.com/articles/… 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
我得到fatal: refusing to merge unrelated histories
C
Community

更改密码是个好主意,但对于从存储库历史记录中删除密码的过程,我建议使用 BFG Repo-Cleaner,它是一种更快、更简单的替代 git-filter-branch,专门用于从 Git 存储库中删除私有数据。

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

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

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

$ git gc --prune=now --aggressive

BFG 通常比运行 git-filter-branch 快 10-50 倍,并且围绕以下两个常见用例简化和定制选项:

删除疯狂的大文件

删除密码、凭证和其他私人数据

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


@Henridv我不确定natacado接受的答案在这方面与我自己的答案有何不同?我们的两个答案都专门针对问题的关键句子:“有没有办法删除 Git 历史记录中特定文件的所有痕迹?” - 即他们谈论 Git 历史重写。 /how/ NixNinja /should/ 为他的应用程序提供密码的问题在他的问题或任何当前答案中都没有提到。碰巧的是,BFG 专门解决了意外后果的问题,请参阅 rtyley.github.com/bfg-repo-cleaner/#protected-commits
这是一个巨大的胜利。经过几次尝试,我能够使用它非常彻底地从私人仓库中剥离包含敏感信息的提交,并用修改后的历史记录强制更新远程仓库。附带说明的是,你必须确保你的 repo (HEAD) 的提示本身是干净的,没有敏感数据,因为这个提交被认为是“受保护的”并且不会被这个工具修改。如果不是,只需手动清理/更换并git commit。否则,为开发人员工具箱中的新工具 +1 :)
@Henridv 根据我最近的评论,假设您的应用程序当前位于分支的尖端或头部(即最新提交),它不应该像您预期的那样破坏您的应用程序。此工具将在遍历和修改其余提交历史记录时明确报告您的最后一次提交 These are your protected commits, and so their contents will NOT be altered。但是,如果您需要回滚,那么是的,您只需在刚刚回滚到的提交中搜索 ***REMOVED***
BFG +1(如果您已安装 Java 或不介意安装它)。一个问题是,如果文件包含在 HEAD 中,BFG 拒绝删除该文件。所以最好先进行一次提交,删除所需的文件,然后再运行 BFG。之后,您可以恢复最后一次提交,现在它不会改变任何事情。
这实际上应该被接受为正确答案。照包装盒上说的做!
C
Ciro Santilli Путлер Капут 六四事

如果你推送到 GitHub,强制推送是不够的,删除仓库或者联系支持

即使你在之后强制推一秒钟,也不够,如下所述。

唯一有效的行动方案是:

是什么泄露了像密码一样的可更改凭证?是的:立即修改您的密码,并考虑使用更多的 OAuth 和 API 密钥!不(裸照):您是否关心存储库中的所有问题是否都被核爆?否:删除存储库 是:如果泄漏对您非常重要,请联系支持人员,您愿意让存储库停机以降低泄漏的可能性,在您等待 GitHub 支持回复时将其设为私有你

是的:立即修改您的密码,并考虑使用更多的 OAuth 和 API 密钥!

不(裸照):您是否关心存储库中的所有问题是否都被核爆?否:删除存储库 是:如果泄漏对您非常重要,请联系支持人员,您愿意让存储库停机以降低泄漏的可能性,在您等待 GitHub 支持回复时将其设为私有你

您是否关心存储库中的所有问题是否都被核爆?否:删除存储库 是:如果泄漏对您非常重要,请联系支持人员,您愿意让存储库停机以降低泄漏的可能性,在您等待 GitHub 支持回复时将其设为私有你

否:删除存储库

是的:如果泄漏对您非常重要,请联系支持人员,您愿意让存储库停机以降低泄漏的可能性,在您等待 GitHub 支持回复您时将其设为私有

联系支持

如果泄漏对您非常重要,以至于您愿意让存储库停机以降低泄漏的可能性,请在等待 GitHub 支持回复您时将其设为私有

一秒钟后强制推动是不够的,因为:

GitHub 长期保持悬空提交。但是,如果您联系 GitHub 工作人员,他们确实有权删除此类悬空提交。当我将所有 GitHub 提交电子邮件上传到一个仓库时,我亲身体验了这一点,他们要求我将其删除,所以我照做了,他们做了 gc。但是,必须删除包含数据的拉取请求:因此,在初始删除后的一年内,该 repo 数据仍然可以访问。悬空提交可以通过以下方式查看: 提交 Web UI:https://github.com/cirosantilli/test-dangling/commit/53df36c09f092bbb59f2faa34eba15cd89ef8e83(Wayback machine) API:https://api.github.com/repos/cirosantilli /test-dangling/commits/53df36c09f092bbb59f2faa34eba15cd89ef8e83 (Wayback machine) 在提交时获取源的一种便捷方法是使用下载 zip 方法,该方法可以接受任何参考,例如:https://github.com/cirosantilli/myrepo /存档/SHA.zip

提交网页界面:https://github.com/cirosantilli/test-dangling/commit/53df36c09f092bbb59f2faa34eba15cd89ef8e83(回程机)

API:https://api.github.com/repos/cirosantilli/test-dangling/commits/53df36c09f092bbb59f2faa34eba15cd89ef8e83(回程机)

可以通过以下方式获取丢失的 SHA:列出 API 事件类型”:“PushEvent”。例如我的:https://api.github.com/users/cirosantilli/events/public(Wayback machine)有时更方便,通过查看试图删除内容的拉取请求的 SHA

列出 API 事件类型”:“PushEvent”。例如我的:https://api.github.com/users/cirosantilli/events/public(Wayback 机器)

有时更方便的是,通过查看试图删除内容的拉取请求的 SHA

有像 http://ghtorrent.org/ 和 https://www.githubarchive.org/ 这样的抓取工具会定期汇集 GitHub 数据并将其存储在其他地方。我找不到他们是否抓取了实际的提交差异,这不太可能,因为会有太多数据,但在技术上是可行的,并且 NSA 和朋友可能有过滤器来仅存档与人或感兴趣的提交相关的内容。

但是,如果您删除存储库而不是仅仅强制推送,则提交确实会立即从 API 中消失并给出 404,例如 https://api.github.com/repos/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3b2b2d653f7a3824 即使您重新创建具有相同名称的另一个存储库,这也有效。

为了测试这一点,我创建了一个 repo: 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

另请参阅:How to remove a dangling commit from GitHub?

现在正式推荐使用 git filter-repo,而不是 git filter-branch

Git 2.5 本身的 git filter-branch 的联机帮助页中提到了这一点。

使用 git filter repo,您可以使用以下命令删除某些文件:Remove folder and its contents from git/GitHub's history

pip install git-filter-repo
git filter-repo --path path/to/remove1 --path path/to/remove2 --invert-paths

这会自动删除空提交。

或者您可以将某些字符串替换为:How to replace a string in a whole Git history?

git filter-repo --replace-text <(echo 'my_password==>xxxxxxxx')

如果存储库是分叉网络的一部分,则将存储库设为私有或删除它可能无济于事,并且可能会使问题变得更糟。 GitHub 上的分叉网络似乎共享一个内部裸存储库,因此一个分叉中的提交也可以通过其他分叉检索。将存储库设为私有或将其删除会导致从分叉网络中分裂,敏感提交现在在每个剩余的裸存储库中重复。在两个裸存储库上运行 GC 之前,这些提交将继续可以通过 fork 访问。
J
Jason Goemaat

我推荐 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

请注意,您对 expire 和 prune 的使用不正确,如果您未指定日期,则默认为所有 2 周以上的 prune 提交。你想要的是所有提交,所以这样做:git gc --aggressive --prune=now
@Adam Parkin我将保留答案中的代码,因为它来自David Underhill网站上的脚本,你可以在那里发表评论,如果他改变它我会改变这个答案,因为我真的不知道 git出色地。修剪之前的 expire 命令不会影响它吗?
@MarkusUnterwaditzer:那个不适用于推送提交。
也许您应该将所有命令都放在答案中;它会更加一致,并且不需要单独的帖子的心理组合:)
n
nachoparker

您可以使用 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 的贡献者,让我把这些放在一起


v
vertigo71

这是我在 Windows 中的解决方案

git filter-branch --tree-filter "rm -f 'filedir/filename'" HEAD git push --force

确保路径正确,否则将无法正常工作

我希望它有帮助


S
Stephen Rauch

使用过滤器分支:

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

l
lostphilosopher

需要明确的是:接受的答案是正确的。先试试看。但是,对于某些用例,它可能会变得不必要地复杂,特别是如果您遇到令人讨厌的错误,例如“fatal: bad revision --prune-empty”,或者真的不关心您的 repo 的历史。

另一种选择是:

cd 到项目的基础分支删除敏感代码/文件 rm -rf .git/ # 从代码中删除所有 git 信息转到 github 并删除您的存储库按照本指南将您的代码推送到新的存储库,就像您通常那样 - https: //help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/

这当然会从您的 github 存储库和本地 git 存储库中删除所有提交历史记录分支和问题。如果这是不可接受的,您将不得不使用另一种方法。

将此称为核选项。


E
Ercan

在我的 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

b
b01

迄今为止,我不得不这样做几次。请注意,这一次仅适用于 1 个文件。

获取修改文件的所有提交的列表。底部的将是第一个提交: git log --pretty=oneline --branches -- pathToFile 要从历史记录中删除文件,请使用第一个提交 sha1 和上一个命令中的文件路径,并将它们填充到这个命令: git filter-branch --index-filter 'git rm --cached --ignore-unmatch ' -- ..


p
przbadu

所以,它看起来像这样:

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

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