In this example, we explore the git stash
command and learn how we can use it to quickly put away uncommitted changes and retrieve these again. This can be useful when being interrupted with an urgent task and you are not yet ready to commit the work you currently have in your working directory. With the git stash
command, you save the state of your current working directory with/without a staging area and restore a clean state of the working tree.
In this example, we'll use the cookbook-tips-tricks
repository. We'll use the master
branch, but before we are ready to try the stash
command, we need to create some changes in the working directory and the staging area:
$ git clone https://github.com/dvaske/cookbook-tips-tricks.git $ cd cookbook-tips-tricks $ git checkout master
Make some changes to foo
and add them to the staging area:
$ echo "Just another unfinished line" >> foo $ git add foo
Make some changes to bar
and create a new file:
$ echo "Another line" >> bar $ echo "Some content" > new_file $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: foo Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: bar Untracked files: (use "git add <file>..." to include in what will be committed) new_file
We can see that we have one file added to the staging area, foo
, one modified file, bar
, and an untracked file in the work area as well, new_file
.
With the preceding state of our repository, we can stash away the changes so that we can work on something else. The basic command will put away changes from the staging area and changes made to the tracked files; it leaves untracked files in the working directory:
$ git stash Saved working directory and index state WIP on master: d611f06 Update foo and bar HEAD is now at d611f06 Update foo and bar $ git status On branch master Your branch is up-to-date with 'origin/master'. Untracked files: (use "git add <file>..." to include in what will be committed) new_file nothing added to commit but untracked files present (use "git add" to track)
Now we can work on something else and create and commit this. We'll change the first line of the foo
file and create a commit with this change:
$ sed -i '' 's/First line/This is the very first line of the foo file/' foo $ git add foo $ git commit -m "Update foo" [master fa4b595] Update foo 1 file changed, 1 insertion(+), 1 deletion(-)
We can see the current work we have stashed away with the git stash list
command:
$ git stash list stash@{0}: WIP on master: 09156a4 Update foo and bar
To get back the changes we stashed away, we can pop them from the stash
stack:
$ git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) Untracked files: (use "git add <file>..." to include in what will be committed) new_file nothing added to commit but untracked files present (use "git add" to track) $ git stash pop Auto-merging foo On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: bar modified: foo Untracked files: (use "git add <file>..." to include in what will be committed) new_file no changes added to commit (use "git add" and/or "git commit -a") Dropped refs/stash@{0} (91b68271c8968fed01032ad02322292f35be8830)
Now, the stashed changes are available again in the working repository and the stash entry is deleted. Note that the changes are applied only to the working directory, though one of the files was staged when we created the stash.
We have created two commits: one for the index and one for the work area. In Gitk, we can see the commits that stash creates to put the changes away:
We can also see the state of the branches after we created the commit, as shown in the following screenshot:
Git actually creates two commits under the refs/stash
namespace. One commit contains the content of the staging area. This commit is called index on master
. The other commit is the work in progress in the working directory, WIP on master
. When Git puts away the changes by creating commits, it can use its normal resolution methods to apply the stashed changes back to the working directory. This means that if a conflict arises when applying the stash, it needs to be solved in the usual way.
In the preceding example, we saw only the very basic usage of the stash
command, putting away changes to untracked files and changes added to the staging area. It is also possible to include untracked files in the stash
command. This can be done with the --include-untracked
option. We can add foo
to the staging area; firstly, to have the same state as when we created the stash earlier and then to create a stash that includes untracked files:
$ git add foo $ git stash --include-untracked Saved working directory and index state WIP on master: 691808e Update foo HEAD is now at 691808e Update foo $ git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working directory clean
Now, we can see that new_file
has disappeared from the working directory. It is included in the stash and we can check this with Gitk. It will show up as another commit of untracked files:
$ gitk master stash
We can also make sure that the changes we added to the staging area are added back to the staging area after we apply the stash, so we end up with the exact same state as before we stashed away our changes:
$ git stash pop --index On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: foo Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: bar Untracked files: (use "git add <file>..." to include in what will be committed) new_file Dropped refs/stash@{0} (ff331af57406948619b0671dab8b4f39da1e8fa2)
It is also possible to only put away the changes in the working directory, keeping the changes in the staging area. We can do this either for only the tracked files or by stashing away untracked files (--include-untracked
) as follows:
$ git stash --keep-index --include-untracked Saved working directory and index state WIP on master: 00dd8f8 Update foo HEAD is now at 00dd8f8 Update foo $ git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: foo