如何替换git历史记录中文件中的文本?

如何替换git历史记录中文件中的文本?,git,substitution,git-filter-branch,git-rewrite-history,bfg-repo-cleaner,Git,Substitution,Git Filter Branch,Git Rewrite History,Bfg Repo Cleaner,我一直使用基于接口的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客户端(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' {} \;"

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

可能是shell扩展问题。如果筛选器分支在计算命令时丢失了
“*.php”
周围的引号,则它可能会扩展为零,从而
git ls files-z
列出所有文件


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

您可以通过将
-name“pattern”
传递到
find
来避免接触不需要的文件

这对我很有用:

git filter-branch --tree-filter "find . -name '*.php' -exec sed -i -e \
    's/originalpassword/newpassword/g' {} \;"

我在/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分支时,它会逐个检查您提交的每个修订--树过滤器在每个提交的修订版上运行findsed.sh脚本,保存它,然后前进到下一个修订版

find命令查找特定文件或文件集,并对该文件执行(-exec)sed编辑器。sed是一个命令,它将regex置于s/之后,并将其替换为/g和/g之间的字符串(在我的示例中为空)。{}是对find命令给出的文件路径的引用。文件路径被提供给sed,以便sed知道要处理什么;只需结束-exec命令

将shell脚本和命令分离成不同的部分可以减少引用“”或“”时的复杂性

特性

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

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

有效的其他命令

此外,我在findsed.sh文件中发现了这些命令(一次只能使用一个命令,而不是multple,因此请注释#其他命令):

享受吧

我建议使用,这是一种比
git过滤器分支更简单、更快的替代方法
,专门用于从git历史重写文件

您应该在这里仔细地遵循以下步骤:-但核心部分是:下载(需要Java 7或更高版本)并运行以下命令:

$ java -jar bfg.jar  --replace-text replacements.txt -fi *.php  my-repo.git
replacements.txt
文件应包含您想要执行的所有替换,格式如下(每行一项-注意不应包含注释):

PASSWORD1#将文字字符串“PASSWORD1”替换为“***已删除***”(默认值)
PASSWORD2=>examplePass#替换为“examplePass”
PASSWORD3==>#替换为空字符串
正则表达式:password=\w+=>password=#替换,使用正则表达式
正则表达式:\r(\n)=>1美元#用Unix换行符替换Windows换行符
您的整个存储库历史记录将被扫描,并且
.php
文件(大小小于1MB)将执行替换:将替换任何匹配的字符串(不在您的最新提交中)

完全披露:我是《BFG回购清洁剂》的作者。

与Git 2.24(2019年第4季度)合著

等价物为,使用,及其:

使用
expressions.txt

literal:originalpassword==>newpassword

加上

使用
--path glob
(或
--path
)会导致
git筛选器分支
仅保留与这些规范匹配的文件
仅替换特定文件中文本的功能在bfg ish中可用,如
-fi
,或。
否则,看起来只有在自定义提交回调的情况下才可能这样做。


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

有关
git filter repo的更多信息

提供了基本知识,这里是一些更多的信息

安装

从git 2.5开始,至少它没有与主线git一起装运,因此:

使用技巧

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

git filter-repo --replace-text <(echo 'my_password==>xxxxxxxx') HEAD
使用Python API替换


对于更复杂的替换,您可以使用Python API,请参见:

并不能解决您的问题,但这类似于我不久前提出的一个问题:实际上,关于如何删除文件,有很多答案。我需要替换一个字符串。@Jimmy Cuadra,请看我的编辑,我实际上使用了不同的脚本,弄混了。也许这有助于您获得正确的命令。这一行程序是什么样子的?正是您现在传递给
--tree filter'…'
的内容。很好的建议;将实际可执行脚本传递给
literal:originalpassword==>newpassword
python3 -m pip install --user git-filter-repo
git filter-repo --replace-text <(echo 'my_password==>xxxxxxxx') HEAD
git filter-repo --replace-text <(echo 'my_password==>xxxxxxxx') --refs HEAD~2..HEAD