如何在文件夹模式下使用git filter-branch

3

我已经把一堆敏感数据提交到了本地仓库,但这些数据还没有发布。

这些敏感数据分散在项目的不同文件夹中,我想完全从git历史记录中删除它们。

所有相关文件夹都具有相同的名称,并位于不同文件夹中的同一级别。以下是我的文件夹结构示例:

root
    folder1
           ./sensitiveData
    folder2
           ./sensitiveData
    folder3
           ./sensitiveData


使用以下命令,我可以逐个删除包含敏感数据的文件夹:
git filter-branch -f --index-filter 'git rm -r --cached --ignore-unmatch javascript/folder1/.sensitiveData' --prune-empty HEAD

我想一次性删除所有包含敏感数据的文件夹,因为它们太多了,同时我也希望学习一下如何实现这个操作。

但是使用以下命令时,没有任何内容被重写,并且我收到了警告消息'refs/heads/master' is unchanged:

git filter-branch -f --index-filter 'git rm -r --cached --ignore-unmatch javascript/*/.sensitiveData' --prune-empty HEAD

在我看来,有两种策略:

  1. 要么我的模式出了问题,我需要进行更改。
  2. 要么我应该使用bash循环。

如果可能的话,选项一似乎更合理。


不要只写一行代码,你需要编写一个完整的脚本,在其中实现你想要的任何功能。 - 0andriy
所以在这个上下文中 * 不会扩展吗? - user1984
我不知道,但是即使这样做了,如果有很多文件夹,你的命令行可能会变得太长。这种方法一开始就是错误的。 - 0andriy
地址是相对的,因此它不会深入到您在命令中看到的三个级别以下。 - user1984
2个回答

2

当你运行命令时,首先由你的shell进行评估。因此:

原始回答:

'git rm -r --cached --ignore-unmatch javascript/*/.sensitiveData'

最初的回答:单引号保护整个内容不受 shell 影响,并将其作为 --index-filter 传递给 git filter-branch 以供后续使用。此时,单引号已经消失。
问题在于:git filter-branch 给定的过滤器会由另一个 shell(技术上来说是运行 git filter-branch 的 shell)在过滤时进行评估。这个其他的 shell 使用 eval 命令来执行以下命令:
eval $filter

所以现在这个第二个shell重新解释为:
git rm -r --cached --ignore-unmatch javascript/*/.sensitiveData

它通过空格分隔参数,根据当前工作目录扩展星号,并在扩展结果上调用git rm -r --cached --ignore-unmatched
如果扩展成功,就会发生一件事;否则,就会发生另一件事。具体发生什么取决于shell(bash可以配置为以几种不同的方式运行;POSIX sh更可预测)。
对于--index-filter,实际的当前工作目录通常为空,因此扩展可能会失败。这应该在大多数情况下将星号原样传递给Git。由于git rm的参数(基本上)是pathspec,Git现在将执行自己的扩展。这应该已经起作用了,所以要么路径本身有问题,要么目录不为空,要么你的shell有些奇怪,使得扩展失败没有将文字javascript/*/.sensitiveData传递给git rm
你可以使用以下方法从此方程式中取出一些变量:
'git rm -r --cached --ignore-unmatch javascript/\*/.sensitiveData'

"原始答案"
请让第二个外壳看到:
git rm -r --cached --ignore-unmatch javascript/\*/.sensitiveData

which will force the second shell to pass:

javascript/*/.sensitiveData

直接使用git rm命令即可删除。虽然本应该可以直接删除,但有趣的是需要检查javascript/*/.sensitiveData是否能够匹配特定提交中的正确文件,此时您可以在这些提交上使用git ls-tree -r进行手动操作。

感谢@torek提供的详细答案。我转义了星号\*,但没有帮助。我还按照您建议的运行了命令git ls-tree -r javascript/*/.sensitiveData,但出现以下错误fatal: Not a valid object name javascript/folder1/.sensitiveData,这似乎表明此处的模式至少是有效的。有什么想法吗? - user1984
1
git ls-tree -r 需要一个 提交哈希 ID 参数。这里的想法是查看你想要删除这些文件的提交之一,以确保 javascript/*/.sensitiveData 应该匹配所有文件。假设其中一个包含你想要删除的文件的提交是 a123456,那么使用 git ls-tree -r a123456 并仔细观察输出。 - torek
谢谢@torek,我选择了另一种使用bash的解决方案(并在此过程中学习了一些bash)。这是一个简单的for in,但它完美地解决了问题。已经从你的答案中学到了很多。将会检查git ls-tree。再次感谢:D - user1984

-1
最终,解决我的问题的是一个使用了 for in 结构的小 bash 脚本。
for name in javascript/*/.sensitiveData
    do git filter-branch -f --index-filter "git rm -r --cached --ignore-unmatch $name" --prune-empty HEAD
done

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