I'm new to git and not quite clear on how stashing works.
Let's say I'm working on branch master and try to git pull
and receive the error that my local changes would be overwritten and need to be stashed or committed. If I haven't staged any of my changes and run git stash
, then do a git pull
and and update successfully, what happens when I git stash apply
?
In general, If someone else modifies files and I run git pull
, what happens when I run git stash apply
? does it overwrite the files that were just updated, regardless of whether or not they were staged when I stashed them? Does it overwrite every file that I just updated with git pull
, with the files that were stashed?
git stash
hangs a stash-bag—this is a peculiar form of a merge commit that is not on any branch—on the current HEAD
commit. A later git stash apply
, when you're at any commit—probably a different commit—then tries to restore the changes git computes by looking at both the hanging stash-bag and the commit it hangs from.
When you're done with the changes, you should use git stash drop
to let go of the stash-bag from the commit it was "stashed on". (And, git stash pop
is just shorthand for "apply, then automatically drop". I recommend keeping the two steps separate, though, in case you don't like the result of "apply" and you want to try again later.)
git stash
is actually fairly complex.
It's been said that "git makes much more sense once you understand X", for many different values of "X", which generalizes to "git makes much more sense once you understand git". :-)
In this case, to really understand stash
, you need to understand how commits, branches, the index/staging-area, git's reference name space, and merges all work, because git stash
creates a very peculiar merge commit that is referred-to by a name outside the usual name-spaces—a weird kind of merge that is not "on a branch" at all—and git stash apply
uses git's merge machinery to attempt to "re-apply" the changes saved when the peculiar merge commit was made, optionally preserving the distinction between staged and unstaged changes.
Fortunately, you don't actually need to understand all of that to use git stash
.
Here, you're working on some branch (master
) and you have some changes that aren't ready yet, so you don't want to commit them on the branch.1 Meanwhile someone else put something good—or at least, you hope it's good—into the origin/master
over on the remote repo, so you want to pick those up.
Let's say that you and they both started with commits that end in - A - B - C
, i.e., C
is the final commit that you had in your repo when you started working on branch master
. The new "something good" commits, we'll call D
and E
.
In your case you're running git pull
and it fails with the "working directory not clean" problem. So, you run git stash
. This commits your stuff for you, in its special weird stash-y fashion, so that your working directory is now clean. Now you can git pull
.
In terms of drawing of commits (a graph like you get with gitk
or git log --graph
), you now have something like this. The stash is the little bag-of-i-w
dangling off the commit you were "on", in your master
branch, when you ran git stash
. (The reason for the names i
and w
is that these are the "i"ndex / staging-area and "w"ork-tree parts of the stash.)
- A - B - C - D - E <-- HEAD=master, origin/master
|\
i-w <-- the "stash"
This drawing is what you get if you started working on master
and never did any commits. The most recent commit you had was thus C
. After making the stash, git pull
was able to add commits D
and E
to your local branch master
. The stashed bag of work is still hanging off C
.
If you made a few commits of your own—we'll call them Y
, for your commit, and Z
just to have two commits—the result of the "stash then pull" looks like this:
.-------- origin/master
- A - B - C - D - E - M <-- HEAD=master
\ /
Y - Z
|\
i-w <-- the "stash"
This time, after stash
hung its stash-bag off Z
, the pull
—which is just fetch
then merge
—had to do a real merge, instead of just a "fast forward". So it makes commit M
, the merge commit. The origin/master
label still refers to commit E
, not M
. You're now on master
at commit M
, which is a merge of E
and Z
. You're "one ahead" of origin/master
.
In either case, if you now run git stash apply
, the stash script (it's a shell script that uses a lot of low level git "plumbing" commands) effectively2 does this:
git diff stash^ stash > /tmp/patch
git apply /tmp/patch
This diffs stash
, which names w
—the "work tree" part of the stash—against the correct3 parent. In other words, it finds out "what you changed" between the proper parent commit (C
or Z
, as appropriate) and the stashed work-tree. It then applies the changes to the currently-checked-out version, which is either E
or M
, again depending on where you started.
Incidentally, git stash show -p
really just runs that same git diff
command (with no > /tmp/patch
part of course). Without -p
, it runs the diff with --stat
. So if you want to see in detail what git stash apply
will merge in, use git stash show -p
. (This won't show you what git stash apply
can attempt to apply from the index part of the stash, though; this is a minor gripe I have with the stash script.)
In any case, once the stash applies cleanly, you can use git stash drop
to remove the reference to the stash-bag, so that it can be garbage-collected. Until you drop it, it has a name (refs/stash
, aka stash@{0}
) so it sticks around "forever" ... except for the fact that if you make a new stash, the stash
script "pushes" the current stash into the stash reflog (so that its name becomes stash@{1}
) and makes the new stash use the refs/stash
name. Most reflog entries stick around for 90 days (you can configure this to be different) and then expire. Stashes don't expire by default, but if you configure this otherwise, a "pushed" stash can get lost, so be careful about depending on "save forever" if you start configuring git to your liking.
Note that git stash drop
"pops" the stash stack here, renumbering stash@{2}
to stash@{1}
and making stash@{1}
become plain stash
. Use git stash list
to see the stash-stack.
1It's not bad to go ahead and commit them anyway, and then do a later git rebase -i
to squash or fixup further second, third, fourth, ..., nth commits, and/or rewrite the temporary "checkpoint" commit. But that's independent of this.
2It's a fair bit more complex because you can use --index
to try to keep staged changes staged, but in fact, if you look in the script, you'll see the actual command sequence git diff ... | git apply --index
. It really does just apply a diff, in this case! Eventually it invokes git merge-recursive
directly, though, to merge in the work tree, allowing the same changes to have been brought in from elsewhere. A plain git apply
would fail if your patch does something the "good stuff" commits D
and E
also does.
3This uses git's parent-naming magic syntax, with a little advance planning inside the stash
script. Because the stash is this funky merge commit, w
has two or even three parents, but the stash script sets it up so that the "first parent" is the original commit, C
or Z
, as appropriate. The "second parent" stash^2
is the index state at the time of the commit, shown as i
in the little hanging stash-bag, and the "third parent", if it exists, is unstaged-and-maybe-ignored files, from git stash save -u
or git stash save -a
.
Note that I assume, in this answer, that you have not carefully staged part of your work-tree and that you are not using git stash apply --index
to restore the staged index. By not doing any of this, you render the i
commit pretty much redundant, so that we need not worry about it during the apply
step. If you are using apply --index
or equivalent, and have staged items, you can get into a lot more corner cases, where the stash won't apply cleanly.
These same caveats apply, with yet more corner cases, to stashes saved with -u
or -a
, that have that third commit.
For these extra-hard cases, git stash
provides a way to turn a stash into a full-fledged branch—but I'll leave all that to another answer.