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

4
我看到的大多数git filter-branch示例都是基于文件名来删除文件的。但我不一定想这样做。相反,我已经确定了一些要删除的文件的blob(而不是提交)SHA1值,无论它们在存储库中的位置如何。(由于我们的存储库历史记录,文件往往会频繁移动而不改变。)
告诉git filter-branch根据它们的blob SHA1删除文件的最佳方法是什么?

2
如果您能够使用BFG Repo Cleaner,它对于这个操作有简单的支持,并且比使用filter-branch快得多。 - Mark Adelsberger
4个回答

8
您的任务是通过哈希标识符从Git历史记录中删除blob。您可以使用BFG而不是`git-filter-branch`,特别是使用`--strip-blobs-with-ids`标志,这样可能会更快更容易:

-bi, --strip-blobs-with-ids <blob-ids-file> ...使用指定的Git对象ID删除blobs

请仔细遵循使用说明,核心部分只有这个:
$ java -jar bfg.jar  --strip-blobs-with-ids <blob-ids-file>  my-repo.git

请注意,<blob-ids-file>文件应包含Git对象ID,而不是纯SHA-1哈希值的内容。
对于给定的文件,您可以使用git hash-object计算Git对象ID:
$ git hash-object README.md
a63b49c2e93788cd71c81015818307c7b70963bf

您可以看到,这个值与简单的SHA-1哈希不同:
$ sha1sum README.md
7b833f7b37550e2df719b57e8c4994c93a865aa9  README.md

那是因为Git对象ID对Git头部以及文件内容进行哈希,即使它使用相同的SHA-1算法。

BFG通常比运行git-filter-branch快至少10-50倍,并且一般更容易使用。

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


我认为这里没有提到的一件事是:默认情况下,BFG不会从头提交中删除内容,对吧?因此,如果您想要删除一个blob的所有实例,您必须关闭该选项... - Mark Adelsberger
1
没错,你可以使用 --no-blob-protection 命令来禁用此功能。对于一个实时的代码库,我的建议是在进行历史记录重写之前,手动修复当前的文件树并确保一切正常。如果人们不使用我的工具来破坏他们的生产系统,那对我来说会更好!https://rtyley.github.io/bfg-repo-cleaner/#protected-commits - Roberto Tyley
我注意到你不喜欢使用“--no-blob-protection”;但是,值得一提的是,由于重写通常在镜像克隆上完成,如果出现问题,我们只需不将其推回原始版本库即可,因此不会造成任何实质性损害。 - Mark Adelsberger
用户可能难以意识到从镜像克隆中他们已经破坏了某些东西!如果他们直接将更新后的镜像推回主Git仓库,那么可能会在生产环境或其他地方进行连续部署,然后他们才会意识到问题-我宁愿不要在那个阶段增加BFG的不确定性行动-最好通过简单的手动提交来发现文件依赖问题,每个人都知道如何撤销... - Roberto Tyley
@RobertoTyley 如果你的工具允许用户指定要更新哪些引用,而不是全部更新,那就太好了。我想从发布到Github的一些文件中删除,同时在单独的分支上保留未经过滤的副本。使用你的工具做这件事很麻烦。 - fuz
@RobertoTyley 你说“仔细遵循使用说明”。但是在使用说明中根本没有提到--strip-blobs-with-ids。BFG是一个很棒的工具。但我不得不花费大量时间搜索有关如何使用任何选项的详细信息。 - John Pankowicz

1

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 $4}' | xargs git rm --cached --ignore-unmatch 656565randomstring546464" --prune-empty --tag-name-filter cat -- --all

随机字符串是为了避免当grep返回无匹配时,git rm抛出错误。


0

filter-branch 版本在 index-filter 中可能长这样:

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

那就是,读取索引信息格式,找到有趣的 blob 并将模式设置为 0(标记为待移除),然后将其写回索引。

1
虽然我仍然建议使用BFG,但我必须钦佩这个答案的极客勇气:heart: - Roberto Tyley
我认为这必须通过树过滤器来完成,这会比我概述的索引过滤器方法更慢。但这只是纠结细节;BFG是完成此任务的最佳工具。 - Mark Adelsberger

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值(因为将其用作树过滤器只会使本来就慢的方法变得更慢)。


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