975

I created a tag on the master branch called v0.1 like this:

git tag -a v0.1

But then I realized there were still some changes I needed to merge into master for release 0.1, so I did that. But now my v0.1 tag is stuck on (to invoke the post-it note analogy) the wrong commit. I want it to be stuck on the most recent commit on master, but instead it is stuck on the second most recent commit on master.

How can I move it to the most recent commit on master?

Stevoisiak
  • 16,510
  • 19
  • 94
  • 173
eedeep
  • 10,313
  • 3
  • 16
  • 14

10 Answers10

1333

Use the -f option to git tag:

-f
--force

    Replace an existing tag with the given name (instead of failing)

You probably want to use -f in conjunction with -a to force-create an annotated tag instead of a non-annotated one.

Example

  1. Delete the tag on any remote before you push

    git push origin :refs/tags/<tagname>
    
  2. Replace the tag to reference the most recent commit

    git tag -fa <tagname>
    
  3. Push the tag to the remote origin

    git push origin master --tags
    
Greg Hewgill
  • 828,234
  • 170
  • 1,097
  • 1,237
  • 16
    This only works if you haven't pushed the code off your machine. If you have, the best answer is 'there's plenty of numbers in the world' as it's probably not worth the hassle. – Chris Huang-Leaver Feb 25 '13 at 13:27
  • 52
    If you had already pushed your tag you can still update the remote tag with a forced push `git push -f origin ` – rc_luke Oct 16 '13 at 21:33
  • 3
    You don’t have to use forced push, if the tag reference can be fast forwarded to the new place. – GergelyPolonkai Nov 25 '14 at 11:12
  • 14
    What is not mentioned here and in the docs is, that this indeed does move the tag message, if no new message is given. – Twonky Jan 13 '15 at 16:06
  • 13
    Note that in #3 `git push origin master --tags` is going to push tags, _and the `master` branch if you have committed any changes to it locally_. Just use `git push origin --tags` if all you want to do is push tags. – c32hedge Dec 07 '16 at 20:26
  • `git tag -fa ` worked perfectly, it moved the tag and opened up the existing message in my text editor, just had to exit to keep it. You can then push the tag with `git push -f` to force it to update on the server. Looking at the remote tags on github with `git ls-remote` I do see duplicates of all the tags with `^{}` appended, those actually point to the commit with the tag, It's something with annotated tags because that is always there and the actual tag ref without those characters points to an object with the tag message and commit information, not the commit hash. – Jason Goemaat Jan 18 '17 at 01:28
  • @eedeep it seems those `^{}` are always there for annotated tags, they actually reference the commit hash, where the ref without those is to the annotation. Try `git show ` on both those hashes, the straight tag name hash has the tag information (tagger, date, comment) above the commit information. – Jason Goemaat Jan 18 '17 at 01:33
  • 1
    @BenHocking actually you could simplified the statement `git push origin :refs/tags/` to `git push origin :` – Dwza Apr 19 '18 at 12:44
  • 1
    What does the 1st step do? How is `git push origin :refs/tags/` deleting anything? Doesn't it just push the tag to the origin? – Dan M. Jan 24 '19 at 13:43
  • 1
    @DanM.: The important part is that there is nothing before the colon. See [How to delete a remote tag?](https://stackoverflow.com/questions/5480258/how-to-delete-a-remote-tag) for more details. – Greg Hewgill Jan 24 '19 at 17:35
  • @GregHewgill ok, thanks. I didn't quite get why extra deletion step might be necessary. Why simply force pushing is not enough? – Dan M. Jan 24 '19 at 17:39
  • @DanM.: I'm not quite sure, I didn't write that part of the answer (it was added by somebody else, incorporating information from some of the previous comments above). – Greg Hewgill Jan 24 '19 at 17:43
  • Good to know: on GitLab users with Developer role have permission to add Tags, but not remove them — you need to be Owner or Maintainer. – Pavel Alexeev Feb 08 '19 at 16:08
  • Is `master` required in `git push origin master --tags`? I just use `git push origin --tags`. – Ian Jamieson May 29 '19 at 23:32
  • 1
    What happens to the repositories of those who have already pulled those tags? Do they get duplicate tags or git handles the initial tag removal automatically? – ZenJ May 13 '20 at 21:16
287

More precisely, you have to force the addition of the tag, then push with option --tags and -f:

git tag -f -a <tagname>
git push -f --tags
Daniel
  • 3,195
  • 1
  • 11
  • 11
224

To sum up if your remote is called origin and you're working on master branch:

git tag -d <tagname>
git push origin :refs/tags/<tagname>
git tag <tagname> <commitId>
git push origin <tagname>
  • Line 1 removes the tag in local env.
  • Line 2 removes the tag in remote env.
  • Line 3 adds the tag to different commit
  • Line 4 pushes the change to the remote

You can also exchange line 4 to git push origin --tags to push all the changes with tags from your local changes.

Basing on @stuart-golodetz, @greg-hewgill, @eedeep, @ben-hocking answers, comments below their answers and NateS comments below my answer.

Nat
  • 9,591
  • 5
  • 52
  • 99
94

Delete it with git tag -d <tagname> and then recreate it on the correct commit.

Stuart Golodetz
  • 19,132
  • 3
  • 45
  • 79
77

I try to avoid a few things when using Git.

  1. Using knowledge of the internals, e.g. refs/tags. I try to use solely the documented Git commands and avoid using things which require knowledge of the internal contents of the .git directory. (That is to say, I treat Git as a Git user and not a Git developer.)

  2. The use of force when not required.

  3. Overdoing things. (Pushing a branch and/or lots of tags, to get one tag where I want it.)

So here is my non-violent solution for changing a tag, both locally and remotely, without knowledge of the Git internals.

I use it when a software fix ultimately has a problem and needs to be updated/re-released.

git tag -d fix123                # delete the old local tag
git push github :fix123          # delete the old remote tag (use for each affected remote)
git tag fix123 790a621265        # create a new local tag
git push github fix123           # push new tag to remote    (use for each affected remote)

github is a sample remote name, fix123 is a sample tag name, and 790a621265 a sample commit.

Ivan
  • 3,463
  • 28
  • 26
  • I think OPs tag was annotaged. The third line could be like this instead `git tag -a fix123 790a621265 # create a new local, annotaged tag` – Johan Bergens Feb 23 '21 at 13:48
31

I'll leave here just another form of this command that suited my needs.
There was a tag v0.0.1.2 that I wanted to move.

$ git tag -f v0.0.1.2 63eff6a

Updated tag 'v0.0.1.2' (was 8078562)

And then:

$ git push --tags --force
Nakilon
  • 32,203
  • 13
  • 95
  • 132
12

Alias to move one tag to a different commit.

In your sample, to move commit with hash e2ea1639 do: git tagm v0.1 e2ea1639.

For pushed tags, use git tagmp v0.1 e2ea1639.

Both alias keeps you original date and message. If you use git tag -d you lost your original message.

Save them on your .gitconfig file

# Return date of tag. (To use in another alias)
tag-date = "!git show $1 | awk '{ if ($1 == \"Date:\") { print substr($0, index($0,$3)) }}' | tail -2 | head -1 #"

# Show tag message
tag-message = "!git show $1 | awk -v capture=0 '{ if(capture) message=message\"\\n\"$0}; BEGIN {message=\"\"}; { if ($1 == \"Date:\" && length(message)==0 ) {capture=1}; if ($1 == \"commit\" ) {capture=0}  }; END { print message }' | sed '$ d' | cat -s #"

### Move tag. Use: git tagm <tagname> <newcommit> 
tagm = "!GIT_TAG_MESSAGE=$(git tag-message $1) && GIT_COMMITTER_DATE=$(git tag-date $1) && git tag-message $1 && git tag -d $1 && git tag -a $1 $2 -m \"$GIT_TAG_MESSAGE\" #"

### Move pushed tag. Use: git tagmp <tagname> <newcommit> 
tagmp = "!git tagm $1 $2 && git push --delete origin $1 && git push origin $1 #"
10

One other way:

Move tag in remote repo.(Replace HEAD with any other if needed.)

$ git push --force origin HEAD:refs/tags/v0.0.1.2

Fetch changes back.

$ git fetch --tags
1

If you want to move an annotated tag, changing only the targeted commit but preserving the annotation message and other metadata use:

moveTag() {
  local tagName=$1
  # Support passing branch/tag names (not just full commit hashes)
  local newTarget=$(git rev-parse $2^{commit})

  git cat-file -p refs/tags/$tagName | 
    sed "1 s/^object .*$/object $newTarget/g" | 
    git hash-object -w --stdin -t tag | 
    xargs -I {} git update-ref refs/tags/$tagName {}
}

usage: moveTag <tag-to-move> <target>

The above function was developed by referencing teerapap/git-move-annotated-tag.sh.

vossad01
  • 10,262
  • 7
  • 50
  • 102
  • 1
    It seems this is no longer needed: `git tag -f -a my_tag` already preserves the message of a previous message (with git version 2.11.0). – Matthijs Kooijman Jul 19 '18 at 15:41
1

If you use github and want change commit for release (for example you find that don't commit smth after creating release).You can use

git push origin :refs/tags/<tagname>

After this command github delete your tag and your release will become a draft. It means you can recreate release and select commit. Your files and your message will be saved.