(注意:行尾不是这里的罪魁祸首。)
这个案例很有趣。问题似乎在于,无论如何,在 git 的算法中,这两组添加的行是在两个不同的地方添加的。 Git 对代码一无所知,只是简单地决定,由于这两个(有些不同的)更改将行添加到原始的不同部分,因此只需添加这两个不同的更改即可。
你应该从中吸取的教训之一是 git 不聪明。它只是遵循通常起作用的一堆简单规则,但是仅仅因为它认为它成功合并了两组差异,并不意味着结果是正确的。这个例如,自动化测试是一个好主意的另一个原因。
注意:通过脚本重新创建问题的完整步骤显示在此答案的底部。
让我们看看在我们要求 git 合并之前 BranchA 和 BranchB 的状态。关键部分是当我们将合并基础(在此特定设置中的分支 common 的尖端)与两个实际的尖端提交进行比较时,我们得到的 git diff。为了查看这些差异,我使用了git diff 的三点形式:
$ git diff BranchB...BranchA
diff --git a/demo-file b/demo-file
index 1d822d4..d222dc7 100644
--- a/demo-file
+++ b/demo-file
@@ -11,6 +11,8 @@ Note that CR-LF is not an issue
get { return valueForKey<int?>("realPortNum") ; }
set { takeValueForKey("realPortNum", value); }
}
+ // ADSO-3530
+ public Decimal? tradeItemDryPhyWetQty
public string riskMktCode
{
$ git diff BranchA...BranchB
diff --git a/demo-file b/demo-file
index 1d822d4..52802fa 100644
--- a/demo-file
+++ b/demo-file
@@ -12,6 +12,9 @@ Note that CR-LF is not an issue
set { takeValueForKey("realPortNum", value); }
}
+ //ADSO-3530
+ public Decimal? tradeItemDryPhyWetQty
+
public string riskMktCode
{
get { return valueForKey<string>("riskMktCode") ; }
$
第一个命令,git diff BranchB...BranchA,告诉 git:
查找由BranchB 和BranchA 标识的提交。 (这些分别是 BranchB 和 BranchA 上的两个最尖端的提交。在这种情况下,它们也是这两个分支上尚未在公共分支上的 only 提交,因为我们只在BranchA 上进行了一次提交,在BranchB 上进行了一次提交。在许多实际情况下,一个分支上可能有 10、20 或更多次提交,以及 2、5 甚至 50 次或更多另一个提交,但 git 只为这一步找到两个最尖端的提交。)
找到这两个提交的合并基础。合并基地是两个分支在历史上重新结合的地方。在这种情况下,合并基础非常明显:它是分支common 顶端的提交,这是两个分支BranchA 和BranchB 作为单独分支出现的地方。 common 尖端的提交位于所有三个 分支(以及任何其他分支,例如默认的master 分支)。
将合并基础与 second 提交进行比较,即BranchA 的提示。
第二个命令git diff BranchA...BranchB 的工作原理非常相似。唯一的变化是两个输入提交是以另一个顺序选择的。 Git 找到相同的合并基础,但现在将提交与 BranchB 的提示提交进行比较。
再看看上面引用的差异。 有两种不同的差异结果。
第一个差异表明 git 应该在第 11 行修改以上下文开头的块。上下文有三行“高于”行(第 11、12 和 13 行,它们是 get、set 和右大括号行) ,然后我们添加了注释和函数声明行,然后是三行“下面”上下文。
第二个差异表明 git 应该在第 12 行(而不是第 11 行)开始的块中添加三行文本。上下文的三个“上方”行是集合、大括号和空白行,这些行本身不会被第一个差异更改(尽管它们会在 之间插入一些文本他们)。然后我们添加了三行(注释、函数声明和空白行),然后我们有了尾随上下文。
请注意,git 已经确定我们新添加的初始空白行已经存在,而是我们添加了一个后续空白行,我们的添加发生在第 14 行,而不是第 13 行。 这解释了为什么这两个添加不冲突:就 git 而言,BranchA 更改是“在第 11+3 行添加两行”,BranchB 更改是“在 old-line-12+ 添加三行3(现在是添加两行后的第 14+3 行)”。
结果是 git 添加了两个文本块,即使它们非常相似。
重现问题的脚本如下。
#! /bin/sh
tdir=/tmp/mergetest
die() {
echo "fatal: $@" 1>&2
exit 1
}
set -e
[ -d $tdir ] && die "$tdir: already exists -- hint: rm -rf $tdir"
mkdir $tdir
cd $tdir
git init
echo "This repository is for demonstrating git merge." > README
git add README
git commit -m initial
# Create common file on common branch.
git checkout -b common
cat << END > demo-file
This is a demo file,
meant to illustrate how git merge works,
why git merge is not very bright,
and why it is therefore necessary to INSPECT THE MERGE RESULTS
(automated tests are good).
The next few lines are not line 241 through 250 here,
but do match the original sample input.
Note that CR-LF is not an issue
(this host Unix-ish system uses simple newlines).
{
get { return valueForKey<int?>("realPortNum") ; }
set { takeValueForKey("realPortNum", value); }
}
public string riskMktCode
{
get { return valueForKey<string>("riskMktCode") ; }
set {
takeValueForKey("riskMktCode", value);
Finally, we have some
trailing text so as to provide
plenty of context area for git,
when it is doing its comparisons of the
merge-base version of the file
against the two branch versions.
END
git add demo-file
git commit -m 'create common base'
# Set variable to two-line form that we will add to both files.
samepart=" // ADSO-3530
public Decimal? tradeItemDryPhyWetQty"
# Make version on BranchA with two added lines.
git checkout -b BranchA
ed - demo-file << END
13a
$samepart
.
w
q
END
git add demo-file
git commit -m 'branch A: add declaration for tradeItemDryPhyWetQty'
# Make alternate version on BranchB with three added lines;
# note that we start from the common base.
git checkout -b BranchB common
ed - demo-file << END
13a
$samepart
.
w
q
END
git add demo-file
git commit -m 'branch B: add declaration for tradeItemDryPhyWetQty'
# Show which commit is the merge-base.
mergebase=$(git merge-base BranchA BranchB)
echo "The merge base is commit $(git rev-parse --short $mergebase)".
# View diffs. Could use "git diff $mergebase BranchA" here.
echo "Here is what we added in BranchA, vs the common base:"
git diff BranchB...BranchA
# Could use "git diff $mergebase BranchB" here.
echo "And, here is what we added in BranchB, vs the common base:"
git diff BranchA...BranchB
echo "Now we merge the two (on BranchA in this case)"
git checkout BranchA
git merge --no-edit BranchB
echo "Comparing the result to the merge base, we get:"
git diff $mergebase HEAD