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 commitabout 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
HEADcommit version, so thatgit statusdoesn'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), theourscopy (stage 2), and thetheirscopy (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
filecontains Git's attempt to resolve the conflict. If we view that, we can see it did not go well. Note that I havemerge.conflictStyleconfigured todiff3so 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
HEADcommit 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 addto 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 theHEADcopy of the file into the index, without touching the work-tree copy. Here,git restoreorgit 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 commitnow would make another commit that matches the current commit (fileREADMEstill 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 oursorgit merge -s recursive -X ours, for instance:(My working tree is still a bit of a mess, of course, and
git statuswill show that my working tree copy offilediffers 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.)