我试图更好地理解git-rebase背后的魔力。今天我对以下行为感到非常惊喜,我没想到。
TLDR:我重新绑定了一个共享分支,导致所有提交sha1都发生了变化。尽管如此,派生的分支能够准确地识别出它的原始提交是否为#ali;"进入具有不同sha1的新提交。 rebase根本没有造成任何混乱。
选择一个主分支:grp.plot(linestyle="solid",
linewidth=df2[df2['id'] == key]['weight'] / 2.0,
x = "x", y = "y", ax = ax, label = key)
将其分支到branch-X,并添加了一些额外的提交:M1
。
记下git-log输出。
将branch-X分支到branch-Y,并添加了一个额外的提交:M1-A1-B1-C1
。记下git-log输出。
将新提交添加到主分支的提示:M1-A1-B1-C1-D1
将branch-X重新引导到更新后的主人:M1-M2
。请注意,A2-B2-C2都具有与A1-B1-C1相同的消息,内容和作者日期。但是,它们具有完全不同的sha1值以及提交日期。根据{{3}},SHA1不同的原因是因为提交的父级已经改变。
将分支-Y重新引导到更新的分支-X上。结果:M1-M2-A2-B2-C2
。
值得注意的是,只有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仍然知道它们是链接在一起的。它是如何做到的?鉴于上述行为,this writeup何时真正危险?
答案 0 :(得分:7)
在内部,git rebase
列出了应该重新设定的提交,然后为这些提交计算patch-id。与commit id不同,它只散列 patch 的内容,而不是树的内容和提交对象。 因此,A1和A2虽然具有不同的标识符,但具有相同的patch-id。然后,git rebase
会跳过其patch-id已存在的补丁。
有关详情,请在此处搜索patch-id
:https://git-scm.com/book/en/v2/Git-Branching-Rebasing
上面的相关部分(图表缺失):
如果团队中的某个人强行推动覆盖您所依据的工作的变更,那么您面临的挑战就是找出您的工作内容以及他们重写的内容。
事实证明,除了提交SHA-1校验和之外, Git还计算校验和,该校验和仅基于提交引入的补丁。这称为“patch-id”。
如果你下载了重写的作品,并在你的合作伙伴的新提交之上重新定义它,那么 Git通常可以成功找出你的唯一内容,并将它们应用到新分支之上。强>
例如,在上一个场景中,如果我们在Someone推送重新提交的提交而不是进行合并,而是放弃了你的工作,我们运行git rebase teamone / master,Git会:
- 确定我们的分支机构(C2,C3,C4,C6,C7)所特有的工作
- 确定哪些不是合并提交(C2,C3,C4)
- 确定哪些尚未重写到目标分支(仅C2和C3,因为C4与C4相同的补丁')
- 将这些提交应用到teamone / master
的顶部这仅适用于C4和C4'你的伴侣制作的几乎完全相同的补丁。否则,rebase将无法判断它是否重复并且将添加另一个类似C4的补丁(可能无法干净地应用,因为更改已经至少在某种程度上存在)。
答案 1 :(得分:5)
实际上有几种不同的方法git rebase
用于消除冗余副本。
第一个也是最安全的方法是通过git cherry
用于识别樱桃选择提交的相同方法。但是,如果您阅读了链接文档,那么关于如何工作的唯一线索就在最后,手册页链接到the git patch-id
documentation。
阅读第二本手册页,可以让您了解如何实现等效性"得到建立:Git只是计算输出的git patch-id
,例如,任何普通(非合并)提交的git show
。实际上,它运行git diff-tree
而不是面向用户的git show
,但效果大致相同。
但是仍然缺少某些内容,而git rebase
或 git cherry
中的文档记录很少。它在git rev-list
中记录得更好,这是一个相当令人生畏的手册页。有两个键:对称差异的概念,使用the gitrevisions documentation中描述的三点语法,--left-right
和--cherry-mark
选项{{1} }}
了解我们如何使用DAGlet如:
git rev-list
并使用...--o--o--L1--L2--L3 <-- left
\
R1--R2--R3 <-- right
选择三个left...right
和L
提交,R
选项本身很有意义:它标记文本输出中的哪些提交来自三个点的左侧,右侧提交。
这里的第二步是发现--left-right
可以为每个&#34; side&#34;计算每个提交的补丁ID。然后,Git可以将所有左侧补丁ID与所有右侧补丁ID进行比较。 git rev-list
选项及其相关选项使用这些选项标记等效或不等价的提交,或省略等效提交。
这个特定难题的最后一部分是--cherry-mark
没有,正如文档声称的那样,使用git rebase
。相反,它使用等效的<upstream>..HEAD
来获取要复制的提交集。 (对于这些选项,我们还必须添加git rev-list --cherry-pick --right-only --no-merges <upstream>...HEAD
和--topo-order
。)
--reverse
用于提及提交的第二种方法是现在内置于git rebase
的{{1}}机制。这种机制特别难以描述,而且依赖于reflog条目来了解过去在分支上 的提交,但不再是。它有时也会产生不良结果,并且在这种特殊的变形中没有用处。
我在这里主要提到它,因为有人在寻找--fork-point
遗漏某些提交的原因可能会遇到叉点机制失灵的情况。参见,例如:
答案 2 :(得分:1)
第二个rebase上的branch-Y提交为空
里面真的没有魔法。 Rebase搜索共同历史记录并忽略它(在这种情况下仅提交M1)。将历史记录与重新分支的分支(Y)分离,并尝试在新的基础(分支-X)上选择它。
拣货方法从先前和挑选的提交中获取补丁。由于A1,B1和C1为空,它只是跳过这些提交。然后只选择D1,因此创建了一个D2(使用新的SHA作为标题中的父链接更改;正如问题中正确说明的那样)。