How to resolve git stash conflict without commit?
Solution 1:
Don't follow other answers...
Well, you can follow them, of course. 🙂 But I don't think that doing a commit and then resetting the branch to remove the commit you just created and similar workarounds suggested in other answers is the clean way to solve this issue.
Clean solution
The following solution seems to be much cleaner to me and it's also suggested by the Git itself — try to execute git status
in the repository with a conflict:
Unmerged paths:
(use "git restore --staged <file>..." to unstage)
(use "git add <file>..." to mark resolution)
Note: The restore
command has been introduced in Git version 2.23.0. Older versions of Git suggested to use the command git reset HEAD <file>...
instead of git restore --staged <file>...
. You could also use git reset
to unstage any and all files in the staging area (called the index). Restore command's equivalent is git restore --staged .
(the dot is necessary and it specifies any file). Currently, any of those commands may be used and the outcome is the same. If you want to learn about the differences between those commands, check the documentation.
So let's do what Git suggests (without making and reverting any pointless commits):
- Manually (or ideally using some merge tool, see below) resolve the conflict(s).
- Use
git restore --staged .
to mark conflict(s) as resolved and unstage all files in the staging area. If you want to unstage only specific files, use the commandgit restore --staged <file>
instead. You don't have to executegit add
before. - Finally, remove the stash with
git stash drop
, because Git doesn't do that automatically on conflict.
Translated to the command-line commands:
$ git stash pop
# ...resolve conflict(s)
$ git restore --staged .
$ git stash drop
Explanation of the default behavior
There are two ways of marking conflicts as resolved: git add
and git restore --staged <file>...
. While git restore --staged <file>...
marks the conflicts as resolved and removes files from the index, git add
also marks the conflicts as resolved, but keeps files in the index.
Adding files to the index after a conflict is resolved is on purpose. This way you can differentiate the changes from the previous stash and changes you made after the conflict was resolved. If you don't like it, you can always use git restore --staged .
to remove everything from the index.
Merge tools
I highly recommend using any of 3-way merge tools for resolving conflicts, e.g. KDiff3, Meld, etc., instead of doing it manually. It usually solves all or the majority of conflicts automatically itself. It's huge time-saver!
Solution 2:
Suppose you have this scenario where you stash your changes in order to pull from origin. Possibly because your local changes are just debug: true
in some settings file. Now you pull and someone has introduced a new setting there, creating a conflict.
git status
says:
# On branch master
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: src/js/globals.tpl.js
no changes added to commit (use "git add" and/or "git commit -a")
Okay. I decided to go with what Git suggested: I resolved the conflict and committed:
vim src/js/globals.tpl.js
# type type type …
git commit -a -m WIP # (short for "work in progress")
Now my working copy is in the state I want, but I have created a commit that I don't want to have. How do I get rid of that commit without modifying my working copy? Wait, there's a popular command for that!
git reset HEAD^
My working copy has not been changed, but the WIP commit is gone. That's exactly what I wanted! (Note that I'm not using --soft
here, because if there are auto-merged files in your stash, they are auto-staged and thus you'd end up with these files being staged again after reset
.)
But there's one more thing left: The man page for git stash pop
reminds us that "Applying the state can fail with conflicts; in this case, it is not removed from the stash list. You need to resolve the conflicts by hand and call git stash drop
manually afterwards." So that's exactly what we do now:
git stash drop
And done.
Solution 3:
Instead of adding the changes you make to resolve the conflict, you can use git reset HEAD file
to resolve the conflict without staging your changes.
You may have to run this command twice, however. Once to mark the conflict as resolved and once to unstage the changes that were staged by the conflict resolution routine.
It is possible that there should be a reset mode that does both of these things simultaneously, although there is not one now.
Solution 4:
git checkout stash -- .
worked for me.
Note: this can be dangerous since it doesn't try to merge the changes from the stash into your working copy, but overwrites it with the stashed files instead. So you can lose your uncommitted changes.
Solution 5:
git add .
git reset
git add .
will stage ALL the files telling git that you have resolved the conflict
git reset
will unstage ALL the staged files without creating a commit