Tagging and Releases
Why Tags Matter for QA
Tags mark specific points in Git history -- typically releases. For QA engineers, tags create an audit trail: which commit was released, what was tested, and what the test results were. When a customer reports a bug on "version 2.4.0," you can check out that exact tag and reproduce the issue.
Creating Tags
Lightweight Tags
A lightweight tag is just a pointer to a commit. It has no additional metadata.
# Create a lightweight tag
git tag v2.4.0
# Tag a specific commit (not HEAD)
git tag v2.4.0 abc123f
Annotated Tags (Recommended)
Annotated tags store the tagger's name, date, and a message. For releases, always use annotated tags.
# Create an annotated tag
git tag -a v2.4.0 -m "Release 2.4.0 - passed full regression suite"
# Create an annotated tag with detailed test information
git tag -a v2.4.0 -m "Release 2.4.0
Regression suite: 342/342 passed
Browser coverage: Chrome 120, Firefox 121, Safari 17
CI run: https://github.com/org/repo/actions/runs/12345
Known issues: SHOP-567 (low priority, cosmetic)"
Working with Tags
# List all tags
git tag
# List tags matching a pattern
git tag -l "v2.4.*"
# Show tag details (annotated tags include the message)
git show v2.4.0
# Push a specific tag to remote
git push origin v2.4.0
# Push all tags
git push origin --tags
# Delete a local tag
git tag -d v2.4.0-rc1
# Delete a remote tag
git push origin --delete v2.4.0-rc1
# Checkout a specific tag (detached HEAD state)
git checkout v2.4.0
# Create a branch from a tag (for hotfixes)
git checkout -b hotfix/2.4.1 v2.4.0
Semantic Versioning
Most projects use semantic versioning (SemVer): MAJOR.MINOR.PATCH
| Component | When to Increment | Example |
|---|---|---|
| MAJOR | Breaking changes (API incompatible) | v2.0.0 to v3.0.0 |
| MINOR | New features (backward compatible) | v2.3.0 to v2.4.0 |
| PATCH | Bug fixes (backward compatible) | v2.4.0 to v2.4.1 |
Pre-release tags indicate versions that are not yet stable:
git tag -a v2.4.0-rc1 -m "Release candidate 1 for 2.4.0"
git tag -a v2.4.0-beta.1 -m "Beta 1 for 2.4.0"
git tag -a v2.4.0-alpha.3 -m "Alpha 3 for 2.4.0"
QA and Versioning
| Version Stage | QA Activity |
|---|---|
| Alpha | Exploratory testing, feature-level tests |
| Beta | Full regression, performance testing, cross-browser |
| Release Candidate | Final regression, sign-off checklist, no new features |
| Release | Smoke tests on production, monitoring |
Associating Test Results with Releases
The gold standard is a release process where test results are permanently linked to the release tag.
In the Tag Message
git tag -a v2.4.0 -m "Release 2.4.0
Test Results:
Unit tests: 1,247/1,247 passed
Integration tests: 89/89 passed
Browser tests: 342/342 passed (Chrome 120, Firefox 121, Safari 17)
Visual regression: 0 diffs detected
Performance: LCP 1.2s (budget: 2.5s), FID 45ms (budget: 100ms)
CI Run: https://github.com/org/repo/actions/runs/12345
Test Report: https://qa-reports.example.com/releases/2.4.0
Known Issues:
SHOP-567: Tooltip misalignment on mobile (low priority)
SHOP-589: Slow search on large datasets (medium, fix planned for 2.5.0)"
In GitHub Releases
GitHub Releases provide a richer interface than raw tags. You can add formatted release notes, attach binary artifacts, and mark pre-releases.
# Create a GitHub release from a tag
gh release create v2.4.0 \
--title "Release 2.4.0" \
--notes "## What's New
- Redesigned checkout flow
- Added PayPal support
- Fixed 12 bugs from 2.3.x
## Test Results
- Full regression: 342/342 passed
- [CI Run](https://github.com/org/repo/actions/runs/12345)
- [Test Report](https://qa-reports.example.com/releases/2.4.0)
## Known Issues
- SHOP-567: Tooltip misalignment on mobile"
In CI/CD Pipeline (Automated)
The best approach: automate release tagging as part of the pipeline.
# Tag and release after all tests pass
create-release:
needs: [unit-tests, integration-tests, browser-tests]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Determine version
id: version
run: echo "VERSION=$(cat package.json | jq -r .version)" >> $GITHUB_OUTPUT
- name: Create tag
run: |
git tag -a "v${{ steps.version.outputs.VERSION }}" \
-m "Release ${{ steps.version.outputs.VERSION }} - all tests passed"
git push origin "v${{ steps.version.outputs.VERSION }}"
Release Workflow for QA
A typical QA-involved release workflow:
- Feature freeze: No new features on the release branch. Only bug fixes.
- Run full regression: Execute the complete test suite (automated + manual exploratory).
- Document results: Update the release checklist with test results.
- Create release candidate tag:
v2.4.0-rc1 - Deploy RC to staging: Smoke test in a production-like environment.
- Sign-off: QA approves the release based on defined criteria.
- Tag the release:
v2.4.0with test result metadata. - Deploy to production: Run post-deploy smoke tests.
- Monitor: Watch error rates and performance metrics for 24-48 hours.
Comparing Releases
Git makes it easy to see what changed between releases -- essential for writing test plans.
# See all commits between two releases
git log v2.3.0..v2.4.0 --oneline
# See the diff between two releases
git diff v2.3.0..v2.4.0
# See which files changed
git diff v2.3.0..v2.4.0 --stat
# See commits that touched test files
git log v2.3.0..v2.4.0 -- tests/
This helps you answer: "What changed since the last release?" -- the first question in any regression test planning session.
Hands-On Exercise
- Create an annotated tag for a "release" with test result metadata in the message
- Push the tag and create a GitHub Release with formatted release notes
- Practice checking out a tag and creating a hotfix branch from it
- Use
git log v1..v2to compare two releases and identify what needs regression testing - Set up an automated release tag in your CI pipeline that runs after all tests pass
- Write a release sign-off checklist that your team can use for every release