git rebase如何从源(通常是功能)分支中选择启动提交?
我猜git可以回到src和dst分支的共同祖先。
如果两个分支没有共同的提交怎么办?
最佳答案
要知道的一件事-您可能已经知道了-可以通过复制提交来进行基础调整。它只复制正确的提交,使新副本在新库结束后立即发布。
选择要变基(复制)的提交实际上是使用最关键的事情之一来了解Git及其提交选择的。了解这一点后,您还将了解git log
和git rev-list
的工作原理。1
首先,请记住,Git的提交是由一个图构成的(特别是有向非循环图或DAG,但您不必为此担心很长时间了)。每个提交都会记住其父级,或者对于合并提交,会记住其所有父级。当我们绘制的图的部分中没有合并提交时,我们将获得树结构而不是任意DAG。当您没有合并时,Rebase最有效,因为rebase通常会丢弃合并。
我们可以并且您应该绘制这些图。您可以让Git为您做到这一点,例如git log --graph
。它在垂直方向上绘制它们,这在我们这里没有足够的空间,因此我们将在水平方向上绘制它们,在右侧使用较新的提交,在左侧使用较旧的提交。
这是一个示例图:
...<- o <- o <- o <- o
\
o <- o <- o <- o
每个
o
代表图中的某些提交。在形式图论中,每个提交都将是图中的一个顶点,但有时将它们称为节点,而我倾向于使用“节点”和“提交节点”来描述它们。每个提交节点的“真实名称”是一个Git哈希,它是丑陋的40个字符的大型
a234567...
之一。给定一个Git哈希,Git可以在存储库中查找任何对象(当然包括提交)。但是,以某种方式,我们必须记住这些“真名”,这是完全不值得纪念的。但是,由于每个提交都会记住其父级,因此我们可以从任何提交开始,并在历史上倒退(但不能向前!)。我们需要记住分支的最新提交或最尖端提交。通过让Git将庞大的丑陋哈希保存在分支名称(例如
master
或develop
)中,我们可以让Git为我们做到这一点。您可以使用
git rev-parse
将此类名称转换为哈希:$ git rev-parse master
08bb3500a2a718c3c78b0547c68601cafa7a8fd9
这意味着
master
指向实名为08bb350...
的提交。该提交内部具有先前提交的真实名称,依此类推。让我们再次绘制示例图,但是这次添加分支名称。我也将使其更加紧凑:我们知道提交总是指向“向后”(指向他们的父母),因此不需要将它们绘制为箭头,我们可以使用连接线。这次我要用
*
标记两个提交:...--*--*--o--o <-- master
\
o--o--o--o <-- develop
请注意,名称
master
特别选择了master
分支的最顶端。同样,名称develop
仅选择develop
分支的尖端。但是Git通常不只是选择一个提交。通常,当我们告诉Git特别关注一项提交时,我们实际上是在要求Git考虑该提交及其所有父项。当我们从
master
开始并向后工作时,我们得到了两个完全在master
上的提交(技巧,而技巧之前的一个),然后我们获得了第二个*
提交和第一个当我们从
*
开始并向后工作时,我们得到四个分别在develop
上的提交,然后是第二个develop
提交,然后是第一个*
,依此类推。也就是说,两个
*
提交实际上都在两个分支上。请注意,我们可以像这样轻松地绘制图形:
o--o <-- master
/
...--*--*--o--o--o--o <-- develop
或像这样:
o--o <-- master
/
...--*--*
\
o--o--o--o <-- develop
所有这些图都表示同一张图,并且
*
没什么特别的。这是
master
必须解决的问题的核心如果要在
rebase
上重新建立develop
的基础,则master
必须以某种方式选择仅在git rebase
上的四个提交,同时还要排除也在develop
上的所有提交。这就是Git的
master
语法出现的地方。奇怪的是,rebase不使用它!这是有原因的,但是现在让我们看一下语法。使用这种语法(在这种情况下,使用X..Y
),我们要求Git从尖端提交(master..develop
的尖端)开始,并选择可以追溯到最开始的每次提交,那里;但也要从develop
的尖端开始,并取消选择每一次返回的提交。我喜欢将其视为临时绘画,包括绿色(开始)和红色(停止)。我们可以先做绿色绘画,先在
master
上绘画四个o
,再加上两个develop
,再加上之前的其他东西,然后从母版上的两个*
开始将红色绘画放在顶部,然后继续接下来的两个o
及其之前的所有内容。或者,我们可以先进行红色绘制,然后进行绿色绘制,但是一旦发现红色节点,就停止绘制。无论哪种方式,我们都只用四个<-cc>提交“涂成绿色”结束。这是
*
知道复制这四个提交而不是其他任何提交的方式。develop
开始的位置通常是您当前的分支:$ git branch
diff-merge-base
master
precious
* stash-exp
(所以在这种情况下,我目前在
git rebase
上)。rebase
复制到的位置(或更确切地说,是“之后复制”)来自stash-exp
的参数:$ git rebase master
事实证明,这也是
rebase
获得“红色提交”(不要复制的内容)概念的地方。Rebase有效地接受了您的参数(例如
git rebase
)和您当前的分支名称(在我的情况下为git rebase
,但假设为master
),并使用stash-exp
2获取要复制的提交的ID:$ git rev-list master..develop
(当然,您必须在变基之前运行此程序)。
皱纹#1
当您运行
develop
时,它将尝试检查另一个分支(您要重新建立基础的分支)是否具有您拥有的提交副本。也就是说,假设我们看这样绘制的版本图: o--o
/
...--*--*
\
o--o--o--o
在此图中,有两个来自最终公共
git rev-list
提交的派生。我们可以轻松地将任何一个重新建立基础。但是,如果最上面的git rebase
提交之一匹配或多或少匹配最下面的*
提交之一怎么办?忽略多余的东西会很好。让我们将底线重新定位到顶部,但我们将这些提交标记为o
,o
,A
和B
,并注意在顶部,C
之一就像 o--B'
/
...--*--*
\
A--B--C--D
(例如,这是使用
D
时得到的图形)。提交o
和B
基本上是彼此的副本。因此,当我们调整较低的四个提交的基准时,我们实际上应该只复制cherry-pick
,B
和B'
,得到: o--B'
/ \
...--*--* A'-C'-D'
\
A--B--C--D
最后,让我们重新贴上标签。我们希望
A
指向C
,而D
指向master
,如下所示: o--B' <-- master
/ \
...--*--* A'-C'-D' <-- develop
\
A--B--C--D [abandoned]
原始
B'
链会发生什么?我们在这里将其标记为“放弃”,但实际上,Git使用reflog机制挂了一段时间(例如,我们可以要求Git查找找到原始提交develop
的D'
)和还有特殊名称A--B--C--D
,develop@{1}
设置为指向D
。默认情况下,reflog条目会保留30天3,而名称ORIG_HEAD
会保留30天,直到某些内容(通常是另一个变基)将其覆盖。额外皱纹#2
有时,这种Git魔术(用
rebase
这样的名称来“涂成红色”,然后使用相同的名称来决定将副本放置在何处)是不够的。在某些情况下,您需要告诉D
在某个特定点停止复制,而将新副本放置在其他位置。在这种情况下,您可以使用ORIG_HEAD
:master
(the rebase documentation调用红色颜料“ stop”自变量
git rebase
)。默认值是git rebase --onto
既是git rebase --onto target upstream
目标,也是停止复制红色油漆指示符,并且当停止点在要复制到的点的历史记录中的正确位置时,它才起作用。通常是这样,而且在很多情况下(但并非总是如此),某些分支upstream
作为upstream
给出的是一个--onto
远程跟踪分支,您将其设置为4作为upstream
的上游。我认为这就是为什么rebase将此参数称为foo
。如果两个分支没有共同的提交怎么办?
如果两个分支没有共同的提交怎么办?
在这种情况下,“红色油漆提交节点”步骤对“绿色油漆提交节点”步骤无效:
o--o--o--o <-- master
o--o--o <-- unrelated
如果您在分支
origin/foo
上并运行foo
,则Git有效地将三个upstream
分支提交涂成绿色,将四个unrelated
分支提交涂成红色,然后将涂成绿色的提交涂成绿色。从git rebase master
的尖端提交可以达到三个提交。然后,rebase代码复制这些提交:o--o--o--o <-- master
\
o--o--o <-- unrelated
o--o--o [abandoned]
1Well,
unrelated
大约有一百万个标志,所以这有点夸大其词,因为它对所有标志没有太大帮助。 :-)2这里有很多附带问题:有时
master
实际上直接使用unrelated
,有时却没有。但是,效果几乎相同。3这是可配置的:
git rev-list
和git rebase
控制默认值,并且可以为特定模式设置其他名称。4您可以使用
git rev-list
显式设置此设置,但是对于这些种类的分支,通常在最初使用gc.reflogExpire
创建分支时会自动设置。设置后,没有附加参数的gc.reflogExpireUnreachable
也会自动找到它。
关于git - Git如何选择重新设定基准的起点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38685546/