我试图更好地理解git-rebase的魔力。今天我惊喜地发现了以下行为,这是我没有预料到的。
简而言之:我对一个共享分支进行了rebase,导致所有提交的sha1值都发生了变化。尽管如此,一个派生分支仍然能够准确地识别出其原始提交被“别名”为具有不同sha1的新提交。rebase一点问题也没有产生。
细节
以主分支M1
为例:
从主分支中分离出分支X,添加一些额外的提交:M1-A1-B1-C1
。记录下git-log输出。
从分支X中分离出分支Y,添加一个额外的提交:M1-A1-B1-C1-D1
。记录下git-log输出。
在主分支的末尾添加一个新提交:M1-M2
将分支X rebase到更新后的主分支上:M1-M2-A2-B2-C2
。请注意,A2-B2-C2都具有与A1-B1-C1相同的消息、内容和作者日期。但是,它们具有完全不同的sha1值,以及提交日期。根据这篇文章,SHA1不同的原因是提交的父项已更改。
将分支Y rebase到更新后的分支X上。结果:M1-M2-A2-B2-C2-D2
。
值得注意的是,只有D1提交被应用(并成为D2)。在分支Y中的A1-B1-C1提交完全被git-rebase忽略了。您可以在输出日志中看到这一点。
这很棒,但是git-rebase如何知道要忽略A1-B1-C1?git-rebase如何知道A2-B2-C2与A1-B1-C1相同,因此可以安全地忽略它们?我一直认为git使用sha1标识符来跟踪提交,但尽管上述提交具有不同的sha1,git仍然以某种方式知道它们彼此链接。它是如何做到的?鉴于上述行为,在共享分支上rebase何时真正危险?
A1
的更改已经以A2
的形式存在于更新的 branch-X 上,因此它只是将A1
作为冗余内容丢弃了。 - Eevee--keep-empty
),但实际上git rebase
在开始之前就已经将它们从列表中剥离了。对于非交互式和交互式的 rebase,具体机制是不同的(非交互式默认使用git format-patch
而不是git cherry-pick
),但两种类型的 rebase 都使用复杂的git rev-list
命令来省略等效补丁提交记录。(如果你确实添加了--keep-empty
,则非交互式的 rebase 将被强制使用 cherry-pick,因为 format-patch 不会生成空补丁!) - torek