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.
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.
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.
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.
For example, here I have made some changes on my branch:
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:
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:
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
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:
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)
stash@{0}
and stash@{1}
, stash@{0}
will be deleted first.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:
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