45

我一直使用基于接口的 git 客户端(smartGit),因此对 git 控制台没有太多经验。

但是,我现在需要在历史记录中的所有 .txt 文件中替换一个字符串(因此,不是擦除整个文件,而只是替换一个字符串)。我找到了以下命令:

git filter-branch --tree-filter 'git ls-files -z "*.php" |xargs -0 perl -p -i -e "s#(PASSWORD1|PASSWORD2|PASSWORD3)#xXxXxXxXxXx#g"' -- --all

我试过这个,不幸的是注意到虽然密码确实被改变了,但所有的二进制文件都被破坏了。图像等都会被破坏。

有没有更好的方法来做到这一点,不会损坏我的二进制文件?

谢谢。

编辑:

我和某事混在一起了。导致二进制文件损坏的实际代码是:

$ git filter-branch --tree-filter "find . -type f -exec sed -i -e 's/originalpassword/newpassword/g' {} \;"

奇怪的是,顶部的代码实际上用我的密码删除了所有文件。

4

6 回答 6

110

我建议使用BFG Repo-Cleaner,这是一种更简单、更快的替代方案,git-filter-branch专门用于从 Git 历史中重写文件。

您应该在这里仔细按照以下步骤操作:https ://rtyley.github.io/bfg-repo-cleaner/#usage - 但核心位是这样的:下载BFG 的 jar(需要 Java 7 或更高版本)并运行此命令:

$ java -jar bfg.jar  --replace-text replacements.txt -fi *.php  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
regex:\r(\n)==>$1               # Replace Windows newlines with Unix newlines

将扫描您的整个存储库历史记录,并且.php文件(大小小于 1MB)将执行替换:任何匹配的字符串(不在您最近的提交中)都将被替换。

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

于 2013-03-31T14:42:07.760 回答
41

您可以通过传递-name "pattern"find.

这对我有用:

git filter-branch --tree-filter "find . -name '*.php' -exec sed -i -e \
    's/originalpassword/newpassword/g' {} \;"
于 2010-11-06T17:04:27.913 回答
13

对于 Git 2.24(2019 年第四季度),git filter-branch(和 BFG)已弃用

newren/git-filter-repo不做你想做的事
它的示例部分有一个几乎是您想要的示例

cd repo
git filter-repo --path-glob '*.txt' --replace-text expressions.txt

expressions.txt

literal:originalpassword==>newpassword

但是,警告:正如Hasturkun评论中添加的那样

使用--path-glob(或--path只保留符合这些规范的git filter-branch文件。bfg-ish as或script 中提供了仅替换特定文件中的文本的功能。 否则,看起来这目前只能通过自定义提交回调实现。 见第 74 期
-filint-history

newren/git-filter-repo

这是有道理的,考虑到该--replace-text选项本身就是一个blob 回调

于 2019-10-05T20:40:13.647 回答
6

我在 /usr/local/git/findsed.sh 创建了一个文件,内容如下:

find . -name 'githubDirToSubmodule.sh' -exec sed -i '' -e 's/What I want to remove//g' {} \;

我运行了命令:

git filter-branch --tree-filter "sh /usr/local/git/findsed.sh"

命令说明

当您运行 git filter-branch 时,它会逐个检查您曾经提交的每个修订版。--tree-filter 在每个提交的修订版上运行 findsed.sh 脚本,保存它,然后进入下一个修订版。

find 命令查找特定文件或文件集并在该文件上执行 (-exec) sed 编辑器。sed 是一个命令,它在 s/ 之后使用正则表达式并将其替换为 / 和 /g 之间的字符串(在我的示例中为空白)。{} 是对 find 命令给出的文件路径的引用。文件路径被提供给 sed,因此 sed 知道要处理什么。\; 刚刚结束 -exec 命令。

将 shell 脚本和命令分成单独的部分可以减少引号 '' 或 "" 的复杂性。

特点

我在 Mac 上成功实现了这一点,显然 sed 是 Mac 上的一个特定(旧?)版本。这很重要,因为它有时表现不同。确保执行 sed -i '' 否则它会在文件末尾添加“-e”,认为这就是我想要命名备份文件的名称。-i '' 表示不制作备份文件,只需在原地编辑文件,不需要备份文件。

指定 -name 'filename.sh' 帮助我避免了另一个我无法解决的问题。还有另一个带有 .sh 的文件,该文件以没有换行符结尾。sed 出于某种原因,会在末尾添加一个换行符,尽管 's/blah/blah/g' 与该文件中的任何内容都不匹配。因此,我没有解决这个问题,而是告诉 find 忽略所有其他文件。

其他有效的命令

此外,我发现这些命令可以在 findsed.sh 文件中使用(一次只有一个命令,而不是多个命令,所以将其他命令注释掉):

find . -name '.publishNewZenPackFromGithub.sh.swp' -exec rm -f {} \;
find . -name '*' -exec grep -H PassToRemove {} \;

享受!

于 2011-11-07T19:43:45.827 回答
3

更多信息git-filter-repo

https://stackoverflow.com/a/58252169/895245提供了基础知识,这里有更多信息。

安装

至少从 git 2.5 开始,它没有随主线 git 一起提供:https ://superuser.com/questions/1563034/how-do-you-install-git-filter-repo/1589985#1589985

python3 -m pip install --user git-filter-repo

使用提示

这是我倾向于使用的更常见的方法:

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

在哪里:

  • Bash 进程替换允许我们不为简单替换创建文件
  • HEAD使其仅影响当前分支

仅修改一系列提交

如何使用 git filter-repo 而不是整个分支历史记录仅修改一系列提交?

git filter-repo --replace-text <(echo 'my_password==>xxxxxxxx') --refs HEAD~2..HEAD

使用 Python API 替换

对于更复杂的替换,可以使用 Python API,请参阅:如何使用 git filter-repo 作为具有 Python 模块接口的库?

于 2020-10-01T18:54:44.347 回答
2

可能是外壳扩展问题。如果 filter-branch 在评估命令时丢失了引号"*.php",则它可能会扩展为空,从而git ls-files -z列出所有文件。

您可以检查过滤器分支源或尝试不同的引用技巧,但我要做的只是制作一个执行树过滤器并传递该脚本的单行 shell 脚本。

于 2010-11-05T22:56:52.510 回答