13

Why does Git's stash commit need two parents?

In the Hacker's Guide to Git, I see this mental model for stashes:

enter image description here

The guide tells me that stash@{0} needs both A and B as parents. Why? Why doesn't the stash just point to B, getting rid of the need for C? I think I am missing something in my Git comprehension.

2 Answers 2

8

Because two things are stashed: the indexed content and the worktree content. Both are derived from the checked-out commit. When popping the stash, both can be restored.

7
  • So the changes in B have been committed but not pushed, and the changes in C have neither been committed nor pushed. Is that right? Dec 21, 2014 at 22:08
  • No, the content in B had been added aka staged, but not committed; the content in C was in your worktree.
    – jthill
    Dec 21, 2014 at 22:13
  • Still not sure about C, then : what is the content here that differs from B? Dec 21, 2014 at 22:40
  • Anything you changed but didn't git add.
    – jthill
    Dec 21, 2014 at 22:41
  • 1
    Your working changes (tracked, but not added) should for sure be kept, that is the intention of stashing those changes right? Later you decide to pop the stash or discard it, or leave it.
    – vgoff
    Jan 3, 2015 at 23:45
6

It doesn't always require two parents, sometimes it actually requires three.

The following diagram illustrate the simplest scenario, with two parents:

       .----S
      /    /
-----H----I

Here H represents your current head, which is the master branch in your example, and two child commits are created when you run git-stash.

The first is I, which represents the index at the time of stashing. In other words, this commit contains the changes that were staged before stashing. It has a single parent, which is the commit which HEAD pointed to. The second (S) is the stash commit, which contains files that were modified before stashing. It has two commits because its changes might be on top of those in I and those in H, that is, they might affect files that were staged or not.

The other scenario happens when you provide the -u switch to the stash command, which tells Git to also stash untracked files. The diagram now becomes:

       .----S----.
      /    /    /
-----H----I    U

The new U commit has all the changes introduced by the untracked files. Notice that it wouldn't make sense for this commit to have any parents since those files don't exist in the current HEAD. The stash commit S now has three parents: H, I and U and now when applying the stash commit Git will also apply the untracked changes.

You can see those diagrams and the diffs clearly by running something like git log --graph stash@{0} or gitk stash@{0}.

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.