如何使用 git filter-branch 通过 blob SHA1 删除文件?

How to use git filter-branch to remove a file by blob SHA1?

我见过的大多数删除文件的 git 过滤器分支示例都是根据 文件名 删除文件。我不一定要那样做。相反,我已经确定了我要删除的文件的一些 blob(未提交)SHA1,无论它们在存储库中的什么位置。 (由于我们的回购历史,文件往往会在一堆文件中移动而不会改变。)

告诉 git filter-branch 根据文件的 blob SHA1 删除文件的最佳方法是什么?

您的任务是通过散列标识符从 Git 历史记录中删除 blob。您可能会发现使用 BFG 比使用 git-filter-branch 更快更容易,特别是使用 --strip-blobs-with-ids 标志:

-bi, --strip-blobs-with-ids <blob-ids-file> ...strip blobs with the specified Git object ids

仔细看usage instructions,核心部分就是这个:

$ java -jar bfg.jar  --strip-blobs-with-ids <blob-ids-file>  my-repo.git

请注意,<blob-ids-file> 文件应包含 Git object id,而不是 blob 内容的普通 SHA-1 哈希值。

对于给定的文件,您可以使用 git hash-object:

计算 Git object id
$ git hash-object README.md
a63b49c2e93788cd71c81015818307c7b70963bf

您可以看到这个值与简单的 SHA-1 哈希不同:

$ sha1sum README.md
7b833f7b37550e2df719b57e8c4994c93a865aa9  README.md

...那是因为 Git object id 散列了一个 Git header,连同文件的内容,即使它确实使用相同的SHA-1 算法。

BFG 通常比 运行 git-filter-branch 至少快 10-50 倍,而且通常更易于使用。

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

filter-branch 版本在 index-filter 内部可能看起来像这样:

git ls-files -s |
  sed -r '/ 02c97746d64fbfe13007a1ab4e9b9e4bbd99f42f /s/^100(644|755)/0/' |
  git update-index --index-info

也就是说,读取 index-info 格式,找到感兴趣的 blob 并将模式设置为 0(将其标记为删除),然后将其写回索引。

正如@RobertTyley 在他的回答中指出的那样,您最好使用 BFG。但是,要按要求回答问题(如何使用 filter-branch 执行此操作):

不幸的是,没有很好的方法。您可以编写一个脚本来获取与索引中的 SHA 值关联的所有文件名。作为起点,如果您要删除哈希为 DEADC0DE

的文件
git rev-list -n 1 --objects HEAD |grep ^DEADC0DE |cut -c 42-

然后您将输入每一行(也许 xargs?)作为

中的 <filename>
git rm --cached <filename>

并且您将使用该脚本作为您的 index-filter 值(因为将其用作树过滤器只会使本已缓慢的方法变得更慢)。

git filter branch --index-filter 将每次提交迭代地放入索引中,因此可以使用 git ls-files -s.

从哈希中恢复文件名

我这样做是为了删除散列为 2d341f0223ff、6a4558fa76d1 和 4d0a90cba061 的 blob:

git filter-branch --force --index-filter "git ls-files -cdmo -s | grep ' 2d341f0223ff\| 6a4558fa76d1\| 4d0a90cba061' | awk '{print }' | xargs git rm --cached --ignore-unmatch 656565randomstring546464" --prune-empty --tag-name-filter cat -- --all

随机字符串是为了避免git rmgrepreturns不匹配时报错。