为什么“Git rebase”会将提交压缩到先前的提交中,而不是下一个提交中?

5
当我在一个分支中压缩提交记录(使用 git rebase -i)时,我总是感到烦恼,因为压缩的提交记录与较旧的提交记录合并,而不是与较新的提交记录合并。
我不明白为什么要设计成这样。当我提交一个正在进行的工作(WIP)时,它代表着尚未编译或尚未完成的代码。当我最终提交“终于可行!”的提交记录并在合并之前压缩时,将这些 WIP 提交记录合并到“终于可行!”的提交记录中,而不是与之前的提交记录合并,更有意义。压缩 WIP 实质上“打破”了我知道不能编译的先前的提交记录。
为了解决这个问题,我的工作流程是将从“可行!”到第一个 WIP 提交记录之前的所有提交记录都压缩。但这不是很愚蠢吗?其他人怎么做,使得将 WIP 压缩到以前的提交记录变得有意义呢?
3个回答

3

在当前的Git版本中(从2.32.0版本开始包含;在commit 9e3cebd97cbd47909e683e617d5ffa2781f0adaa引入),您可以在交互式rebase的待办事项列表中指定fixup -C来选择用于压缩/修复的提交消息,例如:

pick 456789 Finished topic XYZ
pick abcdef WIP your incomplete commit
fixup 012345 WIP resolving compiler errors
fixup 6789ab WIP adding unit tests
fixup cdef01 WIP refactoring
fixup -C 234567 Implemented feature ABC
pick 89abcd Another feature

最终会有2个提交记录:"完成了主题XYZ"和"实现了功能ABC",无需手动调整任何提交消息(因此可以避免删除所有那些“WIP”行)。
如果你仍然想编辑最终的提交消息,请使用-c而不是-C
pick 456789 Finished topic XYZ
pick abcdef WIP your incomplete commit
fixup cdef01 WIP refactoring
fixup -c 234567 Implemented feature ABC
pick 89abcd Another feature

可以使用exec行执行任何其他附加步骤:

pick 456789 Finished topic XYZ
pick abcdef WIP your incomplete commit
fixup cdef01 WIP refactoring
fixup -C 234567 Implemented feature ABC
exec git commit --amend --reset-author -CHEAD
pick 89abcd Another feature

在 Git 2.32.0 之前,您可以通过以下待办事项列表解决此限制:

pick 456789 Finished topic XYZ
pick abcdef WIP your incomplete commit
fixup cdef01 WIP refactoring
fixup 234567 Implemented feature ABC
exec git commit --amend -C 234567 # commit id from line above
pick 89abcd Another feature

好观点,已点赞(比我2013年的回答更为更新)。您认为Git 2.38 git merge -i --update-refs选项怎么样? - VonC
@VonC git merge -i?你是不是想说 git rebase -i? :) 我同意 --update-refs 是好的和有用的(https://dev59.com/OnE85IYBdhLWcg3whUBr),但它并不适用于这个问题。我的理解是这个问题是关于提交信息而不是其它分支或引用的。 - knittl
是的,我想说的是变基,抱歉。我同意它在这里不适用,我只是想要你对这个新功能的反馈。 - VonC
@VonC rebase --update-refs 是 Git 中非常受欢迎和有用的新增功能。这是我工作流程中一直缺少的东西 :) - knittl
1
啊哈,这就是它的作用!感谢您让我知道。九年后,我有了答案。 :) 还值得注意的是,-c(小写)也可以让您修改提交消息。 - jmbeck
@jmbeck 等待的人会得到好事 :) 对,我完全忘记了 -c。已经更新了我的回答! - knittl

0

将最新的提交压缩到旧的提交中应该会保留两者的差异。仅仅因为你“挑选”了旧的提交,并不意味着你修复工作中的错误的新工作被遗漏了。

尝试使用git rebase -i HEAD~4,但将4替换为您想要合并的提交数,从HEAD开始,这可能是您的工作状态。

有一个很好的指南介绍如何使用交互式rebase,可以在http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html上找到。


0

那为什么不使用git reset --soft,将HEAD(只有HEAD)重置回第一个WIP,并从那里提交(使用最近和最终工作的WIP的索引和工作树)。

这样,您可以快速将这些提交合并在一起。

请参阅“如何使用git合并我的最后X个提交?”了解更多信息。

这与我在“git reset --soft的实际用途?”中得出的结论相一致:

每次:

  • 您对最终结果(工作树和索引方面)感到满意
  • 您对带您达到该结果的所有提交都不满意:

git reset --soft就是答案。


注意:从Git 2.42(2023年第三季度)开始,当用户编辑“rebase -i”待办事项文件以使其以“fixup”开头时,这将使其无效,该命令会在返回错误并将控制权交还给用户之前截断文件的其余部分。
停止截断以便更容易纠正此类格式不正确的待办事项文件。
请参阅commit 9645a08(2023年7月22日),作者为Alex Henrie(alexhenrie)
(由Junio C Hamano -- gitster --commit 8bfb359中合并,2023年8月2日) sequencer:尽管第一行无效,仍然完成解析待办事项列表。 签名:Alex Henrie 在编辑待办事项列表之前,会将其重写以缩短被选择的提交的OID,并附上有关编辑列表的建议。
确切的建议取决于是否首次编辑待办事项列表。
在编辑待办事项列表后,会将其重写以延长被选择的提交的OID,并删除建议。
如果无法解析已编辑的列表,则跳过此最后一步骤。

ddb81e5("rebase-interactive: use todo_list_write_to_file() in edit_todo_list()",2019-03-05,Git v2.22.0-rc0 -- merge)之前,如果无法解析现有的待办事项列表,则初始重写也会被跳过。
这导致的不幸结果是,如果在初始编辑后无法解析列表,则当用户重新编辑列表时给出的建议是错误的。
此更改依赖于todo_list_parse_insn_buffer()即使无法解析也返回整个待办事项列表。
不幸的是,如果列表以"fixup"命令开头,则会被截断,剩余的行会丢失。
通过在初始"fixup"提交后继续解析,解决此问题,就像我们在看到其他无效行时所做的那样。


因为在一个分支中,我经常有几个里程碑。当分支准备好合并时,我喜欢将WIP(或fixup提交)压缩到以下里程碑提交中。这样可以让我保留一些特性完成后的历史记录。强制我早期压缩是不好的。如果里程碑不好,我想重置怎么办?我会失去通往里程碑的历史记录。 - jmbeck

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