Chapter 11Lesson 02~120 minutes

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.

Tag labgit describeExplicit tag pushOptional signing

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.

2. Create a lightweight marker and an annotated release

BASE=$(git rev-parse HEAD)
git tag build-check
git tag -a v1.0.0 -m "Release 1.0.0: initial stable API"

git show-ref --tags
git cat-file -t build-check
git cat-file -t v1.0.0
git rev-parse build-check
git rev-parse v1.0.0^{tag}
git rev-parse v1.0.0^{commit}

Expected: build-check resolves directly to a commit; v1.0.0 resolves to a tag object; its peeled commit equals BASE.

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.

8. Understand --follow-tags without making it the only release policy

git push --follow-tags pushes the ordinary refs selected by the push plus missing annotated tags that point at commit-ish objects reachable from the refs being pushed. Lightweight tags are not included by that rule.

git config --show-origin --get push.followTags
git push --dry-run --follow-tags origin trunk

Explicit tag pushes are easier to audit in a release job when you want exactly one version ref published.

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
Do not publish a training signature as a production release. Key provisioning, trust distribution, rotation, and CI signing identity are governance topics. If no local key exists, skip the signing commands and continue the no-signing lab.

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

Disposable demonstration tag only. Never silently reuse a real published version name.
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

  1. You want a durable release annotation with message/tagger metadata but no cryptographic setup. Lightweight or annotated?
  2. A branch push succeeded but v2.1.0 is absent remotely. Which explicit ref should the release job push?
  3. git describe HEAD ignores an exact lightweight tag. Which option includes lightweight tags?
  4. A release tag was published on the wrong commit. Should you silently force-move the same public version name?

14. Cleanup

Confirm the parent directory before deletion.

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.

Next

Turn tag mechanics into release policy

Lesson 3 chooses signing backends and key configuration, version naming, changelog policy, support-branch/backport rules, and the boundary between Git conventions and server-enforced release governance.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.