Git rebase 意外的合并结果
Git rebase unexpected merge result
假设以下提交结构:
A ---- B(HEAD)
\
C
这些修订中的文件内容。
甲:
1
乙:
1
2
C:
0
1
我想将 B 合并到 C。如果我通过 git merge C
这样做,我会遇到预期的冲突。但是如果我做 git rebase C
然后 git 默默地做,没有任何冲突,但结果是意想不到的:
0
12
任何人都可以向我解释为什么会这样吗?我什至可以依赖 rebase 吗?文档说变基会在另一个提交的末尾重新应用提交。那么为什么提交在第二行添加“2”不会导致任何冲突和 "eats" 换行符呢?
你可以在这个 repo https://gitlab.com/timofei.davydik/rebase_problem.git
上测试它
git checkout test
git rebase master
在每种情况下,这些问题似乎都是由文件最后一行缺少换行符引起的,并且在面对缺少换行符时可能是 rebase 的错误。从技术上讲,当缺少最后一个换行符时,文本文件不是 "valid",我怀疑这意味着这种情况很少被测试。
我在您的示例存储库中重现了该行为 - 感谢您提供它! - 然后用相同的文件创建了一个不同的沙盒,但在每个状态的最后一行有一个换行符。
对于文件最后的换行符,合并和变基都产生了:
0
1
2
如您所料。
我认为 git merge
在您的示例中看到了冲突,因为它没有将 1
和 1\n
识别为相同,而 git rebase
看到 "insert 0
ahead of 1
"继续,因为它在第一行找到 1
,即使 1
.
中缺少换行符
git rebase
产生 0\n12
的事实表明它从 C 中获取了 0\n
和 1
,然后从 B 中获取了 2
,缺少换行符来自 1
和 2
,因为它们在您的文件中丢失。
我会说 rebase 考虑 1
== 1\n
是一个错误,但除此之外,根据输入输出是合理的。
实际上,merge
和 rebase
不会以相同的方式移动东西。所以,假设你目前站在分支 B:
git merge C
会将分支 C 的最高状态带到当前分支 B。例如,当您在主分支上工作并尝试汇总本地开发分支的结果时,这很有用。
git rebase C
会将您的完整分支 B 移动到 C 之上(作为目标目标)。为此,git 实际上会通过向后浏览您的分支找到所有特定于 B 的提交,直到他发现第一个提交对您的两个分支都是通用的(这里是 "A" 点)。然后它将重播分支B在C之上的每次提交,即:重新应用每个变更集。
这首先意味着 rebase
操作不是合并操作。那么,B引入的changeset其实就是"adding a 2 right after the 1".
@@ -1 +1,2 @@
1
+2
在 "A",您的文件仅包含“1”,因此您最终会得到“1”“2”。
在 "C",您的文件包含“0”“1”,因此您将得到“0”“1”“2”。
假设以下提交结构:
A ---- B(HEAD)
\
C
这些修订中的文件内容。
甲:
1
乙:
1
2
C:
0
1
我想将 B 合并到 C。如果我通过 git merge C
这样做,我会遇到预期的冲突。但是如果我做 git rebase C
然后 git 默默地做,没有任何冲突,但结果是意想不到的:
0
12
任何人都可以向我解释为什么会这样吗?我什至可以依赖 rebase 吗?文档说变基会在另一个提交的末尾重新应用提交。那么为什么提交在第二行添加“2”不会导致任何冲突和 "eats" 换行符呢? 你可以在这个 repo https://gitlab.com/timofei.davydik/rebase_problem.git
上测试它git checkout test
git rebase master
在每种情况下,这些问题似乎都是由文件最后一行缺少换行符引起的,并且在面对缺少换行符时可能是 rebase 的错误。从技术上讲,当缺少最后一个换行符时,文本文件不是 "valid",我怀疑这意味着这种情况很少被测试。
我在您的示例存储库中重现了该行为 - 感谢您提供它! - 然后用相同的文件创建了一个不同的沙盒,但在每个状态的最后一行有一个换行符。
对于文件最后的换行符,合并和变基都产生了:
0
1
2
如您所料。
我认为 git merge
在您的示例中看到了冲突,因为它没有将 1
和 1\n
识别为相同,而 git rebase
看到 "insert 0
ahead of 1
"继续,因为它在第一行找到 1
,即使 1
.
git rebase
产生 0\n12
的事实表明它从 C 中获取了 0\n
和 1
,然后从 B 中获取了 2
,缺少换行符来自 1
和 2
,因为它们在您的文件中丢失。
我会说 rebase 考虑 1
== 1\n
是一个错误,但除此之外,根据输入输出是合理的。
实际上,merge
和 rebase
不会以相同的方式移动东西。所以,假设你目前站在分支 B:
git merge C
会将分支 C 的最高状态带到当前分支 B。例如,当您在主分支上工作并尝试汇总本地开发分支的结果时,这很有用。git rebase C
会将您的完整分支 B 移动到 C 之上(作为目标目标)。为此,git 实际上会通过向后浏览您的分支找到所有特定于 B 的提交,直到他发现第一个提交对您的两个分支都是通用的(这里是 "A" 点)。然后它将重播分支B在C之上的每次提交,即:重新应用每个变更集。
这首先意味着 rebase
操作不是合并操作。那么,B引入的changeset其实就是"adding a 2 right after the 1".
@@ -1 +1,2 @@
1
+2
在 "A",您的文件仅包含“1”,因此您最终会得到“1”“2”。 在 "C",您的文件包含“0”“1”,因此您将得到“0”“1”“2”。