ChatGPT解决这个技术问题 Extra ChatGPT

How can I move a tag on a git branch to a different commit?

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?


O
Oleg Valter is with Ukraine

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

Delete the tag on any remote before you push git push origin :refs/tags/ Replace the tag to reference the most recent commit git tag -fa Push the tag to the remote origin git push origin master --tags


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.
If you had already pushed your tag you can still update the remote tag with a forced push git push -f origin <tagname>
You don’t have to use forced push, if the tag reference can be fast forwarded to the new place.
What is not mentioned here and in the docs is, that this indeed does move the tag message, if no new message is given.
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.
D
Daniel

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

This answer completed the accepted answer by including -f to push the tags.
G
Gabriel Staples

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

git tag -d <tagname>                  # delete the old tag locally
git push origin :refs/tags/<tagname>  # delete the old tag remotely
git tag <tagname> <commitId>          # make a new tag locally
git push origin <tagname>             # push the new local tag to the remote 

Description:

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 change line 4 to git push origin --tags to push all of your local tag changes/updates to the remote repo.

The above answer is based on content in the question by @eedeep, as well as answers by Stuart Golodetz, Greg Hewgill, and @ben-hocking, and comments below their answers, and @NateS's original comments below my answer.


Line 4 works for pushing an explicit single updated tag to the remote repository, in case you don't want to update all tags, as I didn't.
S
Stuart Golodetz

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


@eedeep: I think Greg's response is actually better here to be fair.
Keep it simple. Delete it, do what you did before again.
This should be the accepted answer, for its simplicity. Also does not use -f force excessively.
@chinnychinchin. This is one place where use of force really is not excessive. It's no different than saying "accept mine" during a merge.
I
Ivan

I try to avoid a few things when using Git.

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.) The use of force when not required. 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.


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
I hate that this is the best answer. I wonder why it has to be so complicated? Tags are well suited for marking which commit is for production, so I need to change which commit it's tacked to quite often. Using a branch which you just rebase all the time is currently easier, but it's redundant when the environment is of no concern to your repository contents (something worth striving for).
N
Nakilon

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

J
Juan Antonio Tubío

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 #"

А
Алексей Югов

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

This is more "transactional" than the other answers.
К
Константин Золин

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.


C
Community

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

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


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).