When there's a conflict, git status
shows this:
Unmerged paths:
(use "git restore --staged <file>..." to unstage)
(use "git add <file>..." to mark resolution)
both modified: some_file
Now I can git add some_file
to mark resolution, as the command suggests, but this also stages some_file
, which I might not want to.
How can I mark the file for resolution without adding it to the index? In two steps it's easy:
git add some_file
git restore --staged some_file
but how do I do it in one step, if it's possible?
If doing so is not recommended by best practices, please, tell me.
Mu. (In other words, this question presupposes something that cannot exist.)
A merge conflict is represented by having multiple copies of a file in the index / staging-area.1 That is, instead of just one file being staged—this one file either matches the current commit, and Git just shuts up about it, or doesn't, and Git prints
staged for commit
about it—there are three files staged for commit. Git can't actually commit three copies of one file and won't let you commit, so you must resolve this situation ... but the act of resolving consists of erasing those three files and putting one file in place.That one file that you put in place is added to the index. It either matches the current commit's file, and Git says nothing, or it doesn't, and Git says
staged for commit
. The file exists either way.If what you want is to make Git's index contain, as the one copy of the file, the copy that is in the current commit, you can do that. But that means that you have added that version of the file as the one staged for commit.
1This isn't quite right technically: a merge conflict is represented by having any nonzero stage entry in the index. However, when you do get merge conflicts, most often you wind up with one or more files, each of which has three entries. The other cases occur with add/add, modify/delete, rename/rename, and rename/delete conflicts. These also leave more than one entry representing the "same" file.
Example
Let's illustrate this. First, let's create a tiny repository and set up a conflict:
Now let's see what's in Git's staging area / index:
These are the files in Git's staging area, complete with their staging numbers. Stage number zero indicates that there is no merge conflict; there cannot be any other entries for such a file. Note that both files are staged for commit! They just match the
HEAD
commit version, so thatgit status
doesn't saystaged for commit
.Now we'll run the merge:
Voila! Here are our three copies of the file named
file
. They have nonzero staging numbers; these represent the merge base copy of the file (stage 1), theours
copy (stage 2), and thetheirs
copy (stage 3). If we like, we can view the files:git show :1:file
,git show :2:file
, and so on:"Their" file, from the tip of branch
branch
, is the one where line 2 readsfoo
(ours readsbar
).Now, the working tree copy of file
file
contains Git's attempt to resolve the conflict. If we view that, we can see it did not go well. Note that I havemerge.conflictStyle
configured todiff3
so that I get the merge base version of the text as well:We now return to your question, which I'll quote again
Pick one of the three copies that exist in the three stages, or the copy that exists in the
HEAD
commit or any other commit, and choose that as the copy to go into slot zero in Git's index. Erase the other two slots. The file is now staged for commit.It's already in the index. You're subtracting two or three copies, probably leaving the third and moving it to slot zero; or you subtract away all three copies and add one new one to the index.
You can use
git add
to copy the working tree file into the index, or you can choose some existing Git-ified copy and move that in. Let's say you'd like to stick theHEAD
copy of the file into the index, without touching the work-tree copy. Here,git restore
orgit reset
(both will do this job) are the way to go:(Note that the hash ID here is the same one we saw in slot 2 earlier.)
Since the index copy is the file Git will commit, a
git commit
now would make another commit that matches the current commit (fileREADME
still matches the current commit). Since I'm in the middle of a merge, with all conflicts resolved, this will make the final merge commit, with the snapshot matching what I would have gotten had I usedgit merge -s ours
orgit merge -s recursive -X ours
, for instance:(My working tree is still a bit of a mess, of course, and
git status
will show that my working tree copy offile
differs from my index copy offile
, as the working tree version still has the unresolved conflict in it. But Git didn't use that copy, to make the commit: it used the index copy.)