In Git, how does an annotated tag differ from a lightweight tag?
answer
- one creates an object, the other does not
- cat-file -t answers it immediately
- who tagged, and when?
- git describe ignores one of them by default
basics
~20 sA lightweight tag is only a ref under refs/tags pointing straight at a commit. An annotated tag creates a real tag object holding a tagger, a date, a message and an optional signature, and the ref points at that object.
solid answer
~40 s`git tag v1.0` creates a **lightweight** tag: a single entry under `refs/tags/` containing a commit hash and nothing else — an alias, effectively a branch name that nobody moves. `git tag -a v1.0 -m "..."` creates an **annotated** tag: Git writes a fourth kind of object into the object database, containing the target object's hash, the target's type, the tag name, a tagger identity with timestamp, and a message; the ref then points at that tag object, which in turn points at the commit. So `git cat-file -t v1.0` prints `tag` for one and `commit` for the other. Annotated tags carry provenance and are the ones `git describe` uses by default, so releases should be annotated.
code
console · 8 lines$ git tag -a v1.0 -m "release one"
$ git cat-file -p v1.0
object d2b5d5322e03b5733ebfa5debcd995cfc63f718f
type commit
tag v1.0
tagger Tester <[email protected]> 1787132500 +0400
release onego deeper
Recall the two commands and the one-line difference: plain git tag makes a bare pointer, git tag -a records a message, a tagger and a date.
Explain that an annotated tag is a fourth object type with its own hash, and that the ref points at that object which in turn points at the commit — so the tag must be peeled to reach the commit.
Justify the policy: releases get annotated (and signable) tags because provenance and git describe depend on it, and know that tags are inert pointers Git never advances on your behalf.
Own tagging as part of the release contract — which artefacts are named, whether the naming record must be attributable and verifiable, and what breaks downstream when a tag is a bare alias with no author or date.
## Two things called "tag" A tag is a ref under the `refs/tags/` namespace, and by convention nothing ever moves it. What differs between the two flavours is *what the ref points at*. **Lightweight**: `git tag v1.0` writes the commit's hash directly into `refs/tags/v1.0`. No object is created. There is no record of who made the tag, when, or why. **Annotated**: `git tag -a v1.0 -m "release one"` creates a **tag object** — the fourth object type alongside blob, tree and commit — and points the ref at that object instead. Printing it shows a small header block and a message: - `object <hash>` — what is being tagged. - `type <objecttype>` — usually `commit`. - `tag <name>` — the name recorded inside the object itself. - `tagger <name> <email> <epoch> <timezone>`. - a blank line, then the tag message. Because the object carries a message and an identity, an annotated tag is a first-class, describable artefact: it has its own hash, and it can be signed so the signature covers the tag record. ## How to tell them apart `git cat-file -t v1.0` prints `tag` for an annotated tag and `commit` for a lightweight one — the quickest check. `git show` on an annotated tag prints the tagger and message before the commit; on a lightweight tag it goes straight to the commit. ## Peeling Since an annotated tag ref points at a tag object rather than a commit, anything that wants the commit must *peel* the tag — follow the `object` line down. Git does this automatically almost everywhere, which is why a tag name works fine as a revision argument. When refs are compacted into the packed-refs file, Git even stores the peeled commit hash on a following line beginning with `^`, so lookups do not need to open the tag object. ## Which to use Use annotated tags for anything anyone else will consume — releases above all. The reasons are concrete: they record who tagged and when, they can be signed, and `git describe` considers only annotated tags unless you pass `--tags`, so a lightweight tag will not name your build. Lightweight tags are appropriate as private bookmarks — a throwaway marker before a risky rebase, for example. ## Details worth knowing - A tag object can point at *any* object type, not just a commit: a tree, a blob, or even another tag object. - The tag name is stored twice — in the ref path and inside the object — so renaming the ref of an annotated tag leaves a stale name inside the object. - Tags are not branches: nothing about a tag makes Git advance it when you commit, and `HEAD` never points into `refs/tags/` in the normal way.
- How would you check, on the command line, which kind of tag you are looking at?Ask for the object type: `git cat-file -t v1.0` prints `tag` for an annotated tag, because the ref points at a tag object, and `commit` for a lightweight one, because the ref points straight at the commit. `git show` also reveals it — an annotated tag prints tagger and message first.
- Can an annotated tag point at something other than a commit?Yes. The tag object records both the target hash and its type, so it can tag a blob, a tree, another tag, or a commit. Tagging a commit is simply the overwhelmingly common case; tools that want the commit peel the tag by following its object line.
saying these in an interview costs you the question
- Says both tag kinds create an object in the database
- Calls a tag a branch that Git moves for you
- Thinks a lightweight tag records who created it
- Assumes git describe treats both kinds identically