git rebase如何从源(通常是特征)分支中选择一个起始提交?
我猜git回到了src和dst分支的共同祖先。
如果两个分支没有共同提交怎么办?
答案 0 :(得分:8)
要知道一件有用的事情 - 您可能已经知道这一点 - rebase通过复制提交工作。它只复制正确的提交,使新副本在新基础结束后立即生效。
对rebase(复制)提交的选择实际上使用了一个关键的事情来了解Git及其提交选择。理解了这一点后,您还会了解git log
和git 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在分支名称中保存大丑陋的哈希值,如master
或develop
。
您可以使用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
上重新rebase
,develop
必须以某种方式挑选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之前运行)。
当您运行stash-exp
时,它会尝试检查其他分支 - 您重新定位的分支 - 是否具有您拥有的提交副本。也就是说,假设我们查看我们绘制的版本图:
develop
在此图表中,最终公共git rev-list
提交有两个分支。我们可以轻松地将其中一个重新绑定到另一个上。但是,如果其中一个顶级$ git rev-list master..develop
提交或多或少匹配一个底线git rebase
提交呢?省略额外的东西会很好。让我们将底线改为顶部,但让这些提交标记为 o--o
/
...--*--*
\
o--o--o--o
,*
,o
和o
并注明,顶部,其中一个A
就像B
:
C
(例如,这是您使用D
时获得的图表。提交o
和B
基本上是彼此的副本。因此,当我们对较低的四次提交进行修改时,我们真的应该只复制 o--B'
/
...--*--*
\
A--B--C--D
,cherry-pick
和B
,并给予:
B'
最后,让我们重新打开标签。我们希望A
指向C
,D
指向 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)覆盖它为止。
有时候,这个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 这是可配置的:unrelated
和o--o--o--o <-- master
\
o--o--o <-- unrelated
o--o--o [abandoned]
控制默认值,还可以为特定模式设置其他名称。
4 您可以使用git rev-list
明确设置此项,但对于这些类型的分支,当您使用git rebase
创建分支时,它通常会自动设置原来。一旦设置,git rev-list
,没有额外的参数,也会自动找到它。