git merge
默认使用一种名为 "递归" 的策略。它可以执行三方合并:从一侧获取补丁,将其应用于另一侧以生成新文件。在更复杂的情况下,例如分支和合并发生较多且两个提交之间存在多个合并基时,它也会以递归方式执行。
我最近遇到了同样的情况:
$ git merge-base --all 90d64557 05b3dd8f
711d1ad9772e42d64e5ecd592bee95fd63590b86
f303c59666877696feef62844cfbb7960d464fc1
$
由于有2个合并基础,因此无法进行3方合并。因此,“递归”策略首先递归到这两个提交的合并中来解决此问题:
$ git merge-base 711d1ad9 f303c596
3f5db59435ffa4a453e5e82348f478547440e7eb
$
好的,只有一个合并基础(merge base),所以3方合并可以开始。双方都进行了哪些更改?
$ git diff
normalize/coll.py | 116 ++++++++++++++++++++++++++++++++++++++-----------
normalize/visitor.py | 49 ++++++++++-----------
tests/test_property.py | 10 +++--
3 files changed, 120 insertions(+), 55 deletions(-)
$ git diff
normalize/identity.py | 38 +++++++++++++++++++++++++++++++-------
tests/test_property.py | 2 ++
2 files changed, 33 insertions(+), 7 deletions(-)
$
这两个差异都对同一个文件进行了更改,因此不能使用简单的策略仅从每个方向上获取每个文件的较新版本(即索引合并)来解决它们。相反,git从一侧获取新文件,并尝试将另一侧的补丁应用于该文件。结果是一个组合提交,可以仿真如下:
$ git checkout -b tmp
Switched to a new branch 'tmp'
$ git reset --hard f303c59666877696feef62844cfbb7960d464fc1
HEAD is now at f303c59 record_id: throw a more helpful error if record identity is not hashable
$ git merge 711d1ad9772e42d64e5ecd592bee95fd63590b86
Auto-merging tests/test_property.py
Merge made by the 'recursive' strategy.
normalize/coll.py | 116 ++++++++++++++++++++++++++++++++++++++-----------
normalize/visitor.py | 49 ++++++++++-----------
tests/test_property.py | 10 +++--
3 files changed, 120 insertions(+), 55 deletions(-)
$ git diff --stat 3f5db594 HEAD tests/test_property.py
tests/test_property.py | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
$
然后它返回到原始的三路合并,使用这个合并结果作为起点;这也涉及对同一文件的更改:
$ git diff
normalize/selector.py | 17 +
tests/test_selector.py | 19 ++
$ git diff
normalize/selector.py | 29 +++++++++++++++++
tests/test_selector.py | 9 +++++++++
$
然而,再次更改是针对文件的不同部分进行的,因此将差异应用于另一侧成功了。
所以,C git 能够通过首先在两个起始点的两个合并基础上进行三方合并,然后在被合并的两个原始提交和第一个合并的中间结果之间进行另一次三方合并来解决此合并。
Github 的自动解决方案没有这样做。它并不一定无法实现,我不确定它实现了多少递归策略,但它会保守地出错,这正是你期望看到这样的大绿色按钮所做的 :-).