我们假设我已在branch1
上开发了一项功能,并使用GitHub Pull Request将其发送给代码审核。在对其进行审核时,我会对branch2
进行一些后续工作。
branch2 -> D --> E --> F
/
branch1 -> A --> B --> C
/
master M
我的评论员喜欢我的作品!无需更改。我使用GitHub的Squash and merge功能合并了branch1
的提取请求。
在git pull
上运行master
并删除branch1
后,我就离开了这种情况:
branch2 -> A --> B --> C -> D --> E --> F
/
master M --> S
要为branch2
发送一个外观整洁的PR,我想让我的提交树看起来像这样:
branch2 -> D' --> E' --> F'
/
master M --> S
S
处的代码(#34生成的提交; Squash和合并#34; branch1
生成的提交)与C
相同,因为它只是压扁版A --> B --> C
。
实现此目标的一种方法是在branch2
上运行这样的序列:
git reset --hard S
git cherry-pick D E F
但是以这种方式列出所有提交变得乏味,而且这真的感觉就像一个rebase。当然,git rebase master
无法工作,因为提交A
,B
和C
需要消失。
从一个祖先提交的压缩版本中修改分支的最佳方法是什么?
答案 0 :(得分:10)
将git rebase
与--onto
一起使用。这仍然有点棘手,所以为了方便起见,你会想要做一件与众不同的事情。
顺便说一下,我认为用 right 侧的分支名称绘制这些图表会更好,指向一个特定的提交。这是因为在Git中,提交在多个分支上,而分支名称 do 只是指向一个特定的提交。它也值得翻转内部箭头(因为Git真的以这种方式存储它们)或者只是使用连接线以免意味着错误的方向。
因此:
D--E--F <-- branch2
/
A--B--C <-- branch1
/
M <-- master
提交A
到C
确实在branch1
和 branch2
,同时提交D
到{{1}在F
上只有 。 (提交branch2
及更早版本在所有三个分支上。)
M
做的是选择所有 1 提交从当前分支到达,但无法从 git rebase upstream
参数,然后复制它们(使用upstream
或等效项),以便它们在 git cherry-pick
提交后立即生效。
壁球后 - &#34;合并&#34; (不是真正的合并),如果你运行upstream
然后快进你的git fetch
,你就会得到同样的东西,但我会留下master
并将标签放在离开并在此处添加branch1
:
origin/master
(或者,如果您没有快进 D--E--F <-- branch2
/
A--B--C <-- branch1
/
M--S <-- master, origin/master
,只需master
分即可提交origin/master
。
您现在想告诉Git使用cherry-pick复制S
,然后移动标签D-E-F
以指向上次复制的提交。您 希望复制branch2
,因为它们已合并到A-B-C
中。您希望副本在S
后追踪S
现在指向的位置 - 无论您是否已更新origin/master
。因此:
master
git checkout branch2
git rebase --onto origin/master branch1
现在是upstream
而不是branch1
,但master
会告诉Git将副本放在哪里:{{1} }仅用于界定不要复制的内容。所以现在Git复制--onto
并更改branch1
指向那里:
D-E-F
和现在您可以删除名称branch2
。 (现在你可以快进 D--E--F [abandoned]
/
A--B--C <-- branch1
/
M--S <-- master?, origin/master
\
D'-E'-F' <-- branch2
如果你还没有 - 当你这样做时并不重要,事实上你不需要你自己branch1
来所有。)
1 更准确地说,rebase选择的提交是(a)不合并提交和(b)与排除集中的某些提交没有相同的master
,使用对称区别。也就是说,而不是master
,Git实际上在git patch-id
upstream..HEAD
上运行git rev-list
,用upstream...HEAD
或类似的方式来挑选提交。实现方式略有不同,具体取决于特定类型的rebase。