Checking Out a Tag
Checking out a tag lets you inspect the exact state of the codebase at a historical release point. However, unlike checking out a branch, checking out a tag puts Git into a special state called detached HEAD. Understanding this state — and knowing when to create a branch from the tag instead — is essential for working safely with tagged releases.
Checking out a tag puts you in detached HEAD
Check out a tag
git checkout v1.0.0 # Note: switching to 'v1.0.0'. # # You are in 'detached HEAD' state. You can look around, make experimental # changes and commit them, and you can discard any commits you make in this # state without impacting any branches by switching back to a branch. # # If you want to create a new branch to retain commits you make, you may # do so (now or later) by using -c with the switch command. Example: # # git switch -c <new-branch-name> # # Or undo this operation with: # # git switch - # # HEAD is now at 3f8a2c9 Final tweaks before release
What detached HEAD means
In a normal state, HEAD points to a branch name, and that branch name points to a commit. When HEAD points directly to a commit SHA instead of a branch name, you are in detached HEAD state.
Normal state vs detached HEAD
Normal state (HEAD → branch → commit):
─────────────────────────────────────────────────
.git/HEAD contains: ref: refs/heads/main
HEAD ──▶ main ──▶ commit 9a8b7c6
│
A───B───C ◀── main (HEAD is here via the branch)
Detached HEAD state (HEAD → commit directly):
─────────────────────────────────────────────────
.git/HEAD contains: 3f8a2c9d... (raw SHA)
A───B───C───D ◀── main
│
HEAD ──▶ C (3f8a2c9, tagged v1.0.0)
tag: v1.0.0
New commits made in this state are NOT on any branch:
A───B───C───D ◀── main
│
C (v1.0.0, HEAD) ──▶ E (new commit, orphaned)
(E has no branch pointing to it — will be GC'd)Modern syntax: git switch for tags
Use git switch --detach for clarity
# Modern, explicit way to enter detached HEAD at a tag git switch --detach v1.0.0 # HEAD is now at 3f8a2c9 Final tweaks before release # Check what you are on git status # HEAD detached at v1.0.0 # nothing to commit, working tree clean # Or use the older git checkout syntax (still valid): git checkout v1.0.0
Exploring code in detached HEAD (read-only usage)
The most common reason to check out a tag is to read and explore the codebase at a past release — inspecting files, running the old version, reproducing a bug. For read-only exploration, detached HEAD is perfectly fine.
Safe read-only exploration of a tagged release
# Check out the tag git checkout v1.0.0 # HEAD is now at 3f8a2c9 Final tweaks before release # Explore the files — everything looks like it did at v1.0.0 ls src/ # (files as they existed at v1.0.0) cat package.json | grep '"version"' # "version": "1.0.0" # Run the old version npm install && npm start # When done, return to your branch git switch main # Switched to branch 'main' # Your branch is up to date with 'origin/main'.
Creating a branch from a tag to make changes
If you need to make changes based on a tagged release — for example, applying a hotfix to an older version — you must create a branch from the tag. This gives your new commits a permanent home.
Create a branch from a tag and commit to it
# Option 1: checkout the tag, then create a branch git checkout v1.0.0 git switch -c hotfix-1.0.1 # Switched to a new branch 'hotfix-1.0.1' # Option 2 (direct, no intermediate checkout needed): git switch -c hotfix-1.0.1 v1.0.0 # Switched to a new branch 'hotfix-1.0.1' # Now you are on a real branch — commits are safe git status # On branch hotfix-1.0.1 # nothing to commit, working tree clean # Make your fix git add . git commit -m "Fix critical null-pointer exception in payment handler" # Tag the hotfix release git tag -a v1.0.1 -m "Hotfix release 1.0.1" # Push the branch and tag git push -u origin hotfix-1.0.1 git push origin v1.0.1
Using git switch -c vs git checkout -b
Command | Modern/Legacy | Notes |
|---|---|---|
| Modern (Git 2.23+) | Preferred — clear intent, explicit |
| Classic (all Git versions) | Still widely used, works identically |
| Two steps | Works but less efficient than one-step |
Confirm which tag you are on
Verify your current position in history
# Check HEAD position git status # HEAD detached at v1.0.0 # nothing to commit, working tree clean # Describe the current commit relative to tags git describe --tags HEAD # v1.0.0 (exactly on the tag) git describe --tags HEAD # v1.0.0-3-gabc1234 (3 commits after v1.0.0, not on the tag itself) # Show all tags pointing to the current commit git tag --points-at HEAD # v1.0.0 # Show the full commit info git log -1 HEAD # commit 3f8a2c9d... # (tag: v1.0.0)
Return to a branch after exploring a tag
Exit detached HEAD and return to a branch
# Return to main (or whichever branch you were on) git switch main # Switched to branch 'main' # Or using the classic syntax git checkout main # If you are not sure which branch to return to: git branch # * (HEAD detached at v1.0.0) # feature-auth # hotfix-1.0.1 # main git switch main
Common errors
Error: tag not found
git checkout v9.9.9 # error: pathspec 'v9.9.9' did not match any file(s) known to git # Fix: verify the tag exists git tag | grep v9 # (nothing — the tag does not exist locally) # Maybe it exists on the remote but was not fetched yet: git fetch --tags git checkout v9.9.9
Accidentally made commits in detached HEAD
# You made commits while detached — now you want to save them
git log --oneline -3
# e5f6a7b (HEAD) My important fix
# d4e5f6a Another change
# 3f8a2c9 (tag: v1.0.0) Final tweaks before release
# Save the commits by creating a branch before switching away
git switch -c my-fix-branch
# Switched to a new branch 'my-fix-branch'
# (commits are now safe on this branch)
# If you already switched away (commits are now dangling):
# Find them with reflog
git reflog
# abc1234 HEAD@{1}: commit: My important fix
# 3f8a2c9 HEAD@{3}: checkout: moving from main to v1.0.0
git switch -c my-fix-branch abc1234