Stashing, Tagging and Releases
By the end of this lesson
Set work aside temporarily and mark released versions.
Two tools that look unrelated and solve adjacent problems. Stashing sets unfinished work aside so you can do something else. Tagging puts a permanent, human-readable name on a commit so you can find it again years later.
One is for the next twenty minutes. The other is for the next five years.
# Something urgent has come up and your work is half-finished
git stash push -m "Half-done CSV export, missing header row"
# Your working directory is now clean
git status
# Deal with the urgent thing, then come back
git stash list
# Restore the most recent stash and remove it from the list
git stash pop
# Or restore it and keep it stashed as well
git stash apply stash@{0}
# Include untracked files, which stash ignores by default
git stash push -u -m "New exporter class, not yet added"- git stash push records your uncommitted changes, then returns your working directory to the last commit. The -m message is optional and worth writing — an unlabelled stash is unidentifiable within a day.
- git stash list shows the stack, newest first. The stash@{0} notation is how you refer to an entry.
- pop restores and removes; apply restores and keeps. Use apply when you want to try the changes on a different branch without losing the stash if it goes badly.
- Without -u, new files that Git does not track yet are left behind in your working directory. This catches people out: they stash, switch branch, and find an unexpected file sitting there.
Tags name a commit permanently
A tag is a name for one specific commit that does not move. A branch pointer advances as you work; a tag stays where it was put. That fixedness is the entire feature — six months after release 2.4.0, the tag still points at exactly what was shipped.
There are two kinds. A lightweight tag is a bare name pointing at a commit. An annotated tag is a stored object with its own author, date and message, and it can be signed. For anything that represents a release, use an annotated tag: you will want to know who created it and what it contained.
# Annotated tag on the current commit
git tag -a v2.4.0 -m "Release 2.4.0 - expense export and charity VAT rate"
# Tag a commit you forgot to tag at the time
git tag -a v2.3.1 9f3c2a1 -m "Release 2.3.1 - invoice rounding fix"
# Tags are not pushed by default
git push origin v2.4.0
# What has been released?
git tag --list "v2.*"
# What exactly was in that release?
git show v2.4.0
# What changed since the last release?
git log --oneline v2.3.1..v2.4.0- -a makes it annotated. The message is a release note in miniature and is worth more than the version number alone.
- You can tag any commit by naming its identifier, which matters when you realise after the fact that a release was never tagged.
- git push sends branches, not tags. A tag that exists only on your machine helps nobody, and this is the step most often forgotten.
- The last command is how release notes get written: every commit between two tags, in order.
Branches and tags are both names for commits, and they behave in opposite ways:
| Branch | Tag | |
|---|---|---|
| Moves when you commit | Yes — it follows your work | No — it stays on the commit it named |
| What it is for | Work in progress, a line of development | A fixed point worth returning to, usually a release |
| Pushed by default | Yes, once an upstream is set | No — it must be pushed explicitly |
| Expected lifetime | Deleted once the work is merged | Kept indefinitely |
| Changing it after sharing | Normal — that is what branches do | Avoid. Others rely on it meaning one thing |
Summary
- git stash sets uncommitted work aside and gives you a clean working directory
- Stash ignores untracked files unless you pass -u, which is the usual source of confusion
- Stash is for hours, not weeks — anything longer belongs on a branch as a commit
- A tag is a fixed name for one commit; use annotated tags for releases so the author and message are recorded
- Tags are not pushed with your branches and must be pushed explicitly
Practice
Attempt each one before opening the solution. Getting it wrong first is how the idea sticks.
Try it yourself
Stash, switch, return
Make an uncommitted change and also create a brand new file. Stash with a message, then run git status.
Notice what is still there. Then restore the stash and confirm you have your change back.
Show solution
Your edit to the tracked file is gone from the working directory and safely in the stash. The new file is still sitting there, because stash ignores untracked files unless you pass -u.
That asymmetry is the one piece of stash behaviour worth remembering. Most surprises with stash come from it.
git stash push -m "Partial rate-limit work"
git status # the new file is still listed as untracked
git stash pop
# To include it next time
git stash push -u -m "Partial rate-limit work, with new files"Think about it
Think about it
A customer reports a bug in version 2.3.0, which was released four months ago. Main has moved on by two hundred commits.
What does the tag let you do that a commit identifier written in a document would not?
Show solution
The tag is in the repository, so anyone can reach that exact state without finding the right document: git switch -c investigate-2.3.0 v2.3.0 gives them the released code.
It also makes comparison straightforward. git log v2.3.0..v2.4.0 lists what changed between releases, which is how you narrow down which change introduced a fault.
A commit identifier in a wiki page does the same thing in principle. It relies on the page still existing and being correct, which is a weaker guarantee than the repository itself.
Saved in this browser only.