Tags, Releases and Cherry-pick
7 pages · ~5 min✓ Reviewed
Builds on Stash & Worktrees. Next up: History Archaeology.
Tags: Lightweight vs Annotated
1. What a Tag Is
- A tag is a fixed, human-readable name for one commit
- A branch moves as you commit; a tag never moves
- Typical use: label a release, like
v1.0
2. Two Kinds
- Lightweight:
git tag v1.0is a ref straight at a commit, no metadata - Annotated:
git tag -a v1.0 -m "Release 1.0"creates a tag object - The object stores tagger, date, message and an optional GPG signature
| Lightweight | Annotated | |
|---|---|---|
| Stores | ref only | tag object |
| Tagger, date | none | yes |
git show | commit only | tag info + commit |
git describe | ignored by default | used |
| Use for | private temporary marks | releases |
3. Common Mistake
Releases need -a
A lightweight tag is a bookmark: no author, no date, and git describe ignores it by default. Tag every release with annotated tags.
Pushing and Managing Tags
1. Pushing Tags
git pushdoes not send tags- One tag:
git push origin v1.0 - All tags:
git push origin --tags - Safer:
git push --follow-tagssends only annotated tags reachable from pushed commits
2. Create, List, Inspect
bash
git tag -a v0.9 <sha> -m "msg" git tag -l 'v1.*' git show v1.0
tag an older commit by giving its sha; -l takes a glob pattern
3. Delete a Tag
| Where | Command |
|---|---|
| Local | git tag -d v1.0 |
| Remote | git push origin --delete v1.0 |
| Remote (refspec) | git push origin :refs/tags/v1.0 |
4. Mistakes
Semantic Versioning and git describe
1. SemVer Format
| Part | Bump for | Example |
|---|---|---|
| MAJOR | breaking change | 1.5.0 → 2.0.0 |
| MINOR | compatible feature | 1.4.2 → 1.5.0 |
| PATCH | compatible fix | 1.5.0 → 1.5.1 |
- A bump resets lower numbers to 0
- Pre-release
1.0.0-rc.1sorts before1.0.0
2. git describe
v1.2.0-5-g3f9a1c2
- 1v1.2.0nearest annotated tag
- 2-5commits after the tag
- 3-g3f9a1c2g + abbreviated hash
Cherry-Pick and Its Conflicts
1. What It Copies
git cherry-pick <sha>copies one commit onto the current branch- The result is a new commit with a new hash
-napplies the changes but does not commit
| Command | Picks |
|---|---|
cherry-pick <sha> | one commit |
cherry-pick A B C | several named commits |
cherry-pick A^..C | inclusive range A to C |
2. Conflicts
- Replays the commit's diff as a three-way merge
- Conflicts when the target changed the same lines
--abortabandons the pick;--skipdrops one commit
Backports with cherry-pick -x
1. Backport with -x
- Backport = apply a fix from
mainto a maintained release branch -xappends (cherry picked from commit <sha>) to the new message- It records the origin, so the pick is easy to trace and audit
- Not added by default; use it only for picks between public branches
bash
git switch release-1.2 git cherry-pick -x 3f9a1c2 git log -1 --format=%B
the new commit gets a new hash
output
Fix null check in parser (cherry picked from commit 3f9a1c2d8e1b)
2. Cherry-pick vs merge
| cherry-pick | merge | |
|---|---|---|
| Brings | one commit | the whole history |
| Use for | a fix to backport | joining branches |
| Risk |
Cheat Sheet: Tags and Patch Transfer
1. Tags and Describe
| Command | Effect |
|---|---|
git tag v1 | lightweight tag |
git tag -a v1 -m msg | annotated tag |
git push origin v1 | push one tag |
git push --follow-tags | push commits + their annotated tags |
git tag -d v1 | delete locally |
git describe --tags --always --dirty- Output tag-N-gHASH: N commits after the tag, e.g.
v1.2.0-5-g3f9a1c2
2. Cherry-pick
- cherry-pick -x shacopy commit
- Conflictfix files, git add
- --continuefinish pick
- --abort / --skipgive up / drop one
Check yourself
You run git tag -a v1.0 -m "r" then git push origin main. Does the remote have v1.0?
- No:
git pushof a branch never sends tags. - Run
git push origin v1.0, or use--follow-tags.
HEAD is 3 commits past a lightweight tag v2.0, and no annotated tags exist. What does git describe do?
- It fails: by default it only uses annotated tags.
git describe --tagsprintsv2.0-3-gplus the short hash.
git cherry-pick <merge-sha> errors out. What is the fix and why?
- A merge has two parents, so Git can't tell which diff to replay.
- Use
git cherry-pick -m 1 <merge-sha>to pick the first parent as mainline.