问题
I popped a stash and there was a merge conflict. Unlike the question that is listed as a duplicate, I already had some uncommitted changes in the directory which I wanted to keep. I don't just want to make the merge conflict disappear, but also to get my directory back to the state it was before the pop.
I tried git merge --abort
, but git claimed no merge was in progress. Is there an easy way to abort a pop without destroying the changes I originally had in the directory?
回答1:
Ok, I think I have worked out "git stash unapply". It's more complex than git apply --reverse
because you need reverse merging action in case there was any merging done by the git stash apply
.
The reverse merge requires that all current changes be pushed into the index:
git add -u
Then invert the merge-recursive
that was done by git stash apply
:
git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1
Now you will be left with just the non-stash changes. They will be in the index. You can use git reset
to unstage your changes if you like.
Given that your original git stash apply
failed I assume the reverse might also fail since some of the things it wants to undo did not get done.
Here's an example showing how the working copy (via git status
) ends up clean again:
$ git status
# On branch trunk
nothing to commit (working directory clean)
$ git stash apply
Auto-merging foo.c
# On branch trunk
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: foo.c
#
no changes added to commit (use "git add" and/or "git commit -a")
$ git add -u
$ git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1
Auto-merging foo.c
$ git status
# On branch trunk
nothing to commit (working directory clean)
回答2:
My use case: just tried popping onto the wrong branch and got conflicts. All I need is to undo the pop but keep it in the stash list so I can pop it out on the correct branch. I did this:
git reset HEAD --hard
git checkout my_correct_branch
git stash pop
Easy.
回答3:
Edit: From the git help stash
documentation in the pop section:
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.
If the --index option is used, then tries to reinstate not only the working tree's changes, but also the index's ones. However, this can fail, when you have conflicts (which are stored in the index, where you therefore can no longer apply the changes as they were originally).
Try hardcopying all your repo into a new dir (so you have a copy of it) and run:
git stash show
and save that output somewhere if you care about it.
then: git stash drop
to drop the conflicting stash
then: git reset HEAD
That should leave your repo in the state it was before (hopefully, I still haven't been able to repro your problem)
===
I am trying to repro your problem but all I get when usin git stash pop
is:
error: Your local changes to the following files would be overwritten by merge:
...
Please, commit your changes or stash them before you can merge.
Aborting
In a clean dir:
git init
echo hello world > a
git add a & git commit -m "a"
echo hallo welt >> a
echo hello world > b
git add b & git commit -m "b"
echo hallo welt >> b
git stash
echo hola mundo >> a
git stash pop
I don't see git trying to merge my changes, it just fails. Do you have any repro steps we can follow to help you out?
回答4:
If you don't have to worry about any other changes you made and you just want to go back to the last commit, then you can do:
git reset .
git checkout .
git clean -f
回答5:
OK, I think I have managed to find a work-flow that will get you back to where you need to be (as if you had not done the pop).
TAKE A BACKUP BEFOREHAND!! I don't know whether this will work for you, so copy your whole repo just in case it doesn't work.
1) Fix the merge problems and fix all the conflict by selecting all the changes that come from the patch (in tortoisemerge, this shows up as one.REMOETE (theirs)).
git mergetool
2) Commit these changes (they will already be added via the mergetool command). Give it a commit message of "merge" or something you remember.
git commit -m "merge"
3) Now you will still have your local unstaged changes that you started originally, with a new commit from the patch (we can get rid of this later). Now commit your unstaged changes
git add .
git add -u .
git commit -m "local changes"
4) Reverse the patch. This can be done with the following command:
git stash show -p | git apply -R
5) Commit these changes:
git commit -a -m "reversed patch"
6) Get rid of the patch/unpatch commits
git rebase -i HEAD^^^
from this, remove the two lines with 'merge' and 'reversed patch' in it.
7) Get your unstanged changes back and undo the 'local changes' commit
git reset HEAD^
I've run through it with a simple example and it gets you back to where you want to be - directly before the stash was popped, with your local changes and with the stash still being available to pop.
回答6:
I solved this in a somewhat different way. Here's what happened.
First, I popped on the wrong branch and got conflicts. The stash remained intact but the index was in conflict resolution, blocking many commands.
A simple git reset HEAD
aborted the conflict resolution and left the uncommitted (and UNWANTED) changes.
Several git co <filename>
reverted the index to the initial state. Finally, I switched branch with git co <branch-name>
and run a new git stash pop
, which resolved without conflicts.
回答7:
Some ideas:
Use
git mergetool
to split the merge files into original and new parts. Hopefully one of those is the file with your non-stash changes in it.Apply the diff of the stash in reverse, to undo just those changes. You'll probably have to manually split out the files with the merge conflicts (which hopefully the above trick will work for).
I didn't test either of these, so I don't know for sure of they will work.
回答8:
If DavidG is correct that it didn't pop the stash because of the merge conflict, then you merely need to clean up your working directory. Quickly git commit
everything you care about. (You can reset
or squash
the commit later if you're not done.) Then with everything you care about safe, git reset
everything else that git stash pop
dumped into your working directory.
回答9:
Use git reflog
to list all changes made in your git history. Copy an action id and type git reset ACTION_ID
回答10:
I could reproduce clean git stash pop
on "dirty" directory, with uncommitted changes, but not yet pop that generates a merge conflict.
If on merge conflict the stash you tried to apply didn't disappear, you can try to examine git show stash@{0}
(optionally with --ours
or --theirs
) and compare with git statis
and git diff HEAD
. You should be able to see which changes came from applying a stash.
回答11:
If there were no staged changes before the git stash pop
, as in the question, then the following two commands should work.
git diff --name-only --cached | xargs git checkout --ours HEAD
git ls-tree stash@{0}^3 --name-only | xargs rm
The first reverses any merges from the stash, successful or not. The second deletes any untracked files introduced by the stash.
From man git stash
: The working directory must match the index.
Which @DavidG points out, the stash pop
will fail if any currently unstaged modified files conflict. As such, we shouldn't need to worry about unwinding merge conflicts beyond getting back to HEAD
. Any remaining modified files are then unrelated to the stash, and were modified before the stash pop
If there were staged changes, I'm unclear on whether we can rely on the same commands and you may want to try @Ben Jackson's technique. Suggestions appreciated..
Here is a testing setup for all of the various cases https://gist.github.com/here/4f3af6dafdb4ca15e804
# Result:
# Merge succeeded in m (theirs)
# Conflict in b
# Unstaged in a
# Untracked in c and d
# Goal:
# Reverse changes to successful merge m
# Keep our version in merge conflict b
# Keep our unstaged a
# Keep our untracked d
# Delete stashed untracked c
回答12:
I'm posting here hoping that others my find my answer helpful. I had a similar problem when I tried to do a stash pop on a different branch than the one I had stashed from. On my case I had no files that were uncommitted or in the index but still got into the merge conflicts case (same case as @pid). As others pointed out previously, the failed git stash pop did indeed retain my stash, then A quick git reset HEAD plus going back to my original branch and doing the stash from there did resolve my problem.
回答13:
Start by committing all content
First peak at your stash
git diff stash@{0} HEAD
If you think you want to use that stash, preserve your git stash, but see what would happen if it was applied
git stash apply
Take a peek at the difference between the truth before the stash and now that you have applied the stash
git diff HEAD
Happy Path: Like what you see?
git add .
git commit -m "before stash or some other commit message"
Then cleanup your applied stash
git stash drop stash@{0}
Not happy with the stash applied? You have options!
1. Don't like any of the changes the stash provides?
Return to state before applying the stash locally
git checkout .
2. Like some files but some not?
Remove any/all files you don't want
git checkout path/to/file
Add any files that you want in this commit
git add path/to/file
Now just commit and push
git add .
git commit -m "your commit message here"
git push
Then cleanup your applied stash
git stash drop stash@{0}
来源:https://stackoverflow.com/questions/8515729/aborting-a-stash-pop-in-git