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

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

git tag -l

列出了它相对于所有其他标记的顺序。我不知道这是否有意义,但这让我相信新的标记对象并不是我想要的。我可以接受这一点,因为我真的只关心标记名是否与文档匹配,但我宁愿“正确”地做,假设有正确的方法可以做到这一点。


当前回答

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

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_TAG和NEW_TAG值以匹配新旧标记名。

此命令只处理未签名的标记,但将此解决方案扩展到已签名的标记应该是一件简单的事情。

Goal

要真正做到不可区分的重命名,带注释标记的所有元素在新标记中都应该相同。git标记文档指定带注释标记的部分。

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

回答动机

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

程序

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

步骤1:获取现有标签信息

首先,我们需要获取现有标记的信息。这可以通过对每个引用使用:

命令:

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: 88a6169
Tagger date: Mon Dec 14 12:44:52 2020 -0600
Tagger name: John Doe
Tagger email: <j.doe@example.com>
Tagged commit: cda5b4d
Tag name: old
Tag message: Initial tag

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

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

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

提交ID和提交消息可以直接传递给git标记。

可以使用git环境变量git_COMMITTER_name、git_COMMITTER_email、git_COMMITTER_date设置标记器信息(名称、电子邮件和日期)。本文中的日期用法在git标记的回溯标记文档中描述;另外两个是我通过实验发现的。

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: 580f817
Tagger date: Mon Dec 14 12:44:52 2020 -0600
Tagger name: John Doe
Tagger email: <j.doe@example.com>
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: <j.doe@example.com>
Tagged commit: cda5b4d
Tag name: old
Tag 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:将更改推送到远程存储库

假设您正在远程存储库中工作,现在可以使用gitpush推送更改:

git push origin new :old

这将推送新标记,并删除旧标记。

其他回答

您还可以在不签出的情况下重命名远程标记,方法是在单个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

对于冒险者,只需一个命令即可完成:

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

这个wiki页面有一个有趣的单行线,它提醒我们可以推送几个参考:

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

并要求其他克隆器执行git pull-prune标记

因此,我们的想法是:

<new tag>对于<old tag>:refs/tags/<old tag>:refs/tags/<new tag>引用的每个提交,删除<旧标签>::refs/tags/<旧标签>

请参见示例“更改git存储库中标记的命名约定?”。

如果它已经发布,你就不能删除它(不冒被涂上柏油和羽毛的风险)。“Git方式”是:

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

或者,

疯狂的事情。你真的想把新版本称为“X”,尽管其他人已经看到了旧版本。所以只需再次使用git标记-f,就像您还没有发布旧的一样。

这太疯狂了,因为:

Git不会(也不应该)在用户背后更改标签。因此,如果有人已经得到了旧标签,那么在你的树上做一个git拉动不应该只是让他们覆盖旧标签。如果有人从你那里获得了发布标签,你不能通过更新自己的标签来更改他们的标签。这是一个很大的安全问题,因为人们必须能够信任自己的标签名。如果你真的想做这件疯狂的事,你需要坦白,告诉别人你搞砸了。

手册页的所有礼貌。

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

关于“Git方式”和(in)理智的引用是不正确的,因为它是在谈论保留标记名,但使其引用不同的存储库状态。