git-rebase如何识别"别名"承诺?

时间:2017-08-23 19:14:02

标签: git git-rebase git-commit

我试图更好地理解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何时真正危险?

3 个答案:

答案 0 :(得分:7)

在内部,git rebase列出了应该重新设定的提交,然后为这些提交计算patch-id。与commit id不同,它只散列 patch 的内容,而不是树的内容和提交对象。 因此,A1和A2虽然具有不同的标识符,但具有相同的patch-id。然后,git rebase会跳过其patch-id已存在的补丁。

有关详情,请在此处搜索patch-idhttps://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用于消除冗余副本。

修补程序-ID

第一个也是最安全的方法是通过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...rightL提交,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作为标题中的父链接更改;正如问题中正确说明的那样)。