我可以安全地将一个分支变基到另一个分支然后掌握吗?

Can I safely rebase one branch into other and then to master?

我必须开发分支,我根据分支 A.

中的代码找到了分支 B

我想将 A 变基为 B,这样我就可以继续开发 B

很快 A 将合并到 master(在 B 之前),但不是现在。然后,当我合并 B 时,它是否会破坏引用,使 A 重新基于它?

然后我可以在 master 上对 B 进行变基,一切都会好起来的吗?还是我需要执行任何特殊步骤?

请注意 git(以及几乎所有其他版本控制系统)将此变基称为 onto,而不是 in到.

你确实可以做这种rebase。然而,重要的是要知道当你变基一些提交时,git 实际上 复制 它们到 new 提交。这会影响共享代码的每个人,if 他们已经有了 old 提交。

图片

假设你现在拥有的可以这样画:

...--N--O--P--Q--R   <-- master
      \     \
       \     J--K    <-- A
        \
         E--F--G     <-- B

这里分支A指向提交K,分支B指向提交G,分支master指向提交R(每个提交现在都有这些单字母代码,以便我们可以更轻松地识别它;实际上,它们的名称是 SHA-1 哈希值,如 bfcd84f529b...)。因此 NR 的提交在 master 上(连同隐藏在 N 左侧的所有提交);提交 NOPJK(以及 N 之前的所有提交)都在分支 A;并提交 NEFG(以及 N 左侧的所有提交一如既往)在分支 B 上。

合并基地

提交N是分支Bmaster合并基础,提交P是合并基础分支 Amaster。合并基础只是1 分支分叉之后的提交,如果你像我们刚才那样画图就很容易看出。这对您的 git rebase 很重要,因为 git 使用的默认方法是查找此合并基础提交,并且 copy 提交 after那个点。 (而且,这意味着在你做一个变基之前,你最好画一下图,这样你就可以看到你将要做什么。)

复制提交

要自己复制单个提交,请使用命令 git cherry-pick。你现在不需要这样做——rebase 会为你做——但了解它是如何工作的是个好主意,所以让我们简要介绍一下。

在git中,每次提交都是一个完全独立的快照。例如,考虑在分支 B 上提交 F。提交 F 包含与您的项目关联的完整源代码树,截至您提交 F 时。提交 EG 也是如此。如果我们比较提交E与提交F,我们将看到在"the tree for commit E"和[之间发生了什么变化 =311=]。同样,如果我们比较 FG,我们会发现从 FG 发生了什么变化。

将一个提交转换为一组更改——称为变更集——允许我们复制一个提交。例如,假设我们看到从 NE 发生了什么。然后进一步假设我们检出提交 K 并进行 相同的更改 ,然后将其提交到一个新的临时分支:

...--N--O--P--Q--R   <-- master
      \     \
       \     J--K    <-- A
        \        \
         \        E'   <-- temp branch
          \
           E--F--G   <-- B

我将新提交称为 E',而不是给它一个新的单个字母,因为它是提交 E副本。当然,它与提交 ​​E 不完全相同。一方面,它有不同的父级(它有提交 K 作为它的父级);它可能与 E 有许多其他差异:特别是在提交 OPJK 中发生的任何事情,因为我们已经现在应用 "the changes from N to E onto "K 中的内容。

rebase只是重复拷贝

git rebase 作品作者:

  1. 识别要复制的提交;
  2. 正在使用临时分支复制它们;和
  3. 重新指向原来的分支。

如果你 git checkout B 然后 git rebase A,在步骤 1 中识别的提交是那些在 BA 之间的合并基础之后的提交,直到分支B。这里的 AB 是因为你当前的分支是 B 而你说 git rebase A.

练习:看图并识别 BA 的合并基础。可能有点难发现,但是如果我们像这样重新绘制图形呢?

             Q--R    <-- master
            /
...--N--O--P--J--K   <-- A
      \
       E--F--G       <-- B

(这张图从拓扑上来说,和我们原来画的图完全一样,但是现在超明显commit N是merge base .)

简而言之,这些是提交 EFG。所以 rebase 继续第 2 步并复制这三个提交,将副本放在 ("onto") 分支的尖端之后 A(因为你说 git rebase A)。

因此,在变基过程的这一点上,我们现在有:

             Q--R            <-- master
            /
...--N--O--P--J--K           <-- A
      \           \
       \           E'-F'-G'  <-- temp
        \
         E--F--G             <-- B

rebase 现在只剩下最后一件事要做,就是将当前分支B 重新指向刚刚复制的最后一次提交。这很简单:

             Q--R            <-- master
            /
...--N--O--P--J--K           <-- A
      \           \
       \           E'-F'-G'  <-- B
        \
         E--F--G             [abandoned]

原始提交,EG,在这一点上大部分都消失了(它们仍然通过 reflog 分支 B 但除非你特意去那里看,否则你不会再看到它们了)。 E'G' 的新副本已添加到分支 A 的末端。

为什么这一切都很重要

所有这些复制和改组都发生在您的存储库中,并且不会发生在您存储库的任何其他人的副本中。这意味着任何同事或中央服务器还没有这些副本。如果您 git push 将副本发送到中央服务器,服务器将获得副本——但是如果服务器有原始的、现在被遗弃的提交,并且如果服务器调用它 "branch B",您将必须强制推送到服务器。

执行此强制推送将使服务器丢弃其原始提交的副本,并放置复制的提交并调用它们 "branch B"。这是真的即使,在服务器上,在提交G之后有一个提交H,分支B指向提交H换句话说,这可以 "lose" 从服务器提交。 这是第一个问题:你必须与使用服务器的其他人协调,这样你就不会丢失提交。

此外,在执行强制推送后,and/or 如果您的同事正在共享您的存储库(通过 fetching/pulling 来自它),您的同事可能需要调整 他们的 存储库来说明复制提交和移动分支标签。对他们来说,这是一个上游变基他们必须从中恢复。这是第二个问题:您必须与使用该分支的其他人协调,以便他们知道从上游 rebase 中恢复。

简而言之,请确保您的所有合作开发人员都同意这一点。

如果您有其他人与您一起工作,并且他们共享您将变基的分支,请确保他们都知道。如果您将变基的这些提交 发布——如果它们完全是您自己的存储库私有的——那么这种协调根本不需要任何努力,因为没有其他人可以通知,所以没有人可以反对。

正在合并,可能会再次变基

Soon A will be merged to master (before B), but not now. Then, when I merge B, will it break the reference from having A rebased on it?

同样,回答这些问题的方法是从绘制提交图开始。让我们使用新的、重新定位的绘图,然后添加将 B 合并到 master 中的合并提交。由于我们要显示合并,我们需要再次重新绘制图形,以便更容易连接行:

...--N--O--P-Q-R             <-- master
            \
             J--K            <-- A
                 \
                  E'-F'-G'   <-- B

现在我们将 运行 git checkout mastergit merge A 进行新的合并提交 M:

...--N--O--P-Q-R--M          <-- master
            \    /
             J--K            <-- A
                 \
                  E'-F'-G'   <-- B

我们不必在此处更改 B 中的任何内容,我们可以像往常一样继续开发它。但是,如果我们想要——并且如果我们所有的合作开发者都同意——我们现在可以再次变基B,这次是master

如果我们 git checkout B; git rebase masterrebase 将像往常一样从找到 Bmaster 之间的合并基开始。什么是合并基础?查看图表并找到 masterB 连接的点。它绝对不是新的合并提交 M,但也不再是 N

分支 B 上的提交是 NOPJKE'F'G'(以及 N 左侧我们看不到的任何提交)。这部分很简单。棘手的部分是分支 master 上的提交。这些是 NOPQRM,但 JK。这是因为这两个提交可以通过跟随提交 Msecond parent.

来实现

因此,合并基础(定义为两个分支上的 "closest" 提交)实际上是提交 K。这正是我们想要的!要将 B 变基到 master,我们 想要 git 复制 E'F'G'.新副本 E''F''G'' 将在 M 之后,我们将得到:

                    E''-F''-G''   <-- B
                   /
...--N--O--P-Q-R--M               <-- master
            \    /
             J--K                 <-- A
                 \
                  E'-F'-G'        [abandoned]

这里真正的教训是绘制图形,因为它会帮助您弄清楚发生了什么。


1至少只有一个这样的点就简单了。在更复杂的图中,可以有多个合并基础。不过,使用复杂的图形进行变基需要的内容比我在这个特定的 SO 帖子中写的要多得多。