如何让 git 通过放置 "ours first, then theirs" 来解决合并冲突

How do I get git to resolve merge conflicts by putting "ours first, then theirs"

我希望能够通过先放置 "ours" 块,然后放置 "theirs" 块来自动解决 "two branches added blocks at the same line" 冲突。

所以不要像这样的冲突:

16:09:44 tims@firebat ~/git/merge_conflict_example (master|MERGING) $ cat test.txt
good morning
good evening
<<<<<<< HEAD
g'day
=======
aloha
>>>>>>> branch1
hello
hi

我会得到:

16:09:44 tims@firebat ~/git/merge_conflict_example (master) $ cat test.txt
good morning
good evening
g'day
aloha
hello
hi

没有冲突。

我想可能会有类似 git merge branch1 -X oursthentheirs

我在此处使用的示例可在 git@bitbucket.org:abznak/merge_conflict_example.git

获得

你可以使用 "ours" merge 将旧分支合并到当前分支 策略如下

$ git merge -s ours old

没有什么内置的,但是如果你把merge.conflictstyle设置为diff3,写一个程序就相对容易了(也许用perl或者python,或者我会用 awk 编写一个俗气的版本)检查 "original" 部分是否为空——这将检测 "both added" 情况——如果是,只需删除冲突标记:

good morning
good evening
<<<<<<< HEAD
g'day
|||||||
=======
aloha
>>>>>>> branch1
hello
hi

这是我的 awk 脚本(我不认为它很好,但它适用于样本输入)。请注意,它不会很好地处理 "nested conflicts"(即,如果两个原始冲突文件包含看起来像冲突标记的内容,则会出错)。

BEGIN { in_conflict = retained_left = retained_mid = retained_right = 0 }
function handle_retained(is_eof) {
        # If the section between ||||||| and ======= is empty,
        # retained_mid+1 == retained_right.  Otherwise print
        # all the retained conflict lines.
        if (retained_mid + 1 == retained_right) {
                s1 = retained_left + 1  # after <<<<<<<
                e1 = retained_mid - 1   # before |||||||
                s2 = retained_right + 1 # after =======
                e2 = NR - 1             # before >>>>>>>
        } else {
                s1 = retained_left; e1 = NR
                s2 = 1; e2 = 0
        }
        for (i = s1; i <= e1; i++)
                print retained[i]
        for (i = s2; i <= e2; i++)
                print retained[i]
        delete retained
        if (is_eof) {
                # this should never happen!
                print "WARNING: ended input while still in conflict marker"
                exit(1)
        }
}
/^<<<<<<</ { in_conflict = 1; retained_left = NR }
{
        if (!in_conflict)
                print
        else
                retained[NR] = [=11=]
}
/^\|\|\|\|\|\|\|/ { if (in_conflict) retained_mid = NR }
/^=======/ { if (in_conflict) retained_right = NR }
/^>>>>>>>/ { if (in_conflict) handle_retained(0); in_conflict = 0 }
END { if (in_conflict) handle_retained(1) }

很简单。只需将 merge gitattribute 设置为 union。 来自 https://git-scm.com/docs/gitattributes:

union

Run 3-way file level merge for text files, but take lines from both versions, instead of leaving conflict markers. This tends to leave the added lines in the resulting file in random order and the user should verify the result. Do not use this if you do not understand the implications.

例如,我刚刚添加了一个包含文本 *.txt merge=union:

的 .gitattributes 文件
10:58:21 tims@thor ~/git/merge_conflict_example (master) $ cat .gitattributes
*.txt merge=union

与运行合并:

10:58:26 tims@thor ~/git/merge_conflict_example (master) $ git merge origin/branch1
Auto-merging test.txt
[...]
Merge made by the 'recursive' strategy.
 test.txt | 1 +
 1 file changed, 1 insertion(+)

达到预期效果:

10:58:42 tims@thor ~/git/merge_conflict_example (master) $  cat test.txt 
good morning
good evening
g'day
aloha
hello
hi