Git & Command Line · Lesson 10 of 12

Stash, Tags and Releases

Use git stash to park unfinished work, create annotated tags for versions, follow semantic versioning and publish GitHub Releases from your tags.

  • Intermediate
  • 14 min read
  • 4 objectives

Before this lessonLesson 9: Undoing Things and Team Workflows

What you will learn

  • Stash tracked and untracked changes and restore them
  • Manage several stashes
  • Create, push and delete annotated tags
  • Version releases with SemVer and GitHub Releases

Your Progress

0 of 12 lessons 0%

  • Lessons0 / 12
  • Completed0
  • Est. time left~ 3 hours

Create a free account to keep your progress on every device.

Tip: pressing Next marks this lesson complete automatically.

Two features that look unrelated but share a theme: bookmarking. Stash bookmarks unfinished work so you can switch context. Tags bookmark a finished commit forever, usually a release. The undo lesson showed the basic stash commands; here we go further and connect tags to real releases on GitHub.

Stash in more detail

A stash is a hidden commit holding your uncommitted changes. By default it only saves tracked files. Add -u to include untracked (new) files too, which is almost always what you want:

echo wip >> app.txt; touch new.txt
git stash push -u -m "wip: login form"
git status --short
git stash list
Output
Saved working directory and index state On main: wip: login form
stash@{0}: On main: wip: login form

git status --short printed nothing: the working tree is clean and you can switch branches, pull, or fix an urgent bug. To look inside a stash before restoring it:

git stash show -p stash@{0}
Output
diff --git a/app.txt b/app.txt
index 626799f..70c5f1c 100644
--- a/app.txt
+++ b/app.txt
@@ -1 +1,2 @@
 v1
+wip

pop, apply and drop

  • git stash pop: re-apply the newest stash and delete it from the list.
  • git stash apply stash@{1}: re-apply a specific stash but keep it in the list (useful to apply to several branches).
  • git stash drop stash@{1}: delete one stash; git stash clear deletes all of them.
  • git stash branch fix-login stash@{0}: create a new branch from where the stash was made and apply it there, which avoids conflicts.
  • git stash push -m "msg" -- src/login.js: stash only specific paths.
git stash pop
Output
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:   app.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	new.txt

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (f9e2c61e6f5ee088835c6768aa9da02e4dbcff4d)

Tags: permanent names for commits

A branch pointer moves with every commit; a tag stays put. That makes tags perfect for marking "this exact commit is version 1.0.0". There are two kinds:

  • Lightweight: just a name, git tag v1.1.0-beta. Fine for private bookmarks.
  • Annotated: a full object with tagger, date and message, git tag -a v1.0.0 -m "...". Use these for releases; many tools (including git describe) prefer them.
git tag -a v1.0.0 -m "Version 1.0.0" HEAD~1   # tag an older commit
git tag v1.1.0-beta                            # lightweight, on HEAD
git tag -l
git show v1.0.0 --stat | head -8
Output
v1.0.0
v1.1.0-beta
tag v1.0.0
Tagger: Ada <ada@stackcone.com>
Date:   Wed Sep 23 10:09:04 2026 +0530

Version 1.0.0

commit e4f486beed43f592dcbd14500341a228d673fa40
Author: Ada <ada@stackcone.com>

git describe --tags names the current commit relative to the nearest tag (for example v1.0.0-3-g9f8e7d6, meaning three commits after v1.0.0), which is handy for build version strings.

Sharing and deleting tags

git push does not send tags by default. Push them explicitly:

git push origin v1.0.0          # one tag
git push --follow-tags          # annotated tags reachable from pushed commits

git tag -d v1.1.0-beta          # delete locally
git push origin --delete v1.1.0-beta   # delete on the remote

git fetch --tags                # get tags others pushed
git switch --detach v1.0.0      # look at the code as it was at v1.0.0

Semantic versioning

Most projects name tags with SemVer: vMAJOR.MINOR.PATCH.

  • PATCH (1.0.0 to 1.0.1): bug fixes only.
  • MINOR (1.0.1 to 1.1.0): new features, still backward compatible.
  • MAJOR (1.1.0 to 2.0.0): breaking changes that need users to update their code.
  • Pre-releases add a suffix: 2.0.0-beta.1, 2.0.0-rc.1.

GitHub Releases

A GitHub Release is a tag plus release notes and optional downloadable files. You can create one from the web UI (Releases, Draft a new release) or with the GitHub CLI, which can also write the notes from merged pull requests:

git tag -a v1.2.0 -m "Version 1.2.0"
git push origin v1.2.0
gh release create v1.2.0 --generate-notes
gh release create v2.0.0-rc.1 --prerelease --title "2.0 RC 1" dist/app.zip

In the CI lesson you will see how pushing a tag like v1.2.0 can automatically build and publish a release.

Recap

  • git stash push -u -m parks all changes, including new files.
  • pop restores and deletes; apply restores and keeps; stash branch avoids conflicts.
  • Use annotated tags for releases and push them explicitly.
  • Name versions with SemVer and never move a published tag.
  • gh release create --generate-notes turns a tag into a GitHub Release.
# Write your solution here

Finished reading? Mark this lesson complete to track your progress.

Up next · Lesson 11Cherry-pick, Bisect and ReflogCopy commits between branches with git cherry-pick, find the commit that introduced a bug with git bisect run, and recover lost work with git reflog.