247

We have several annotated tags in our Git repository. The older tags have bogus messages that we would like to update to be in our new style.

% git tag -n1
v1.0 message
v1.1 message
v1.2 message
v2.0 Version 2.0 built on 15 October 2011.

In this example, we would like to make v1.x messages look like the v2.0 message. How would we do this?

Trilarion
  • 9,318
  • 9
  • 55
  • 91
jared
  • 4,889
  • 2
  • 17
  • 24

10 Answers10

287

git tag <tag name> <tag name>^{} -f -m "<new message>"

This will create a new tag with the same name (by overwriting the original).

Stevoisiak
  • 16,510
  • 19
  • 94
  • 173
Andy
  • 38,684
  • 13
  • 64
  • 66
  • 8
    Does this maintain the original tag's date? – James M. Greene May 22 '13 at 16:06
  • 19
    Answer to my own comment question: Yes, it _does_ change the date. :( – James M. Greene May 22 '13 at 16:18
  • 10
    See the "On Backdating Tags" section in `git tag --help`. – dahlbyk May 22 '13 at 16:20
  • 6
    It should also be noted that you can also append multiple messages (they get separated by a new line - on GitHub) `git tag -f -m "" -m "" -m ""` – Blair McMillan Feb 12 '14 at 00:07
  • 1
    After doing this, git show shows both the original and the new messages, so some ref must still exist to the original tag. How do I locate/remove/gc the old tag ref ? – Chris Morley Feb 26 '14 at 20:59
  • Are you certain it's not the tag and commit message your seeing? – Andy Feb 26 '14 at 21:54
  • Pretty sure. git log shows 2 tag objects: bash-4.1$ git show 9.03.050 tag 9.03.050 Tagger: Testing Date: Mon Feb 24 21:05:50 2014 -0500 v9.03.050 bash-4.1$ GIT_COMMITTER_DATE="Mon Feb 24 21:05:50 2014 -0500" git tag 9.03.050 9.03.050 -f -m 9.03.050 Updated tag '9.03.050' (was d70cae7) bash-4.1$ git show 9.03.050 tag 9.03.050 Tagger: Chris Morley Date: Mon Feb 24 21:05:50 2014 -0500 9.03.050 tag 9.03.050 Tagger: Testing Lebanon Date: Mon Feb 24 21:05:50 2014 -0500 v9.03.050 bash-4.1$ – Chris Morley Feb 27 '14 at 01:26
  • 5
    @ChrisMorley looks at my answer below http://stackoverflow.com/a/23532519/603949 - in short, use `^{}` when you want to replace the `old tag` – Sungam Aug 23 '14 at 11:24
  • 3
    To preserve the original date, based on @dahlbyk observation: `git checkout SHA1_OF_COMMIT` `GIT_COMMITTER_DATE="$(git show --format=%aD | head -1)" git tag TAG_MODIFIED TAG_MODIFIED -f -m "New message for tag"` – Jose_GD Dec 23 '14 at 22:49
  • 1
    **Warning**: This creates a duplicate tag that points to your old tag. See [Sungam's answer](https://stackoverflow.com/a/23532519/3357935) for a safer solution. – Stevoisiak Mar 16 '18 at 18:36
  • And if the tag was pushed before editing its message, how do you push this edited tag? – alondono Nov 28 '18 at 01:30
  • The `^{}` thing didn't work for me. After some searching I determined that [this is a Windows thing](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects): `cmd.exe` uses `^` as a shell escape, so you need to double it up. – Karl Knechtel Aug 13 '20 at 16:12
  • Please note that the idea of `^{}` edited in by Stevoisiak basically comes from [Sungam's answer](https://stackoverflow.com/a/23532519/1536976). – Trilarion May 29 '21 at 12:12
97

To update a complex message, just specify the annotated tag option with -a or the signed tag option with -s:

git tag <tag name> <tag name>^{} -f -a

This will open an editor with the contents of your old tag message.

John Kugelman
  • 307,513
  • 65
  • 473
  • 519
Eric Hu
  • 17,470
  • 8
  • 49
  • 67
  • 2
    The `^{}` thing didn't work for me. After some searching I determined that [this is a Windows thing](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects): `cmd.exe` uses `^` as a shell escape, so you need to double it up. – Karl Knechtel Aug 13 '20 at 16:13
  • Please note that the idea of `^{}` edited in by John Kugelman basically comes from [Sungam's answer](https://stackoverflow.com/a/23532519/1536976). – Trilarion May 29 '21 at 12:12
42

git tag <tag name> <tag name>^{} -f -a

This is an improvement: without ^{} it will create a new tag object that reference the old tag object, where both of them will have the same tag name.

<tag name>^{} will resolve the tag/reference until it finds the first commit hash.

rubo77
  • 15,234
  • 23
  • 111
  • 195
Sungam
  • 1,455
  • 1
  • 18
  • 23
  • 4
    @BrentFoust, that works only when your head is at the tagged commit `usage: git tag [-a|-s|-u ] [-f] [-m |-F ] []` – Sungam Oct 09 '14 at 00:47
  • 2
    The `^{}` thing didn't work for me. After some searching I determined that [this is a Windows thing](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects): `cmd.exe` uses `^` as a shell escape, so you need to double it up. – Karl Knechtel Aug 13 '20 at 16:13
39

TL;DR

You can do this by deleting your tag and recreating it while spoofing the date and author:

> git tag -d <tag-name>
> [GIT_COMMITTER_DATE=<original-commit-date>] \
> [GIT_AUTHOR_NAME=<original-author-name>] \
> git tag <tag-name> [commit]

Whole story:

Building on Sungram's answer (originally proposed as an edit):

1. Accepted answer

This is an improvement over Andy and Eric Hu's answers. Their answers will create a new tag object that references the old tag object and both are going to have the same name.

To illustrate this, consider the following:

> git tag tag1 tag1 -f -a  # accepted answer
> git rev-list --objects -g --no-walk --all
[ example output: ]
6bdcc347fca041a5138f89fdf5276b3ebf9095d5
260ab7928d986472895b8c55e54569b3f3cb9517 tag1
a5797673f610914a45ef7ac051e3ee831a6e7c25 tag1
f22d6308c3cd330a3b0d86b9bf05562faf6b6f17

> git show tag1
tag tag1
Tagger: [tagger]
Date:   [date of updated tag]
[Updated description]

tag tag1
Tagger: [tagger]
Date:   [date of original tag]
[Original description]

[tagged commit details]

2. Sungram's improvement

Using <tag name>^{} as the second argument of git tag will instead delete all previous tags with the same name.

Consider the continuation of the previous terminal session:

> git tag tag1 tag1^{} -f -a  # suggested improvement
> git rev-list --objects -g --no-walk --all
[ example output: ]
6bdcc347fca041a5138f89fdf5276b3ebf9095d5
75f02acacfd7d91d55b5bcfdfb1f00aebeed15e3 tag1
f22d6308c3cd330a3b0d86b9bf05562faf6b6f17 

> git show tag1
tag tag1
Tagger: [tagger]
Date:   [date of updated tag]
[Updated description]

[tagged commit details]

3. Save the date

Lastly, if you want to keep the date of the original tag as the date of the updated tag, use some awk (or similar) magic or just paste the date you want instead. The following is a substitute for the second example (otherwise the original date would be lost due to overriding):

> GIT_COMMITTER_DATE="$(git show tag1 |                              # get info about the tag cascade including the date original of the original tag
> awk '{
>     if ($1 == "Date:") {
>         print substr($0, index($0,$3))
>     }
> }' |                                                               # extract all the dates from the info
> tail -2 | head -1)"                                               `# get the second to last date, as the last one is the commit date` \
> git tag tag1 tag1^{} -a -f                                         # finally, update the tag message, but save the date of the old one
>
> git rev-list --objects -g --no-walk --all
6bdcc347fca041a5138f89fdf5276b3ebf9095d5
e18c178f2a548b37799b100ab90ca785af1fede0 tag1
f22d6308c3cd330a3b0d86b9bf05562faf6b6f17
> git show tag1
tag tag1
Tagger: [tagger]
Date:   [date of original tag]
[Updated description]

[tagged commit details]

References:

4. DIY

Alternatively to updating the tags, you can just delete them and create them again. As it turns out updating just adds a new tag and makes it point to the old one, or alternatively, just implicitly deletes the old one and creates a new one to point to the same commit anyway.

You can achieve this by issuing:

> git tag -d <tag-name>
> [GIT_COMMITTER_DATE=<original-commit-date>] \
> [GIT_AUTHOR_NAME=<original-author-name>] \
> git tag <tag-name> [commit]

Here [optional] is an optional field; <required> is a required field. Of course, you can add any flags after the git tag command that you normally would.

Community
  • 1
  • 1
stanm
  • 2,855
  • 1
  • 22
  • 29
  • 3
    Thanks for pointing out that " Their answers will create a new tag object"! – cwhsu May 12 '16 at 09:41
  • [Quoting Andreas Schwab](http://git.661346.n2.nabble.com/Should-GIT-AUTHOR-NAME-EMAIL-set-the-tagger-name-email-td7566159.html): ```The tagger is controlled by the committer info. (...) GIT_COMMITTER_{NAME,EMAIL}. A tagger isn't really an author.``` – Ivan Vučica Oct 01 '17 at 20:28
13

@Andy 's solution as present in 2016

git tag <tag-name> <tag-name> -f -a

is wrong. After it, with

git show

command, we will see stack tags with same name.

It add a new tag with same tag name and new message at commit <tag-name>. But it don't remove old tag. It's a special case of this command:

git tag [<commit> | <old-tag>] <tag-name>

But just <old-tag> is same with <tag-name>.


Correct solution is simple, just update tag is OK.

git tag <tag-name> -f -a

Remember, only ONE here.

If we want change tag, which isn't HEAD, we need an extra <commit> argument.

git tag <commit> <tag-name> -f -a
Trilarion
  • 9,318
  • 9
  • 55
  • 91
liuyang1
  • 1,415
  • 1
  • 15
  • 22
  • YES! You're right. Thanks for pointing that. After re-write the tag annotated a few times, I was check my tag with `git show ` and I see all the previous editions. – Manoel Vilela Jul 29 '17 at 18:11
  • The problem is: if I need update some tag which isn't `HEAD`, passing the extra ``, the tag opened is empty. I expected the old tag to just edit. Is there way? – Manoel Vilela Jul 29 '17 at 18:32
  • Please note that Andy's solution has been updated since you answered. Perhaps it would be nice to start your answer with a message saying that it has been fixed? Also could it be that your command `git tag -f -a` has and reversed? It looks this way when comparing with other answers and the docs, but I'm no expert. – Jacob Akkerboom Jul 26 '19 at 14:07
10

we would like to make v1.x messages look like the v2.0 message

With Git 2.17 (Q2 2018), there will be an alternative to creating a new tag with git tag <tag name> <tag name> -f -m "<new message>", since "git tag" learned an explicit "--edit" option that allows the message given via "-m" and "-F" to be further edited.

See commit 9eed6e4 (06 Feb 2018) by Nicolas Morey-Chaisemartin (nmorey).
(Merged by Junio C Hamano -- gitster -- in commit 05d290e, 06 Mar 2018)

tag: add --edit option

Add a --edit option which allows modifying the messages provided by -m or -F, the same way git commit --edit does.

VonC
  • 1,042,979
  • 435
  • 3,649
  • 4,283
4

Using the answers above (especially Sungam's), this is my alias one-liner for .gitconfig. Replaces existing tag and preserves the commit date.

[alias]
    tm = "!sh -c 'f() { export GIT_COMMITTER_DATE=$(git log -1 --format=%ci $0); git tag -f -a $0 $0^{}; }; f '"

Improvements?

Trilarion
  • 9,318
  • 9
  • 55
  • 91
h0tw1r3
  • 5,957
  • 1
  • 26
  • 33
  • 1
    Also keeps the author: `tag-amend = "!sh -c 'f() { name=$(git log -1 --format=%an $0); email=$(git log -1 --format=%ae $0); date=$(git log -1 --format=%ci $0); GIT_AUTHOR_NAME=\"${name}\" GIT_COMMITTER _NAME=\"${name}\" GIT_AUTHOR_EMAIL=\"${email}\" GIT_COMMITTER_EMAIL=\"${email}\" GIT_AUTHOR_DATE=\"${date}\" GIT_COMMITTER_DATE=\"${date}\" git tag -f -a $0 $0^{}; }; f '"` – minterior Nov 25 '16 at 14:33
  • 2
    Just tried this. Instead of giving the replacement tag the author and date info from the tag itself, it uses the info from the commit the tag points to. This is not necessarily the same and, in fact, is not the same for our case most of the time. We have a multi-repo infrastructure and use annotated tags in a 'core' repo to record info about pushes that span multiple repos. So, in core, the commit being pointed at might not even be part of the real push. The info in the annotated tag should reflect the real pushes in the other repos. – tanager Dec 06 '18 at 20:09
  • @tanger See stackoverflow.com/a/63142048/3124256 for a way to get the tagger info instead. – DylanYoung Jul 31 '20 at 04:36
3

You will have to tag again, using the -f force flag.

git tag v1.0 -f -m "actual message"
manojlds
  • 259,347
  • 56
  • 440
  • 401
  • 4
    This solution supposes that the current git head is at version 1.0. This can mess things up if it isn't, as it changes the revision associated to version 1.0. Andy's solution avoids this pitfall. – Eric O Lebigot May 29 '12 at 09:28
1

Here's a set of aliases that should do it for you based on the existing answers (especially Sungam's) here:

# Edit an existing tag, preserving the date and tagger
tag-amend = "!f() { : git tag ;\
    eval \"`git x-tag-environment-string`\";\
    git tag -a -f --edit -m \"$(git x-tag-message \"$1\")\" \"$1\" \"$1^{}\" \"${@:2}\";\
}; f"

# Rewrite an existing tag, preserving the date and tagger (accepts -m and -F)
tag-rewrite = "!f() { : git tag ;\
    eval \"`git x-tag-environment-string`\";\
    git tag -a -f \"$1\" \"$1^{}\" \"${@:2}\";\
}; f"

# Helpers to Extract the Tag Data
x-tag-data = tag -l --format
x-tag-message = x-tag-data '%(contents)'
x-tagger-name = x-tag-data '%(taggername)'
x-tagger-email = x-tag-data '%(taggeremail)'
x-tag-date = x-tag-data '%(taggerdate:rfc2822)'
x-tag-environment-string = "!f() { echo '\
    export GIT_COMMITTER_DATE=${GIT_COMMITTER_DATE-`git x-tag-date \"$1\"`};\
    export GIT_COMMITTER_NAME=${GIT_COMMITTER_NAME-`git x-tagger-name \"$1\"`};\
    export GIT_COMMITTER_EMAIL=${GIT_COMMITTER_EMAIL-`git x-tagger-email \"$1\"`};\
';}; f"

These aliases accept a single tag name and any other flags to git tag and can be modified to support name changes pretty easily as well.

Usage:

# opens editor to edit existing message
git tag-amend <tag name>

# add a new paragraph to the existing message
git tag-amend <tag name> -m "<new paragraph>"

# replace the message with a new one
git tag-rewrite <tag name> -m "<new message>"

Support for light-weight tags

Use creatordate, creatorname, and creatoremail instead of the tagger... variants. The creator... shortcuts will use tagger... if it exists and fallback to committer....

Trilarion
  • 9,318
  • 9
  • 55
  • 91
DylanYoung
  • 1,835
  • 22
  • 27
  • Beware of possible quoting issues. I'll be posting an edit that makes this more robust soon, but basically it comes down to using `for-each-ref --shell` rather than `tag -l` and letting git take care of the quoting. – DylanYoung Aug 04 '20 at 18:33
-1

If you are using a GUI like smartgit just

  1. create the same tag again at the same position with the new message
  2. choose "overwrite existing tag"
  3. force-push the tag to the upstream repository
rubo77
  • 15,234
  • 23
  • 111
  • 195