为什么使用git rebase -i重新排序提交记录不会破坏历史记录?

7
据我理解,提交是文件的快照,如果我进行更改A然后进行更改B,更改B的提交文件已经包含了更改A的内容,那么重新排序会使更改A成为多余的吗?

Git 根据需要执行差异比较;rebase 将差异应用于新的基础内容。 - jthill
1个回答

9

事实上,这是一个非常好的问题,因为提交是快照。

rebase之所以有效,是因为rebase实际上是重复使用git cherry-pick(在前面包装一点以找出要选择的内容,在结尾处再进行更多的包装以移动分支标签),而git cherry-pick通过将提交变成一个变更集来工作。

例如,假设您有以下提交序列:

          A--B--C   <-- topic
         /
...--o--*--o--o     <-- mainline

为了将 topic 放到 mainline 上,我们需要(1) 找出在 topic 中但不在 mainline 上的提交(这些是顶部行上以标记为 * 结尾的 CBA),然后 (2) 将它们复制到我们将添加到 mainline 末尾以下的新提交中。
首先,Rebase 找到三个标记为 * 后续提交,并将它们放入一个列表中(逆序: A, B, C)(默认情况下它还会省略合并提交,但这里没有合并)。然后,它为每个提交执行 cherry pick 操作。
对于 cherry pick 操作,Git 会将 A* 进行 diff,将两个快照转换为 change set。然后,Git 将更改应用到 mainline 的最新提交上,并创建一个新的提交,称其为 A',位于匿名分支上。
          A--B--C   <-- topic
         /
...--o--*--o--o     <-- mainline
               \
                A'  <-- HEAD

要挑选出 B,Git 会将 BA 进行比较。将这些更改应用于 A' 将生成另一个提交 B'。重复此过程以获得 C'
          A--B--C         <-- topic
         /
...--o--*--o--o           <-- mainline
               \
                A'-B'-C'  <-- HEAD

最后,Git将topic标签从C中剥离,并将其指向C'。旧的链被抛弃了(虽然您仍然可以通过reflogs找到它,而且rebase也会将C的ID复制到特殊名称ORIG_HEAD中):
          A--B--C         [abandoned]
         /
...--o--*--o--o           <-- mainline
               \
                A'-B'-C'  <-- topic

现在rebase已经完成。

请注意,如果需要(如果diffs不能立即应用),每个复制都使用git的合并机制完成。每一个可能导致合并冲突,需要停止rebase并从您那里获取帮助。 (或者更糟糕的是,您可能会得到一个错误合并,尽管这在实践中很少见。)

当然,如果你重新排序提交(通过在交互式rebase中移动pick行),我们只需更改我们选择和应用每个提交的顺序。樱桃拣选操作仍然按照相同的方式工作:将挑选的提交与其父提交进行比较(C vs BA vs *B vs A)。


但是差异数据取决于先前提交中文件的内容,重新排序后会发生变化。或者这种重新排序只是为了美观? - Aspiring Dev
新的副本是新的副本,这意味着所有的内容都是全新的。例如,当你在挑选B时,如果主线的尖端有一个冲突的更改,就会出现合并冲突。当你解决了冲突并继续变基时,新的提交B'与原始提交B有不同的更改。BB'仍然存在于存储库中,下一个挑选(假设它是为C)将在尝试将该更改应用于B'之前将CB进行比较(由于B'现在已经不同,可能会再次出现合并冲突!)。 - torek
在阅读了您的评论和答案后,我现在明白了。似乎我只是被术语所迷惑了。现在我看到了,提交可以重新排序,但是无论您是否重新排序,重定基后的结果提交都是全新的提交。这些提交还具有不同的“快照文件”,这是仅将差异从挑选的提交应用于主线尖端的结果。因此,重新排序是一种表面上的变化,因为被重新排序的原始提交保持不变。如果我理解正确,请告诉我。 - Aspiring Dev
1
是的,你说得对,至少在正确的条件下是可以的。你也可以将其视为一种代数运算。假设你从一个数字开始(比如 x=10),然后有对 x 进行更改的提交:x=12、x=5、x=6。将每个更改转换为增量:+2、-7、+1。重新排列这些增量:-7、+1、+2。应用到 10 上得到:10-7=3、+1=4、+2=6。最终结果相同,但过程中间值可能不同。重新排列只有在其中一个中间值取决于另一个中间值时才会出错,例如提交 C 需要 B 完成的某些操作。 - torek
我现在明白了,谢谢! - Aspiring Dev

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