Here's my situation: git merge master
results in 50 files with merge conflicts. I want 45 of them to just be accepted from master
and be done, and I want to manually resolve conflicts in the remaining 5. All 45 of those files are in directory some/dir
. The other 5 are scattered elsewhere. IF I wanted just to accept master's changes for all 50 conflicts, I'd run this:
git merge -X theirs master
# OR (same thing):
git merge --strategy-option theirs master
But I don't want that, like I said. I'd like something more like this:
git merge -X theirs master some/dir
so that it automatically chooses "theirs" (master's) side for all conflicts in some/dir
, but otherwise let's me manually fix the conflicts. This, however, doesn't exist.
So, my work-around is this:
SHORT DESCRIPTION:
Start a merge, manually fix just the few files I need to, among the many many conflicting files. Back up any files I just fixed. Abort the merge. Redo the merge with git merge -X theirs master
to automatically accept master
's changes for ALL conflicts in ALL files. Manually copy my few manually-fixed files back into the repo, and commit them.
This avoids having to manually fix 50 files, which can be very tedious and time-consuming when only 5 really require my attention.
FULL, DETAILED STEPS:
Start a normal merge:
git merge master
Manually resolve conflicts in just the 5 files I want to do that in, and save each file. Then, MAKE COPIES OF THEM to a location outside of the repo (or at least in a folder that is ignored by the repo). I now have copies of those 5 files I manually resolved. Here is one way to accomplish this:
mkdir -p temp # create `temp` dir inside the repo.
# Note: if you're not aware, the ".git/info/exclude" file in your repo is
# an **untracked** form of a ".gitignore" file for the repo. We will use
# this file below.
# Add a `/temp/` entry to the ".git/info/exclude" file to keep
# the repo from tracking the /temp/ dir, but withOUT using the
# shared ".gitignore" file since this is my own personal setting
# not everyone else on the team necessarily wants.
echo -e "\n# don't track this temporary folder for my arbitrary use\n/temp/" \
>> .git/info/exclude
# Now manually make copies of your 5 files you just resolved conflicts in:
cp some/dir/file1.cpp temp
cp some/dir/file2.cpp temp
cp some/dir/file3.cpp temp
cp some/dir/file4.cpp temp
cp some/dir/file5.cpp temp
Do git merge --abort
to abort the merge, then do git merge -X theirs master
to redo the merge, except this time automatically accepting all of master's changes for all 50 files (ie: for all conflicts).
Now, manually copy my manually-resolved backup copies from above back into the repo on top of their respective files:
cp temp/file1.cpp some/dir
cp temp/file2.cpp some/dir
cp temp/file3.cpp some/dir
cp temp/file4.cpp some/dir
cp temp/file5.cpp some/dir
Finally, do git add -A
and git commit --amend
to amend them to the merge commit, and voila! I have the equivalent of an automatic resolution on the 45 files, and the manual resolution on the 5 files.
Is there a better way?
I think this way works pretty well and is pretty effective, but I'm open to learning alternatives, especially if they are faster or easier.
Update: here is the answer. The currently-most-upvoted answer is in fact not correct, and produces the wrong behavior under certain really important circumstances, despite being the most-upvoted.
After upvoting and marking correct this answer 6 months ago, I realized today after a very large and very botched merge
where this was needed that that answer is wrong. Let's say you did this, to update your feature_branch
with the latest changes from master
:
git checkout feature_branch
git merge master
# conflicts result...
...and there are a ton of conflicts. You see like 25 of them are inside some/dir
, and you want to keep all conflicting changes from feature_branch
, so you do any of these 3 commands (all the same thing in this case):
git checkout -- some/dir
# OR
git checkout HEAD -- some/dir
# OR
git checkout feature_branch -- some/dir
Well, you just messed up! master
(the --theirs
side for git merge
) had also added some changes and new files inside some/dir
, many of which had ZERO CONFLICTS with your feature_branch
changes (the --ours
side for git merge
), and all of which you want (this is the whole point of merging latest master
into your feature_branch
!) but now they are all gone because you just OVERWROTE THEM with your some/dir
from feature_branch
. That's not what we want at all!
# RIGHT ANSWER
# NB: you must be **in the middle of resolving conflicts in a `git merge`**
# in order for this to have the behavior I describe here.
# **Keep conflicting changes** for all conflicts within files inside
# some/dir, from the "--ours" (`feature_branch` in this case) side.
# This does NOT delete files added by the `master` (`--theirs`) side which
# have **no conflicts** with your `feature_branch` (`--ours`) side.
git checkout --ours -- some/dir
git add some/dir
git merge --continue
Again, that is NOT the same thing as this:
# WRONG ANSWER
# **Overwrite this entire `some/dir` directory** with everything from
# the `feature_branch` (`--ours`) side.
# This DOES delete files added by the `master` (`--theirs`) side which
# have **no conflicts** with your `feature_branch` (`--ours`) side,
# which is NOT what we want.
git checkout feature_branch -- some/dir
git add some/dir
git merge --continue
# merge latest master into feature_branch to get those upstream changes
# from other people into your feature_branch
git fetch origin master:master
git checkout feature_branch
git merge master
# conflicts result here...
# Keep all changes from `feature_branch` for conflicts in some/dir
git checkout --ours -- some/dir
# OR, keep all changes from `master` for conflicts in some/dir
git checkout --theirs -- some/dir
git add some/dir
git merge --continue
Done!
For details and clarity, see my answer here: Who is "us" and who is "them" according to Git?. This is covered in the "WARNING WARNING WARNING" section of that answer, although this answer is a more-thorough example of it, and better shows the problem.