1515

Today I was looking through the logs for a project and realized that I fat fingered a tag name some time ago. Is there some way to rename the tag? Google hasn't turned up anything useful.

I realize I could check out the tagged version and make a new tag, I even tried that. But that seems to create a tag object that isn't quite right. For one,

git tag -l

lists it out of order relative to all of the other tags. I have no idea if that's significant, but it leads me to believe that the new tag object isn't quite what I want. I can live with that, because I really only care that the tag name matches the documentation, but I'd rather do it "right", assuming there is a right way to do this.

4
  • Did you use the same invocation, i.e. if the old tag was annotated/signed tag, is the new tag also of this kind, or is it lightweight tag? Commented Jun 23, 2009 at 8:07
  • 1
    Both the incorrect old tag, and the desired new tag, should be annotated and unsigned. The old tag was created with 'git tag -a bad_tag_name', so I'd like to do something along the lines of 'git tag -a good_tag_name'. Commented Jun 23, 2009 at 22:01
  • I should point out that I also want this magical tag rename process to preserve the annotation from the tag being renamed. Actually, I would really like to change just the name and nothing else. Commented Jun 23, 2009 at 22:09
  • 8
    git log --oneline --decorate --graph is helpful when cleaning up tags.
    – Joel Purra
    Commented May 4, 2012 at 18:00

12 Answers 12

2582

Here is how I rename a lightweight tag old to new:

git tag new old           # Create a new local tag named `new` from tag `old`.
git tag -d old            # Delete local tag `old`.
git push origin new :old  # Push `new` to your remote named "origin", and delete
                          #     tag `old` on origin (by pushing an empty tag
                          #     name to it).

The colon in the push command removes the tag from the remote repository. If you don't do this, Git will create the old tag on your machine when you pull. Finally, make sure that the other users remove the deleted tag. Please tell them (co-workers) to run the following command:

git fetch --prune --prune-tags

Note that if you are renaming an annotated tag, you need to ensure that the new tag name is referencing the underlying commit and not the old annotated tag object that you're about to delete. If the old tag is an annotated tag, or you aren't sure, you can use ^{} to dereference the object until a non-tag object is found:

# create a new annotated tag "new" referencing the object
# which the old annotated tag "old" was referencing:
git tag -a new old^{}
10
  • 18
    One thing to note is that if you use git tag new old is that "old" makes it into the "new" tag iff "old" is not a lightweight tag. To see that, try: git tag -m tralala old; git tag new old; git tag -d old; git cat-file tag new | grep old. That's because "new" points to the "old" tag, not to the commit "old" points to. Commented Dec 17, 2013 at 16:43
  • 6
    To delete an old tag from the remote, you can also do git push -d [remote] [tag]. Commented Jan 30, 2019 at 21:36
  • 1
    @Stevoisiak is this still the case? Trying the mentioned solution the new tag is pointing to the same commit after doing all the steps
    – escapedcat
    Commented Oct 2, 2020 at 6:01
  • 3
    Following the suggested steps I noticed that the new tag lost the Note on Github, dang. Make sure you keep a copy of that so you can add that note back.
    – Jens
    Commented Nov 28, 2020 at 9:58
  • 1
    Note that if you are using GitHub and have an associated release to the old tag, it is useful to first push the new tag without deleting the old one, edit the release to point to the new tag, and finally delete the old tag. Otherwise, you will have to re-publish the release.
    – Rafael-WO
    Commented May 12 at 10:26
335

The original question was how to rename a tag, which is easy: first create NEW as an alias of OLD: git tag NEW OLD then delete OLD: git tag -d OLD.

The quote regarding "the Git way" and (in)sanity is off base, because it's talking about preserving a tag name, but making it refer to a different repository state.

5
  • 4
    Answer above is slightly preferable as it includes the git push origin business.
    – Roly
    Commented Sep 29, 2013 at 20:39
  • easiest way, work great to rename a previous release tag created with Gitflow Commented Nov 17, 2016 at 10:02
  • 20
    Warning: Using git tag new old will create a tag pointing to the old tag, not the old tag's commit. (See Why can't I checkout my tag from Git GUI?)
    – Stevoisiak
    Commented Mar 14, 2018 at 17:30
  • 2
    @Stevoisiak So what is the correct way? Commented Jun 6, 2023 at 17:48
  • @Fourth I unfortunately can't remember the solution I used since the comment is 5 years old. I just remembered running into issues with the pointer-tag and wanted to make sure others had a heads up.
    – Stevoisiak
    Commented Oct 18, 2023 at 18:41
138

In addition to the other answers:

First you need to build an alias of the old tag name, pointing to the original commit:

git tag new old^{}

Then you need to delete the old one locally:

git tag -d old

Then delete the tag on you remote location(s):

# Check your remote sources:
git remote -v
# The argument (3rd) is your remote location,
# the one you can see with `git remote`. In this example: `origin`
git push origin :refs/tags/old

Finally you need to add your new tag to the remote location. Until you have done this, the new tag(s) will not be added:

git push origin --tags

Iterate this for every remote location.

Be aware, of the implications that a Git Tag change has to consumers of a package!

6
  • Warning: Using git tag new old will create a tag pointing to the old tag, not the old tag's commit. (See Why can't I checkout my tag from Git GUI?)
    – Stevoisiak
    Commented Mar 14, 2018 at 17:30
  • 1
    @StevenVascellaro Thanks for the link. For the next time, please file an edit – answering is a community effort as well. Thanks.
    – kaiser
    Commented Mar 14, 2018 at 21:13
  • I didn’t make an edit because I have not tested the code for myself yet. (Note the submit date on the linked question)
    – Stevoisiak
    Commented Mar 14, 2018 at 21:26
  • Once we do git tag new old^{}, then we don't need git tag new_tag_name old_tag_name (the first step).
    – Number945
    Commented May 13, 2018 at 7:04
  • 2
    I used this solution as a one-liner and it seemed to work like a charm! git tag NEW_TAG OLD_TAG^{} && git tag -d OLD_TAG && git push origin :refs/tags/OLD_TAG && git push origin NEW_TAG Commented Aug 27, 2020 at 20:15
38

This wiki page has this interesting one-liner, which reminds us that we can push several refs:

git push origin refs/tags/<old-tag>:refs/tags/<new-tag> :refs/tags/<old-tag> && git tag -d <old-tag>

and ask other cloners to do git pull --prune --tags

So the idea is to push:

  • <new-tag> for every commits referenced by <old-tag>: refs/tags/<old-tag>:refs/tags/<new-tag>,
  • the deletion of <old-tag>: :refs/tags/<old-tag>

See as an example "Change naming convention of tags inside a git repository?".


hIpPy mentions in the comments:

I used rmandvikar/dotfiles bin/tag-cleanup to copy tags in bulk to a different naming format, and it worked like a charm.

#!/bin/sh

#usage:
#   ... | tag-cleanup
#
# Gen bulk-rename script for git tags from 'nuget-' to 'v'.

# todo: cleanup local tags
sed 's,nuget-,,' | \
    sed -E 's,(.+),nuget-\1 v\1,' | \
    sed -E 's,(.+) (.+),git push github refs/tags/\1:refs/tags/\2 :refs/tags/\1,' | \
    column -t -o ' '

hlpPy illustrates the use of the script with:

$ git tag | grep -F 'nuget-' | tag-cleanup 

I convert all tags from format nuget-1.2.3 to format v1.2.3.
I feel the nuget- tag naming format was an early mistake from me, and I wanted to fix it for a while now.

Tags now: rmandvikar/csharp-trie/tags.

11
  • 2
    Beware that this leaves the original tag name in the annotation for annotated tags!! I'm not sure if that actually implies anything though, at least in the current versions.
    – gbr
    Commented Aug 20, 2015 at 18:26
  • 2
    @VonC I'm not sure I understand what you're asking; maybe I wasn't clear: the annotation objects contain a tag field that's set to the tag's name, you can see it with git cat-file -p <tag>; with your method on my system I do get the 'renamed' tag ref (<new-tag>), but its tag field is still <old-tag>.
    – gbr
    Commented Aug 20, 2015 at 23:25
  • 5
    @gbr Isn't what the OP wanted? He mentioned "I should point out that I also want this magical tag rename process to preserve the annotation from the tag being renamed. Actually, I would really like to change just the name and nothing else" (stackoverflow.com/questions/1028649/how-do-you-rename-a-git-tag/…)
    – VonC
    Commented Aug 21, 2015 at 5:33
  • 1
    I'm getting a warning when I do git describe immediately after using this approach: warning: tag 'old-tag' is really 'new-tag' here<new line>initial-tag
    – M. Justin
    Commented Dec 14, 2020 at 17:05
  • 4
    Unlike most answers on this page, this preserves the tag date.
    – M. Justin
    Commented Dec 14, 2020 at 17:06
29

If it's published, you can't delete it (without risking being tarred and feathered, that is). The 'Git way' is to do:

The sane thing. Just admit you screwed up, and use a different name. Others have already seen one tag-name, and if you keep the same name, you may be in the situation that two people both have "version X", but they actually have different "X"'s. So just call it "X.1" and be done with it.

Alternatively,

The insane thing. You really want to call the new version "X" too, even though others have already seen the old one. So just use git-tag -f again, as if you hadn't already published the old one.

It's so insane because:

Git does not (and it should not) change tags behind users back. So if somebody already got the old tag, doing a git-pull on your tree shouldn't just make them overwrite the old one.

If somebody got a release tag from you, you cannot just change the tag for them by updating your own one. This is a big security issue, in that people MUST be able to trust their tag-names. If you really want to do the insane thing, you need to just fess up to it, and tell people that you messed up.

All courtesy of the man pages.

5
  • 6
    Or you can tag (with correct name) this incorrectly named tag. Commented Jun 23, 2009 at 8:06
  • 8
    Thanks, I've been over that man page a million times already. Fortunately the bad tag hasn't been published anywhere. Even if it was, this is an internal project and I'm the only developer (for the moment). I'm think I'm fairly safe from both the tarring and the feathering, but only if I can get the repo to match the docs. Commented Jun 23, 2009 at 22:06
  • I sometimes use tags for my own personal references. Eg. it could be a 'ok_jb' tag. I use this, because some of the people I work with cannot build for my platform, so sometimes there will be build errors. I can then quickly get a version that builds, by checking out that tag. When the new sources builds, I just move the tag, or I rename it to builds##, where ## is a number (depending on the project). I may also emphasize when a special feature was introduced, by adding a tag.
    – user1985657
    Commented Oct 10, 2014 at 2:04
  • 11
    Poor answer. "Don't do it" is never the correct answer to "How can I do it?". The user wasn't asking if you think it is a good idea to do that or if people will like that. If someone asks "How can I cut off my hand", either tell him how it's done or leave him alone but he won't need someone telling him that cutting of a hand may not be such a great idea. And you can do it. You can add a new tag and delete the old one, it's technically possible, even in a remote repository.
    – Mecki
    Commented Nov 3, 2015 at 15:08
  • 7
    This seems to answer the question "How do I make an existing tag point to a different revision?" instead of the OP's question, "How do I rename a tag?" It's also unclear how telling people you messed up is going to solve the problem (even though it's a good idea in general).
    – LarsH
    Commented Mar 9, 2016 at 19:49
27

As an add on to the other answers, I added an alias to do it all in one step, with a more familiar *nix move command feel. Argument 1 is the old tag name, argument 2 is the new tag name.

[alias]
    renameTag = "!sh -c 'set -e;git tag $2 $1; git tag -d $1;git push origin :refs/tags/$1;git push --tags' -"

Usage:

git renametag old new
4
  • 3
    This didn't work for me, since it failed at !sh (question was regarding Windows Git), however, after updating the format to the following, it worked: renametag = "!f() { git tag $2 $1; git tag -d $1; git push origin :refs/tags/$1; git push --tags; }; f". Commented Jun 21, 2019 at 17:03
  • 2
    This could be made safer by changing ; to && so the command stops in case of error, rather than deleting/pushing tags even if e.g. the new tag creation failed. Also, could push just the new tag rather than doing a full push with all annotated & lightweight tags: git push origin :$1 $2.
    – M. Justin
    Commented Dec 14, 2020 at 22:22
  • 1
    Hey @SunnyPatel, I have added your amazing alias to my BashScripts gitconfig project. Thanks! github.com/hopeseekr/BashScripts/blob/v2.3.0/gitconfig#L37-L38 Commented Oct 30, 2021 at 12:25
  • @TheodoreR.Smith Glad it helped! I appreciate a shout out! :) Commented Nov 5, 2021 at 14:31
27

Follow the 3 step approach for a one or a few number of tags.

Step 1: Identify the commit/object ID of the commit the current tag is pointing to

command: git rev-parse <tag name>
example: git rev-parse v0.1.0-Demo
example output: db57b63b77a6bae3e725cbb9025d65fa1eabcde

Step 2: Delete the tag from the repository

command: git tag -d <tag name>
example: git tag -d v0.1.0-Demo
example output: Deleted tag 'v0.1.0-Demo' (was abcde)

Step 3: Create a new tag pointing to the same commit id as the old tag was pointing to

command: git tag -a <tag name>  -m "appropriate message" <commit id>
example: git tag -a v0.1.0-full  -m "renamed from v0.1.0-Demo" db57b63b77a6bae3e725cbb9025d65fa1eabcde
example output: Nothing or basically <No error>

Once the local git is ready with the tag name change, these changes can be pushed back to the origin for others to take these:

command: git push origin :<old tag name> <new tag name>
example: git push origin :v0.1.0-Demo v0.1.0-full
example output: <deleted & new tags>
4
  • 1
    It is missing steps to push back deleted tag: git push origin :refs/tags/v0.1.0-Demo and to push back tags (with other pending things) git push --tags
    – Star Wolf
    Commented Oct 3, 2019 at 9:52
  • A downside to this exact approach is that it doesn't keep the existing tag's annotated message (though that's easily adjusted)
    – M. Justin
    Commented Dec 12, 2020 at 0:03
  • @StarWolf I edited the answer to add those details
    – M. Justin
    Commented Dec 14, 2020 at 22:30
  • I used git show-ref --tags -d to get a list of SHA in one terminal. Then I ended up chaining these all together since it seemed that my IDE kept pulling in remote tags :uhgg: ie: git tag -d v19.12.27 && git tag -a v0.19.12-27 -m "renamed from v19.12.27" 6b8550023199938c9a679021ecc587a8e9427738 && git push origin :v19.12.27 v0.19.12-27 it would be nice to just make this a helper alias. I did notice mine complained about the tag being a nested tag, but I'm not sure what that means so I'm just moving forward with this.
    – CTS_AE
    Commented Nov 11, 2021 at 17:56
21

A duplicate annotated tag — including all tag info such as tagger, message, and tag date — can be created by using the tag info from the existing tag.

SOURCE_TAG=old NEW_TAG=new; deref() { git for-each-ref \
"refs/tags/$SOURCE_TAG" --format="%($1)" ; }; \
GIT_COMMITTER_NAME="$(deref taggername)" \
GIT_COMMITTER_EMAIL="$(deref taggeremail)" \
GIT_COMMITTER_DATE="$(deref taggerdate)" git tag "$NEW_TAG" \
"$(deref "*objectname")" -a -m "$(deref contents)"

git tag -d old

git push origin new :old

Update the SOURCE_TAG and NEW_TAG values to match your old and new tag names.

This command only addresses unsigned tags, though it should be a simple matter to extend this solution to signed tags.

Goal

To truly be an indistinguishable rename, all elements of an annotated tag should be the same in the new tag. The git-tag documentation specifies the parts of an annotated tag.

Tag objects (created with -a, -s, or -u) are called "annotated" tags; they contain a creation date, the tagger name and e-mail, a tagging message, and an optional GnuPG signature.

Answer motivation

From what I can tell, all the other answers have subtle gotchas, or don't fully duplicate everything about the tag (e.g. they use a new tag date, or the current user's info as the tagger). Many of them call out the re-tagging warning, despite that not applying to this scenario (it's for moving a tag name to a different commit, not for renaming to a differently named tag). I've done some digging, and have pieced together a solution that I believe addresses these concerns.

Procedure

An annotated tag named old is used in the example, and will be renamed to new.

Step 1: Get existing tag information

First, we need to get the information for the existing tag. This can be achieved using for-each-ref:

Command:

git for-each-ref refs/tags --format="\
Tag name: %(refname:short)
Tag commit: %(objectname:short)
Tagger date: %(taggerdate)
Tagger name: %(taggername)
Tagger email: %(taggeremail)
Tagged commit: %(*objectname:short)
Tag message: %(contents)"

Output:

Tag commit: 88a6169
Tagger date: Mon Dec 14 12:44:52 2020 -0600
Tagger name: John Doe
Tagger email: <[email protected]>
Tagged commit: cda5b4d
Tag name: old
Tag message: Initial tag

Body line 1.
Body line 2.
Body line 3.

Step 2: Create a duplicate tag locally

A duplicate tag with the new name can be created using the info gathered in step 1 from the existing tag.

The commit ID & commit message can be passed directly to git tag.

The tagger information (name, email, and date) can be set using the git environment variables GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL, GIT_COMMITTER_DATE. The date usage in this context is described in the On Backdating Tags documentation for git tag; the other two I figured out through experimentation.

GIT_COMMITTER_NAME="John Doe" GIT_COMMITTER_EMAIL="[email protected]" \
GIT_COMMITTER_DATE="Mon Dec 14 12:44:52 2020 -0600" git tag new cda5b4d -a -m "Initial tag

Body line 1.
Body line 2.
Body line 3."

A side-by-side comparison of the two tags shows they're identical in all the ways that matter. The only thing that's differing here is the commit reference of the tag itself, which is expected since they're two different tags.

Command:

git for-each-ref refs/tags --format="\
Tag commit: %(objectname:short)
Tagger date: %(taggerdate)
Tagger name: %(taggername)
Tagger email: %(taggeremail)
Tagged commit: %(*objectname:short)
Tag name: %(refname:short)
Tag message: %(contents)"

Output:

Tag commit: 580f817
Tagger date: Mon Dec 14 12:44:52 2020 -0600
Tagger name: John Doe
Tagger email: <[email protected]>
Tagged commit: cda5b4d
Tag name: new
Tag message: Initial tag

Body line 1.
Body line 2.
Body line 3.

Tag commit: 30ddd25
Tagger date: Mon Dec 14 12:44:52 2020 -0600
Tagger name: John Doe
Tagger email: <[email protected]>
Tagged commit: cda5b4d
Tag name: old
Tag message: Initial tag

Body line 1.
Body line 2.
Body line 3.

As a single command, including retrieving the current tag data:

SOURCE_TAG=old NEW_TAG=new; deref() { git for-each-ref "refs/tags/$SOURCE_TAG" --format="%($1)" ; }; GIT_COMMITTER_NAME="$(deref taggername)" GIT_COMMITTER_EMAIL="$(deref taggeremail)" GIT_COMMITTER_DATE="$(deref taggerdate)" git tag "$NEW_TAG" "$(deref "*objectname")" -a -m "$(deref contents)"

Step 3: Delete the existing tag locally

Next, the existing tag should be deleted locally. This step can be skipped if you wish to keep the old tag along with the new one (i.e. duplicate the tag rather than rename it).

git tag -d old

Step 4: Push changes to remote repository

Assuming you're working from a remote repository, the changes can now be pushed using git push:

git push origin new :old

This pushes the new tag, and deletes the old tag.

2
  • Great! Just for signed messages, it invalidates them. To re-sign then, the last part of the single-liner must become: -sm "$(deref contents:subject)\n\n$(deref contents:body)"
    – ankostis
    Commented May 6, 2022 at 10:37
  • THIS is the only real answer! All others will create a new tag with wrong author and date. Commented Aug 7, 2023 at 3:32
6

For the adventurous it can be done in one command:

mv .git/refs/tags/OLD .git/refs/tags/NEW
7
  • 9
    This won't work if your refs are packed, i.e. if you've run git gc recently
    – forivall
    Commented Dec 7, 2012 at 1:21
  • 2
    This also will only affect the local repo. If you have a remote configured, I'm not sure what negative effects this could cause. I do not recommend this approach. Commented May 9, 2013 at 21:08
  • 2
    Note also that for annotated tags this will probably be even more troublesome, as the 'annotation' blob among the other things contains the original name of the tag. Actually I'm not sure if that's used by anything (hopefully at least by verify-tag), but I wouldn't take a chance.
    – gbr
    Commented Aug 20, 2015 at 18:12
  • 2
    @gbr This works just fine.(Of course, the note by @forivall is to be taken into account.) This trick has been massively used for ages in the ALT Sisyphus build system. Look at how the sources for a package are stored, e.g.: git.altlinux.org/gears/g/gear.git . The readable tags like 2.0.7-alt1 are the signed tags submitted by the maintainers to the build system. The cryptic tags gb-sisyphus-task164472.200 are put there by the build system to track the task ID that has built&published the pkg from this source. They are dumb copies (cp), with the maintainer's message untouched. Commented May 11, 2016 at 22:54
  • 1
    @forivall If the refs are packed, the solution from stackoverflow.com/a/24245862/94687 will work with analogous effect: git push . refs/tags/OLD:refs/tags/NEW Commented Jun 16, 2021 at 0:38
3

Regardless of the issues dealing with pushing tags and renaming tags that have already been pushed, in case the tag to rename is an annotated one, you could first copy it thanks to the following single-line command line:

git tag -a -m "`git cat-file -p old_tag | tail -n +6`" new_tag old_tag^{}

Then, you just need to delete the old tag:

git tag -d old_tag

I found this command line thanks to the following two answers:

Edit:
Having encountered problems using automatic synchronisation of tags setting fetch.pruneTags=true (as described in https://stackoverflow.com/a/49215190/7009806), I personally suggest to first copy the new tag on the server and then delete the old one. That way, the new tag does not get randomly deleted when deleting the old tag and a synchronisation of the tags would like to delete the new tag that is not yet on the server. So, for instance, all together we get:

git tag -a -m "`git cat-file -p old_tag | tail -n +6`" new_tag old_tag^{}
git push --tags
git tag -d old_tag
git push origin :refs/tags/old_tag
3

You can also rename remote tags without checking them out, by duplicate the old tag/branch to a new name and delete the old one, in a single git push command.

Remote tag rename / Remote branch → tag conversion: (Notice: :refs/tags/)

git push <remote_name> <old_branch_or_tag>:refs/tags/<new_tag> :<old_branch_or_tag>

Remote branch rename / Remote tag → branch conversion: (Notice: :refs/heads/)

git push <remote_name> <old_branch_or_tag>:refs/heads/<new_branch> :<old_branch_or_tag>

Output renaming a remote tag:

D:\git.repo>git push gitlab App%2012.1%20v12.1.0.23:refs/tags/App_12.1_v12.1.0.23 :App%2012.1%20v12.1.0.23

Total 0 (delta 0), reused 0 (delta 0)
To https://gitlab.server/project/repository.git
 - [deleted]               App%2012.1%20v12.1.0.23
 * [new tag]               App%2012.1%20v12.1.0.23 -> App_12.1_v12.1.0.23
1

based on the answer by M. Justin

#!/usr/bin/env bash

# rename git tag and preserve most data
# delete the old tag

# push new tag, delete old tag:
# git push origin new_tag :old_tag

# based on https://stackoverflow.com/a/65296616/10440128

set -e

function deref() {
  git for-each-ref "refs/tags/$1" --format="%($2)"
}

function git_tag_rename() {
  local tag1="$1"
  local tag2="$2"
  [ -z "$tag1" ] && return 1
  [ -z "$tag2" ] && return 1
  GIT_COMMITTER_NAME="$(deref $tag1 taggername)" \
  GIT_COMMITTER_EMAIL="$(deref $tag1 taggeremail)" \
  GIT_COMMITTER_DATE="$(deref $tag1 taggerdate)" \
  git tag "$tag2" "$(deref $tag1 '*objectname')" -a \
    -m "$(deref $tag1 contents)" &&
  git tag -d "$tag1"
}

if [ -z "$1" ] || [ -z "$2" ]; then
  echo "usage: $0 tag_1 tag_2" >&2
  exit 1
fi

git_tag_rename "$1" "$2"
3
  • how can i use this? is this a powershell script?
    – norca
    Commented Feb 29 at 9:00
  • its a bash script
    – milahu
    Commented Feb 29 at 10:24
  • I needed to add the "-admin" suffix to all tags in the repo of an app called "Admin" while I was merging the whole history of the repo into another repo as part of a larger reorganization of the codebase. I stored your script as retag.sh and ran git tag --list | while read tag; do ./retag.sh "$tag" "$tag-admin"; done. Worked well, thanks!
    – Palec
    Commented May 13 at 15:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.