I don't want a visual merge tool, and I also don't want to have to vi the conflicted file and manually choose the between HEAD (mine) and the imported change (theirs). Most of the time I either want all of their changes or all of mine. Commonly this is because my change made it upsteam and is coming back to me through a pull, but may be slightly modified in various places.
Is there a command line tool which will get rid of the conflict markers and choose all one way or another based on my choice? Or a set of git commands which I can alias myself to do each one.
# accept mine
alias am="some_sequence;of;commands"
alias at="some_other_sequence;of;commands"
Doing this is rather annoying. For 'accept mine' I have tried:
randy@sabotage ~/linus $ git merge test-branch
Auto-merging Makefile
CONFLICT (content): Merge conflict in Makefile
Automatic merge failed; fix conflicts and then commit the result.
randy@sabotage ~/linus $ git checkout Makefile
error: path 'Makefile' is unmerged
andy@sabotage ~/linus $ git reset --hard HEAD Makefile
fatal: Cannot do hard reset with paths.
How am I supposed to get rid of these change markers?
I can do:
git reset HEAD Makefile; rm Makefile; git checkout Makefile
But this seems rather round about, there must be a better way. And at this point, I'm not sure if git even thinks the merge happened, so I don't think this necessarily even works.
Going the other way, doing 'accept theirs' is equally messy. The only way I can figure it out is do:
git show test-branch:Makefile > Makefile; git add Makefile;
This also gives me a messed up commit message, which has Conflicts: Makefile in it twice.
Can someone please point out how to do the above two actions in a simpler way? Thanks
The solution is very simple. git checkout <filename>
tries to check out file from the index, and therefore fails on merge.
What you need to do is (i.e. checkout a commit):
To checkout your own version you can use one of:
git checkout HEAD -- <filename>
or
git checkout --ours -- <filename>
(Warning!: If you are rebasing --ours
and --theirs
are swapped.)
or
git show :2:<filename> > <filename> # (stage 2 is ours)
To checkout the other version you can use one of:
git checkout test-branch -- <filename>
or
git checkout --theirs -- <filename>
or
git show :3:<filename> > <filename> # (stage 3 is theirs)
You would also need to run 'add' to mark it as resolved:
git add <filename>
Try this:
To accept their changes: git merge --strategy-option theirs
To accept your changes: git merge --strategy-option ours
theirs
, but manually resolve the conflicts otherwise? Ex: git merge --strategy-option theirs some/dir
. So, if a conflict is in a file from path some/dir
, then accept theirs, but if it is not, then ask me?
fatal: No current branch.
Based on Jakub's answer you can configure the following git aliases for convenience:
accept-ours = "!f() { git checkout --ours -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"
accept-theirs = "!f() { git checkout --theirs -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"
They optionally take one or several paths of files to resolve and default to resolving everything under the current directory if none are given.
Add them to the [alias]
section of your ~/.gitconfig
or run
git config --global alias.accept-ours '!f() { git checkout --ours -- "${@:-.}"; git add -u "${@:-.}"; }; f'
git config --global alias.accept-theirs '!f() { git checkout --theirs -- "${@:-.}"; git add -u "${@:-.}"; }; f'
[alias]
section in your ~.gitconfig
or use git config --global accept-ours "..."
. Have edited my answer.
~/.gitconfig
.
!f() { git checkout --ours -- "${@:-.}" git add -u "${@:-.}; }; f
Based on kynan's answer, here are the same aliases, modified so they can handle spaces and initial dashes in filenames:
accept-ours = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --ours -- \"$@\"; git add -u -- \"$@\"; }; f"
accept-theirs = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --theirs -- \"$@\"; git add -u -- \"$@\"; }; f"
The ideal situation for resolving conflicts is when you know ahead of time which way you want to resolve them and can pass the -Xours
or -Xtheirs
recursive merge strategy options. Outside of this I can see three scenarious:
You want to just keep a single version of the file (this should probably only be used on unmergeable binary files, since otherwise conflicted and non-conflicted files may get out of sync with each other). You want to simply decide all of the conflicts in a particular direction. You need to resolve some conflicts manually and then resolve all of the rest in a particular direction.
To address these three scenarios you can add the following lines to your .gitconfig
file (or equivalent):
[merge]
conflictstyle = diff3
[mergetool.getours]
cmd = git-checkout --ours ${MERGED}
trustExitCode = true
[mergetool.mergeours]
cmd = git-merge-file --ours ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
trustExitCode = true
[mergetool.keepours]
cmd = sed -i '' -e '/^<<<<<<</d' -e '/^|||||||/,/^>>>>>>>/d' ${MERGED}
trustExitCode = true
[mergetool.gettheirs]
cmd = git-checkout --theirs ${MERGED}
trustExitCode = true
[mergetool.mergetheirs]
cmd = git-merge-file --theirs ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
trustExitCode = true
[mergetool.keeptheirs]
cmd = sed -i '' -e '/^<<<<<<</,/^=======/d' -e '/^>>>>>>>/d' ${MERGED}
trustExitCode = true
The get(ours|theirs)
tool just keeps the respective version of the file and throws away all of the changes from the other version (so no merging occurs).
The merge(ours|theirs)
tool re-does the three way merge from the local, base, and remote versions of the file, choosing to resolve conflicts in the given direction. This has some caveats, specifically: it ignores the diff options that were passed to the merge command (such as algorithm and whitespace handling); does the merge cleanly from the original files (so any manual changes to the file are discarded, which could be good or bad); and has the advantage that it cannot be confused by diff markers that are supposed to be in the file.
The keep(ours|theirs)
tool simply edits out the diff markers and enclosed sections, detecting them by regular expression. This has the advantage that it preserves the diff options from the merge command and allows you to resolve some conflicts by hand and then automatically resolve the rest. It has the disadvantage that if there are other conflict markers in the file it could get confused.
These are all used by running git mergetool -t (get|merge|keep)(ours|theirs) [<filename>]
where if <filename>
is not supplied it processes all conflicted files.
Generally speaking, assuming you know there are no diff markers to confuse the regular expression, the keep*
variants of the command are the most powerful. If you leave the mergetool.keepBackup
option unset or true then after the merge you can diff the *.orig
file against the result of the merge to check that it makes sense. As an example, I run the following after the mergetool
just to inspect the changes before committing:
for f in `find . -name '*.orig'`; do vimdiff $f ${f%.orig}; done
Note: If the merge.conflictstyle
is not diff3
then the /^|||||||/
pattern in the sed
rule needs to be /^=======/
instead.
sed -i ''
instead. It just allows for in-place editing. I should have used lowercase anyway so I’ll update the post.
Success story sharing
--ours
and--theirs
means exactly the opposite of what I intuitively thought when trying out this command...git show
– this skips newline normalization.--
is used by Git to separate revisions (branch names etc.) from path names (filenames, directories). It is important if Git cannot decide if a name is the name of branch or the name of file. This follows POSIX (or GNU) convention of using double dash to separate options from arguments (filenames).theirs
/ours
can appear swapped if you are resolving conflicts in the context of a rebase operation. Because rebase works by checking out the target branch then cherry-picking commits from "your" branch onto the target, the incoming change ("theirs") is from "your" branch, and the current branch is the target branch ("ours").