我们最近经历了一些与git的合并问题,虽然它可能确实做了“正确的事情”(TM)但它并不是我所期望的。我已将问题减少到位于https://github.com/geretz/merge-quirk的小型公共github存储库。
quirk.c存在于master上。 merge-src和merge-dst都是从master上的相同版本分支出来的,这是最终合并的共同祖先。 merge-src添加了注释和一些代码。 merge-dst重新格式化代码并更改现有注释,但没有添加注释或代码。
git merge检测并声明冲突。
git clone https://github.com/geretz/merge-quirk.git
cd merge-quirk
git checkout merge-dst
git merge origin/merge-src
Auto-merging quirk.c
CONFLICT (content): Merge conflict in quirk.c
Automatic merge failed; fix conflicts and then commit the result.
然而,如果一个天真/信任的开发人员在冲突标记的quirk.c中选择origin / merge-src代码块,那么thisLineMightDisapper(sic)函数调用将会丢失。
在我的心理模型中,如果在冲突状态下HEAD和origin / src中存在thisLineMightDisapper函数调用,则它应存在于两个冲突块中,如果它不存在于冲突状态,则它应存在于冲突状态之外两个冲突块。为什么它只出现在HEAD块内?
<<<<<<< HEAD
// a few comment lines - change 1
// that existed - change 2
// in the common ancestor - change 3
// that get changed - change 4
if(1)
{
if (f(1, 2))
{
if (thisLineMightDisapper(42))
=======
// a few comment lines
// that existed
// in the common ancestor
// added this line in merge-src branch
// that get changed
if(1) {
if (f(1, 2))
>>>>>>> origin/merge-src
{
t = time(0);
}
}
}
if (anotherFunction(1,2))
{
t = time(0)
f(0);
}
}
文件
主/ quirk.c
// a few comment lines
// that existed
// in the common ancestor
// that get changed
if(1) {
if (f(1, 2))
{
if (thisLineMightDisapper(42)) {
t = time(0);
}
}
}
}
合并-SRC / quirk.c
// a few comment lines
// that existed
// in the common ancestor
// added this line in merge-src branch
// that get changed
if(1) {
if (f(1, 2))
{
if (thisLineMightDisapper(42)) {
t = time(0);
}
}
}
if (anotherFunction(1,2))
{
t = time(0)
f(0);
}
}
合并-dst的/ quirk.c
// a few comment lines - change 1
// that existed - change 2
// in the common ancestor - change 3
// that get changed - change 4
if(1)
{
if (f(1, 2))
{
if (thisLineMightDisapper(42))
{
t = time(0);
}
}
}
}
答案 0 :(得分:2)
这里发生的事情是你将语义与文本身份混为一谈。 Git认为移位和分裂的结果是完全不同的线条。你最后会遇到一个冲突的大块头和一个没有冲突的大块头,并且在冲突的大块中添加了一条线(非常)容易混淆地类似于在非冲突中删除的线:即使它们在语义上相同,< / p>
if (thisLineMightDisapper(42)) {
和
if (thisLineMightDisapper(42))
{
不一样,git通常会非常错误,就像它们一样。第二个在master和merge-src中未被触及,并在merge-dst中被删除,与冲突的hunk分开,并通过引入虚假匹配来添加。所以git认为删除是没有冲突的并且自动充当它。
当这里,甚至git checkout -m --conflict diff3
令人困惑的时候,你可以拉出与他人合并的看法与
$ sh -xc 'git diff ...MERGE_HEAD; git diff MERGE_HEAD...'
+ git diff ...MERGE_HEAD
diff --git a/quirk.c b/quirk.c
index c149623..79dc4a2 100644
--- a/quirk.c
+++ b/quirk.c
@@ -2,6 +2,7 @@
// a few comment lines
// that existed
// in the common ancestor
+ // added this line in merge-src branch
// that get changed
if(1) {
if (f(1, 2))
@@ -11,4 +12,10 @@
}
}
}
+
+ if (anotherFunction(1,2))
+ {
+ t = time(0)
+ f(0);
+ }
}
+ git diff MERGE_HEAD...
diff --git a/quirk.c b/quirk.c
index c149623..0de7516 100644
--- a/quirk.c
+++ b/quirk.c
@@ -1,14 +1,16 @@
- // a few comment lines
- // that existed
- // in the common ancestor
- // that get changed
- if(1) {
- if (f(1, 2))
+ // a few comment lines - change 1
+ // that existed - change 2
+ // in the common ancestor - change 3
+ // that get changed - change 4
+ if(1)
+ {
+ if (f(1, 2))
+ {
+ if (thisLineMightDisapper(42))
{
- if (thisLineMightDisapper(42)) {
- t = time(0);
- }
+ t = time(0);
}
+ }
}
}
+ git diff ...MERGE_HEAD
并且一些研究会告诉你git确定一个单独的大括号作为常见内容,导致它被治疗
- if (thisLineMightDisapper(42)) {
- t = time(0);
- }
+ t = time(0);
作为一个没有冲突的变化。
这对我有用的一件事是
git merge --abort
git merge -Xignore-space-change origin/merge-src
在识别更改边界时正确忽略重新格式化。与锤击任何东西一样,锤击源变化有时需要exactly the right tool,如果你尝试使用错误的一个,它就不会结束。所以它就在这里:你需要一个略微不同的锤子头,你的合并选择略有不同。在更常见的情况下,忽略空间变化是标签损坏的一个因素。