Table of Contents

Introduction

If you've ever tagged the wrong commit, or typo'd v1.O with a capital O, you'll want the tag gone before anyone notices. git tag -d deletes it from your repository in one step. The part that trips people up is that a tag you've already pushed also lives on the remote, and -d doesn't touch that copy.

In this article, we'll:

  1. Watch git tag -d v1.0 remove a tag on a small repository
  2. Confirm that the commit it named is still there
  3. Cover deleting the tag on the remote, and what that means for other people's clones

What is git tag -d?

git tag -d <name> deletes the ref .git/refs/tags/<name> (or its line in .git/packed-refs). That's it. The commit the tag pointed at is untouched, and so is every branch. For an annotated tag, the tag object itself (tagger, date, message) stays in the object database until garbage collection removes it, but nothing refers to it any more.

Tags don't have a reflog the way branches and HEAD do, so the hash Git prints when it deletes one is worth a glance.

Watch it happen

Our sample repo has a v1.0 tag on the previous commit: v1.0 points at a0b2db3, "Add user settings page", and HEAD and main are one commit ahead on 8c02d5b, "Update dependencies". Here's git tag -d v1.0:

  1. Before: the tag v1.0 points at a0b2db3.
  2. Git deletes the v1.0 tag ref.
  3. a0b2db3 itself stays in the history, still the parent of 8c02d5b, and main and HEAD don't move.

Before and after

The commits are identical in both graphs, and main still reaches a0b2db3, so nothing is at risk of being lost. Git's one-line output gives you the undo:

The raw git output, if you want to read along in text

git log --oneline --graph --all

before

* 1117a34 (feature) Add search tests
* e5869f0 Fix typo in search box
* fc19889 Add search box
| * 8c02d5b (HEAD -> main) Update dependencies
| * a0b2db3 (tag: v1.0) Add user settings page
|/  
* 96c4fc2 Fix header layout
* ae65976 Add login page
* 3e1ffe4 Add project skeleton
* 4114b2c Initial commit

after

* 1117a34 (feature) Add search tests
* e5869f0 Fix typo in search box
* fc19889 Add search box
| * 8c02d5b (HEAD -> main) Update dependencies
| * a0b2db3 Add user settings page
|/  
* 96c4fc2 Fix header layout
* ae65976 Add login page
* 3e1ffe4 Add project skeleton
* 4114b2c Initial commit

what git printed

Deleted tag 'v1.0' (was a0b2db3)

"(was a0b2db3)" is the value the tag held. Here it's the commit hash itself, which tells you v1.0 was a lightweight tag. An annotated tag would show the hash of the tag object instead.

Deleting the tag on the remote

git tag -d is local only. If you pushed v1.0, origin still has it, and the next git fetch from anyone (including you) can bring it right back. To delete it on the remote:

git push origin --delete v1.0

or the older refspec form, which pushes "nothing" into the tag:

git push origin :refs/tags/v1.0

Spelling out refs/tags/ is a good habit if you also have a branch with the same name, so Git can't pick the wrong one.

Other people's clones keep their copy of the tag even after it's gone from the remote, because git fetch doesn't remove tags by default. They can run git fetch --prune --prune-tags to drop tags the remote no longer has, or git tag -d v1.0 themselves.

I've deleted and re-pushed a release tag exactly once, on one of my own small projects, after tagging before the version bump was committed. Because nobody had fetched it yet, it was harmless. The Git docs have a section called "On Re-tagging" that is worth reading before you ever do it on a tag other people have pulled, since their clones won't pick up the moved tag on their own.

Is it safe?

Caution git-sim pre-flight

deletes v1.0; the commit it points at stays, but the name is gone (recreate it with git tag v1.0 <sha>).

Deleting a tag removes a name, never a commit. The caution is about people: a release tag that others use to find a version will silently disappear or, if you recreate it somewhere else, point at something different for you than for them.

How to undo it

For a lightweight tag, recreate it with the hash from the output:

git tag v1.0 a0b2db3

For an annotated tag, the printed hash is the tag object, which still exists until garbage collection. Pointing the ref back at it restores the tag with its message intact:

git update-ref refs/tags/v1.0 <tag-object-sha>

Here's the tag being created in the first place, on the same kind of repository (in that example v1.0 goes on 8c02d5b, the current commit):

Try it on your repository

pip install git-sim
git-sim tag -d v1.0

git-sim shows which commit loses its label and confirms the commit itself stays, without deleting anything.

Common questions

How do I delete a tag on GitHub or another remote?

git push origin --delete <tag>, or git push origin :refs/tags/<tag>. Deleting it locally with git tag -d doesn't affect the remote.

Does deleting a tag delete the commit?

No. It only removes the name. The commit stays wherever it was, and if a branch reaches it, nothing changes at all.

How do I delete all local tags?

git tag -d $(git tag -l) in a Unix shell. To make your local tags match the remote afterwards, run git fetch --tags.

How do I move a tag to a different commit?

Delete it and create it again, or use git tag -f <name> <commit>. If the tag was already pushed, you'll also need git push --force origin <name>, and everyone who fetched the old one has to delete their copy. The "On Re-tagging" section of the git tag documentation explains why.

Summary

In this article, we watched git tag -d remove v1.0 from a0b2db3 while the commit stayed exactly where it was, and covered deleting a tag from the remote and cleaning it out of other clones.

Next steps

git tag covers creating tags and why they don't push on their own. git branch -d is the same kind of cleanup for the ref that moves.