Free lesson · Git Mastery
What Are Tags
What Are Tags
A note from Swati Bajpai, Engineering Manager at Google Search Infrastructure.
The Markers That Don't Move
Tags are named references to specific commits. Like branches, they're pointer files in the .git/refs/ directory. Unlike branches, they don't move. A tag created today pointing at a specific commit will still point at that same commit a year from now, regardless of what happens to the branch the commit was on.
This immovability is the whole point of tags. Branches mark "where development is currently happening." Tags mark "where development was at a meaningful point in history." Releases, milestones, deployment checkpoints, security fixes - anything you want to be able to reference unambiguously later - gets a tag.
I manage a 14-person team at Google Search Infrastructure. We ship code on a release cadence that's tied to upstream Google's infrastructure cycles. Every release we ship gets tagged. Every hotfix we cherry-pick onto a release gets tagged. The tags become the canonical reference for "what code was running where, when." When a regression appears, the first question is "between which two tags did the behavior change?" - the tags structure the investigation.
This topic covers what tags are conceptually. Topic 10.2 covers the two kinds of tag. Topics 10.3 through 10.6 cover the operations. Topics 10.7 and 10.8 cover signed tags and release workflows.
What a Tag Actually Is
At the data level, a tag is one of two things:
- A pointer file in
.git/refs/tags/containing a commit hash (a lightweight tag) - A pointer file plus a tag object in the repository's object store containing metadata (an annotated tag)
Topic 10.2 covers the distinction in depth. For now, the relevant point is that tags are named references - like branches, you can use them anywhere a commit reference is expected.
git log v1.0
git diff v1.0..v2.0
git checkout v1.0
git show v1.0
Each of these treats the tag v one dot zero as a reference to whatever commit was tagged. The commands work the same as they would with a commit hash, branch name, or relative reference. Tags integrate into Git's normal reference machinery.
How Tags Differ From Branches
The three differences that matter:
Tags don't move. A branch's pointer moves as you commit. A tag's pointer stays at the commit it was created at.
Tags aren't checked out for normal work. Switching to a tag (git switch --detach v1.0) enters detached HEAD state (Topic 6.9). You can examine the tagged code, but commits made from there are detached.
Tags carry semantic meaning. A tag named v2.4.0 says "this is version 2.4.0 of the product." A branch named release/2.4 says "this branch holds the work for the 2.4 release." Tags are about points in history; branches are about lines of development.
The team at Google Search Infrastructure uses tags exclusively for release identification. We never refer to "the latest commit on the release branch" as the version; we tag and refer to the tag. The discipline gives us stable references that the platform team and customers can rely on.
What Tags Are For
The use cases in roughly the order of frequency:
Release identification. This is the dominant use. Every shipped version of a product gets a tag like v2.4.0. The tag is what binaries are built from, what release notes reference, what bisect commands traverse to find regressions.
Deployment checkpoints. When you deploy code to production, tag the commit you deployed from. Later investigations into "what was running when this incident started" can use the tag to find the exact code.
Significant milestones. A team might tag the commit just before a substantial refactor - pre-refactor-2024-q3 - so they can compare current state against that historical baseline.
Build artifacts. CI/CD pipelines often tag the commit a build came from. The tag becomes the link between source code and shipped artifact.
Audit and compliance. Some regulated environments require traceable releases. Tags are the lightweight mechanism that provides traceability.
In each case, the tag's immovability is what makes it useful. A reference that might change is less useful for "what was this exact code?"
A Specific Example
A typical Search Infrastructure tag sequence:
v2.4.0 # initial 2.4 release
v2.4.1 # hotfix for a CVE
v2.4.2 # hotfix for a performance regression
v2.5.0 # next minor version
v2.5.1 # hotfix
v3.0.0 # major version with breaking changes
The semantic versioning convention - major dot minor dot patch - encodes meaning in the tag name itself. The major bump signals a breaking change. The minor bump signals new features. The patch bump signals bug fixes. Customers reading the tag names can infer compatibility without looking at individual changes. Topic 10.8 covers semantic versioning in depth.
The tag names are decisions. Once a release is tagged, the name is permanent - customers reference it, documentation cites it, dependent systems pin to it. Bad tag names cause downstream confusion. The team's discipline is to think about the tag name before creating it.
When to Tag and When Not
A practical guide:
Tag:
- Every release shipped to customers
- Every hotfix that goes to production
- Major milestones worth being able to reference later
- Specific commits at known-good states (for emergency rollback references)
Don't tag:
- Every commit on main (that's what the commits themselves are for)
- Personal milestones with no team value
- Branches (that's what branches are for)
- Work-in-progress markers (use branches)
The principle: tag what other people, future you, or external systems need to reference unambiguously. Don't tag what's just useful for your own short-term work.
A Pattern at Google
The team's release tagging discipline:
- The release manager creates the release tag at the moment the release branch is cut
- The tag uses the semantic version:
v2.4.0 - The tag is annotated (Topic 10.2 covers annotated vs lightweight) with a message summarizing the release
- The tag is signed (Topic 10.7 covers signing) by the release manager
- The tag is pushed to the remote (Topic 10.6 covers pushing tags)
- CI pipelines pick up the tag and build the release artifact
- The artifact's metadata references the tag
Seven steps that take roughly five minutes for the release manager. The output is a permanent, signed, traceable record of the release. Customer support, security teams, and engineering can all reference the same tag for the same release. The discipline is what makes the platform usable at the scale we operate at.
The discipline matters more than the mechanics. Any team can run git tag v1.0. The discipline of doing it for every release, consistently, with the same convention, is what makes the tags useful as a system.
The Cost of Inconsistent Tagging
A specific lesson from earlier in my career: a team I worked with at LinkedIn had inconsistent tagging. Some releases were tagged; some weren't. Some used semantic versioning; others used dates; others used arbitrary names. When a security investigation needed to identify which deployed versions contained a specific commit, the answer required hours of digging through deployment logs.
After that incident, the team standardized on annotated, signed, semantic version tags for every release. The standardization meant subsequent investigations took minutes instead of hours. The cost was minimal - the discipline of running a few extra commands at release time - and the benefit compounded over years.
This is the kind of process discipline I focus on as a manager. The technical mechanics are simple. The consistency is what creates value.
What Comes Next
Topic 10.2 covers the two kinds of tag - lightweight and annotated - and when to use each. Topic 10.3 covers creating tags. Topic 10.4 covers listing them. Topic 10.5 covers deleting them. Topic 10.6 covers pushing them to remotes. Topic 10.7 covers signed tags. Topic 10.8 covers release workflows.
What Goes Wrong
Warning: Common tag misconceptions:
Treating tags as movable like branches. They aren't, in normal use. Tags are stable references.
Tagging too much. Not every commit needs a tag; only the ones other people will need to reference.
Inconsistent tag naming. Mix of
v1.0,1.0,release-1.0, and1.0.0makes the tag list confusing.Forgetting to push tags. Local tags are invisible to colleagues. Push them when relevant.
Tagging at the wrong commit. Verify the commit before creating the tag.
Reusing tag names by deleting and recreating. The new tag is a different reference; anyone who fetched the old one is now in conflict.
Try It
Exercise: Build the mental model:
- In a practice repository, list tags with
git tag. If there aren't any, that's fine for now.- Look at how tags appear in
git log --decorateoutput - they're decorations on specific commits.- Run
git show v1.0(substitute a tag that exists). Observe the output.- Look at how
.git/refs/tags/is structured:ls .git/refs/tags/.- Read the tag names in a public open-source project like the Linux kernel or Git itself. Note the conventions.
- Identify a commit in your work history that should have been tagged but wasn't. Note the cost of not having that tag for future reference.