1

Manpage of git stash says

A stash entry is represented as a commit whose tree records the state of the working directory, and its first parent is the commit at HEAD when the entry was created. The tree of the second parent records the state of the index when the entry is made, and it is made a child of the HEAD commit. The ancestry graph looks like this:

       .----W
      /    /
-----H----I

where H is the HEAD commit, I is a commit that records the state of the index, and W is a commit that records the state of the working tree.

In the example, is the stash entry represented by commit W?

What does it mean by the stash entry's commit W having two parents I and H?

Is W the result of (two-way or three-way) merging the two commits I and H?

Thanks.

  • When I do a `git stash list --pretty=full --parents --full-history`, I see only one parent on each commit, and that parent SHA always matches the next commit in the list. I think everything in the stash I'm looking at is stashes of working trees, not any index stages or combination of the two, though. – Kaz Jan 23 '19 at 01:21
  • `git stash list -p` is showing me garbage, actually. It shows completely the wrong diff. I just made two trivial one-line changes. I staged one of them, left the other working. Did a `git stash save cruft`. When I do `git stash show -p` it shows these changes as a diff, correctly. If I `git stash list -p`, it shows completely a wrong set of changes for `cruft`: it shows a change set touching many files, bumping copyright notices to 2019: a change I made several revisions ago on this branch that is not stashed at all. – Kaz Jan 23 '19 at 01:28
  • @Kaz: `git stash list -p` is getting confused by the fact that the stash commit has the form of a merge, without being a merge. If you accidentally run `git show stash` instead of `git stash show`, that has the same issue. This happens to me a bit too often: I might have a touch of dysxlexia. :-) – torek Jan 23 '19 at 01:32
  • @torek `git show stash` is a thing? evidently, it seems to mean the same as `git show stash@{0}`. I get diff-of-diff syntax out of this. – Kaz Jan 23 '19 at 01:37
  • `git show` shows commits (well, it shows almost anything now!). `stash` is a valid reference if `refs/stash` exists, and it points to the `W` commit of the stash. So `git show stash` shows the stash commit, and since it *looks like* a merge, `git show` treats it as one. – torek Jan 23 '19 at 01:40

1 Answers1

2

In the example, is the stash entry represented by commit W?

Yes—or more properly, no, it's represented by commit W and commit I, but W suffices to find I. Either of W or I suffices to find H, and git stash will find all three commits automatically.

What does it mean by the stash entry's commit W having two parents I and H?

Nothing special. That just makes it easier—very very slightly easier—for git stash apply and other git stash functions to find both I and H.

Not shown in the documentation is the third commit of a stash. This third commit is optional; it is present only if you used -a / --all or -u / --include-untracked. The third commit is the third parent of W. The stash code will test to see if that third parent exists, in order to decide whether the stash is a two-commit stash or a three-commit stash.

(The third commit, if present, has the form of a root commit, as it has no parent. It's also peculiar in that it holds exactly and only files that were untracked, and maybe also ignored depending on which flag you used, without holding any of the work-tree files at all. I typically call the third commit U.)

Is W the result of (two-way or three-way) merging the two commits I and H?

No. The stash code is merely using (abusing?) the form that merge commits use, but for a different purpose: to wedge two, or sometimes three, commits into an easy-to-inspect form for the stash code to inspect. The special name refs/stash can then hold the hash ID of the W commit, from which the other commit(s) are located.

(The git stash code makes use of special temporary index files to make the W and U commits. Making the I commit is easier: it just runs git write-tree and git commit-tree directly.)

torek
  • 448,244
  • 59
  • 642
  • 775