This is the situation:
git stash
on branch Agit checkout B
git stash
on branch Bgit checkout A
git stash pop
on branch AAfter step 7 of the list above, the changes I had made on branch A before stashing did not come back but the changes I had made on branch B popped on branch A. I used cmd z
on branch A and the file went to its previous state, which had the changes I had made. It seemed the HEAD of branch A moved to the HEAD of branch B. The same happened when I git checkout B
and git stash pop
on that branch: branch B had all the changes made on branch A but not the changes I had made on branch B. I used cmd z
again to revert to the branch state I needed.
This caused me a lot of problems for sometime, until I was allowed to commit code again on the project (nobody could commit for a while because there was an automated push to the server on commits and the manager didn't want new code in the server until they ran some tests). How can I pop only the changes made on the branches themselves and not changes made on other branches?
What git stash
does is make a commit.1
Of course, what git commit
does is make a commit. So why do we have git stash
at all?
A significant difference between these commands is that the commits git stash
makes are not on any branches. This allows you to stash
when on one branch, then move to another branch and apply the stash there. In other words, it lets you move in-progress work.
You can often, but not always, move in-progress work anyway. See Git - checkout another branch when there are uncommitted changes on the current branch. When you can't, though, you can use git stash
to deal with this.
On the other hand, if you want to "stash on a branch", as in your case, you are probably better off just making a regular commit, rather than a special stash commit. Such commits are easier to deal with, and also do not have a bug that git stash
has. You are not likely to hit this bug but "regular commits are simpler and easier to deal with" (commits on branches, vs stashes) is a pretty good reason to avoid git stash
.
If you prefer to use git stash
anyway, note that each new stash "pushes" the previous ones up higher in a "stash stack". The old stash becomes stash@{1}
, and what was stash@{1}
becomes stash@{2}
, and so on. When you drop
(discard) or pop
(attempt to apply, then discard) a stash, the stacked ones above it move back down—so if you were to git stash drop stash@{3}
when you had stash@{4}
and stash@{5}
as well, you would be left with stash@{3}
and stash@{4}
now.
You can name any stash, including the most recent one, this way: stash@{0}
means the same thing as stash
. (Git actually implements all this using the reflog for stash
.)
1In fact, it makes at least two commits, and sometimes three. The two commits store the index and work-tree state; the third commit, if present, is from -u
or -a
and stores the unstaged (-u
) or all (-a
) files. The work-tree commit is a very odd merge commit, with the index commit as its second parent and the third commit, if present, as its third parent. The first parent of the work-tree commit, and the sole parent of the index commit, is the commit that was current when you ran git stash
.
If you draw commit graph fragments—which, any time you are doing anything complicated in Git, is a good idea—the index-and-work-tree commit pair kind of dangles off the original commit, with the refs/stash
reference pointing to the pair, rather than the branch name. It looks almost like a small handbag, or a food cache hung from a tree branch to keep it away from bears, or some such, so I like to refer to this as a "stash bag".
Torek (as usual) provides an excellent answer, but I believe the succinct answer here is just to note that the stash contains data as a stack (LIFO). So when you pushed A's work and then B's work, it first pops B then A. So when you went back to A and then did the first pop, you got the saved B work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With