今天,我在查看一个项目的日志,发现我在前段时间发现了一个标签名。有什么方法可以重命名标记吗?谷歌没有发现任何有用的东西。
我意识到我可以检查标记的版本并制作一个新的标记,我甚至尝试过。但这似乎创建了一个不太正确的标记对象。例如,
git tag -l
列出了它相对于所有其他标记的顺序。我不知道这是否有意义,但这让我相信新的标记对象并不是我想要的。我可以接受这一点,因为我真的只关心标记名是否与文档匹配,但我宁愿“正确”地做,假设有正确的方法可以做到这一点。
今天,我在查看一个项目的日志,发现我在前段时间发现了一个标签名。有什么方法可以重命名标记吗?谷歌没有发现任何有用的东西。
我意识到我可以检查标记的版本并制作一个新的标记,我甚至尝试过。但这似乎创建了一个不太正确的标记对象。例如,
git tag -l
列出了它相对于所有其他标记的顺序。我不知道这是否有意义,但这让我相信新的标记对象并不是我想要的。我可以接受这一点,因为我真的只关心标记名是否与文档匹配,但我宁愿“正确”地做,假设有正确的方法可以做到这一点。
当前回答
无论如何处理推送标记和重命名已推送的标记,如果要重命名的标记是带注释的标记,您可以首先通过以下单行命令行复制它:
git tag -a -m "`git cat-file -p old_tag | tail -n +6`" new_tag old_tag^{}
然后,您只需删除旧标记:
git tag -d old_tag
由于以下两个答案,我找到了此命令行:
https://stackoverflow.com/a/26132640/7009806(第二条评论)https://stackoverflow.com/a/49286861/7009806
编辑:使用自动同步标记设置fetch时遇到问题。prunTags=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 --tags
git tag -d old_tag
git push origin :refs/tags/old_tag
其他回答
您还可以在不签出的情况下重命名远程标记,方法是在单个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
通过使用现有标记中的标记信息,可以创建一个重复的带注释标记,包括所有标记信息,如标记器、消息和标记日期。
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
这将推送新标记,并删除旧标记。
对于冒险者,只需一个命令即可完成:
mv .git/refs/tags/OLD .git/refs/tags/NEW
如果它已经发布,你就不能删除它(不冒被涂上柏油和羽毛的风险)。“Git方式”是:
理智的事情。承认你搞砸了,换个名字。其他人已经看到了一个标签名称,如果你保持相同的名称,你可能会遇到这样的情况:两个人都有“版本X”,但实际上他们有不同的“X”。所以只要叫它“X.1”就行了。
或者,
疯狂的事情。你真的想把新版本称为“X”,尽管其他人已经看到了旧版本。所以只需再次使用git标记-f,就像您还没有发布旧的一样。
这太疯狂了,因为:
Git不会(也不应该)在用户背后更改标签。因此,如果有人已经得到了旧标签,那么在你的树上做一个git拉动不应该只是让他们覆盖旧标签。如果有人从你那里获得了发布标签,你不能通过更新自己的标签来更改他们的标签。这是一个很大的安全问题,因为人们必须能够信任自己的标签名。如果你真的想做这件疯狂的事,你需要坦白,告诉别人你搞砸了。
手册页的所有礼貌。
无论如何处理推送标记和重命名已推送的标记,如果要重命名的标记是带注释的标记,您可以首先通过以下单行命令行复制它:
git tag -a -m "`git cat-file -p old_tag | tail -n +6`" new_tag old_tag^{}
然后,您只需删除旧标记:
git tag -d old_tag
由于以下两个答案,我找到了此命令行:
https://stackoverflow.com/a/26132640/7009806(第二条评论)https://stackoverflow.com/a/49286861/7009806
编辑:使用自动同步标记设置fetch时遇到问题。prunTags=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 --tags
git tag -d old_tag
git push origin :refs/tags/old_tag