在 GitHub "Squash and merge" 之后将分支变基到 master

Rebase branch after GitHub "Squash and merge" onto master

假设我在 branch1 上开发了一项功能,并使用 GitHub 合并请求将其发送出去进行代码审查。在审查期间,我会在 branch2.

上做一些后续工作
 branch2                   -> D --> E --> F
                          /    
 branch1  -> A --> B --> C
         /
 master M

我的审稿人喜欢我的作品!无需更改。我使用 GitHub 的 Squash and merge 功能合并 branch1 的拉取请求。

master 上 运行 宁 git pull 并删除 branch1 后,我留下了这种情况:

 branch2  -> A --> B --> C -> D --> E --> F
         /
 master M --> S

要为 branch2 发送一个看起来很干净的 PR,我想让我的提交树看起来像这样:

 branch2        -> D' --> E' --> F'
               /
 master M --> S

S 处的代码("Squash and merge" 为 branch1 生成的提交)与 C 相同,因为它只是 [=26= 的压缩版本].

实现此目的的一种方法是 运行 在 branch2:

上这样的序列
git reset --hard S
git cherry-pick D E F

但是以这种方式列出所有提交变得乏味,这真的感觉 像一个变基。 git rebase master 当然不会起作用,因为提交 ABC 需要消失。

将分支从其祖先提交之一的压缩版本中变基的最佳方法是什么?

git rebase--onto 结合使用。这仍然有点棘手,所以为了简单起见,您需要更早地做一件不同的事情。

我认为最好在 右侧 一侧绘制这些带有分支名称的图表,指向一个特定的提交。这是因为在 Git 中,提交在 多个分支 上,而分支名称实际上 do 仅指向一个特定的提交。也值得反转内部箭头(因为 Git 确实以这种方式存储它们)或仅使用连接线以免暗示错误的方向。

因此:

          D--E--F   <-- branch2
         /    
  A--B--C       <-- branch1
 /
M          <-- master

提交 AC 确实在 branch1 branch2 上,而提交 D Fbranch2 只有 。 (提交 M 及更早版本在所有三个分支上。)

git rebase <em>upstream</em> 所做的是 select all1 提交 可从 当前分支到达,但 无法从 到达 upstream 参数,然后复制它们(使用 git cherry-pick 或等效项),以便它们在 upstream 提交之后立即出现。

在 squash-"merge"(不是真正的合并)之后,如果你 运行 git fetch 然后 fast-forward 你的 master,你有和你画的一样,但我把 branch1 留在里面,把标签放在左边,然后在这里添加 origin/master

          D--E--F   <-- branch2
         /    
  A--B--C       <-- branch1
 /
M--S       <-- master, origin/master

(或者,如果您还没有 fast-forward 您的 master,则只有 origin/master 指向提交 S)。

你现在想要告诉 Git 用 cherry-pick 复制 D-E-F,然后移动标签 branch2 指向最后复制的提交。您不想复制A-B-C,因为它们已并入S。您希望副本位于 S 之后,origin/master 现在指向它——无论您是否已更新 master。因此:

git checkout branch2
git rebase --onto origin/master branch1

upstream 现在是 branch1 而不是 master,但是 --onto 告诉 Git 在哪里放置副本:branch1 仅用于划定 不可复制的内容 。所以现在 Git 复制 D-E-F 并更改 branch2 指向那里:

          D--E--F   [abandoned]
         /    
  A--B--C       <-- branch1
 /
M--S       <-- master?, origin/master
    \
     D'-E'-F'   <-- branch2

现在你可以删除名字branch1。 (现在你可以 fast-forward master 如果你还没有——你什么时候做并不重要,事实上你根本不需要你自己的 master。 )


1更准确地说,rebase selects 提交是 (a) 不合并提交和 (b) 与 git patch-id 不同一些在排除集中提交,使用对称差异。也就是说,而不是 <em>upstream</em>..HEAD,Git 实际上 运行s git rev-list on <em>upstream</em>...HEAD,用 --cherry-mark 或类似的,来挑选提交。根据特定类型的变基,实现略有不同。