今天,我在查看一个项目的日志,发现我在前段时间发现了一个标签名。有什么方法可以重命名标记吗?谷歌没有发现任何有用的东西。
我意识到我可以检查标记的版本并制作一个新的标记,我甚至尝试过。但这似乎创建了一个不太正确的标记对象。例如,
git tag -l
列出了它相对于所有其他标记的顺序。我不知道这是否有意义,但这让我相信新的标记对象并不是我想要的。我可以接受这一点,因为我真的只关心标记名是否与文档匹配,但我宁愿“正确”地做,假设有正确的方法可以做到这一点。
今天,我在查看一个项目的日志,发现我在前段时间发现了一个标签名。有什么方法可以重命名标记吗?谷歌没有发现任何有用的东西。
我意识到我可以检查标记的版本并制作一个新的标记,我甚至尝试过。但这似乎创建了一个不太正确的标记对象。例如,
git tag -l
列出了它相对于所有其他标记的顺序。我不知道这是否有意义,但这让我相信新的标记对象并不是我想要的。我可以接受这一点,因为我真的只关心标记名是否与文档匹配,但我宁愿“正确”地做,假设有正确的方法可以做到这一点。
当前回答
除其他答案外:
首先,您需要构建旧标记名的别名,指向原始提交:
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标签更改对包的消费者的影响!
其他回答
对于冒险者,只需一个命令即可完成:
mv .git/refs/tags/OLD .git/refs/tags/NEW
通过使用现有标记中的标记信息,可以创建一个重复的带注释标记,包括所有标记信息,如标记器、消息和标记日期。
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
这将推送新标记,并删除旧标记。
作为对其他答案的补充,我添加了一个别名,用更熟悉的*nixMove命令感觉一步完成所有操作。参数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方式”是:
理智的事情。承认你搞砸了,换个名字。其他人已经看到了一个标签名称,如果你保持相同的名称,你可能会遇到这样的情况:两个人都有“版本X”,但实际上他们有不同的“X”。所以只要叫它“X.1”就行了。
或者,
疯狂的事情。你真的想把新版本称为“X”,尽管其他人已经看到了旧版本。所以只需再次使用git标记-f,就像您还没有发布旧的一样。
这太疯狂了,因为:
Git不会(也不应该)在用户背后更改标签。因此,如果有人已经得到了旧标签,那么在你的树上做一个git拉动不应该只是让他们覆盖旧标签。如果有人从你那里获得了发布标签,你不能通过更新自己的标签来更改他们的标签。这是一个很大的安全问题,因为人们必须能够信任自己的标签名。如果你真的想做这件疯狂的事,你需要坦白,告诉别人你搞砸了。
手册页的所有礼貌。
您还可以在不签出的情况下重命名远程标记,方法是在单个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