我和git有点麻烦。
这就是我所做的。
我可以知道如何强制将master的所有更改合并到我的分支中吗?
提前致谢。
答案 0 :(得分:2)
让我们说你的功能分支feature
和master
就像这样开始:
master: A -> B -> C
feature: A -> D
将master
合并到功能后,事情就是这样:
master: A -> B -> C
feature: A -> D -> M # M is a merge commit
接下来,通过执行feature
,恢复 git revert
分支中的合并。这意味着您告诉Git 添加新提交以撤消合并的结果。现在这是两个分支的状态:
master: A -> B -> C
feature: A -> D -> M -> R # R is a revert commit
当您尝试将master
拉入feature
时,Git会告诉您,您已经是最新的,因为您已经是!恢复提交在功能上解除了合并期间发生的任何事情,但现在您的feature
分支中有两个新的提交。
要回到您在feature
分支中进行错误合并之前所处的状态,您可以在feature
中修改两个合并并恢复提交。完全按照以下步骤操作:
git checkout feature # switch to your feature branch
git reset --hard HEAD~2 # nuke the 'M' and 'R' commits
在此之后,您可以尝试再次与master
合并,一切都应该没问题。当然,请确保正确进行合并。
请注意,此选项涉及通过nuking提交重写 feature
分支的历史记录,如果分支是和,则可能不应该使用此选项您已经使用合并提交推送了分支。如果您属于此类别,请参阅@torek的答案以获取其他选项。
答案 1 :(得分:1)
您至少有三个选项。其中一个请参见Tim Biegeleisen's answer:它是最简单的,如果您没有其他提交且未推送合并(或合并并恢复),则可以使用它。
让我以这种方式重新绘制他的master
- 和 - feature
图表:
A - B - C <-- master
\ (pre-merge)
D <-- feature
和
A - B - C <-- master
\ \ (post-merge)
D --- M <-- feature
使合并提交成为合并提交的事情是它有两个父母:&#34;主线&#34; parent(在这种情况下为D
)和已合并的提交(C
)。
当你要求git执行合并时,git首先找到一个共同的祖先,在这种情况下提交A
。然后它会做两个差异:A
vs D
(主线上发生了什么,feature
)和A
vs C
(分支上发生的事情是合并,master
)。然后它组合了这两个差异集,以便它可以创建一个新的提交,其中包含两行中每个更改的一个副本。 1 如果一切顺利,它会自动提交结果;如果没有,它会因合并冲突而停止,让你解决问题,并提交最终结果。无论哪种方式,最终提交M
都会将提交C
和D
列为其(两个)父提交,而不仅仅是一个父提交。在git中,这是一个合并的定义:一个提交有两个 2 父母。
为了便于说明,我们暂时在feature
上添加一个新的普通提交:
A - B - C <-- master
\ \
D --- M - E <-- feature
如果在分支feature
上你要求git再次合并master
,会发生什么?
Git必须首先找到feature
和master
(又名&#34;合并基础&#34;)的最新共同祖先(提示)。提交E
的祖先,feature
的提示,按照距离的顺序 - E
本身(零退步); M
(后退一步); C
和D
(两个退步:两者都是M
的父母);和A
和B
(后退三步)。 (还有另一种方法可以到达A
后退四步但我们采用更短的路线。)提交C
的祖先,master
的提示,从C
本身开始(零退步)。所以这是合并基础,并且为了合并master
,git应该将C
(合并基础)与C
区分开来......但显然那里没有变化,所以什么都没有合并,你得到了最新的&#34;消息。
但是还原呢?好吧,就git而言,恢复只是一个普通的提交。使其成为回归的原因是它所适用的变化&#34;撤消&#34;来自先前提交的更改。也就是说,要进行恢复,git基本上会执行git diff
(对于合并的任何一方),但是然后在差异所说的任何地方&#34;添加一行&#34;,git 删除< / em>该行代替,或者说#34;删除行&#34;,git 放回已删除的内容。 (当你恢复合并时这有点棘手,因为git必须避免撤消某些东西,当你完成合并时,已经在两个方面完成了;但是git做得对。)
回到手头的问题,即重新进行合并:您可以使用建议的方法,即(实际上)删除不需要的还原和不需要的合并。使用git reset --hard
会做到这一点,让你回到原来的合并前状态:
A - B - C <-- master
\
D <-- feature
(额外提交M
和R
仍然存储在存储库中,但是从视图中隐藏,最终将被垃圾收集。)现在您可以重新进行合并,大概是获取这次是对的。
最简单方法的一个缺点是,当您进行新的(更正的)合并时,它将具有新的且不同的基础SHA-1 ID:
A - B - C <-- master
\ \
D --- M2 <-- feature
就其本身而言,这是必要的,甚至是可取的。但是,如果您已经推送(或以其他方式发布)合并提交M
某处(有或没有推动还原R
),其他人可能拥有该原始错误合并的副本。推送新feature
将需要使用--force
来重写历史记录&#34;。任何选择错误合并M
的人都可能依赖它,重写历史会让他们感到困难。
(你当然可以选择对他们说&#34;运气不好&#34;让他们处理你的重置和重新合并。这是简单的方法,有时甚至是最好的方式。但如果不是,我会按下。)
如果你从糟糕的提交中做了一些好的提交,你可能不想这样做的另一个原因是:
A - B - C <-- master
\ \
D --- M - E - R - F <-- feature
让我们说提交M
是错误的合并,R
是它的回归,但E
和F
都是好的和可取的。如果您使用git reset --hard
清除M
,则还会清除E
和F
。那么现在呢?
Git是git,有很多方法可以解决这个问题。你必须选择:
让我们先解决最后一个问题。您可以使用git format-patch
将这些提交保存为修补程序。这可以让你将其删除(使用git reset --hard
),然后重新应用它们(使用git am
)。或者,您可以将它们保留在存储库中,以便您可以使用git cherry-pick
复制它们,或将它们保留在原始的git提交链中,以便您可以获得快速前进的历史记录。
让我们看看下一个快进项目。这基本上意味着每个发布的提交(您通过git pull
或其他任何方式推送或提供给其他人的每个提交)必须保持不变:您必须只有将添加到此类历史记录中。这真的就是它的全部(当然这意味着将你的错误留在那里供其他人查看)。
最后,让我们看一下中间项目:你真的想重新进行合并,或者只是采取什么措施并修复它?实际上第三个选项,起初看起来有点愚蠢:你可以完全重新进行合并,然后(使用那个)修复破坏的那个。这种方法的优势在于它可以让您轻松地生成快速前进的历史记录。
实际上有几种方法可以做到这一点,但我只会特别展示一种方法。你希望git看到它看到的相同的设置&#34;预合并&#34;即使有合并。我将再次绘制相同的图形,但稍微伸出一点:
A - B - C <-- master
\ \
D \
\ \
----- M - E - R - F <-- feature
现在让我们轻松做一些git,即检查提交D
。我们可以找到其ID(78cefa3
或其他),或者在feature
上重新计算提交:F
为0,R
为1,E
为2回,M
回到3,D
......好吧,它有点棘手:D
和C
都回来了4,但是{{1在&#34;主线&#34;上,D
将命名提交feature~4
。
无论哪种方式,我们都会检查出来...但是我们为它制作了一个新的分支标签,让我们称之为D
:
feature-alt
现在我们有了这张图:
$ git checkout -b feature-alt 78cefa3 # by ID
(我在A - B - C <-- master
\ \
D ....\................ <-- feature-alt
\ \
----- M - E - R - F <-- feature
s中显示.
直接指向提交feature-alt
)。现在,即使有一个D
分支,让我们暂时忽略,并再次绘制图形:
feature
这应该看起来非常熟悉:它是我们进行错误合并时的图表。现在,当我们在A - B - C <-- master
\
D <-- feature-alt
时,我们只需运行:
feature-alt
这次正确合并(如果你愿意,可以使用$ git merge master
,以防止git提交自动合并结果,所以你可以先修复它。)
一旦合并完成并提交,我们就会有:
git merge --no-commit
我在此处添加了A - B - C___ <-- master
\ \ \
D ----\-- M2 <-- HEAD=feature-alt
\ \
----- M - E - R - F <-- feature
,以表明我们仍然使用新的合并提交HEAD=
feature-alt
。请注意,M2
包含父级M
和D
(按此顺序),C
也是如此,M2
的内容 (推测)是不同的(这次合并是正确的)。
此时,您只需M2
提交git cherry-pick
和E
,然后删除分支F
并将feature
重命名为feature-alt
,并使用feature
推送结果。这给了你一个干净的历史与正确的合并和你的两个良好的提交。它不能快进,但会保留您在--force
和E
中所做的更改。
或者,如果F
是正确的合并结果,并且您需要快速前进的历史记录,那么您现在需要的是将M2
中的更改应用于现有分支的提示{ {1}}。由于M2
还原了feature
,您现在可以执行此操作:
R
这就像任何其他樱桃选择一样,除了你必须指定合并的主线(就像M
)。这实际上说,&#34;比较$ git checkout feature
$ git cherry-pick -m 1 feature-alt
(第一个父母)和git revert
并将这些更改应用到当前分支D
&#34;的提示:这是,把正确的合并,而不是错误的合并。
如果一切顺利,你现在就拥有这个:
M2
其中feature
是A - B - C___ <-- master
\ \ \
D ----\-- M2 <-- feature-alt
\ \
----- M - E - R - F - M3 <-- HEAD=feature
的副本(但是是常规的非合并提交)。
如果一切顺利,您现在可以完全删除M3
分支。错误合并在历史记录中保留,其恢复也是如此,并且合并的更正版本看起来好像通过魔法作为普通提交。
如果所有这些都太复杂而您不想希望重复合并,那么您只需还原合并的恢复,即可恢复错误的合并:
M2
变为:
feature-alt
其中A - B - C <-- master
\ \
D --- M - E - R - F <-- feature
撤消A - B - C <-- master
\ \
D --- M - E - R - F - R2 <-- feature
。现在,您可以进行所需的任何修补,并R2
调整R
(甚至只需git commit --amend
而不R2
只需在顶部添加其他修补程序,然后使用{{ 1}}在推进之前将其压扁。)这样做的目的是使git commit
与--amend
完全相同,git rebase -i
是精选合并的R2
。您避免重新进行合并,但当然,您必须弄清楚如何解决错误的合并。
在任何情况下,所有这一切的最终结果都可以在没有M3
的情况下推送,因为它只是在所有以前的上添加新提交。
1 例如,如果M2
- 至 - -f
将一行文字添加到A
和D
- 至 - {{ 1}}向README.txt
添加不同的文本行,新的提交A
将每行添加到每个文件中。但是,如果C
- 到 - main.c
将相同的行添加到M
,在同一个地方,git将知道只添加一个新的副本行,而不是每组更改中的一行。新的A
只会添加一行,而不会重复。
2 真的是两个或更多,但是多臂&#34;章鱼&#34;合并基本上以同样的方式工作。