Search code examples
gitvisualizationgit-stash

Is this a valid visualization of the "git stash" operation?


I can not find a good visualization of the git stash operation. So I created my own based on some rare comments on the internet.

enter image description here

This this visualization correct?

Edit note: I need to change the color of stash@{0} because it's no branch.

Where are stashes stored in my .git directory? I think it's a reference and stored in refs, right?


Legend:

  • C1, C2 - commits
  • Idx - Index
  • WD - working directory
  • dashed line and dashed borders => free for garbage collection

Reworked Image

enter image description here


Solution

  • The "before git stash" and "after git stash" graphs are essentially correct (there are two new commits, not on any branch, pointed-to by refs/stash aka stash@{0}). What's missing from this diagram is that the index and work-tree themselves are reset, in the same way git reset --hard does it—in fact, using git reset --hard—after git stash.

    The "after git stash branch" graph is [edit to match new diagram] much harder to draw, though: git stash branch uses the idx and wd commits to restore the state of the index and work-tree, and then drops them entirely from refs/stash. Those two commits now have no name at all and are therefore eligible for garbage collection (git gc may now remove them as soon as they expire, in terms of "prune time", based on gc.pruneExpire in your configuration). The new branch name test points to the parent commit to which idx and WD pointed, back when idx and WD were still guaranteed to exist.

    Making a new commit immediately at this point (git stash branch <name> && git commit) will make a new commit from the restored index, as in the updated drawing.

    Where are stashes stored in my .git directory?

    The commits themselves are commits, and hence are stored like any other commit, in .git/objects/ (as loose or packed objects).

    I think it's a reference and stored in refs, right?

    Yes, refs/stash is a reference. The git stash code uses (or abuses?) the reflog entries for refs/stash to manage the "stash stack", so both the hash ID in refs/stash itself, and all those in any refs/stash reflog entries, matter: they keep the WD commit alive, by pointing directly to it, and hence also keep the idx commit (the ^2 or second parent of WD) alive (since it is reachable through the WD commit).