Git如何选择rebase起点?

时间:2016-07-31 15:32:04

标签: git rebase

git rebase如何从源(通常是特征)分支中选择一个起始提交?

我猜git回到了src和dst分支的共同祖先。

如果两个分支没有共同提交怎么办?

1 个答案:

答案 0 :(得分:8)

要知道一件有用的事情 - 您可能已经知道这一点 - rebase通过复制提交工作。它只复制正确的提交,使新副本在新基础结束后立即生效。

对rebase(复制)提交的选择实际上使用了一个关键的事情来了解Git及其提交选择。理解了这一点后,您还会了解git loggit rev-list的工作原理。 1

首先,请记住Git的提交形成(特别是 D 竖立 A 循环 G raph或DAG,但你很长时间都不用担心这个问题。每个提交都会记住其父级或其所有父级的合并提交。当我们绘制的图形部分中没有合并提交时,我们将获得结构而不是任意DAG。当你没有合并时,Rebase最有效,因为无论如何,rebase通常会抛弃合并。

我们可以 - 您应该 - 绘制这些图表。你可以让Git为你做这件事,例如git log --graph。它垂直绘制它们,这为我们的目的占用了太多空间,所以我们将水平绘制它们,而右边的更新提交和左边的旧提交。

这是一个示例图表:

...<- o <- o <- o <- o
            \
             o <- o <- o <- o

每个o代表图表中的一些提交。在形式图理论中,每个提交在图中都是 vertex ,但有时这些被称为 nodes ,而我倾向于使用单词&#34; nodes&#34 ;和#34;提交节点&#34;描述它们。

&#34;真实姓名&#34;每个提交节点都是一个Git哈希,是那些丑陋的40个字符a234567...之一。给定一个Git哈希,Git可以在存储库中查找任何对象(当然包括提交)。但不知何故,我们必须记住这些真正的名字&#34;,这些都是令人难忘的。

因为每次提交都会记住它的父级,所以我们可以从任何提交开始并在历史中向后工作(但不是向前!)。我们需要的是记住分支的最近的最多提交。我们让Git为我们这样做,让Git在分支名称中保存大丑陋的哈希值,如masterdevelop

您可以使用git rev-parse将此名称转换为哈希:

$ git rev-parse master
08bb3500a2a718c3c78b0547c68601cafa7a8fd9

这意味着master指向真名为08bb350...的提交。该提交在其中具有先前提交的真实姓名,依此类推。

让我们再次绘制该示例图,但这次添加分支名称。我也会把它变得更紧凑:我们知道提交始终指向&#34;向后&#34; (对他们的父母)所以没有必要用箭头画出那些,我们可以使用连接线。我这次将*标记为两个提交:

...--*--*--o--o        <-- master
         \
          o--o--o--o   <-- develop

请注意,名称 master特别选择master分支的提示。同样,名称develop仅选择develop分支的提示。但是Git通常不会选择一个提交。通常,当我们告诉Git特别查看一个提交时,我们真的要求Git考虑提交所有父提交。

当我们从master开始并向后工作时,我们得到两个提交,专门在master(提示和提示之前),然后我们得到第二个{{1提交,第一个*,等等。

当我们从*开始并向后工作时,我们将获得专门针对develop的四个提交,然后是第二个develop提交,然后是第一个{{1}等等。

也就是说,两个*提交实际上是在两个分支上。

请注意,我们可以像这样轻松地绘制图形:

*

或者像这样:

*

所有这些图表都代表相同的图表, o--o <-- master / ...--*--*--o--o--o--o <-- develop 没有什么特别之处。

这是 o--o <-- master / ...--*--* \ o--o--o--o <-- develop 必须解决的问题的核心

如果我们要在master上重新rebasedevelop必须以某种方式挑选master上仅 的四个提交,同时排除git rebase 的所有提交。

这就是Git develop语法的用武之地。奇怪的是,rebase并没有使用它!这是有原因的,但我们暂时只看一下语法。使用这种语法 - 在这种情况下,使用master - 我们要求Git从提示提示开始,即X..Y的提示,并选择每次提交的时间,一直到开头,它可以从那里;但也从master..develop的提示开始,并且 un - 选择每次提交的时间。

我喜欢将此视为临时绘画提交绿色(go)和红色(停止)。我们可以先做绿色涂料,画上develop上的四个master加上两个o s加上前面的任何东西,然后从两个{{{1}开始涂上红色油漆。 1}}在主人身上继续前进到两个develop以及之前的所有内容。或者,我们可以先做红色涂料,然后做绿色涂料,但一找到红色节点就停止涂漆。无论哪种方式,我们都会结束四个独家 - *提交&#34;涂成绿色&#34;。

o知道如何复制这四个提交,而不是任何其他提交。

*从开始的地方通常是您当前的分支:

develop

(所以在这种情况下,我目前正在git rebase)。

rebase复制到的地方 - 或者更确切地说,&#34;在&#34;之后复制 - 来自$ git branch diff-merge-base master precious * stash-exp 的参数:

stash-exp

事实证明,也是 rebase获得其想法的地方&#34; red commit&#34; (要复制的内容)。

Rebase有效地将您的参数(例如git rebase)和您当前的分支名称(在我的情况下为$ git rebase master ),但让我们说git rebase - 并使用{{ 1}} 2 获取要复制的提交的ID:

master

(当然,你必须在 rebase之前运行)。

额外皱纹#1

当您运行stash-exp时,它会尝试检查其他分支 - 您重新定位的分支 - 是否具有您拥有的提交副本。也就是说,假设我们查看我们绘制的版本图:

develop

在此图表中,最终公共git rev-list提交有两个分支。我们可以轻松地将其中一个重新绑定到另一个上。但是,如果其中一个顶级$ git rev-list master..develop 提交或多或少匹配一个底线git rebase提交呢?省略额外的东西会很好。让我们将底线改为顶部,但让这些提交标记为 o--o / ...--*--* \ o--o--o--o *oo并注明,顶部,其中一个A就像B

C

(例如,这是您使用D时获得的图表。提交oB基本上是彼此的副本。因此,当我们对较低的四次提交进行修改时,我们真的应该只复制 o--B' / ...--*--* \ A--B--C--D cherry-pickB,并给予:

B'

最后,让我们重新打开标签。我们希望A指向CD指向 o--B' / \ ...--*--* A'-C'-D' \ A--B--C--D ,如下所示:

master

原始B'链会发生什么?我们已将其贴上标签&#34;放弃&#34;在这里,但事实上,Git使用 reflog 机制暂停了一段时间 - 例如,我们可以让Git找到develop找到原始提交{{1} - 以及D'设置为 o--B' <-- master / \ ...--*--* A'-C'-D' <-- develop \ A--B--C--D [abandoned] 的特殊名称A--B--C--D。默认情况下,reflog条目会持续30天, 3 ,而名称develop@{1}会一直存在,直到某些东西(通常是另一个rebase)覆盖它为止。

额外皱纹#2

有时候,这个Git魔法 - 使用一个名称,如D到#34;绘制提交红色&#34;,然后使用相同的名称来决定放在哪里副本 - 不够。在某些情况下,您需要在某个特定点告诉ORIG_HEAD 停止复制,但要将副本放在其他位置。在这种情况下,您可以使用rebase

D

the rebase documentation调用红色&#34;停止&#34;参数 ORIG_HEAD )。默认情况下, master 既是git rebase目标又是停止复制红色指示灯,当停止点时它会起作用是在复制点的历史中的正确位置。这通常是真的 - 并且经常(但并非总是),作为某个分支git rebase --onto git rebase --onto target upstream 提供的内容是upstream远程跟踪分支,您将 4 设置为upstream上游,我认为这是为什么rebase调用此参数 --onto

如果两个分支没有共同提交怎么办?

  

如果两个分支没有共同提交怎么办?

在这种情况下,&#34;绘制提交节点为红色&#34;步骤对油漆提交绿色&#34;没有影响。步骤:

upstream

如果你在分支foo上运行origin/foo,那么Git会有效地绘制三个foo - 分支提交绿色和四个upstream - 分支提交红色,然后采用绿色绘制的提交,这是从o--o--o--o <-- master o--o--o <-- unrelated 提示提交的三个提交。然后,rebase代码会复制这些提交:

unrelated

1 好吧,git rebase master有大约一百万个标志,所以这有点夸大其词,因为它不会用所有旗帜帮助你。 : - )

2 这里有许多副作用:有时候unrelated实际上直接使用master,有时它并不是。{1}}。但效果几乎相同。

3 这是可配置的:unrelatedo--o--o--o <-- master \ o--o--o <-- unrelated o--o--o [abandoned] 控制默认值,还可以为特定模式设置其他名称。

4 您可以使用git rev-list明确设置此项,但对于这些类型的分支,当您使用git rebase创建分支时,它通常会自动设置原来。一旦设置,git rev-list,没有额外的参数,也会自动找到它。