git filter-branch命令中的--prune-empty开关无法删除分支开头的空提交

4
我在我的主分支的开头有一个单独的提交,其中包含一个.gitignore文件。
当我运行时
git filter-branch -f --tree-filter 'git rm .gitignore' --prune-empty

新的树仍然包含那个节点,虽然是空的(.gitignore文件已被删除),因此工作的一半已经完成。
为什么 --prune-empty 没有修剪空提交?或者我误解了这个开关吗?
1个回答

4
2016年:git filter-branchprune-empty选项提到:

此开关仅适用于只有一个父提交的提交

如果您修改的提交位于“master分支的开头”,它没有父提交。
即使是空的特定提交也不会被修剪。


如果您的修改提交是“在分支的开头”,
beginning of a branch b
     |
     v
--x--Y--z--z
      \
       b--b

只有当空提交与上一个提交完全相同时,才应该修剪

正如torek在这里提到的

“空提交”实际上是指与上一个提交具有相同树结构的提交:并不是说它没有任何文件,而是说它具有与其父提交相同的所有文件、相同的模式和相同的内容。
这是因为Git为每个提交存储了完整的快照,而不是从一个提交到下一个提交的差异。

正如文档所述:

某些类型的过滤器会生成空提交,这些提交保持树结构不变

因此,具有“0个文件”的提交在filter-branch的视角下并不是一个“空提交”,除非父提交也具有“0个文件”(即相同的空“半秘密”树


注意:这在Git 2.13(2017年第二季度)中已经改变了:“git filter-branch --prune-empty(man)会删除一个成为无操作的单亲提交,但不会删除其树为空的根提交。
查看提交32da746, 提交a582a82, 提交4dacc8f, 提交377a354 (2017年2月23日) 由Devin J. Pohly (djpohly)完成。
(合并者Junio C Hamano -- gitster --提交5296357, 2017年3月14日)

filter-branch:修复对没有父提交的空树的处理

签名作者:Devin J. Pohly

之前,git_commit_non_empty_tree函数总是将没有父提交的任何提交传递给git-commit-tree,而不管树是否为空。
然后,新的提交将被记录在filter-branch修订映射中,并且保持树不变的后续提交将被正确过滤。

通过这个改变,空树的没有父提交现在被正确地修剪,并且在修订映射中记录了一个空文件,表示它被重写为“没有提交”。这与后续提交的父映射自然配合。

git filter-branch现在在其man page中包含以下内容:

一些过滤器会生成不改变树的空提交。如果这些提交只有一个或零个未修剪的父提交,该选项会指示git-filter-branch删除这些提交;合并提交将保持完整。尽管可以通过在提交过滤器中使用提供的git_commit_non_empty_tree函数来实现相同的效果,但不能与--commit-filter选项一起使用。

标题已更改,分支开头添加了...。 - Anders Lindén
1
@AndersLindén 我已经编辑了答案,以解决“分支的开头”这种情况,这促使我重新审视“空提交”的概念。 - VonC
似乎在 git filter-branch 命令之前只有 .gitignore 文件的提交现在在所有方面都是空的。但正如你指出的那样,“此开关仅适用于具有一个且仅有一个父级的提交”。难道这个函数不应该改为“此开关仅适用于具有一个父级或是初始提交的提交”吗? - Anders Lindén
由于最初提交的版本没有父级,因此不会被修剪。这是因为Git中“空提交”的定义,实际上是与父提交相比未经修改的树形结构。@AndersLindén - VonC
是的,但重新表述的建议将包括最初的提交。 - Anders Lindén
显示剩余4条评论

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