Tags, Signed Releases, Semantic Versioning, Changelogs, and Support Lines: Guided Hands-On Workflow and Core Operations
Create, inspect, describe, publish, verify, and safely retire tags in a local release lab with a bare remote; compare lightweight/annotated tags, explicit tag pushes, optional signing, and published-tag replacement hazards.
Learning objectives
- Create lightweight and annotated tags and inspect their object-level differences.
- Use show, cat-file, rev-parse, and for-each-ref to capture release provenance.
- Explain git describe behavior with annotated versus lightweight tags.
- Publish an exact release tag to a local bare remote and verify it independently.
- Diagnose unsigned tags and remote tag-replacement rejection without resorting to unsafe force updates.
1. Build a release lab with a local bare remote
No GitHub/GitLab account is required. The bare repository represents the shared release remote.
Git Bash, Bash, or zsh
mkdir git-release-lab
cd git-release-lab
git init --bare origin.git
git init -b trunk work
cd work
git config user.name "Release Learner"
git config user.email "release-learner@example.invalid"
git remote add origin ../origin.git
git config --local push.followTags false
printf "# Demo Service\n" > README.md
printf "api_version=1\n" > service.conf
git add README.md service.conf
git commit -m "Create service baseline"
git status --short --branch
git log --oneline --decorate -3
PowerShell setup alternative
New-Item -ItemType Directory git-release-lab | Out-Null
Set-Location git-release-lab
git init --bare origin.git
git init -b trunk work
Set-Location work
git config user.name "Release Learner"
git config user.email "release-learner@example.invalid"
git remote add origin ../origin.git
git config --local push.followTags false
Set-Content README.md '# Demo Service'
Set-Content service.conf 'api_version=1'
git add README.md service.conf
git commit -m "Create service baseline"
git status --short --branch
git log --oneline --decorate -3
After setup, the Git commands are the same in both shells. Where
this lesson uses POSIX environment variables or
echo $?, PowerShell learners can use ordinary variables
and $LASTEXITCODE.
3. git show exposes both release annotation and target
change context
git show --no-patch v1.0.0
git cat-file -p v1.0.0
git show --stat v1.0.0
cat-file -p is the clearest object-level view of the
tag object. git show is friendlier for normal release
inspection because it can show the annotation and then the target
commit.
4. Generate a machine-readable release-ref inventory
git for-each-ref --sort=creatordate --format='%(refname:short)|%(objecttype)|%(objectname)|%(*objecttype)|%(*objectname)|%(creatordate:iso8601)' refs/tags
The ref OID and peeled OID are deliberately separate fields. A
provenance report can store both without parsing decorative
git log output.
5. git describe derives a human-readable name from
reachable tags
printf "health=true\n" >> service.conf
git add service.conf
git commit -m "Add health setting"
git tag build-latest
git describe HEAD
git describe --tags HEAD
git describe --long HEAD
By default, git describe considers annotated tags.
Therefore the first form normally produces something like
v1.0.0-1-g<abbrev>. With --tags,
lightweight tags are eligible and an exact lightweight tag such as
build-latest can win.
The suffix abbreviation is repository-dependent; do not hard-code a fixed number of hexadecimal characters in automation.
6. Prove that a normal branch push does not universally publish every tag
git -c push.followTags=false push -u origin trunk
git ls-remote --heads origin
git ls-remote --tags origin
The branch appears on the remote, while the local tags remain absent. This is why release automation should push the intended tag explicitly rather than assume a branch push will publish it.
7. Push one release tag explicitly
git push --dry-run origin refs/tags/v1.0.0:refs/tags/v1.0.0
git push origin refs/tags/v1.0.0:refs/tags/v1.0.0
git ls-remote --tags origin v1.0.0
For an annotated tag, ls-remote --tags can show the
tag-object ref and a peeled ^{} line identifying the
target. Recording both provides useful release evidence.
9. Verify-tag on an unsigned annotated tag fails—and that is useful evidence
git verify-tag v1.0.0
echo $?
Because v1.0.0 was created with -a, not
-s, verification should report that no valid signature
is present and return non-zero. Do not interpret “annotated” as
“signed.”
10. Optional signing path when a free local signing setup already exists
The core lab does not require signing software or a pre-existing key. If your machine already has a configured signing key, inspect configuration first:
git config --show-origin --get gpg.format
git config --show-origin --get user.signingKey
git tag -s v1.0.0-signed -m "Signed release exercise" v1.0.0^{commit}
git verify-tag v1.0.0-signed
11. Compare safe temporary-tag deletion locally and remotely
git tag -a demo-temp -m "Disposable tag"
git push origin refs/tags/demo-temp:refs/tags/demo-temp
git ls-remote --tags origin demo-temp
git push --delete origin demo-temp
git tag -d demo-temp
git ls-remote --tags origin demo-temp
This is a disposable non-release tag. Deleting a published release version has much higher operational impact because consumers may already rely on it.
12. Demonstrate why published tag replacement is rejected by default
git tag -a published-demo -m "First target" "$BASE"
git push origin refs/tags/published-demo:refs/tags/published-demo
git tag -f -a published-demo -m "Moved locally for demonstration" HEAD
git rev-parse published-demo^{commit}
git ls-remote --tags origin published-demo
git push origin refs/tags/published-demo:refs/tags/published-demo
The ordinary push should reject replacing the existing remote tag. That rejection is a safety signal. The repair is not “add force.” For a real published release, create a new version/tag and communicate the correction.
13. Challenge — choose the release operation from evidence
- You want a durable release annotation with message/tagger metadata but no cryptographic setup. Lightweight or annotated?
-
A branch push succeeded but
v2.1.0is absent remotely. Which explicit ref should the release job push? -
git describe HEADignores an exact lightweight tag. Which option includes lightweight tags? - A release tag was published on the wrong commit. Should you silently force-move the same public version name?
14. Cleanup
Git Bash / Bash / zsh
cd ../..
pwd
rm -rf git-release-lab
PowerShell
Set-Location ../..
Get-Location
Remove-Item -Recurse -Force git-release-lab
15. Knowledge check
Question 1. Why did git push origin trunk not
publish the local tags in this lab?
Question 2. What does
git describe --tags change?
Question 3. Why did
git verify-tag v1.0.0 fail?
Question 4. Why is the ordinary rejection of a moved remote tag useful?
Question 5. What does the peeled ^{} remote tag
line identify?
16. Summary
You created and inspected lightweight and annotated tags, peeled
release targets, produced structured tag inventories, used
describe, proved explicit tag publication, verified the
unsigned/signed distinction, and observed default protection against
replacing a published tag.
Authoritative references
git-tag
git-describe
git-push
git-verify-tag
git-for-each-ref
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.