514

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

Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
nosatalian
  • 6,889
  • 4
  • 20
  • 16
  • 6
    I have to give it to you as a three year+ git command line user I find this ridiculously hard to do from memory. It really should be built in by default. – Mauvis Ledford May 07 '13 at 04:36

5 Answers5

726

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>
Thomas Ahle
  • 30,774
  • 21
  • 92
  • 114
Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
  • 1
    Thanks. I'll add that these flags are very new, they aren't in 1.6.0 which is what I was using from source (and distro's like Ubuntu have even older versions of Git) – nosatalian Jun 04 '09 at 20:25
  • 42
    I found it a bit strange that `--ours` and `--theirs` means exactly the opposite of what I intuitively thought when trying out this command... – Joshua Muheim Jul 29 '12 at 12:55
  • 7
    Be careful when using `git show` – this skips newline normalization. – Chronial Dec 05 '12 at 17:03
  • 4
    This is nice for a few files, but when you have many files in conflict (because a date in a comment was changed!), how do you do it ? – JhovaniC Apr 18 '13 at 13:20
  • These are great, I'm assuming we would use these after we see the merge conflicts. But what if I know I always want to accept the "theirs" version of the files, can I do it in the merge command? I tried git merge -X theirs however it accepts their version of each conflict, rather than accepting their file as a whole when there is an issue. – Homan Feb 19 '14 at 18:17
  • I'm confused.. I'm on Windows (no, that's not why I'm confused!) ;-) I get an error when I run `git show :3:filename`.. The only way I have been able to accept the server file is by typing `git checkout BRANCH_MERGED_IN -- file`.. Is this not correct? I tried `git checkout --theirs -- file` and nothing happens.. – kodybrown Apr 11 '14 at 19:36
  • @wasatchwizard: what is the error message? I guess that the problem might be with `:3:` be similar to `C:`... – Jakub Narębski Apr 11 '14 at 20:01
  • @JakubNarębski it seems that the `git show :n:file` command does not work in Windows!? I am not certain I fully understand the `:n:file` part, but I can't get it to do anything, except an error (also tried 1 and 2): `> git show :3:css\myfile.css fatal: Path 'css\myfile.css' does not exist (neither on disk nor in the index)`. The good news is that the first command does work just fine. – kodybrown May 20 '14 at 23:38
  • @JakubNarębski after reading the docs again (painful, I know) I tried a few other things. Turns out, that in order to use the `:n:` syntax I must use the full or relative (linux-style with preceding '.') path in the command and I have to use forward-slashes also. for instance: `> git show :3:./css/myfile.css` works! I can redirect/pipe it to the same filename as in the example or whatever.. Thanks for the help! – kodybrown May 20 '14 at 23:42
  • @wasatchwizard did you try `git show ':3:css/style.css'` (slash not backslash)? – Jakub Narębski May 20 '14 at 23:45
  • 1
    @wasatchwizard `:` is the conventional way to select a "stream" on NTFS. NTFS files can have arbitrary streams, essentially, an NTFS file is a little directory which, if you open it as a file, opens a default file within it for you instead. – jthill Jul 04 '14 at 08:52
  • 1
    What are the self standing --? – Santhos Oct 03 '14 at 08:08
  • 5
    @Santhos: the `--` 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). – Jakub Narębski Oct 03 '14 at 12:05
  • 1
    To clarify, "checkout --ours" keeps our changes and rejects theirs. – mayankcpdixit Jul 15 '15 at 13:02
  • 1
    @JoshuaMuheim Can you clarify what you meant that --ours and --theirs have the opposite to expected results? Maybe my expectations are different, but I'm also not clear whether I'm just missing something... – Joshua Taylor Feb 25 '16 at 16:13
  • 1
    Really confused by what @JoshuaMuheim pointed out in his comment. What is the logic here? I've felt on numerous occasions like git has had its perspective switched. Does anyone think this makes sense? An explanation would be lovely and make my brain less foggy. – Sammaron Nov 10 '16 at 22:40
  • `git checkout test-branch -- ` was useful. Thanks. – Felipe Alvarez Apr 13 '17 at 00:02
  • 9
    @Sammaron @Joshua Muheim; the `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"). – RJFalconer May 09 '18 at 09:17
  • thank the lord i read @JoshuaMuheim comment before committing anything – heug Sep 17 '18 at 21:54
  • this works well as well with folders, ie: `git checkout --ours html/` – Alejandro Moreno Dec 23 '21 at 09:22
  • 1
    @JoshuaMuheim me too at first, but it makes sense when you think about 'LOCAL' or '--ours' always being the base branch, i.e. the branch you merge into or the branch you are rebasing onto. So the feature branch or testing branch can always be viewed as 'the other' branch – polynomial_donut Feb 09 '22 at 12:23
154

Try this:

  • To accept their changes: git merge --strategy-option theirs
  • To accept your changes: git merge --strategy-option ours
Sicco
  • 6,167
  • 5
  • 45
  • 61
Siva Mandadi
  • 3,673
  • 2
  • 18
  • 12
  • 13
    Note that this will keep your changes for ALL conflicting files, so could be dangerous if an unexpected conflict occurs. – John Jul 07 '15 at 15:16
  • 4
    And you can use this for other merge-y commands like cherry-pick and rebase. – idbrii May 17 '16 at 15:16
  • 1
    Is there any way you can specify a directory in which you want only `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? – Gabriel Staples Aug 27 '20 at 19:59
  • Question posted: https://stackoverflow.com/questions/63623581/how-do-i-accept-git-merge-conflicts-from-their-branch-for-only-a-certain-direc – Gabriel Staples Aug 27 '20 at 20:15
  • I am having `fatal: No current branch.` – alper Nov 16 '20 at 23:02
  • error: Merging is not possible because you have unmerged files. hint: Fix them up in the work tree, and then use 'git add/rm ' hint: as appropriate to mark resolution and make a commit. fatal: Exiting because of an unresolved conflict. – Chiel Aug 17 '22 at 18:19
55

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'
kynan
  • 13,235
  • 6
  • 79
  • 81
  • 1
    Not working for me... Are these for bash or some other shell? – user456584 Aug 08 '12 at 17:30
  • These are git aliases, add them to the `[alias]` section in your `~.gitconfig` or use `git config --global accept-ours "..."`. Have edited my answer. – kynan Aug 08 '12 at 22:49
  • 2
    You have no idea how much time this alias saved me. Thumbs up! – Adam Parkin Aug 14 '12 at 06:27
  • Tried the first `git config --global accept-ours ...` command and got: *"sh: !f: event not found"*. Just some feedback. – hakre Aug 11 '13 at 07:49
  • 1
    @hakre Make sure you quote the alias, otherwise your shell will try to interpret it. Or just manually edit your `~/.gitconfig`. – kynan Aug 12 '13 at 09:09
  • @kynan: Yes, I then edited the config. Not so well with shell quoting but I'm getting better :) – hakre Aug 12 '13 at 10:30
  • I copied the first two into `git config -e` and i got an error `expecting binary operator` -- I'm on MacOSX does this make a diff? – qodeninja Oct 04 '13 at 22:22
  • @qodeninja You'll want to use `git config --global -e` or you will edit the repo local configuration. Also make sure it's in the `[alias]` section. – kynan Oct 05 '13 at 10:49
  • @kynan thank you for the info -- the problem was with the syntax. shell logic statements need to have double brackets [[ ]] instead of [] – qodeninja Oct 07 '13 at 18:59
  • @qodeninja depends on your system and shell: `[[` is a shell builtin, whereas `[` is a binary (which might not be available on OSX). – kynan Oct 09 '13 at 12:11
  • I added a couple DOSKEY macros to my Windows machine to accomplish the same thing. Here is ours: `git-ours=git checkout --ours $* & git add -u $*` and here is theirs: `git-theirs=git checkout --theirs $* & git add -u $*`. Maybe it'll help others.. As a side note, the git-theirs works for (and is shorter to type than) `git checkout -- file` to undo changes. – kodybrown May 21 '14 at 00:13
  • 1
    Shell syntax for default values: `!f() { git checkout --ours -- "${@:-.}" git add -u "${@:-.}; }; f` – jthill Jul 04 '14 at 09:00
  • this does not seem to be working with zsh on OS X. getting `zsh: event not found: f()` – wprater Jan 08 '15 at 21:38
  • @wprater Seems your shell is interpreting the `!` as a history substitution. I've fixed it using `'` instead of `"`. – kynan Jan 09 '15 at 15:04
  • @kynan with single quotes, it complains about unclose quotes now. `accept-ours = '!f() { git checkout --ours -- "${@:-.}"; git add -u "${@:-.}"; }; f'` – wprater Jan 12 '15 at 04:48
  • @wprater sorry, I don't use `zsh` so can't verify but I can't see an unclosed quote there. – kynan Jan 12 '15 at 16:55
  • @kynan Should the aliases use double hyphens to indicate filenames follow: `git add -u --` (it's on the checkout, but not add)? (Originally suggested by @Dar's answer.) – idbrii May 17 '16 at 15:51
19

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"
Dar
  • 191
  • 1
  • 2
3

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:

  1. 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).
  2. You want to simply decide all of the conflicts in a particular direction.
  3. 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.

Parakleta
  • 1,121
  • 10
  • 19