1

As far as I'm aware, git stash saves a diff between the working tree and HEAD, it doesn't save the index, the state of the working tree, as well as the index. You can see this by running git log --graph stash@{0} after creating a stash.

git stash pop has an option --index, with this documentation:

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).

I don't understand this documentation. The second sentence in the quoted documentation is confusing to me.

Why do merge conflicts unexpectedly happen? For example:

$ git init test
$ cd test
$ echo initial > file
$ git add file
$ git commit -m initial
$ echo a >> file
$ git add file
$ echo b >> file
$ git stash save --keep-index
$ git stash pop --index
Auto-merging file
CONFLICT (content): Merge conflict in file

I don't understand why a merge conflict happens in the last step. What I want to happen is for git stash pop --index to recognise that the index is already in the correct state, and to change the working tree files to match the stashed files.

Flimm
  • 136,138
  • 45
  • 251
  • 267
  • @musiKk: to demonstrate why I think that `git stash` doesn't save the index, see the linked comment in this blog post http://codeinthehole.com/writing/tips-for-using-a-git-pre-commit-hook/#comment-1040912941 . That doesn't seem the case for files that don't exist in HEAD though. – Flimm Oct 15 '14 at 10:14
  • But that doesn't make it right. The prompt even confirms this: `$ git stash` prints `Saved working directory and index state WIP on branch-name-here ...`. Maybe you have a configuration option set that alters the default behavior? – musiKk Oct 15 '14 at 10:21
  • @musiKk: I don't think so, don't you see the described behaviour in the linked blog post comment as well? Interesting point about the prompt. – Flimm Oct 15 '14 at 10:33
  • 1
    In the comment `git stash --keep-index` is invoked which does what it says: It keeps the index and does not put it into the stash. But a simple `git stash` creates two objects: One for the tree and one for the index. You can easily verify that with `gitk`. – musiKk Oct 15 '14 at 11:02
  • 1
    See http://stackoverflow.com/questions/26021591/why-wip-and-index-commit-listed-on-develop-after-stash/26022071#26022071 – jub0bs Oct 15 '14 at 12:23

1 Answers1

2

'git stash' indeed saves the index - even with --keep-index that leaves your staged changes (but saves them at the same time).

If not adding line 'b', git stash pop should add 'a' to 'a' (still there because of the --keep-index), but is smart enough to realize it is the same modification, and does not do anything and does not trigger a conflit.

When adding line 'b' after the stage, git stash pop thinks the modifications are different, and tries bluntly to add 'a' + 'b' on top of 'a' (as 'a' is still on your tree), raising a conflit.
NB: if 'b' was farther in the code, it would be okay.

So to avoid conflict when restoring the stash, you could first reset your current tree (thus removing the staged modifications), and then pop your stash (which will put you back in the initial state, since the index was saved)

git stash push --keep-index --include-untracked -m "cleaning_before_git_hook"

# here you can for example check your commit with git hooks, run tests, etc.

git reset --hard # to avoid any conflict when popping
git stash pop --index # without --index you will find back your initial code, but it will forget about the staged modifications you had
PJ127
  • 986
  • 13
  • 23