为什么 git merge 让我少了一行
Why does git merge allow me to lose a line
我们最近遇到了一些与 git 的合并问题,虽然它可能 'The Right Thing'(TM) 但不是我所期望的。我已将问题减少到位于 https://github.com/geretz/merge-quirk.
的小型 public github 存储库
quirk.c 存在于 master 上。 merge-src 和 merge-dst 都是从 master 上的同一版本分支出来的,master 是最终合并的共同祖先。 merge-src 添加注释和一些代码。 merge-dst 重新格式化代码并更改现有注释但没有添加的注释或代码。
git 合并检测并声明冲突。
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.
但是,如果 naive/trusting 开发人员在标记为 quirk.c 的冲突中选择 origin/merge-src 代码块,则 thisLineMightDisapper (sic) 函数调用将会丢失。
在我的心智模型中,如果 thisLineMightDisapper 函数调用同时存在于 HEAD 和 origin/src 的冲突状态中,它应该存在于两个冲突块中,如果它不存在于冲突状态中,它应该存在在两个冲突块之外。为什么它只出现在 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);
}
}
文件
master/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);
}
}
}
}
merge-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);
}
}
merge-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);
}
}
}
}
这里发生的事情是您将语义与文本标识混淆了。 Git 将移动和拆分的结果视为完全不同的线。你最终得到一个冲突的大块和一个不冲突的大块,并且在冲突的大块中添加的一行与在不冲突的大块中删除的一行(非常)混淆地相似:即使它们在语义上相同,
if (thisLineMightDisapper(42)) {
和
if (thisLineMightDisapper(42))
{
不一样,而git假装它们是一样的通常是非常错误的。第二个在 master 和 merge-src 中未受影响,在 merge-dst 中被删除,通过引入虚假匹配与冲突的 hunk 分开。所以git认为删除没有冲突并自动合并。
当像这里一样,即使 git checkout -m --conflict diff3
令人困惑时,您也可以将 hunks merge 与
一起查看
$ 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,如果你用错误的方式尝试它,结果不会很好。所以它就在这里:你需要一个稍微不同的锤子头,一个稍微不同的合并选项。在更常见的情况下,忽略 space 更改会导致选项卡损坏。
我们最近遇到了一些与 git 的合并问题,虽然它可能 'The Right Thing'(TM) 但不是我所期望的。我已将问题减少到位于 https://github.com/geretz/merge-quirk.
的小型 public github 存储库quirk.c 存在于 master 上。 merge-src 和 merge-dst 都是从 master 上的同一版本分支出来的,master 是最终合并的共同祖先。 merge-src 添加注释和一些代码。 merge-dst 重新格式化代码并更改现有注释但没有添加的注释或代码。
git 合并检测并声明冲突。
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.
但是,如果 naive/trusting 开发人员在标记为 quirk.c 的冲突中选择 origin/merge-src 代码块,则 thisLineMightDisapper (sic) 函数调用将会丢失。
在我的心智模型中,如果 thisLineMightDisapper 函数调用同时存在于 HEAD 和 origin/src 的冲突状态中,它应该存在于两个冲突块中,如果它不存在于冲突状态中,它应该存在在两个冲突块之外。为什么它只出现在 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);
}
}
文件
master/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);
}
}
}
}
merge-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);
}
}
merge-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);
}
}
}
}
这里发生的事情是您将语义与文本标识混淆了。 Git 将移动和拆分的结果视为完全不同的线。你最终得到一个冲突的大块和一个不冲突的大块,并且在冲突的大块中添加的一行与在不冲突的大块中删除的一行(非常)混淆地相似:即使它们在语义上相同,
if (thisLineMightDisapper(42)) {
和
if (thisLineMightDisapper(42))
{
不一样,而git假装它们是一样的通常是非常错误的。第二个在 master 和 merge-src 中未受影响,在 merge-dst 中被删除,通过引入虚假匹配与冲突的 hunk 分开。所以git认为删除没有冲突并自动合并。
当像这里一样,即使 git checkout -m --conflict diff3
令人困惑时,您也可以将 hunks merge 与
$ 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,如果你用错误的方式尝试它,结果不会很好。所以它就在这里:你需要一个稍微不同的锤子头,一个稍微不同的合并选项。在更常见的情况下,忽略 space 更改会导致选项卡损坏。