如何在整个Git历史中替换字符串?

时间:2017-10-26 09:37:12

标签: git bash replace

我的一个密码在我的Git仓库中提交了很少的文件。是否有某种方法可以自动将整个历史记录中的其他字符串替换为此密码,以便没有它的痕迹?理想情况下,如果我可以编写简单的bash脚本接收字符串来查找和替换并完成整个工作本身,例如:

./replaceStringInWholeGitHistory.sh "my_password" "xxxxxxxx"

编辑:此问题与that one不重复,因为我要求在不删除整个文件的情况下替换字符串。

3 个答案:

答案 0 :(得分:8)

首先,找到可能包含密码的所有文件。假设密码为abc123,分支为master。您可能需要将仅abc123仅作为普通字符串的文件排除。

git log -S "abc123" master --name-only --pretty=format: | sort -u

然后将“abc123”替换为“******”。假设其中一个文件是foo/bar.txt

git filter-branch --tree-filter "if [ -f foo/bar.txt ];then sed -i s/abc123/******/g foo/bar.txt;fi"

最后,强制将master推送到远程存储库(如果存在)。

git push origin -f master:master

我做了一个简单的测试并且它有效,但我不确定你的情况是否合适。您需要处理来自所有分支的所有文件。至于标签,您可能必须删除所有旧标签,并创建新标签。

答案 1 :(得分:1)

一开始,我要感谢ElpieKay,他发布了我的解决方案的核心功能,我只是自动化了。

所以,最后我有我想要的剧本。我把它分成了彼此依赖的部分,可以作为独立的脚本。它看起来像这样:

censorStringsInWholeGitHistory.sh:

#!/bin/bash
#arguments are strings to censore

for string in "$@"
do
  echo ""
  echo "================ Censoring string "$string": ================"
  ~/replaceStringInWholeGitHistory.sh "$string" "********"
done

用法:

~/censorStringsInWholeGitHistory.sh "my_password1" "my_password2" "some_f_word"

replaceStringInWholeGitHistory.sh:

#!/bin/bash
# $1 - string to find
# $2 - string to replace with

for branch in $(git branch | cut -c 3-); do
  echo ""
  echo ">>> Replacing strings in branch $branch:"
  echo ""
  ~/replaceStringInBranch.sh "$branch" "$1" "$2"
done

用法:

~/replaceStringInWholeGitHistory.sh "my_password" "********"

replaceStringInBranch.sh:

#!/bin/bash
# $1 - branch
# $2 - string to find
# $3 - string to replace with

git checkout $1
for file in $(~/findFilesContainingStringInBranch.sh "$2"); do
  echo "          Filtering file $file:"
  ~/changeStringsInFileInCurrentBranch.sh "$file" "$2" "$3"
done

用法:

~/replaceStringInBranch.sh master "my_password" "********"

findFilesContainingStringInBranch.sh:

#!/bin/bash

# $1 - string to find
# $2 - branch name or nothing (current branch in that case)

git log -S "$1" $2 --name-only --pretty=format: -- | sort -u

用法:

~/findFilesContainingStringInBranch.sh "my_password" master

changeStringsInFileInCurrentBranch.sh:

#!/bin/bash

# $1 - file name
# $2 - string to find
# $3 - string to replace

git filter-branch -f --tree-filter "if [ -f $1 ];then sed -i s/$2/$3/g $1;fi"

用法:

~/changeStringsInFileInCurrentBranch.sh "abc.txt" "my_password" "********"

我将所有这些脚本放在我的主文件夹中,正确使用此版本需要什么。我不确定这是最好的选择,但是现在我找不到更好的选择。当然,每个脚本都必须是可执行的,我们可以用chmod +x ~/myscript.sh来实现。

可能我的脚本不是最佳的,对于大型回购它会处理很长时间,但它有效:)

并且,最后,我们可以将我们的审查回购推送到任何远程:

git push <remote> -f --all

编辑:来自ElpieKay的重要提示:

  

不要忘记删除并重新创建已推送的标签。他们   仍然指向可能包含密码的旧提交。

也许我将来会改进我的脚本以自动执行此操作。

答案 2 :(得分:1)

git filter-repo --replace-text

Git 2.25 man git-filter-branch已经明确建议使用git filter-repo而不是git filter-tree,所以我们开始吧。

安装https://superuser.com/questions/1563034/how-do-you-install-git-filter-repo/1589985#1589985

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

然后使用:

echo 'my_password==>xxxxxxxx' > replace.txt
git filter-repo --replace-text replace.txt

或等同于Bash魔术:

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

使用以下简单的测试存储库进行了测试:https://github.com/cirosantilli/test-git-filter-repository和替换字符串:

d1==>asdf
d2==>qwer

以上默认情况下对所有分支都起作用(因此具有侵入性!!!),仅对选定的分支起作用,请使用:git filter-repo: can it be used on a specific branch?例如:

--refs HEAD
--refs refs/heads/master

选项--replace-text选项记录在:https://github.com/newren/git-filter-repo/blob/7b3e714b94a6e5b9f478cb981c7f560ef3f36506/Documentation/git-filter-repo.txt#L155

-替换文本 ::

带有表达式的文件,如果找到,将被替换。通过 默认情况下,每个表达式都被视为文字文本,但是 支持regex:glob:前缀。您可以结束 以==>行和一些替换文字来选择 替代选择,而不是默认的***REMOVED***

当然,一旦公开地输入了密码,那就太迟了,您将不得不更改密码,因此在这种情况下,我什至不用担心替换:Remove sensitive files and their commits from Git history < / p>

这似乎是一个相同的问题:How to substitute text from files in git history?

在git-filter-repo ac039ecc095d上进行了测试。