I know there is a long way to checkout both modified files by following commands.
git add <filename>
git reset HEAD <filename>
git checkout <filename>
But is there one single command which can checkout all the "unmerged paths"?
I know there is a long way to checkout both modified files by following commands.
git add <filename>
git reset HEAD <filename>
git checkout <filename>
But is there one single command which can checkout all the "unmerged paths"?
The sequence of commands you listed will not produce the --theirs
version. Instead, it produces, in both the work-tree and the index, the --ours
version. There are several ways to achieve this with fewer commands, and for multiple path names.
The second shorted method is perhaps the one you should use:
git checkout --ours path1 path2 ... pathN
or:
git checkout --theirs path1 path2 ... pathN
followed by git add
of those same paths. Be very sure that it is OK to throw out their or your changes with respect to the merge base! Pay particular attention to which commit is the merge base, so that you know what you are keeping and what you are throwing out.
To make this one step shorter, you can use git checkout HEAD
or git checkout MERGE_HEAD
and skip the git add
step; but see below.
The key thing to understand here is that whenever Git detects conflicts, Git stores all three versions of the file in the index. In this situation, you can access any or all of these three versions. This is, for instance, how git mergetool
obtains the versions it calls BASE, LOCAL, and REMOTE, which Git more generally calls the merge base, the HEAD
or --ours
version, and the other or --theirs
version.
Remember that the index is the place where Git keeps a copy of each file that will go into the next commit you make. (Git calls this the index, or the staging area, or sometimes the cache, depending on which part of Git documentation is doing the calling.) In normal operation, there is only one copy of any such file. That is, if you have a file named README.txt
that was in the commit you checked out and is in the work-tree that you can work on, you also have a copy of this same README.txt
in the index.
The copy in the work-tree is an ordinary file, in the same format as any normal file on your computer. The committed copy, stored in the HEAD
commit, is in a special, Git-only format, where it is not only compressed but also entirely read-only: the committed copy can never be changed. You can, of course, make a new and different commit that has a different version of README.txt
, but the way you do that is by first copying that new and different README.txt
into the index.
Note that at any time, you can view the copy in the HEAD
commit with git show HEAD:README.txt
. Similarly, you can view the copy in the index with git show :README.txt
, where the leading colon is special Git syntax for a file from the index.
Typically you copy a new version into the index via git add README.txt
, which simply takes whatever is in the work-tree right now and copies it into the index / staging-area, overwriting the (single) previous copy. But when you hit a merge conflict during git merge
or git cherry-pick
or git revert
or git stash apply
or any other operation that performs the to merge action—what I like to call merge as a verb—the index takes on a new role.
While the index normally holds just one copy of README.txt
, during a conflicted merge, the index holds (up to) three copies of README.txt
. Remember that a merge works by comparing your version of the file—your README.txt
—to some merge base version of that same file, and then also by comparing their version of README.txt
to that same merge base version. By making these two separate comparisons, Git can figure out what you changed, and what they changed. Git then combines these two sets of changes, applying both sets to the merge base version of the file. See also VonC's answer here.
If Git is not able to combine both sets of changes on its own, what Git does at this point is to use what it calls stage slots in the index. Rather than just the one README.txt
in the index, Git can store up to four versions. However, it uses at most three, and it numbers them:
:README.txt
.1:README.txt
.2:README.txt
.:3:README.txt
.When git status
, with or without --short
, shows you unmerged files, what it means is that there are entries in staging slots 1, 2, and 3. In this case slot zero is not used!
Git writes the conflicted copy of README.txt
to the work-tree, so that you can look at the conflicts and resolve it yourself, but it also stores all three inputs in the index. You can check any one of them out, but slots 2 and 3 have a syntax to make it easy:
git checkout --ours README.txt
extracts from slot 2, while:
git checkout --theirs README.txt
extracts from slot 3. All three versions remain in the index, but now the work-tree holds either ours (the HEAD
version) or theirs (the MERGE_HEAD
version).
Running git add
takes whatever is in the work-tree right now, even if it still has merge conflicts in it, and writes that to slot zero, wiping out slots 1 through 3. This causes Git to believe that the conflict is resolved.
You can of course check out, or add, more than one file name at a time, so you can use:
git status --porcelain | grep '^UU ' | cut -f2-
to generate the list of all unmerged files (their short status is UU
), and use that to make a list of files to extract. This method is not very robust (fails in the presence of files with funny characters or spaces in their names) but suffices for simple cases:
files=$(git status --porcelain | grep '^UU ' | cut -f2-)
git checkout --theirs $files
git add $files
To shorten all of this one step, we can make use of a peculiar fact about git checkout
. Using git checkout --ours path
or git checkout --theirs path
has Git extract a file from the index (in its Git-only format there) to the work-tree (in normal format), which does not change anything in the index itself. But using git checkout tree-ish path
has Git extract a file from a commit, not from the index. When Git does this, it first copies that file into the index. Only after the file is in the index does git checkout
copy the file to the work-tree.
Copying a file into the index has the side effect of deleting any stage 1, 2, and 3 slot copies of the file, writing the file to staging slot zero. So if the file was previously unmerged, it has suddenly become merged.
Note that if we are in a conflicted merge, the hash ID of the commit we are merging is stored in MERGE_HEAD
. If we are in the middle of a cherry-pick, the hash ID of the commit we are picking is in CHERRY_PICK_HEAD
. (See the other commands for where they store their hash IDs.)
Hence, even though the copy that is in staging slot 2 came from the HEAD
commit, and the copy in staging slot 3 came from MERGE_HEAD
, if we deliberately extract a file from HEAD
or MERGE_HEAD
into the index and then into the work-tree, this writes the file to staging slot zero. The effect is to resolve the merge while also choosing our (HEAD
) or their (MERGE_HEAD
) version of the file.
Thus, for files that do not have difficult names, the command:
git checkout HEAD $(git status --porcelain | grep '^UU ' | cut -f2-)
does the trick to choose --ours
, while:
git checkout MERGE_HEAD $(git status --porcelain | grep '^UU ' | cut -f2-)
does the trick to choose --theirs
for git merge
(only!). Again, do not use this unless you know exactly what you are doing.