Git tags have several uses to me.
There’s the classic use of “here’s something we released in the past”. It doesn’t need a branch, because it’s no longer under development, but you may need to refer to it at some point. Presumably you have some regular patterns for naming tags, and perhaps you use annotated tags to contain release information. It’s just good release practice to have branches be for active development only, because you can always create a branch from a tag if you need to start doing work on it again.
There’s another use of “I have some dead/obsolete development work, but I’d still like it to stick around in the permanent record”. I prefer this to spelunking in the reflog, because sooner or later you’ll garbage-collect, and if there are no live references to commits, those commits will go away. Obviously, you should not keep actual garbage, but a historical record can be a valuable thing. And when you tire of that history, you can delete it just by removing the tags. I switched to this instead of keeping branches around, and it makes my repos feel a bit cleaner.
Lightweight tags have the advantage of not actually being blobs, but simply associating a string with a commit. Annotated tags let you add extra information, in the form of a commit message, and there are other benefits as well (you can sign tags, for example). I see both as valuable for both kinds of tags. Some projects only use annotated tags – for example, in looking through the Git source itself, it seems like all the tags are annotated tags. My preference is to just have annotated tags.
There’s one troublespot where it comes to sharing tags, and that is that tags are in a single namespace, unlike branch refs. Since people rarely share tags, this isn’t an issue. But if you fetch tags from a remote repository, they go into the same .git/refs/tags location as your local tags. One suggestion I saw that was interesting was to have a pattern for naming tags based on remotes, so that you could keep your tags separate from pulled-in remote tags. It’s not automatic, though, you have to do it manually. There aren’t common workflows yet around sharing tags, as far as I know.
While tags are normally stored in .git/refs/tags, if you look in that directory, you might only see a few tag files. Refs (tags and branches) can be packed up into a single .git/packed_refs file for efficiency’s sake, and this works very well for tags, since tag refs normally never change. A ref will get unpacked if it needs to change. This can be done manually with git pack-refs, or a git gc will also do it when it runs automatically.
As of Git 1.9.0, git fetch –tags fetches both branches and tags. By itself, git fetch will only get tags referenced by commits that are brought down, but it won’t bring down new tags pointing to commits that you already have. One down-side to git fetch –tags is that it will fetch and replace all tags. Normally this is fine, but may be dangerous if you have multiple remotes attached to a single repository, especially if those remotes are disjoint. Just keep this in mind that you may need to explicitly pull tags in some cases.
See a separate post I have yet to write about git log/git rev-list and proper use of –all, –branches, –tags and –remotes.
Create an annotated tag (assumes that the tag message is in the file <tagmessage>):
git tag -a release-1.5.1 -F <tagmessage>
Show the tag and/or related commit (for annotated tag, will show the annotated tag and then the commit; for lightweight tag, will show just the commit):
git show release-1.5.1
Show tags in <remote> repository, where <remote> is the name of a remote attached to your local repository:
git ls-remote --tags <remote>
Show the most recent annotated tag on the current branch:
Push a specific tag (and related objects) to a remote repository:
git push <remote> release-1.5.1
Push all tags not already in the remote repository:
git push <remote> --tags
Delete a tag in the local repository
git tag -d release-1.5.1
Delete a tag in a remote repository (note: this has the same perils as rebasing, others could be depending on this tag, but it’s not bad in and of itself):
git push <remote> :refs/tags/release-1.5.1
Git book: Git Basics – Tagging
StackOverflow: Git: distinguish between local and remote tags