git - Git如何选择重新设定基准的起点?

标签 git rebase

git rebase如何从源(通常是功能)分支中选择启动提交?

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

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

最佳答案

要知道的一件事-您可能已经知道了-可以通过复制提交来进行基础调整。它只复制正确的提交,使新副本在新库结束后立即发布。

选择要变基(复制)的提交实际上是使用最关键的事情之一来了解Git及其提交选择的。了解这一点后,您还将了解git loggit 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将庞大的丑陋哈希保存在分支名称(例如masterdevelop)中,我们可以让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提交之一匹配或多或少匹配最下面的*提交之一怎么办?忽略多余的东西会很好。让我们将底线重新定位到顶部,但我们将这些提交标记为ooAB,并注意在顶部,C之一就像

          o--B'
         /
...--*--*
         \
          A--B--C--D


(例如,这是使用D时得到的图形)。提交oB基本上是彼此的副本。因此,当我们调整较低的四个提交的基准时,我们实际上应该只复制cherry-pickBB',得到:

          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查找找到原始提交developD')和还有特殊名称A--B--C--Ddevelop@{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-listgit rebase控制默认值,并且可以为特定模式设置其他名称。

4您可以使用git rev-list显式设置此设置,但是对于这些种类的分支,通常在最初使用gc.reflogExpire创建分支时会自动设置。设置后,没有附加参数的gc.reflogExpireUnreachable也会自动找到它。

关于git - Git如何选择重新设定基准的起点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38685546/

相关文章:

git - SmartGIT 不要求 ssh key

git 更改分支的起源(rebase)

svn - Git rebase 到稍后创建的分支

Git rebase vs 重新创建分支并应用更改

git - 为什么 Git 不问我就记住并使用来自中止的 rebase 的冲突解决方案?

git - 如何让 git 只存储我的用户名而不是我的密码?

git - 使用 git push 保存 .sqlite

git rebase : passing multiple strategy options

git - 无法将 sslVerify 设置为 false

git - 如何知道我是否正在使用 Mercurial 进行 histedit?