如何在Git历史中替换单词并正确调试相关问题?

4

我正在尝试从我的Git历史记录中删除敏感数据,如密码。我不想删除整个文件,只想用removedSensitiveInfo替换密码。在浏览了众多StackOverflow主题和其他网站后,我想到了这个方法。

git filter-branch --tree-filter "find . -type f -exec sed -Ei '' -e 's/(aSecretPassword1|aSecretPassword2|aSecretPassword3)/removedSensitiveInfo/g' {} \;"

当我运行这个命令时,它似乎正在重写历史记录(它显示正在重写的提交并需要几分钟时间)。然而,当我检查是否确实删除了所有敏感数据时,结果发现它仍然存在。
供参考,以下是我的检查方式:
git grep aSecretPassword1 $(git rev-list --all)

这段文字是关于Git技术的。它描述了一个问题:在使用正则表达式和sed命令替换文件中的单词时,没有得到预期的结果。用户想知道如何调试这个问题,以及可能出现的其他问题。用户已经搜索过相关主题的StackOverflow帖子,但没有找到解决方案。

你能不能简单地从代码库中删除所有与密码相关的引用并更改密码?换句话说,你首先应该解决在代码库中存在这样的密码的问题。如果你将它们从代码库中移除,然后更改密码,那么旧密码仍然存在于历史记录中,你也不需要担心了。 - Mike Brant
理论上我是可以的,但在我的情况下不太实际。 - Marc
2个回答

12

git-filter-branch是一个功能强大但难以使用的工具 - 你需要知道几个晦涩的事情才能正确地使用它完成你的任务,每一个都可能导致你看到的问题。因此,与其立即尝试调试它们,不如退一步看看原始问题:

  • 替换所有文本文件中给定的字符串(例如密码)(无需指定特定的文件/文件类型)
  • 确保更新后的Git历史记录不包含旧密码文本
  • 尽可能简单地完成以上操作

这个问题有一个量身定制的解决方案:

使用BFG...而不是git-filter-branch

BFG Repo-Cleaner是一个更简单的选择,专门设计用于从Git存储库历史记录中删除密码和其他不需要的数据。

BFG在以下方面帮助您解决这个问题:

  • BFG速度快10-720倍faster
  • 它可以自动运行在所有标签和引用上,不像git-filter-branch - 只有当你添加了非凡的--tag-name-filter cat -- --all命令行选项时才会这样做(请注意,您在问题中给出的示例命令没有此选项可能是问题的原因之一
  • BFG 不会生成任何refs/original/引用 - 因此您无需执行额外的步骤来删除它们
  • 您可以将密码表示为简单的字面字符串,而不必担心正则表达式转义正确性。如果确实需要,BFG也可以处理正则表达式。

使用BFG

请仔细按照使用步骤执行 - 核心部分只是this命令:

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

replacements.txt文件应该包含您想要进行的所有替换,格式如下(每行一个条目 - 请注意不应包括注释):

PASSWORD1 # Replace literal string 'PASSWORD1' with '***REMOVED***' (default)
PASSWORD2==>examplePass         # replace with 'examplePass' instead
PASSWORD3==>                    # replace with the empty string
regex:password=\w+==>password=  # Replace, using a regex

您的整个代码库历史记录将被扫描,所有小于1MB的文本文件都将执行替换操作:任何匹配的字符串(不在您的最新提交中)都将被替换。
完全透明:我是BFG Repo-Cleaner的作者。

2
不错 - 运行得很好... 而且,我从来没有见过一个开源项目在运行时印有政治信息。 - Joe J
嘿@Roberto,请帮忙!在我完成了替换git repo历史记录中密码的步骤(它们不再存在于当前版本 - 在上游/分支中只存在于历史记录中),并创建PR之后 - 我看到很多无关的更改在PR中 - 它试图更新其他文件(这些文件没有密码)。这是预期的吗?还是我漏掉了什么(我不想将此PR与如此多的更改合并)。它应该只更新所需的文件(其中密码已替换为REMOVED,我遵循了https://rtyley.github.io/bfg-repo-cleaner/的指示。 - lowLatency
@Roberto - 只想添加更多细节,$ bfg --replace-text passwords.txt my-repo.git 后结果看起来很好,因为它只显示了那些有密码的文件。但是在 git push 和创建 PR 后,它显示了很多其他文件,包括 Readme.md 文件。我正在尝试做一个快速 POC,在对所有密码和几个其他存储库进行操作之前,期待得到帮助! - lowLatency

2

看起来不错。请记住,filter-branch会保留原始提交记录,存储在refs/original/下面,例如:

$ git commit -m 'add secret password, oops!'
[master edaf467] add secret password, oops!
 1 file changed, 4 insertions(+)
 create mode 100644 secret
$ git filter-branch --tree-filter "find . -type f -exec sed -Ei '' -e 's/(aSecretPassword1|aSecretPassword2|aSecretPassword3)/removedSensitiveInfo/g' {} \;"
Rewrite edaf467960ade97ea03162ec89f11cae7c256e3d (2/2)
Ref 'refs/heads/master' was rewritten

然后:

$ git grep aSecretPassword `git rev-list --all`
edaf467960ade97ea03162ec89f11cae7c256e3d:secret:aSecretPassword2

但是:

$ git lola
* e530e69 (HEAD, master) add secret password, oops!
| * edaf467 (refs/original/refs/heads/master) add secret password, oops!
|/  
* 7624023 Initial

(git lola是我为git log --graph --oneline --decorate --all设置的别名)。是的,它在其中,但在refs/original命名空间下。清除它:

$ rm -rf .git/refs/original
$ git reflog expire --expire=now --all
$ git gc
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), done.
Total 6 (delta 0), reused 0 (delta 0)

然后:

$ git grep aSecretPassword `git rev-list --all`
$ 

一如既往,先在仓库的副本上运行filter-branch,以防万一;然后删除原始引用,将reflog设置为“now”,并进行垃圾回收,这意味着内容真正被删除了。


我完全按照您的指示操作,但问题仍然存在。有什么建议可以帮助我以一种能够找出错误所在的方式进行调试吗?我开始感觉正则表达式匹配不正确,尽管它非常基础。 - Marc
您可能还有标签或其他引用挂在“预过滤”提交上。查找git grep找到哪些提交,看看哪些引用会导致它们。您也可以检查有问题的修订(甚至在使用filter-branch之前)并尝试手动find ...以确保sed正在对相关文件执行您希望执行的操作。 - torek
是的,关于存在其他标签/引用,你可能是正确的。我以为这些命令也会过滤它们,但显然我错了。最终我选择了Roberto的BFG解决方案,它完美地解决了问题。 - Marc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接