Git在git rebase之后合并?

时间:2014-03-31 18:21:30

标签: git git-rebase

我正在阅读这篇文章http://supercollider.github.io/development/git-cheat-sheet.html,其中提出了以下工作流程:

git checkout master
git pull --rebase # update local from remote
git rebase master chgs
git checkout master
git merge chgs
git push

我确定我不明白这是如何运作的以及为什么它有用。

第三行git rebase master somechanges是否在chgs的最后一次提交后从master提交了提交但未合并它们,并留下HEAD指向来自chgs的最新提交,如下所示:

(master-commit-n-1)<--(master-commit-n)<--(chgs-commit-k-1)<--(chgs-commit-k)<--HEAD

这会让我chgs签出吗?这就是为什么我需要再次查看master的原因?

为什么我们需要将更改合并到master中?不能制作这样的图表:

(master-commit-n-1)<--(master-commit-n)<--(chgs-commit-k-1)<--(chgs-commit-k)
                                        \.(master-with-merged-chgs-commit-k)<--HEAD

我不明白为什么顶叉很有用。

3 个答案:

答案 0 :(得分:3)

#1

  

不是第三行git rebase master somechanges来自chgs的提交   在master的最后一次提交之后但没有合并它们,并离开HEAD   指向chgs的最新提交,如下所示:

(master-n-1) <- (master-n) <- (chgs-k-1) <- (chgs-k) <- HEAD

chgs重新定位到master的效果相当于将master合并到chgs,因为上次提交时代码的状态将等同于州在merge之后。

#2

  

这会让我chgs签出吗?这就是我需要退房的原因   再次master

#3

  

为什么我们需要将更改合并到master中?这不是图表   像这样:

(master-n-1) <- (master-n) <- (chgs-k-1) <- (chgs-k)
                          \
                           (master-with-merged-chgs-k) <- HEAD

没有。由于您在chgs之上重新定位master,因此git会认识到chgs上的最后一次提交实际上代表了master“+”chgs的状态,因此它只是将master分支“快速转发”到chgs的提示而不进行合并提交:

(n-1) <- (n) <- (k-1) <- (k) <- HEAD/master/chgs

如果您希望 强制 git执行合并提交,您可以将--no-ff标记传递给merge

git merge --no-ff chgs

# Results in this

(n-1) <- (n) <- (k-1) <- (k) <- chgs
            \               \
              --------------(merge) <- HEAD/master

#4

  

我不明白为什么上叉很有用。

更新来自master的上游更改的功能分支非常有用,而无需在功能分支中创建一堆冗余的合并提交。最终结果是您的开发历史变得更简单,更容易使用,而不是包含一些您并不真正需要的额外合并提交,这只会使历史更复杂,更难以操作。

答案 1 :(得分:2)

按顺序:

  

第三行git rebase master somechanges是否在chgs的最后一次提交后从master提交了提交但没有合并它们......

是的,假设somechangeschgs的拼写错误(引用的页面使用了更多的拼写)。

  

并将HEAD指向chgs

的最新提交

是,但只是间接地:HEAD字面上包含ref: refs/heads/chgs,即您选择了分支chgs。它的chgs本身指向最新的提交。

  

这会让我chgs签出吗?

具体来说,使用on branch ...时打印为git status的&#34;当前分支&#34;就是refs/heads/branch文件HEAD中的任何内容},HEAD包含refs: refs/heads/branch时。首先运行git checkout branch可确保更新工作树,然后执行此操作并通过重写HEAD将您置于该分支上。

在内部,git rebase upstream branch调用git checkout branch,所以整个过程从让你进入分支开始。

  

为什么我们需要将更改合并到master中?是不是像[剪掉]那样制作图表?

您需要 1 merge来移动标签。

我更喜欢使用字母或有时只是小o个节点绘制提交图,其中单个破折号或短箭头表示父母关系。并且,从git log --graph --oneline --decorate窃取(但修改)一个叶子,我在右边添加一个带有更长箭头的分支名称。如果HEAD为分支命名,我会在前面添加HEAD=

因此,在变基之后你所拥有的就是:

... - E - F               <-- master
            \
              G - H - I   <-- HEAD=chgs

此处标记为F的提交是分支master的提示,因此master指向F(并且F指向E } 等等)。分支chgs的提示是提交I; I指回H; H指回G;并G指向F。当然HEAD=chgs所以你就在那个分支上。

一旦git checkout master,就像往常一样更新工作树,并使HEAD指向master,指向F。然后,如果您运行git merge chgs,git会查看是否可以快速前进&#34;合并:

... - E - F               <-- HEAD=master
            \
              G - H - I   <-- chgs

当前提交(在这种情况下为F)已经是目标提交(I)的祖先时,可以进行快进合并。如果是这样,分支标签将从当前提交中剥离并简单地粘贴到目标提交:

... - E - F
            \
              G - H - I   <-- chgs, HEAD=master

(已经不再有任何ASCII艺术理由在图纸中保持扭结[从F向右 - 向右到G],但我保留了视觉效果对称性。)

作为Cupcake noted in his faster answer,您可以强制与--no-ff进行真正的合并。我会把它画成:

... - E - F ------------- M   <-- HEAD=master
            \           /
              G - H - I       <-- chgs

在任何一种情况下(快进,或&#34;真正的合并&#34;),一旦完成,您可以安全地删除分支标签 chgs,因为所有它的提交是可以找到的,从master开始并向后工作(如果&#34;真正的合并&#34;则沿着两个分支)。或者,如果您愿意,可以保留它并添加更多提交。

这里有一个有趣的注意事项是,在这种特殊情况下,与提交M相关联的结果工作树与提交I的结果完全相同。关键的区别在于,这会创建一个实际的合并提交(具有多个父项的提交)。第一个父是之前在分支上的任何提交 - 在这种情况下,提交F - 剩余的提交(这里只是一个提交I)是合并的分支。

(你可以覆盖这个 - 工作树是相同的,我的意思是 - 有更多的合并标志,但在这种情况下你不会想要。那种事情,覆盖工作树,主要是指为了&#34;在保留历史的同时杀死了一个分支:看起来像是一个&#34;看起来,我们尝试了这个并且它没有工作,并且明年有人在看代码。 )


1 或者如果您不想移动标签,则不需要。但是,如果您打算将push您的作品发回给某人,或让他们pull给您,他们会查看您的标签。如果你很好地为他们安排标签,那对他们来说真好。但它始终取决于你。

答案 2 :(得分:1)

以下更详细地解释了每个命令正在做什么。

  

git checkout master

切换到master分支。

假设我们在本地有这个(其中*表示当前分支):

M1 -- M2 = master*
       \__ C1 = chgs

  

git pull --rebase

从远程分支更新本地master分支,重新定义对master分支所做的任何本地更改,以便它们在远程更改后进行。

假设这给我们一个新的M3改变:

M1 -- M2 -- M3 = master*
       \__ C1 = chgs

  

git rebase master chgs

重新定义来自本地chgs分支的更改,以便它们从本地master分支继续(但保留在它们自己的分支中)。也改为chgs分支。

M1 -- M2 -- M3 = master
             \__ C1' = chgs*

  

git checkout master

切换回master分支。

M1 -- M2 -- M3 = master*
             \__ C1' = chgs

  

git merge chgs

chgs合并到master

M1 -- M2 -- M3 ------- M4 = master*
             \__ C1' __/ = chgs

  

git push

将这些更改推送到原点。