34

We've done something bad.

We ran git stash save during a merge conflict, and now we can't restore our work.

Things we've tried:

git pull -Xours origin master
git stash apply --index

And:

 git pull origin master
 git stash save --keep-index "merge conflicts"
 git stash apply stash@{1}

Please help!

6
  • Did you actually do anything significant? (Do you actually need to restore the stashed changes?) Can you just reset away the attempted merge, and do it over again?
    – Cascabel
    Commented Jan 25, 2012 at 21:17
  • 1
    Yes and no, respectively. The changes consist of more than one day of merge conflict resolution.
    – bukzor
    Commented Jan 25, 2012 at 21:56
  • 4
    @bukzor: If you need more then one day for merge conflict resolution it might be time to rethink your policies regarding branch handling and work distribution (or merge frequency). Such lengthy merge resolutions are afterall a nice source of hard to find bugs due to the amount of changes done in one commit
    – Grizzly
    Commented Jan 26, 2012 at 2:12
  • What error did you get? Why not a simple 'git stash apply'?
    – inger
    Commented Mar 1, 2012 at 1:57
  • 2
    @inger: Since stash save clears the index and stash apply doesn't modify it, you end up with regular unstaged changes, not a merge. I want to apply those changes during merge, but can't see how.
    – bukzor
    Commented Sep 20, 2012 at 18:48

6 Answers 6

35
+100

The issue seems to be that git stash doesn't save a reference to the branch you were trying to merge in. During a merge, this is stored in a ref named MERGE_HEAD.

To fix it and get back to your previous state, you need to find the revision (let's pretend it's d7a9884a380f81b2fbf002442ee9c9eaf34ff68d) you were trying to merge in, and set MERGE_HEAD to it after you apply the stash.

Then you can apply the stash (with --index to re-stage everything that was staged before), and set your MERGE_HEAD:

git stash apply --index
git update-ref MERGE_HEAD d7a9884a380f81b2fbf002442ee9c9eaf34ff68d
3
  • While the above seems true to me, I am not entirely confident that just setting MERGE_HEAD and unstashing is enough to restore the merge status. I would steer away from using git stash during conflicts, if possible. Commented Sep 21, 2012 at 12:19
  • Had same issue, and tried your solution. It complained that .git/MERGE_MSG didn't exist, I created that file with a custom merge message and then I committed after. I was worried that I would be marked as the author of all the changes in the merge, but it seems to have worked out as it should. Other authors properly creditted. Thanks :)
    – Eldamir
    Commented Jun 15, 2018 at 12:58
  • I had a stash and a pop in my pre-commit hook which was deleting .git/MERGE_HEAD causing a file not found error when trying to commit a merge. It would commit the second time I tried but it wouldn't actually be 'merged'.
    – jigglypuff
    Commented Aug 2, 2018 at 6:48
2

I did the same thing today, and took a different approach (after trial and error) to get back to the state just prior to stashing so I could continue resolving conflicts and complete the merge.

First, after unstashing the partial merge in the destination branch, I captured a list of the files with remaining conflicts (text file or editor tab). This is just the list of unstaged files after unstashing, as the files with conflicts already resolved would have been staged prior to stashing.

$ git status
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   myproject/src/main/java/com/acme/package3/Class3.java
#   modified:   myproject/src/main/java/com/acme/package3/Class4.java
#

Next, I created a patch and reset the branch back to the pre-merge state:

$ git diff HEAD > ~/merge-with-resolved-conflicts.patch
$ git reset --hard HEAD

Then I created a temporary branch (derived from the merge destination branch), and applied the patch:

$ git checkout -b my-temp-branch
$ git apply ~/merge-with-resolved-conflicts.patch
$ git commit -a -m "Merge with resolved conflicts"

So the HEAD of my-temp-branch now contains everything that was merged, including files with conflicts resolved, and files with remaining conflicts.

Then I switched back to original branch, merged again, and looked at the git status

$ git checkout my-branch
$ git merge other-branch
$ git status

The status shows the full list of files with conflicts:

# Unmerged paths:
#   (use "git add <file>..." to mark resolution)
#
#   both modified:      myproject/src/main/java/com/acme/package1/Class1.java
#   both modified:      myproject/src/main/java/com/acme/package2/Class2.java
#   both modified:      myproject/src/main/java/com/acme/package3/Class3.java
#   both modified:      myproject/src/main/java/com/acme/package3/Class4.java
#

Now I needed to compare these two lists of files. Any files in the second list but not the first had already been resolved (in this example, Class1.java and Class2.java). So for each of those files, I pulled in the version with conflicts resolved from the temporary branch (like cherry-pick, but for individual files rather than an entire commit):

$ git checkout my-temp-branch myproject/src/main/java/com/acme/package1/Class1.java
$ git checkout my-temp-branch myproject/src/main/java/com/acme/package2/Class2.java

Having done this, I was back to the state before the stash, so I could resume resolving the remaining conflicts and commit the merge.

1

You've resolved all your conflicts and then you git stash. This reset your working tree to pre-merge stage and puts all the resolutions into a stash.

So, all you need to do is redo the merge and then checkout all the contents (-- .) of the stash to the working tree. NB, run this in the root of your repository.

git merge conflict-branch
cd "$(git rev-parse --show-toplevel)"
git checkout stash@{0} -- .
git commit

ps, you can use git worktree add instead of git stash during a merge.

1

When you are in a conflicted state ( index and working directory), you will not be able to do git stash - it will give an error sating unmerged entries.

Make sure that you have really done a stash. See output of git status and git stash show

2
  • Prior to git stash, I have resolved the conflicts and proceed to use git add. It was after I have git add my resolutions, I made further edits and then did a git stash save msg. So that did succeed.
    – steven_moy
    Commented Jan 26, 2012 at 18:25
  • 1
    @bukzor actually, I just tested this out with msysgit 1.8.3 on Windows 7. If the file is left conflicted (i.e. no attempt to resolve the merge conflict has been made), then git stash save does indeed abort, without stashing the conflicted file. So this is actually correct, it is not false.
    – user456814
    Commented Aug 10, 2013 at 0:27
0

Given your last comment : you can use

git stash megre --no-commit <branch>

to put the index in a "merge" state without committing the changes

then modify it with what you want :

if you have already worked out your merge in the stash :

git reset #to remove the "conflicts" flags
git checkout <initial commit> -- ./ #to revert everything to the previous working state,
git stash apply   #apply your changes

and once everything is in the desired state, git commit


About bukzor's comment : there is actually a big difference between git checkout <tree-ish> and git checkout <tree-ish> -- <files>.

From the reference on git checkout :

  • git checkout <branch> : This form switches branches by updating the index, working tree, and HEAD to reflect the specified branch or commit.

  • git checkout [-p|--patch] <tree-ish> -- <pathspec> : When <paths> or --patch are given, git checkout does not switch branches. It updates the named paths in the working tree from the index file or from a named <tree-ish> (most often a commit).

git checkout <initial commit> would indeed discard the merge informations.

git checkout <initial commit> -- ./ (note the extra -- ./), on the other hand, will keep the merge information, and revert every tracked file to its state in <initial commit>.

2
  • git checkout will remove the merging state, won't it? I'll end up with a normal commit that dups all the changes in master.
    – bukzor
    Commented Sep 21, 2012 at 15:11
  • git stash apply also clears the merge state, explicitly setting the MERGE_HEAD ref as suggested in Evan's answer did the trick for me Commented Nov 20, 2013 at 1:26
0

My solution to get out of this (git stash pop during a merge conflict) was:

  • create and checkout a new (local) branch mytemporarybranch

    git branch mytemporarybranch && git checkout mytemporarybranch

  • commit into this mytemporarybranch

    git commit -m "my messy merge and squash"

  • checkout myoriginalbranch

    git checkout myoriginalbranch

  • merge correctly (no squash pop/apply this time!)

  • squash merge the mytemporarybranch onto the myoriginal branch

    git merge --squash mytemporarybranch

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.