Why this matters
You are halfway through a change. Three files are edited, and one paragraph is only half written. Then someone asks you to fix a typo on another branch, right now. You try to switch branches, and Git refuses because your edits would be overwritten by the switch.
You have three bad options. You can commit the half-written work, which puts unfinished code into the project history. You can copy the files to a folder by hand, and then forget one of them. Or you can throw the edits away, which loses the work.
git stash gives you a fourth option. It takes your unfinished changes off the project, puts them on a shelf, and leaves the project exactly as it was at your last commit. Later you take the changes off the shelf and carry on.
This lesson was checked with Git 2.53.0. Branches are covered in the lesson "Branches and merging", so this lesson only uses them where stashing needs them.
The idea: a labelled shelf
Think of a cook who is making a sauce when the doorbell rings. The cook puts the pan on a shelf with a sticky note that says "tomato sauce, half done", wipes the counter, and answers the door. When the cook is back, the pan goes back on the stove and the sauce carries on from where it stopped.
A stash works the same way. Each shelf item is called a stash entry. Each entry has a label, which is the branch you were on and the message you gave it.
Some words you need first:
- A commit is a saved snapshot of the project, with a message that says what changed.
- The working tree is the set of files you see and edit in your editor.
- A tracked file is a file Git already knows about, because it was in an earlier commit or you added it with
git add. - An untracked file is a new file that Git has never been told about.
- The staging area (also called the index) is the list of changes you have chosen for the next commit, made with
git add.
By default, a stash saves changes to tracked files and anything in the staging area. It does not save untracked files. The section on untracked files below shows how to stash them when you want to.
Set up a project to practise on
Create a small project for a cafe website:
mkdir cafe-site
cd cafe-site
git init -b main
git config user.name "Your Name"
git config user.email "you@example.com"
printf '<h1>Corner Cafe</h1>\n<p>Open every day.</p>\n' > index.html
git add index.html
git commit -m "Add home page"git init -b main creates a new repository whose main line of work is called main. The two git config lines set the name and email that appear on your commits. Use your own. The printf command writes two lines into index.html. You can also create the file in your editor.
Check that the project is clean:
git statusOn branch main
nothing to commit, working tree clean"Working tree clean" means every file matches the last commit. Nothing is on the shelf yet.
Your first stash
Now make an edit you are not ready to commit. Add a footer line to index.html, using your editor or this command:
printf '<h1>Corner Cafe</h1>\n<p>Open every day.</p>\n<footer>Call us: 555-0100</footer>\n' > index.htmlCheck what Git sees:
git statusOn branch main
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: index.html
no changes added to commit (use "git add" and/or "git commit -a")"Changes not staged for commit" means the file changed but you have not chosen it for a commit yet. Now put the change on the shelf and give it a label:
git stash push -m "half-done footer"Saved working directory and index state On main: half-done footerRun git status again and the working tree is clean, the same as the start of this lesson. The footer is no longer in index.html. Check that:
cat index.html<h1>Corner Cafe</h1>
<p>Open every day.</p>The shelf now holds one item. List it:
git stash liststash@{0}: On main: half-done footerstash@{0} is the name of the entry. Git numbers entries from newest to oldest, so stash@{0} is always the most recent one. The text after the colon shows the branch and your label.
To bring the change back, use pop:
git stash popOn branch main
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: index.html
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (877eaf351753a53a815d29a2ce33d2d18e3f8075)pop does two things. It applies the stash to the working tree, and it deletes the entry from the list, as the line Dropped refs/stash@{0} shows. The hash in brackets is the stash's internal ID. Your hash will be different.
Apply keeps the entry
git stash apply does the same work as pop but leaves the entry on the shelf. Use it when you want to try the stash in more than one place, or when you are not sure the stash applies cleanly and want a copy to fall back on.
| Command | Changes come back | Entry stays in the list |
|---|---|---|
git stash pop | Yes | No, it is removed on success |
git stash apply | Yes | Yes |
If you run git stash apply and then decide the entry is no longer needed, delete it with git stash drop.
Two entries and picking one
Each push adds a new entry at the top of the list, so the list can hold several items. Here is a clean run that makes two entries. First, the footer again:
printf '<h1>Corner Cafe</h1>\n<p>Open every day.</p>\n<footer>Call us: 555-0100</footer>\n' > index.html
git stash push -m "footer"Saved working directory and index state On main: footerThen a second change, the opening hours:
printf '<h1>Corner Cafe</h1>\n<p>Open 7am to 9pm.</p>\n' > index.html
git stash push -m "hours"
git stash listSaved working directory and index state On main: hours
stash@{0}: On main: hours
stash@{1}: On main: footerA plain git stash pop takes stash@{0}, the newest entry. To take the older one, name it:
git restore index.html
git stash pop stash@{1}On branch main
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: index.html
no changes added to commit (use "git add" and/or "git commit -a")
Dropped stash@{1} (163279a0c9974792c80a6f84612674a76b453d94)git restore index.html throws away the current edit to the file, so the pop of the older entry starts from a clean file. Only the footer came back, and the hours entry is still on the shelf:
git stash liststash@{0}: On main: hoursNote that git restore is destructive. It removes changes that are in the working tree. Stash first if you are not sure you want to lose them.
To delete an entry you no longer need:
git stash dropDropped refs/stash@{0} (d963da64af1173808f10647f9e94a35b505ed73e)Running git stash pop now shows what happens when the list is empty:
No stash entries found.Switching branches with unfinished work
This is the situation that started the lesson. Git has a rule for switching branches: it refuses to switch if the switch would overwrite a change you have not committed or stashed. Git does not refuse if the file you changed is the same on both branches. In that case the change comes along.
Create a second branch with a new menu page. Use git switch -c, which creates a branch and moves onto it:
git switch -c add-menu
printf '<h1>Menu</h1>\n<p>Coffee, tea, toast.</p>\n' > menu.html
git add menu.html
git commit -m "Add menu page"
git switch mainBack on main, edit index.html but do not commit it. Then switch to add-menu:
git switch add-menuSwitched to branch 'add-menu'
M index.htmlThe switch worked. The line M index.html tells you the uncommitted change came along, because add-menu has the same index.html as main. You can check which branch you are on and what is changed:
git status --short
git branch M index.html
* add-menu
mainThe M in front of the filename means modified. The star in front of add-menu marks the branch you are on.
When the switch is blocked
Now a case where the file differs. Make a branch that changes the opening hours and commit it:
git switch main
git switch -c update-hours
printf '<h1>Corner Cafe</h1>\n<p>Open 7am to 9pm.</p>\n<footer>Call us: 555-0100</footer>\n' > index.html
git commit -am "Change opening hours"
git switch mainNow go back to main and edit the same line in a different way, without committing:
printf '<h1>Corner Cafe</h1>\n<p>Open daily, 8am to 8pm.</p>\n<footer>Call us: 555-0100</footer>\n' > index.html
git switch update-hourserror: Your local changes to the following files would be overwritten by checkout:
index.html
Please commit your changes or stash them before you switch branches.
AbortingGit stopped before it changed anything. Check the status to confirm:
git status --short
git branch --show-current M index.html
mainYour edit is still there, and you are still on main. The error message gives you two choices: commit the change, or stash it. For unfinished work, stash it:
git stash push -m "new hours wording"
git switch update-hoursSaved working directory and index state On main: new hours wording
Switched to branch 'update-hours'You can look at what a stash contains before you bring it back:
git stash show -p stash@{0}diff --git a/index.html b/index.html
index b4188ab..158086c 100644
--- a/index.html
+++ b/index.html
@@ -1,2 +1,3 @@
<h1>Corner Cafe</h1>
-<p>Open every day.</p>
+<p>Open daily, 8am to 8pm.</p>
+<footer>Call us: 555-0100</footer>The -p flag means "show the patch", the list of lines removed (starting with -) and added (starting with +). Lines that start with a space did not change and are shown for context.
What a conflict on pop looks like
Here the stash was made on main, but you are now on update-hours, which already changed the same line. Putting the stash back on top of that line is a conflict. A conflict happens when Git cannot decide which version of a line to keep, so it asks you to choose.
git stash popAuto-merging index.html
CONFLICT (content): Merge conflict in index.html
On branch update-hours
Unmerged paths:
(use "git restore --staged <file>..." to unstage)
(use "git add <file>..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
The stash entry is kept in case you need it again.Open index.html. Git has marked both versions inside the file:
<h1>Corner Cafe</h1>
<<<<<<< Updated upstream
<p>Open 7am to 9pm.</p>
=======
<p>Open daily, 8am to 8pm.</p>
>>>>>>> Stashed changes
<footer>Call us: 555-0100</footer>The part between <<<<<<< and ======= is the version on the branch you are on. The part between ======= and >>>>>>> is the version from your stash. To finish, edit the file so it has the text you want, with the marker lines removed, then run git add index.html. Because the pop did not finish, the stash entry is still on the list. Remove it with git stash drop after you are sure the result is right.
If you would rather back out, the undo is two commands. git restore --staged removes the file from the staging area, and git restore throws away the conflicted version:
git restore --staged index.html
git restore index.html
git status --shortGit prints nothing for git status --short when the tree is clean. The stash entry is still there, so nothing is lost.
Stashing new files with -u
A stash does not include untracked files by default. Make a new file and try to stash it:
printf 'Latte 4.50\n' > prices.txt
git stash push -m "prices draft"No local changes to saveGit saw nothing to save and did not create an entry. The file is still in your folder, and git status --short prints ?? prices.txt to show it is untracked. The ?? marks an untracked file.
To include untracked files, add -u, which means --include-untracked:
git stash push -u -m "prices draft"
git status --short
git stash listSaved working directory and index state On update-hours: prices draft
stash@{0}: On update-hours: prices draft
stash@{1}: On main: new hours wordingNow git status --short prints nothing, and the file is in the stash. Bring it back with git stash pop:
git stash popAlready up to date.
On branch update-hours
Untracked files:
(use "git add <file>..." to include in what will be committed)
prices.txt
nothing added to commit but untracked files present (use "git add" to track)
Dropped refs/stash@{0} (b9c199dc3546e205cdbd5802296a72e05efa76c0)The line Already up to date. comes from an internal step in the pop. It does not mean the file was missed. The file is back, and the entry is dropped.
Stashing one file
Sometimes you want to put only some of your changes away. Add a footer to index.html and a line to prices.txt, then stash only index.html. The path after -- names the file:
printf '<footer>Call us: 555-0100</footer>\n' >> index.html
printf 'Tea 2.00\n' >> prices.txt
git stash push -m "index only" -- index.html
git status --short
git stash listSaved working directory and index state On update-hours: index only
?? prices.txt
stash@{0}: On update-hours: index only
stash@{1}: On main: new hours wordingThe prices.txt file stays in your working folder, and index.html is on the shelf. This is useful when your work is two unrelated changes and you want to switch to something else without touching the second one.
Common mistakes
Expecting a stash to save everything. A stash does not save new files unless you use -u. Run git status --short before you stash and check for lines that start with ??.
Expecting pop to take a specific entry. pop takes the newest entry, stash@{0}. If you want an older one, name it, as in git stash pop stash@{1}. Look at git stash list first.
Treating a stash as a backup. A stash lives only in your local copy of the repository. git push does not send stashes to the remote, so if your computer is lost, the stash is lost too. Use a branch and a commit for work you need to keep.
Leaving stashes behind. A list of old entries with the same label is hard to read. Give every stash a message with -m, and drop entries you no longer need.
Stashing and then forgetting the entry. The change is safe on the shelf, but nothing reminds you it is there. Check git stash list before you finish a session.
Running pop on an empty list. The message No stash entries found. means there is nothing to bring back. It is not an error in your files. Check the list to see what you have.
Switching with an untracked file in the way. If the branch you switch to has a file with the same name as an untracked file in your folder, Git stops:
error: The following untracked working tree files would be overwritten by checkout:
menu.html
Please move or remove them before you switch branches.
AbortingMove the file to another folder, or stash it with -u, and then switch.
Practice
Exercise 1. You changed index.html and want to check out main to read something. Write the commands to put the change on the shelf with the label "menu wording", switch to main, and bring the change back later.
Show the answer
Run git stash push -m "menu wording", then git switch main. When you are ready to go back, switch to your branch with git switch <branch name> and run git stash pop. Check git stash list first if you have more than one entry, so you pop the right one.
Exercise 2. You created a new file called specials.txt and modified index.html. You want to stash both. Which command stashes both, and what does git status --short show afterwards?
Show the answer
Run git stash push -u -m "specials and footer". The -u flag includes the untracked file specials.txt. Afterwards, git status --short prints nothing, because both changes are on the shelf.
Exercise 3. The list shows three entries, and you want the second oldest. What is the name of that entry, and what command brings it back and removes it from the list?
Show the answer
The list is numbered from the newest, so the entries are stash@{0}, stash@{1} and stash@{2}. The second oldest is stash@{1}. Run git stash pop stash@{1}. Pop removes the entry after it applies it without a conflict. If you use git stash apply stash@{1} instead, the entry stays on the list.
Exercise 4. You ran git stash pop and the output says CONFLICT (content): Merge conflict in index.html. Describe what is on your disk now, and what you do to finish.
Show the answer
index.html has conflict markers between <<<<<<<, ======= and >>>>>>>, and the stash entry is still on the list. Edit the file so it reads the way you want, remove the marker lines, run git add index.html, and then run git stash drop to delete the entry. If you want to start over, run git restore --staged index.html and then git restore index.html.
Exercise 5. You have uncommitted changes to index.html, and git switch feature fails with Your local changes to the following files would be overwritten by checkout. Write the full sequence to switch, do a short piece of work on feature, and return to your change.
Show the answer
Run git stash push -m "work in progress", then git switch feature. Do the short piece of work and commit it, then run git switch main (or whichever branch you came from). Run git stash pop to bring the change back. If the file changed on both branches, the pop can stop with a conflict, which you resolve as in exercise 4.
Summary
git stash push -m "label"moves your uncommitted changes onto a shelf and leaves a clean working tree.git stash listshows the entries, newest first, asstash@{0},stash@{1}and so on.git stash popbrings back the newest entry (or the one you name) and removes it on success.git stash applybrings it back and keeps the entry.- A stash does not include new untracked files unless you add
-u, and-- <file>limits a stash to one file. - Git refuses a branch switch that would overwrite your changes, so stash first and pop after you return.
- A stash is local only.
git pushdoes not send it, so commit anything you need to keep.