I only ever manually commit
to my local repo just before push
ing to the remote repo.
But more often I pull
to get my coding partner's changes.
Sometimes we've both worked on the same file and there's a conflict. In these cases he's told me to do a git stash
before my git pull
and then a git stash pop
afterward.
But sometimes this results in git telling me next time that I can't pull
because I have unmerged files. These are usually experimental changes in my local tree that I don't wish to commit
or push
.
A couple of times I've needed to send my work in and the result has been intermediate revisions in the remote repo including my local experiments, debug code, etc, that I never wished to send. I want to avoid making such mess.
Is this due to stash
modifying my local repo? If so, how can I avoid that? If not, what else could be causing it? I'm a total noob at git and only use these few commands.
I want to first mention here that the term index means the same thing as staging area, and that you should remember that there are three versions of a file "active" at any one time: the HEAD
version, the index or "staged" version, and the work-tree version. When you first run git checkout <branch>
you generally have all three versions matching up. Any committed version is permanent—well, as permanent as the commit—and unchangeable: you can't touch the one stored in the current commit. You can overwrite the index and work-tree versions at any time, but the normal pattern is:
git add
to copy the work-tree version back to the index.Repeat steps 2 and 3 until satisfied; or use git add --patch
to build up an index version that's kind of like the work-tree version, but different. (Typically one does this to make a committable version of some file that doesn't have extra debug stuff in it, while running the debug one.) This does mean that the index and work-tree can differ from each other and from the HEAD
commit.
If and when you do run git commit
, this makes a commit from whatever's in the index / staging-area right then. This is why you have to keep git add
ing all the time, to copy from work-tree into index.
As Sajib Khan answered, git stash save
does make commits. More precisely, if git stash save
does anything (sometimes it does nothing, if there are no changes), it makes at least two commits. If you use the --untracked
or --all
flag, it makes three commits. The git stash
documentation has a small diagram of this under its DISCUSSION section. Like the documentation, we'll mostly ignore the third commit.1
What's unusual about these commits is that they are on no branch. The special reference name refs/stash
points to the newly created w
(work-tree) commit. It has at least two parents, one being the HEAD
commit and the other being the i
(index) commit. With --untracked
or --all
there is a third parent (which I call u
) holding the extra, untracked, files.
In all but one case2 that we'll also ignore here, after saving away the index and work-tree versions of each file in the i
and w
commits, git stash save
then runs git reset --hard HEAD
to replace the index and work-tree versions of those files with the versions stored in the HEAD
commit. So your work is now saved, and can be restored later, but no longer exists in either index (aka staging area) or work-tree.
1If (and only if) you use the --all
or --untracked
option to create the third commit, Git also runs git clean
with appropriate options to delete the files stored in this third parent. Keep this in mind: any existing untracked files (whether ignored or not) are never included in either i
or w
. They are not saved at all, and hence not cleaned away either, unless you use these extra options. Note that the definition of an untracked file is simply any file that is not in the index right now. The last two words are critical as well, in a case you are not yet running into, but could eventually.
2The one case occurs when you use the --keep-index
option. In this case, the git stash save
code does something fairly tricky: after making the i
and w
commits, instead of resetting the index and work-tree to HEAD
, it resets them to what's in the i
commit. The purpose of this is to arrange the work-tree to hold the proposed new commit, so that programs that test work-tree files can test the "to be committed" versions of the files. There are several traps here for the unwary, however: see How do I properly git stash/pop in pre-commit hooks to get a clean working tree for tests?
Once you have a stash—i.e., i
and w
commits—saved away, you can mostly-safely run git pull
,3 or better, git fetch
. This will obtain new commits from the other Git you have your Git remembering as origin
, remembering them via origin/master
and origin/develop
and origin/feature/tall
and so on. Then the second step of pull
, which is rebase
or merge
, will rebase—i.e., copy—your existing commits if you have any, or merge your existing commits if you have any, on/with the latest commits you have brought in, and adjust your own current branch to point to the result.
So far, everything has gone well and is just what you are doing. But now we get to the tricky part.
Now you run git stash pop
as your co-worker / coding partner suggests. I recommend starting with git stash apply
instead of git stash pop
, but when it fails—and it is failing, given what else you've mentioned—that doesn't really matter.4 Either way Git attempts to apply the saved commits, so that the changes you saved in the index and/or the work-tree are restored. But this is, as I just said, tricky, because commits are snapshots, not changes. (Also, by default, git stash apply
/ git stash pop
throws away the i
commit. With --index
, it tries to restore the i
commit into the index, too.)
In order to restore the w
commit as changes, rather than as a snapshot, Git uses its merge machinery. The stash script has this actual line in it:
git merge-recursive $b_tree -- $c_tree $w_tree
The effect is as if you had run a git merge
—or even more close, git cherry-pick
—command. Git compares your stashed work tree $w_tree
(commit w
) to the commit that was HEAD
($b_tree
) to see "what you changed", and compares your current index as turned into a partial commit ($c_tree
) against that same $b_tree
to see "what they changed", and merges them.
This merge, like any merge, can fail with merge conflicts. This is what you have described:
... can't pull because I have unmerged files ...
When a merge fails, it leaves the partially-merged results in the work-tree and the original sets of files in the index. Suppose, for instance, that file foo.txt
has a merge conflict. Now instead of three versions of foo.txt
—HEAD
(current commit), index, and work-tree—you have five versions! These are:
HEAD
, as always;$b_tree
, which is the tree that goes with whichever commit was HEAD
back when you ran git stash save
;--ours
: this is whatever was in the index when you started the git stash apply
/ git stash pop
that failed. (This probably matches the HEAD
version.)--theirs
: this is whatever was in $w_tree
, i.e., your stashed changes; andNote that just as with git rebase
and git cherry-pick
, the ours/theirs git checkout
flags are kind of reversed here.
Once you are in this annoying "unmerged index entries" state, there is very little you can do other than either finish it, or abort the operation entirely.
In this particular case, the git stash apply
has already stopped partway through applying, and hence already aborted the subsequent git stash drop
. You therefore still have your stash and can run git reset --hard HEAD
to abort the attempt to apply the stash. Or, you can edit the work-tree files to finish the merge that Git was unable to do, and git add
the files to copy them into the index, so that the index has the (single) merged entry takem from the work-tree, replacing the three higher-staged entries.
This is the same process you must do for any failed merge: you either abort it, and then figure out what to do later; or you finish it now.
Note that in general you should not just git add
the work-tree files "as is", complete with conflict markers. While this solves the "cannot X, you have unmerged index entries", it leaves you with files full of conflict markers, which are not really useful.
These kinds of merge failures (merge conflicts) can also happen when you run git pull
, if Git needs to merge some of your commits with someone else's commits. Or, Git may succeed at doing the merge on its own (or think it succeeds, at least), and then make a new merge commit. In this case you will be prompted to enter a commit message:
I've noticed that a side effect of the
pull
after thestash
sometimes opens up vi for me to enter a commit message.
This indicates that you have made normal commits, on your branch, and that your git pull
has run git merge
, which believes it made a successful merge and now needs the commit message for this new merge commit.
You may want to use git rebase
rather than git merge
here. This is easier if you use git fetch
followed by the second Git command, instead of using git pull
.
Note that if you do use git rebase
(and learn it, especially the very handy git rebase -i
), you can feel free to make all kinds of temporary commits, including:
... local experiments, debug code, etc ...
When you rebase, you'll copy those commits atop the other guy's commits, keeping your work as your own; and you can eventually use git rebase -i
to "squash away" the temporary commits in favor of one big final "real" commit. You do have to be careful not to accidentally git push
them (once you do, copies wind up everywhere and it's overly difficult to get everyone else to give them up).
3I recommend not using git pull
here: instead, split it up into its component parts. First, run git fetch
to obtain new commits from another Git. Once you have the commits, you can look them over if you like. Then use git rebase
or git merge
to incorporate those commits into your branch(es). You need one git rebase
or git merge
per branch, but only one git fetch
before all of them.
Once you're very familiar with how the two component parts it will do for you work, you can safely run git pull
, knowing that when something goes wrong—and something eventually will—you'll recognize what happened and which step failed, and will know what to look at to figure out how to fix it.
4For completeness, though, note that git stash pop
just means git stash apply && git stash drop
. That is, attempt to apply the stash; then if Git thinks that went well, immediately drop the stash. But sometimes Git thinks it went fine, when it didn't. In this particular case it's nice to still have the stash handy, and git stash drop
makes getting it back very hard.