Git & GitHub · lesson 11 of 12
Cherry-picking a Hotfix and Tagging a Release
Port a single commit from an older branch onto main with git cherry-pick, then mark the result with a real annotated tag and inspect it.
By SkillByExample · Published
cherry-pick moves one commit’s changes to a different branch without touching
anything else around it — the tool for “just this one fix,” not a whole merge.
1) A fix committed on the wrong branch
$ git checkout -b hotfix/divide-by-zero c3f2b20
$ # ...add a zero-denominator guard to divide()...
$ git commit -am "Guard divide() against zero denominator"
$ git log --oneline -1
89b2ffe Guard divide() against zero denominator
The hotfix branch started from an older commit (c3f2b20), not main’s current tip —
a realistic shape for a fix made against a release branch that needs to also land on
main.
2) Cherry-pick it onto main
$ git checkout main
$ git cherry-pick 89b2ffe
$ git log --oneline -2
Auto-merging mathlib.mjs
[main 1ae306e] Guard divide() against zero denominator
1ae306e Guard divide() against zero denominator
39e53d1 Add .gitignore
Same message, same diff intent, but a new commit (1ae306e) with main’s current tip
as its parent — main’s own history since the hotfix branched off (everything through
39e53d1) is completely untouched, unlike a merge which would pull in the hotfix
branch’s own ancestry too.
3) Mark this point with an annotated tag
$ git tag -a v1.0.0 -m "First stable release"
$ git tag -n
v1.0.0 First stable release
$ git show v1.0.0 --stat -s
tag v1.0.0
Tagger: Dev <dev@example.com>
Date: Mon Sep 14 15:47:17 2026 +0530
First stable release
commit 1ae306ee9b692ff12cd8b2b63bd6c306af00d35c
Author: Dev <dev@example.com>
Guard divide() against zero denominator
-a makes this a real object with its own tagger and message — git show prints that
metadata first, then falls through to the commit it points at. A tag doesn’t move the
way a branch does; v1.0.0 will always mean exactly this commit.
Practice
-
Create a lightweight tag (
git tag v1.0.0-lw, no-a/-m) alongside the annotated one, then comparegit show v1.0.0-lwagainstgit show v1.0.0— the lightweight one skips straight to the commit, no tagger block. -
Cherry-pick a commit that conflicts: edit the same line on both branches before picking, then resolve the conflict the same way you would a merge conflict (
git add, thengit cherry-pick --continue).Solution
$ git cherry-pick <hash> # CONFLICT (content): Merge conflict in ... $ # ...edit the file, remove markers... $ git add <file> $ git cherry-pick --continueNote the continuation command is
cherry-pick --continue, notcommit— same pattern asrebase --continue, different from a plain merge’scommit. -
Cherry-pick a range of commits at once with
git cherry-pick <hash1>^..<hash2>and confirm each one lands as its own separate new commit, in order. -
Push the tag with
git push origin v1.0.0and confirm a plaingit push(run first, without the tag) does not send it — tags need an explicit push. -
Delete a tag locally with
git tag -d v1.0.0-lwand confirmgit tag -nno longer lists it, while the commit it pointed to is completely unaffected.