行尾:Git合并可创建重复项而不会发生冲突 [英] Line Endings: Git merge creates duplicates without conflict

查看:106
本文介绍了行尾:Git合并可创建重复项而不会发生冲突的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Git自动合并问题:

当在两个不同的分支文件中提交了相同的代码,并且其中一个分支代码在开始时具有额外的CRLF/LF.合并时,它会自动合并,文件创建重复项而不会发生任何冲突.请尽早告知.

When there is Same code committed in two different branches file with one of this branch code having extra CRLF/LF at start. While merging it auto merges the file creates duplicates without any conflict. Please advise earliest.

下图显示了文本文件中所有可能的符号.注意:分支A没有换行(行:245).并且下面的自动合并会创建重复项而不会显示冲突.

Below image shows all the possible symbols in text file. Note: Branch A does not have Line Feed(Line: 245). And Automated Merging below creates duplicates without showing conflict.

推荐答案

(注意:行尾不是此处的罪魁祸首.)

(Note: line endings are not the culprit here.)

这种情况很有趣.问题似乎是,无论如何,在git的算法中,两组添加的行是在两个不同的地方添加的. Git对代码不了解,只是认为由于这两个(略有不同)更改会在原始文件的不同部分添加行,因此可以只添加这两个不同的更改.

This case is interesting. The problem seems to be that the two sets of added lines are, in git's algorithm anyway, added at two different places. Git has no understanding of the code and simply decides that since the two (somewhat different) changes add lines to different sections of the original, it is OK to just add both of those differing changes.

您应该从中学到的教训之一是git是不明智的.它只是遵循一堆通常起作用的简单规则,但是仅仅因为它认为它成功地合并了两个差异比较集,并不意味着结果是正确的.例如,这是自动化测试是一个好主意的另一个原因.

One of the lessons you should take away from this is that git is not smart. It is simply following a bunch of simple rules that usually work, but just because it thinks it merged two sets of diffs successfully, does not mean that the result is correct. This is yet another reason why automated testing is a good idea, for instance.

注意:此答案的底部显示了通过脚本完成重新创建问题的完整步骤.

Note: complete steps, via a script, to re-create the problem appear at the bottom of this answer.

在要求git合并之前,让我们看一下BranchA和BranchB的状态.关键部分是当我们比较合并基础(在此特定设置中,分支common的尖端)与两个实际的尖端提交时,git diff的作用.要查看这些差异,我使用git diff的三点形式:

Let's take a look at the state of BranchA and BranchB just before we ask git to merge. The crucial part is what we get with git diff when we compare the merge base (the tip of branch common, in this particular setup) with the two actual tip commits. To see these diffs, I use the three-dot form of 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:

The first command, git diff BranchB...BranchA, tells git:

  1. 查找由BranchBBranchA标识的提交. (这是分别在BranchBBranchA上的两个最尖端的提交.在这种情况下,它们也是在尚未在公共分支上的这两个分支上的 only 提交.我们只在BranchA上进行了一次提交,而在BranchB上进行了一次提交在许多实际单词中,一个分支上可能有10、20或更多的提交,而2,5,甚至50或更多提交,但是git仅找到该步骤的两个最尖端的提交.)

  1. Find the commits identified by BranchB and BranchA. (These are the two tip-most commits on BranchB and BranchA respectively. In this case, they are also the only commits on those two branches that are not already on the common branch, since we made only the one commit exclusively on BranchA and one commit exclusively on BranchB. In many real-word situations, there might be 10, 20, or more commits on one branch and 2, 5, or even 50 or more commits on the other, but git just finds the two tip-most commits, for this step.)

找到这两个提交的合并基础.合并基础是两个分支在历史上重新结合的地方.在这种情况下,合并基础非常明显:这是分支common尖端的提交,这是两个分支BranchABranchB作为单独的分支出现的地方. common尖端的提交位于所有三个分支(以及其他任何分支,例如默认的master分支)上.

Find the merge base for these two commits. The merge base is a place where the two branches rejoin in history. In this case, the merge base is quite obvious: it's the commit at the tip of branch common, which is where the two branches BranchA and BranchB emerge as separate branches. The commit at the tip of common is on all three branches (and any other branches, such as the default master branch).

将合并基础与 second 提交(即BranchA的提示)进行比较.

Diff the merge-base against the second commit, i.e., the tip of BranchA.

第二个命令git diff BranchA...BranchB的工作原理非常相似.唯一的变化是,以另一顺序选择了两个输入提交. Git找到相同的合并基础,但是现在针对BranchB的最短提交提交的差异.

The second command, git diff BranchA...BranchB, works very similarly. The only change is that the two input commits are selected in the other order. Git finds the same merge base, but now diffs that commit against the tip commit of BranchB.

再看看上面引用的差异.有两个不同的差异结果.

Take another look at the diffs quoted above. There are two different diff results.

第一个差异显示git应该从第11行的上下文开始修改一个块.上下文有上"三行(第11、12和13行,分别是get,set和close括号行) ,然后我们添加了注释和函数声明行,然后在上下文下方有三行.

The first diff shows that git should modify a block beginning with context at line 11. There are three "above" lines of context (lines 11, 12, and 13, these being the get, set, and close brace lines), then we added the comment and function declaration lines, and then there are three lines of "below" context.

第二个差异显示git应该在从第12行(不是第11行)开始的块中添加三行文本.上下文的三个上方"行是设置行,右括号和空白行,这些行本身不会被第一个差异更改(尽管它们将在之间插入一些文本他们).然后我们添加了三行(注释,函数声明和空白行),然后有了尾随上下文.

The second diff shows that git should add three lines of text in a block beginning at line 12 (not line 11). The three "above" lines of context are the set, close-brace, and blank lines, and those lines are not themselves going to be changed by the first diff (though they will have some text inserted between them). Then we added three lines (comment, function declaration, and blank line) and then we have the trailing context.

请注意,git决定已经存在我们新添加的初始空白行,而我们添加了随后的空白行,并且添加行发生在第14行,而不是第13行.解释了为什么这两个添加项不会冲突:就git而言,BranchA更改是在第11 + 3行添加两行",而BranchB更改是在旧行12+添加三行" 3(现在添加两行后现在是第14 + 3行)".

Note that git has decided that our newly added initial blank line was already present and that we, instead, added a subsequent blank line, with our additions happening at line 14, not line 13. This explains why the two additions do not conflict: as far as git is concerned, the BranchA change is "add two lines at line 11+3" and the BranchB change is "add three lines at old-line-12+3 (which is now line 14+3 after adding two lines)".

结果是git 将两个文本块都添加了,即使它们非常相似.

The result is that git adds both blocks of text, even though they are very similar.

#! /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

这篇关于行尾:Git合并可创建重复项而不会发生冲突的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆