如何重命名Git标签?

今天我在查看一个项目的日志,发现我前段时间用了一个胖手指的标签名称。有什么方法可以重命名标签吗?谷歌没有找到任何有用的东西。

我意识到我可以查看标记的版本并制作一个新标签,我甚至尝试过。但这似乎创建了一个不太正确的标签对象。首先,

git tag -l

我不知道这是否重要,但它让我相信新的标签对象不是我想要的。我可以接受,因为我真的只关心标签名称与留档匹配,但我宁愿做“正确”,假设有正确的方法做到这一点。

354134 次浏览

如果它已发布,您不能删除它(也就是说,在不冒被柏油和羽毛的风险的情况下)。'Git方式'是这样做:

理智的事情。承认你搞砸了,用不同的名字。其他人已经看到了一个标签名,如果你保持相同的名字,你可能会遇到两个人都有“版本X”的情况,但他们实际上有不同的“X”。所以就叫它“X.1”,然后就结束了。

或者,

疯狂的事情。即使其他人已经看过旧版本,你也真的想把新版本称为“X”。所以再次使用git-tag-f,就好像你还没有发布旧版本一样。

这很疯狂,因为:

Git不会(也不应该)在用户背后更改标签。因此,如果有人已经获得了旧标签,那么在您的树上执行git拉取不应该只是让他们覆盖旧标签。

如果有人从你那里得到了发布标签,你不能通过更新你自己的标签来改变他们的标签。这是一个很大的安全问题,因为人们必须能够信任他们的标签名称。如果你真的想做疯狂的事情,你需要坦白,告诉人们你搞砸了。

所有的礼貌手册页

最初的问题是如何重命名标签,这很容易:首先创建new作为OLD:git tag NEW OLD的别名,然后删除OLD:git tag -d OLD

关于“the Git way”和(in)sanity的引用是错误的,因为它谈论的是保留标签名称,但使其引用不同的存储库状态。

以下是我如何将标签old重命名为new

git tag new oldgit tag -d oldgit push origin new :old

ush命令中的冒号从远程存储库中删除标签。如果如果不这样做,Git将在您拉取时在您的机器上创建旧标签。最后,确保其他用户删除已删除的标签。请告诉他们(同事)运行以下命令:

git pull --prune --tags

注意如果您正在更改带注释的标签,您需要确保新的标记名称引用了底层提交,而不是旧的注释标记您要删除的对象。因此,使用git tag -a new old^{}而不是git tag new old(这是因为带注释的标签是对象,而轻量级标签不是,更多信息在这个答案)。

对于喜欢冒险的人来说,它可以在一个命令中完成:

mv .git/refs/tags/OLD .git/refs/tags/NEW

除了其他答案:

首先,您需要构建标记名称的别名,指向原始提交:

git tag new old^{}

然后你需要删除旧的当地

git tag -d old

然后删除您远程位置上的标签:

# 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

最后,您需要将新标签添加到远程位置。在您完成此操作之前,添加新标签不会

git push origin --tags

为每个远程位置迭代此操作。

要知道,Git Tag更改的含义给消费者的一个包!

本wiki页面有一个有趣的单行代码,它提醒我们可以推送几个裁判

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

并要求其他克隆人做git pull --prune --tags

所以我们的想法是推动:

  • <new-tag>对于<old-tag>refs/tags/<old-tag>:refs/tags/<new-tag>引用的每个提交,
  • 删除#0:refs/tags/<old-tag>

以“更改git存储库内标签的命名约定?”为例。

作为对其他答案的补充,我添加了一个别名来一步完成所有操作,使用更熟悉的*nix移动命令感觉。参数1是旧标签名称,参数2是新标签名称。

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

用法:

git renametag old new

您还可以重命名远程标签而无需检查它们,方法是在单个git push命令中将旧标签/分支复制到新名称并删除旧标签。

远程标签重命名/远程分支→标签转换:(注意::refs/tags/

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

远程分支重命名/远程标签→分支转换:(注意::refs/heads/

git push <remote_name> <old_branch_or_tag>:refs/heads/<new_branch> :<old_branch_or_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

不管推送标签和重命名已经推送的标签的问题如何,如果要重命名的标签是注释,您可以首先通过以下单行命令行复制它:

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

然后,您只需要删除旧标签:

git tag -d old_tag

由于以下两个答案,我找到了这个命令行:

编辑:
在使用标签自动同步设置fetch.pruneTags=true时遇到问题(如https://stackoverflow.com/a/49215190/7009806所述),我个人建议第一复制服务器上的新标签,然后删除旧标签。这样,删除旧标签时,新标签不会被随机删除,标签的同步希望删除新标签还没有在服务器上。所以,例如,我们得到:

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

按照一个或几个标签的3步方法。

步骤1:识别当前标签指向的提交/对象ID

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

第2步:从存储库中删除标签

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

第3步:创建一个新标记,指向与旧标记指向的相同的提交ID

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" db57b63b77a6bae3e725cbb9025d65fa1eabcdeexample output: Nothing or basically <No error>

一旦本地git准备好更改标签名称,这些更改就可以被推回到原点,让其他人接受这些更改:

command: git push origin :<old tag name> <new tag name>example: git push origin :v0.1.0-Demo v0.1.0-fullexample output: <deleted & new tags>

可以使用现有标记中的标记信息创建重复的注释标记-包括所有标记信息,例如标记器、消息和标记日期。

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

更新SOURCE_TAGNEW_TAG值以匹配您的旧标记名和新标记名。

此命令仅处理未签名标记,尽管将此解决方案扩展到签名标记应该很简单。

目标

要真正成为不可区分的重命名,带注释标记的所有元素在新标记中应该相同。#0留档指定带注释标记的部分。

标记对象(使用-a-s-u创建)称为“注释”标记;它们包含创建日期、标记器名称和电子邮件、标记消息和可选的GnuPG签名。

回答动机

据我所知,所有其他答案都有微妙的陷阱,或者没有完全复制标签的所有内容(例如他们使用新的标签日期,或者当前用户的信息作为标签器)。他们中的许多人喊出了改标警告,尽管不适用于这种情况(它是为了将标签名称移动到不同的提交,而不是重命名为不同命名的标签)。我做了一些挖掘,并且拼凑了一个我认为解决了这些问题的解决方案。

操作流程

示例中使用了名为old的注释标记,并将重命名为new

步骤一:获取标签信息

首先,我们需要获取现有标签的信息。这可以使用#0来实现:

命令:

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

输出:

Tag commit: 88a6169Tagger date: Mon Dec 14 12:44:52 2020 -0600Tagger name: John DoeTagger email: <j.doe@example.com>Tagged commit: cda5b4dTag name: oldTag message: Initial tag
Body line 1.Body line 2.Body line 3.

步骤2:在本地创建重复标签

可以使用步骤1中从现有标记收集的信息创建具有新名称的重复标记。

提交ID和提交消息可以直接传递给git tag

标记器信息(姓名、电子邮件和日期)可以使用git环境变量GIT_COMMITTER_NAMEGIT_COMMITTER_EMAILGIT_COMMITTER_DATE设置。在这种情况下使用的日期在git tag关于回溯标签留档中描述;另外两个我通过实验找到了。

GIT_COMMITTER_NAME="John Doe" GIT_COMMITTER_EMAIL="j.doe@example.com" \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."

两个标签的并排比较表明它们在所有重要的方面都是相同的。这里唯一不同的是标签本身的提交引用,这是预期的,因为它们是两个不同的标签。

命令:

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

输出:

Tag commit: 580f817Tagger date: Mon Dec 14 12:44:52 2020 -0600Tagger name: John DoeTagger email: <j.doe@example.com>Tagged commit: cda5b4dTag name: newTag message: Initial tag
Body line 1.Body line 2.Body line 3.
Tag commit: 30ddd25Tagger date: Mon Dec 14 12:44:52 2020 -0600Tagger name: John DoeTagger email: <j.doe@example.com>Tagged commit: cda5b4dTag name: oldTag message: Initial tag
Body line 1.Body line 2.Body line 3.

作为单个命令,包括检索当前标签数据:

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

步骤3:在本地删除现有标签

接下来,现有的标签应该在本地删除。如果您希望将旧标签与新标签一起保留(即复制标签而不是重命名标签),可以跳过此步骤。

git tag -d old

步骤4:将更改推送到远程存储库

假设您从远程存储库工作,现在可以使用#0推送更改:

git push origin new :old

这将推送new标记,并删除old标记。