据我理解,提交是文件的快照,如果我进行更改A然后进行更改B,更改B的提交文件已经包含了更改A的内容,那么重新排序会使更改A成为多余的吗?
事实上,这是一个非常好的问题,因为提交是快照。
rebase之所以有效,是因为rebase实际上是重复使用git cherry-pick
(在前面包装一点以找出要选择的内容,在结尾处再进行更多的包装以移动分支标签),而git cherry-pick
通过将提交变成一个变更集来工作。
例如,假设您有以下提交序列:
A--B--C <-- topic
/
...--o--*--o--o <-- mainline
topic
放到 mainline
上,我们需要(1) 找出在 topic
中但不在 mainline
上的提交(这些是顶部行上以标记为 *
结尾的 C
、B
和 A
),然后 (2) 将它们复制到我们将添加到 mainline
末尾以下的新提交中。*
后续提交,并将它们放入一个列表中(逆序: A
, B
, C
)(默认情况下它还会省略合并提交,但这里没有合并)。然后,它为每个提交执行 cherry pick 操作。A
与 *
进行 diff,将两个快照转换为 change set。然后,Git 将更改应用到 mainline
的最新提交上,并创建一个新的提交,称其为 A'
,位于匿名分支上。 A--B--C <-- topic
/
...--o--*--o--o <-- mainline
\
A' <-- HEAD
B
,Git 会将 B
与 A
进行比较。将这些更改应用于 A'
将生成另一个提交 B'
。重复此过程以获得 C'
: A--B--C <-- topic
/
...--o--*--o--o <-- mainline
\
A'-B'-C' <-- HEAD
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 B
,A
vs *
,B
vs A
)。
B
时,如果主线的尖端有一个冲突的更改,就会出现合并冲突。当你解决了冲突并继续变基时,新的提交B'
与原始提交B
有不同的更改。B
和B'
仍然存在于存储库中,下一个挑选(假设它是为C
)将在尝试将该更改应用于B'
之前将C
与B
进行比较(由于B'
现在已经不同,可能会再次出现合并冲突!)。 - torek