git stash explained in detail with examples

What is Git Stash

  • The git stash command saves a copy of your uncommitted changes in a queue, off to the side of your project.
  • By uncommitted changes, I mean items in either the staging area or the working directory that have been modified but not committed to the local repository.
  • Each time the stash command is invoked and there is uncommitted content (since the last stash command), git creates a new element on the queue to save that content. That content can be in the staging area, in the working directory, or both.
  • After creating the stash and saving the uncommitted content, Git is basically doing a git reset --hard HEAD operation. However, because you have the stash, you haven’t lost your uncommitted changes.

 

git stash syntax

The syntax for the git stash command is as follows:

git stash list [<options>]
git stash show [<stash>]
git stash drop [-q|--quiet] [<stash>]
git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]
git stash branch <branchname> [<stash>]
git stash [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
[-u|--include-untracked] [-a|--all] [<message>]]
git stash clear
git stash create [<message>]
git stash store [-m|--message <message>] [-q|--quiet] <commit>

 

When to use git stash command

Working of different features in parallel does not make a developer happy, but sometimes it happens. So, at a certain point, we have to break the work on a branch and switch to another one. However, sometimes, we have some modifications that are not ready to be committed, because they are partial, inconsistent, or even won’t compile.

Advertisement

So you can use git stash command if:

  • you don’t want to commit you experimental changes
  • you don’t want to lose local changes with a hard reset

 

How git stash works

The default syntax for creating a stash in Git is git stash <options>

The images in this section shows a visual representation of doing a default stash (without any options). In this case, the asterisk (*) denotes a change that has been made in the working directory and staged in the staging area since the last time you did a commit.

git stash explained in detail with examples
Local environment with an uncommitted change

Now, you run your git stash command:

$ git stash

The following image shows what happens conceptually. Git creates a stash of the staging area and working directory with the uncommitted change. It also resets the local environment back to the last commit (HEAD).

Now you have your in-progress changes stored in the stash, and your local environment is clean (in sync with the last commit). This is the basic operation.

Advertisement
git stash explained in detail with examples
After the initial stash

 

Git stash workflow

Following is a sample git stash workflow to understand the different stages involved:

# Your changes before stashing
git add . 

# Store the changes (stash)
git stash save "Saving changes from edit this file" 

# List stashed changes
git stash list 

# At this stage your local repo is clean
git status 

# To undo stash by popping the stash
git stash pop 

# Verify if the stash was popped
git stash list 

# Verify the pending changes before stash was done
git status

 

Save uncommitted changes

How to stash all the changes

I have this repository git_examples under which I have created git_stash directory. I will be performing different operations under this directory during the course of this article.

Here I have made some changes to one of the scripts under git_stash directory:

deepak@ubuntu:~/git_examples$ git status
On branch my-feature-branch
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   git_stash/create_users.sh

no changes added to commit (use "git add" and/or "git commit -a")

But then I suddenly remembered that I also have to work a different project which is priority. Now I can’t commit these changes as they are not complete so let me just save these changes in the stash while I work on other project:

deepak@ubuntu:~/git_examples$ git stash
Saved working directory and index state WIP on my-feature-branch: 1b6369c Added create_users.sh script

So now my git_examples repo is clean as new:

deepak@ubuntu:~/git_examples$ git status
On branch my-feature-branch
nothing to commit, working tree clean

Following are the changes which are currently stashed:

deepak@ubuntu:~/git_examples$ git stash list
stash@{0}: WIP on my-feature-branch: 1b6369c Added create_users.sh script

We will learn to load the changes from stash in next sections. But before that let us learn more about tracked and untracked files before we jump to the next section.

 

Stash selected files only (git stash –patch)

By default git stash will stash all the uncommitted changes but if you have a requirement to stash only some of the uncommitted changes then you can use -p|--patch argument. This will prompt for stashing each of the files.

Advertisement

For example, here I have made some changes on my branch:

git stash explained in detail with examples

Let’s execute stash with --patch:

deepak@ubuntu:~/git_examples/git_stash$ git stash -p
diff --git a/git_stash/create_users.sh b/git_stash/create_users.sh
index 01dd0d8..28c2576 100644
--- a/git_stash/create_users.sh
+++ b/git_stash/create_users.sh
@@ -1 +1,3 @@
 ## This script creates users
+## Created on 19-07-2021
+## Modified on 20-07-2021
(1/1) Stash this hunk [y,n,q,a,d,e,?]? y

Saved working directory and index state WIP on main: 1b6369c Added create_users.sh script

You can hit ? for a full list of hunk commands. Commonly useful ones are:

  • /: search for a hunk by regex
  • ?: help
  • n: don’t stash this hunk
  • q: quit (any hunks that have already been selected will be stashed)
  • s: split this hunk into smaller hunks
  • y: stash this hunk

So here we have saved our work for this particular file create_users.sh while the other one is still in our local repository and has not been stashed yet (as expected):

deepak@ubuntu:~/git_examples/git_stash$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   create_groups.sh

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   create_groups.sh

List the stashed changes:

deepak@ubuntu:~/git_examples/git_stash$ git stash list
stash@{0}: WIP on main: 1b6369c Added create_users.sh script

 

What are tracked and untracked files in Git

Before we go ahead you must be familiar about tracked and untracked files.

This designation refers to whether or not Git knows about the file—that is, has someone previously added this file to Git? If this file has at least been added to the staging area at some point (and not removed), then Git knows about it, and is managing a version of it, so it is tracked by Git. Otherwise, the file is untracked and Git doesn’t know about it and isn’t managing any versions of it.

An example of an untracked file would be a new file that hasn’t been added to Git. Files in the .gitignore file do not count as untracked because they are ignored by Git.

 

git stash untracked files

When stashing content with Git, by default, it ignores untracked files. In order for Git to stash untracked files, it is necessary to include the -u (--include-untracked) option. This option includes untracked files from the working directory in the stash.

Let’s create a new file under git_stash folder:

deepak@ubuntu:~/git_examples$ touch git_stash/create_groups.sh

This file will be marked as untracked as it is still not added to the repository:

deepak@ubuntu:~/git_examples$ git status
On branch my-feature-branch
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        git_stash/create_groups.sh

nothing added to commit but untracked files present (use "git add" to track)

Let’s try to stash the changes without any additional argument:

deepak@ubuntu:~/git_examples$ git stash
No local changes to save

As expected, stashing failed to recognize the untracked files.

So we must explicitly use -u argument with git stash command to also stash untracked changes:

deepak@ubuntu:~/git_examples$ git stash -u
Saved working directory and index state WIP on my-feature-branch: 1b6369c Added create_users.sh script

List the available stash:

deepak@ubuntu:~/git_examples$ git stash list
stash@{0}: WIP on my-feature-branch: 1b6369c Added create_users.sh script
stash@{1}: WIP on my-feature-branch: 1b6369c Added create_users.sh script

 

Provide description to stashed commits (git stash save)

Normally when Git stashes something, it has a generated comment associated with the element, of the following form:

stash@{#}: WIP on <branch>: <SHA1 of last commit> <last commit message>

Here, WIP stands for work in progress.

Let’s make some more changes to my existing script:

deepak@ubuntu:~/git_examples$ echo "## Description: This is a test script to create users" >> git_stash/create_users.sh

This time we will store our changes with some custom message:

deepak@ubuntu:~/git_examples$ git stash save "Working on issue #7211 to create users with create_users.sh"
Saved working directory and index state On my-feature-branch: Working on issue #7211 to create users with create_users.sh

As you can see, now we have a more descriptive form of the stashed changes:

deepak@ubuntu:~/git_examples$ git stash list
stash@{0}: On my-feature-branch: Working on issue #7211 to create users with create_users.sh
stash@{1}: WIP on my-feature-branch: 1b6369c Added create_users.sh script
stash@{2}: WIP on my-feature-branch: 1b6369c Added create_users.sh script

 

Check what is in the stash

Once you have your changes stashed, you can look at what you have in the queue. To do this, you use the list option of the stash command which we have already used a couple of times above:

deepak@ubuntu:~/git_examples$ git stash list
stash@{0}: On my-feature-branch: Working on issue #7211 to create users with create_users.sh
stash@{1}: WIP on my-feature-branch: 1b6369c Added create_users.sh script
stash@{2}: WIP on my-feature-branch: 1b6369c Added create_users.sh script

In the default output, I have the name of the stash element listed (stash@{#}), followed by information about the branch that was current when the stash was made, and then the last commit that the stash was based on.

This information is useful to a point, but what if you want to see more detail about an item in the stash? As it turns out, stash supports options like those you use with the git log command. Knowing this, you have different ways to get additional information. For example, you can start by using the --oneline option, as used below which shows you the abbreviated SHA1 values for each stash element:

deepak@ubuntu:~/git_examples$ git stash list --oneline
775caa9 (refs/stash) refs/stash@{0}: On my-feature-branch: Working on issue #7211 to create users with create_users.sh
d73cda6 refs/stash@{1}: WIP on my-feature-branch: 1b6369c Added create_users.sh script
57f436e refs/stash@{2}: WIP on my-feature-branch: 1b6369c Added create_users.sh script

Now you can use the git stash show subcommand and pass it the SHA1 value to see a quick summary of the changes that were in progress:

deepak@ubuntu:~/git_examples$ git stash show 775caa9
 git_stash/create_users.sh | 1 +
 1 file changed, 1 insertion(+)

For even more information, you can add the -p (patch) option to see the patch-style differences that were in the change:
git stash explained in detail with examples

 

Restoring stashed changes (unstash)

When getting stored changes out of the stash, Git attempts to reapply the stashed changes from the staging area back into your local environment’s staging area and the stashed changes from your working directory back into your local environment’s working directory. There are two options for doing this: apply and pop.

These options can be used on any branch, not just the branch that the original stash was performed on.

 

git stash apply command

The apply option attempts to put your changes back while leaving a copy of the items as an element still in the queue. Note that you can apply from any element at any position in the queue by referencing the name in the stash (stash@{#}). Unlike a more formal queue, you do not have to do pull elements only from the first or last positions in the queue.

An example usage would be:

deepak@ubuntu:~/git_examples$ git stash apply stash@{1}

Sample output from my terminal:
git stash explained in detail with examples

If the command is successful, your staging area and working directory are updated with the contents of the element from the stash and Git runs a git status command to show you the updated status.

deepak@ubuntu:~/git_examples$ git stash list
stash@{0}: On my-feature-branch: Working on issue #7211 to create users with create_users.sh
stash@{1}: WIP on my-feature-branch: 1b6369c Added create_users.sh script
stash@{2}: WIP on my-feature-branch: 1b6369c Added create_users.sh script

Let me re-stash this change:

deepak@ubuntu:~/git_examples$ git stash -u
Saved working directory and index state WIP on my-feature-branch: 1b6369c Added create_users.sh script

So now we have total 4 stashed changes:

deepak@ubuntu:~/git_examples$ git stash list
stash@{0}: WIP on my-feature-branch: 1b6369c Added create_users.sh script
stash@{1}: On my-feature-branch: Working on issue #7211 to create users with create_users.sh
stash@{2}: WIP on my-feature-branch: 1b6369c Added create_users.sh script
stash@{3}: WIP on my-feature-branch: 1b6369c Added create_users.sh script
NOTE:

There is also a git apply command that is used to apply patches, so be careful not to confuse these two commands.

 

git stash pop command

The pop option works like the apply option, but it removes the element from the queue after updating your environment. Like the apply option, you can pop an element from any position in the queue.

An example would be:

deepak@ubuntu:~/git_examples$ git stash pop stash@{2}

Sample output from my terminal:
git stash explained in detail with examples

Notice that on the queue, because you did the pop on element 2, you are now down to three elements:

deepak@ubuntu:~/git_examples$ git stash list
stash@{0}: WIP on my-feature-branch: 1b6369c Added create_users.sh script
stash@{1}: On my-feature-branch: Working on issue #7211 to create users with create_users.sh
stash@{2}: WIP on my-feature-branch: 1b6369c Added create_users.sh script

 

Merge conflicts while restoring stash changes

When you’re ready to bring content back from the queue into your local environment, chances are that you may have other updates in your local environment that weren’t there when you originally performed the stash. If there are potential merge conflicts between what you are trying to apply or pop and what’s currently in your local environment, what Git does depends on the circumstances.

For example, I had stashed the same change twice above. So let me commit one of the stashed changes:

deepak@ubuntu:~/git_examples$ git stash pop stash@{2}
deepak@ubuntu:~/git_examples$ git add git_stash/create_groups.sh
deepak@ubuntu:~/git_examples$ git commit -m "Created new script to add groups" git_stash/create_groups.sh

Now let me try to pop another stash for the same change:

deepak@ubuntu:~/git_examples$ git stash pop stash@{0}
git_stash/create_groups.sh already exists, no checkout
error: could not restore untracked files from stash
The stash entry is kept in case you need it again.

As expected Git does not allow it because there are unmerged files locally. The solution is to fix the merge issues first and then stage the fixed files.

 

Delete stash changes

You can use git stash drop <stash_id> to delete any of the stashed changes. For example, since I have already created my create_groups.sh script so I will drop the duplicate stash change:

deepak@ubuntu:~/git_examples$ git stash drop stash@{0}
Dropped stash@{0} (4e1aaa8397d07440029407e0817a4a99abed177e)

List the available stash:

deepak@ubuntu:~/git_examples$ git stash list
stash@{0}: On my-feature-branch: Working on issue #7211 to create users with create_users.sh
stash@{1}: WIP on my-feature-branch: 1b6369c Added create_users.sh script

Let me go ahead and delete all the stash.

deepak@ubuntu:~/git_examples$ git stash drop
Dropped refs/stash@{0} (fd1f432e03ccd9f1011341ef9281a8b1e55e48a4)

deepak@ubuntu:~/git_examples$ git stash drop
Dropped refs/stash@{0} (775caa9105d9d20b57e1101a29353a31900f5c41)

 

Create branch from specific stash

You may also choose to create a new branch from any of your stashed changes using following syntax:

git stash branch "BRANCH_NAME" stash@{#}

For example, I have this following stashed changes:

deepak@ubuntu:~/git_examples/git_stash$ git stash list
stash@{0}: On main: Added creation date in create_users.sh

I will create a new branch fix-7211-issue from this stash:

git stash explained in detail with examples

When we create a branch from any stash, this action checks out a new branch based on the commit that you created your stash from, and then pops your stashed changes onto it.

deepak@ubuntu:~/git_examples/git_stash$ git stash list

A new branch was created and our stash list is empty now:

deepak@ubuntu:~/git_examples/git_stash$ git branch
* fix-7211-issue
  main
  my-feature-branch

 

Summary

In this git tutorial we covered git stash command where you can freeze your current work without having to commit an unfinished change. This helps you develop good programmer habits, such as the one that tells you to not commit an unfinished change.

We covered following topics in depth with multiple examples:

  • What is git stashing and how it works
  • When should you use git stash command
  • Different ways to store your changes with stashing
  • Different ways to restore your changes to undo stashing
  • Delete stash changes
  • Create branch from any specific stashed change

 

Further Readings

What is the intended use-case for git stash?

Didn't find what you were looking for? Perform a quick search across GoLinuxCloud

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can either use the comments section or contact me form.

Thank You for your support!!

Leave a Comment