Using git stash

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.

Getting ready

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.

How to do it...

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.

How it works…

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:

How it works…

We can also see the state of the branches after we created the commit, as shown in the following screenshot:

How it works…

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.

There's more…

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 
There's more…

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
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset