Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolving a git merge conflict in favour of ours or theirs without taking the whole file from either

There are numerous questions on Stack Overflow on how to resolve merge conflicts in favour of one or the other branch. But the most common answer I'm finding is using git checkout --ours or git checkout --theirs.

The problem is git checkout will take the entire file from either branch. There could've been parts of the file that merged just fine, and we don't want them removed.

Given a file in a conflicted state, is there a way to remove the conflict markers in favour of "ours" or "theirs" without applying this strategy to the entire merge or taking the entire file from "ours" or "theirs"?

This is definitely possible with a simple script, but I want to know if there's an existing solution I'm not finding.

like image 825
Shinigami Avatar asked Jan 27 '23 09:01

Shinigami


2 Answers

If you want to resolve all remaining conflicts in a file to ours or theirs it's a straightforward sed per side you want and your conflict style.

For most code files, where the conflict markers are so foreign that you don't have to be careful about identifying them, it's so easy you can just type it:

sed -si '/^<<</,/^>>>/ {/^<<</,/^===/d;/^>>>/d}' thefile  # keep theirs
sed -si '/^<<</,/^>>>/ {/^<<</d;/^===/,/^>>>/d}' thefile  # keep ours

And to do that for everything,

sed -si $yourpattern $(git ls-files -m | uniq)

is easiest.

To be more careful with your conflict markers, it's better to make the simple script you referred to, and the diff3 conflict-reporting style needs some changes as well. The usual conflict symbol count is 7, and for best safety make your sed range /^<<<<<<< ours$/,/^>>>>>>> theirs$/, and with diff3 reporting the "keep ours" deletion range starts with ^||||||| base$, not ^=======$.

sed on the Mac has been butchered to refuse to do inline edits without creating a backup file. You have to do e.g. -si.bak and then delete the backup.

like image 60
jthill Avatar answered Jan 28 '23 21:01

jthill


I think you shouldn't work this way. Instead, you should install a suitable merge editor (KDiff3, for example), run git mergetool (which iterates over the files in conflict and runs the installed merge editor) and review the changes manually.

The merge tool will open the three versions of the file in conflict, will automatically take all solvable changes just as you wished, and will highlight remaining conflicts. If you really don't want to review them one-by-one, select menu item MergeChoose xxx for each unresolved conflict, and tadah.

If you are really sure you don't want to use a visual merge tool, you have the following options beneath the one mentioned by torek.

If you want to apply this strategy to each conflicted file during one merge, use the "ours" or "theirs" suboption to the standard merge strategy:

git merge -Xours ...

Don't confuse it with selecting the whole merge strategy with -s ours, which simply takes everything from ours without trying to do a merge.

If you want to apply this strategy to some particular files during every merge, you can define a custom merge driver (in .gitconfig) and apply it to that files (in .gitattributes). This sounds much more complicated than it is. Defining a merge driver simply means to define a command line which takes three input files and creates one output file, you can for example use git merge-file as torek explained it. Once defined, you can assign such a merge driver to any file or pattern by creating a .gitattributes file in the repository root and committing it. Just read about it on gitattributes.

like image 20
Marcus Avatar answered Jan 28 '23 23:01

Marcus