合并成功后Git rebase冲突? [英] Git rebase conflicts after successful merge?
问题描述
我有两个分支:让我们称它们为 master
和功能
。我现在正试图将最近从主变更到功能。我通常比合并更喜欢rebase,但是这两个分支有显着的分歧,所以我决定做一个合并来记录所有的冲突解决方案。我完成了合并,并将其投入功能,并认为我完成了。一切看起来都不错。
现在,我对主进行了一些小改动,并希望将其重新转换为功能,但 git rebase master
现在让我与在上次合并中已经处理的较旧提交发生冲突。奇怪的是, git merge master
没有按预期发生冲突。什么给了?
你的措词(rebase [我通常会将 master
。 (into一词并不适用。)除此之外,我认为绘制提交图总是有帮助的。您可以使用 gitk
或类似的查看器来绘制它,或者使用 git log --graph
,或许使用 - oneline
以绘制垂直方向的图。但是在这里我会画一个水平的,用单个大写字母代表特别有趣的提交,小写 o
代表更无聊的提交。
对于我们的目的,我将忽略任何配置的上游( origin / master
和/或 origin / feature
)。如果它们确实存在,则可能需要将它们添加到自己的图形中,然后注意,当 git rebase
创建提交副本时,它不会移动任何其他标签(包括这些远程跟踪分支)指向现有提交:它只移动一个标签,即当前分支标签。
... - A - o - o - o - F < - master
\\\
B - C - D - E - G < - HEAD - >功能
幸运的是,这相当接近您的预先设定,准确地反映了结果你的 git merge
。在合并之前和之后,提交 A
是原始基础,其中功能
从<$ c分离$ C>主;提交 B
至 E
是在功能
;各种不太有趣的 o
提交是在 master
;并提交 F
是主控的提示。您在分支功能
(以便 HEAD
>命名分支功能
>和 git status
在分支功能上说),然后运行 git merge master
,并进行了合并和提交。
这个合并创建了特性
中的最小提交,它是提交 G ,这是一个合并提交。
之后,您检出分支 master
(使 HEAD
指向名称 master
),并提交了一个新的提交,将 master
,所以让我们将其添加到我们的图表中:
... - A - o - o - o - F - H - - - - - - - master
\\\
B - C - D - E - G < - 特征
最后,你想在特性
git checkout feature
:
... - A - o - o - ○ - F - H < - 主
\\
B - C - D - E - G < - 头 - >功能
现在运行命令 git rebase master
。
$ b $ rebase
does是 copy 提交。
首先,它必须找到要复制的提交。它应该复制的提交是那些可从当前分支到达的提交 - 即从名称特性
- 但不是来自您作为上游提供的名称的分支,即, master
。
以下是我们遇到的一个相当大的问题。看看最近的<$ c(相当密集)段落$ c $> git rebase 文档:
lockquote
当前分支中的所有更改提交,但是不在
<上游>被保存到临时区域。这是与 git log< upstream> .. HEAD
所显示的相同的一组提交。或 git log'fork_point'.. HEAD
,如果 - fork-point
处于活动状态(请参阅 - 下面的叉点
);或者如果指定了 - root
选项, git log HEAD
。
分叉点本身有点复杂,但现在我们可以忽略它,因为使用命令 git rebase master
意味着它已关闭。因此,您可以通过 git log master..HEAD
查看提交。这是提交 B
, C
, D
, E
和 G
(除了通常会发生合并)。
您可能想知道为什么 B
到 E
包含在这里,因为 master
和特性
是commit G
。问题是,当合并提交 G
指向提交 F
(可从master访问)时,提交 F
没有(也不能)指向前到 G
。因此,当我们从 master
(新提交 H
)的提示开始并向后工作时,我们得到 H
, F
,所有无聊 o
s, A
,以及 A
之前的所有内容:这些是排除的提交 。当我们从特性
(提交 G
)的提示开始并向后工作时,我们得到 G
, E
, D
, C
和 B
,然后我们点击排除的第一个( A
)。因此,这些都是rebasing的候选人。
如果您允许rebase继续进行并解决所有冲突,那么您最终会得到:
B' - C' - D' - E'< - HEAD - >特征
/
... - A - o - o - o - F - H - -
\\ -
B - C - D - E - G [弃用]
(假设所有待复制的提交都有实际的复制修改;提交 G
不需要复制,因为这次不会提供任何内容)。
I have two branches: let's call them master
and feature
. I'm now trying to merge recent changes from master into feature. I normally prefer rebases over merges, but these two branches have diverged significantly, so I decided to do a merge instead to have a record of all the conflict resolution. I finished up the merge, and committed it on feature, and thought I was done. Everything looked good.
Now, I made a small change to master and wanted to just rebase that into feature, but git rebase master
now gives me conflicts with older commits that were already dealt with in my last merge. Weirdly, git merge master
gives no conflicts as expected. What gives?
Your phrasing ("rebase [a small change made to master] into feature") seems a bit weird to me, since normally you'll be rebasing feature
onto master
. (The word "into" doesn't really apply well.) That aside, I think it always helps to draw the commit graph. You can use gitk
or a similar viewer to draw it, or git log --graph
, perhaps with --oneline
as well, to draw a vertically oriented graph. But here I'll draw a horizontal one, with single uppercase letters representing particularly interesting commits, and lowercase o
s representing more-boring commits.
For our purposes here I will leave out any configured upstreams (origin/master
and/or origin/feature
). If they do exist, you might want to add them to your own drawing, and then note that when git rebase
makes copies of commits, it does not move any of the other labels (including these remote-tracking branches) pointing to existing commits: it only moves one single label, that of the current branch.
... - A - o - o - o - F <-- master
\ \
B - C - D - E - G <-- HEAD -> feature
With any luck this is reasonably close to your pre-rebase setup, and accurately reflects the result of your git merge
. Both before and after you did the merge, commit A
is the original base at which feature
split off from master
; commits B
through E
were made on feature
; the various less-interesting o
commits were made on master
; and commit F
was the tip of master. You were on branch feature
(so that HEAD
named branch feature
, and git status
said "on branch feature") and you ran git merge master
and did the merge and committed.
This merge created the tip-most commit on feature
, which is commit G
, which is a merge commit.
After that, you checked out branch master
(making HEAD
point to the name master
) and made a new commit that moved the tip of master
, so let's add that to our graph-so-far:
... - A - o - o - o - F - H <-- HEAD -> master
\ \
B - C - D - E - G <-- feature
Finally, you wanted to rebase feature
on the (new tip of) master
so you did git checkout feature
:
... - A - o - o - o - F - H <-- master
\ \
B - C - D - E - G <-- HEAD -> feature
and now you run the command git rebase master
.
What rebase
does is copy commits.
First, it has to find which commits to copy. The commits it should copy are those that are reachable from the current branch—i.e., from the name feature
—but not from the branch whose name you give as the upstream, i.e., master
.
Here's where we run into a rather large problem. Look at this (rather dense) paragraph from recent git rebase
documentation:
All changes made by commits in the current branch but that are not in <upstream> are saved to a temporary area. This is the same set of commits that would be shown by
git log <upstream>..HEAD
; or bygit log 'fork_point'..HEAD
, if--fork-point
is active (see the description on--fork-point
below); or bygit log HEAD
, if the--root
option is specified.
The fork point stuff is itself a bit complicated but we can ignore it for now because using the command git rebase master
means it's turned off. You can therefore see the commits that are to be rebased with git log master..HEAD
. This is commits B
, C
, D
, E
, and G
(except that rebase normally tosses out merges).
You might wonder why B
through E
are included here, given that the merge base of master
and feature
is commit G
. The problem is that, while merge commit G
points back at commit F
(reachable from master), commit F
does not (and cannot) "point forward" to G
. So when we start from the tip of master
(new commit H
) and work backwards, we get H
, F
, all the boring o
s, A
, and everything before A
: these are the commits excluded. When we start from the tip of feature
(commit G
) and work backwards, we get G
, E
, D
, C
, and B
before we hit the first excluded one (A
). Thus these are the candidates for rebasing.
If you allow the rebase to proceed, and resolve all the conflicts, you'll end up with:
B' - C' - D' - E' <-- HEAD -> feature
/
... - A - o - o - o - F - H <-- master
\ \
B - C - D - E - G [abandoned]
(assuming all of the to-be-copied commits have actual changes to copy; commit G
won't need to be copied as it won't contribute anything this time).
这篇关于合并成功后Git rebase冲突?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!