git auto merge on different lines

Viewed 71

I have a repo with the following structure:

master -> b -----\
   \----> a --\   \
    \----------v---v--> c

where branches a, b and c are based off of master, but c just contains a merge of both a and b.

In master, I have the following file (file.txt):

UNUSED_1
UNUSED_2

a modifies this file by writing

A
UNUSED_2

And b modifies this file by writing

UNUSED_1
B

When I try to merge b into c (after merging a) I get a merge conflict:

<<<<<<< HEAD
A
UNUSED_2
=======
UNUSED_1
B
>>>>>>> b

Is there a way to tell git merge that I want

A
B

in file.txt?

This is trivial to solve manually, but this is all done automatically in a script, where I have several "features", each represented by a branch, with the master branch "reserving" space for those features.

If I want a combination of features, I then create a new branch and merge all the required features to build.

The following is a script to reliably re-create the situation described in the post up to the merge conflict:

# Setup `master`
git init .
echo "UNUSED_1" >> "file.txt"
echo "UNUSED_2" >> "file.txt"
git add "file.txt"
git commit -m "Master commit"

# Setup `a`
git checkout -b "a"
rm "file.txt"
echo "A" >> "file.txt"
echo "UNUSED_2" >> "file.txt"
git add "file.txt"
git commit -m "A commit"

# Setup `b`
git checkout "master"
git checkout -b "b"
rm "file.txt"
echo "UNUSED_1" >> "file.txt"
echo "B" >> "file.txt"
git add "file.txt"
git commit -m "B commit"

# Try to merge in `c`
git checkout "master"
git checkout -b "c"
git merge --no-ff "a" -m "Merge A"
git merge --no-ff "b" -m "Merge B"
1 Answers

Is there a way to tell git merge that I want

A
B

in file.txt?

No—but it's not git merge per se that you have to convince. When git merge decides that a file does need a "true merge" because it is changed on "both sides" with respect to the merge base version, Git will pass this file to a low level merge driver.

Git has one built-in low-level merge driver, and the built-in one produces the conflict you see above. But by defining a merge driver in your .git/config and then referring to that merge driver from .gitattributes, you can tell Git: Use my merge driver instead of yours.

Unfortunately, this means you must find or write a low level merge driver.

Fortunately, Git includes a separate program that is a low-level merge driver, called git merge-file.

Unfortunately, this program does the same thing by default (both it and the default low level merge use the same algorithm).

Fortunately, this program has flags.

Unfortunately, these flags are mostly the same as the existing -X flags, and the existing -X flags are insufficient.

Fortunately, git merge-file has one flag that the -X flags don't, namely --union. The union merge operation means take both sets of changes.

Unfortunately, this operation is not tested on your particular case.

Fortunately, you can test it.

(See also frogurt.)

Related