修改后的 git 个标签名称和消息现在被拒绝,因为它们已经存在

Modified git tag names and messages now being rejected because they already exist

我来晚了一个项目,想修改旧的 git 标签名称和消息,所以我根据 Whosebug 上的几个答案进行了更新:

git tag newname oldname
git tag -d oldname
git push origin :refs/tags/oldname
git push --tags

成功了,改了名字,但我也想修改相应的消息,所以我这样做了:

git tag newname newname -f -m "new message for renamed tag"

它似乎有效,当我通过以下方式查看时:

git tag -n1

但是,当我现在尝试 git push --tags 时,出现以下错误:

error: failed to push some refs to 'https://blah@blah/blah/repo-name.git'
hint: Updates were rejected because the tag already exists in the remote.

我不知道现在该怎么办...

1) 我该如何解决这个问题,以便保留新的 names/messages 并推送标签而不会失败?

2) 首先执行此操作的更好方法是什么?

谢谢

最好的方法是不删除标签,而只是将标签更新为新的提交。

# create a new annotated tag and force it to replace the old one
git tag -af <tagname> -m <message>

现在将标签推送到远程:

git push --follow-tags

简答

删除遥控器上的标签,然后再次按下以重新创建它(或使用 --force 一步完成)。

讨论

标签背后的想法是它是一个永不改变的标签(与分支名称的想法相反,是的标签 改变)。因此,Git 通常不愿意使用现有标签并将其指向新的不同位置。

这就是为什么在更改标签时必须使用 -f(强制)标志的原因:

git tag newname newname -f -m "new message for renamed tag"

如果没有 -f,Git 会抱怨标签已经存在,什么都不做。

由于您使用的是 -m,因此您将获得 带注释的标签 。这很重要,否则您不会以任何方式更改标签,也不会有任何问题。

题外话:普通标签与注释标签

我在上面提到,分支(更具体地说,分支名称)和标签都是标签。

master 这样的分支名称通常通过提交 ID 直接指向某个提交。因此 master 是您可以阅读并转换为提交 ID 的内容; git rev-parse 命令正是这样做的:

$ git rev-parse master
3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243

但是,标签名称可以直接指向提交:

$ git tag temp-tag master
$ git rev-parse temp-tag
3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243
$ git tag -d temp-tag

这称为 轻量级标签

或者,它可以指向一个新创建的 git object 代表一个标签(称为 tag object 带注释的标记对象 ,具体取决于调用者是谁以及他们想要的明确程度)。包含消息或您使用 -a 创建的标签构成了这些 带注释的标签之一 :

$ git tag -m foo temp-tag master
$ git rev-parse temp-tag
04565b0274c13ac49a70b8e34cdb9c912e02f0ab

请注意,此带注释的标签与 master 的 ID 不同。它实际上由一对项目组成:一个轻量级样式的标签,指向存储库中带注释的标签对象;和带注释的标记对象,指向提交:

$ git cat-file -p temp-tag | sed 's/@/ /'
object 3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243
type commit
tag temp-tag
tagger Chris Torek <chris.torek gmail.com> 1461965004 -0700

foo
$ git tag -d temp-tag

注意 object 行,其中包含提交的 ID。

(另外:在原始 ID 上尝试 git cat-file -p,包括提交 ID。您会看到带有 tree ID 的东西;在那些上尝试 git cat-file -p,也许通过 lessmore 因为它们可能相当长。另外,尝试 运行ning git rev-parse 哈希值,3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243 的东西,同时缩短它们,例如 git rev-parse 3ad15。这些都是很有启发性的。)

对象一旦创建,就永远无法更改——一点也不能,一点也不能。它们可以完全删除,1但不能更改。因此,要移动带注释的标签,您必须删除旧标签,然后创建一个新标签,它会获得一个新的、不同的哈希 ID。 --force 标志使 git 一步完成。

回到原来的问题

由于标签不应该移动,Git(现在2)检查并确保它们不移动.要移动现有的轻量级或带注释的标签,您必须使用 --force-f。在轻量级标签的情况下,这(至少实际上)删除了标签(但不是提交),然后将同名的新标签附加到新对象。对于带注释的标记,这将同时删除轻量级标记和底层带注释的标记对象,然后创建一个新的底层带注释的标记对象并将新的轻量级标记附加到新的带注释的标记对象。

当然,您可以将其分成两个单独的步骤,这样会更加明显。

同样的规则适用于 git push。由于 git push--force / -f,您可以将两个步骤隐藏(并优化)为一个,但它实际上是一个删除和重新创建。


1删除对象实际上相当复杂。 Git 是围绕添加对象而不是删除对象构建的。 垃圾收集器git gc——内部由一堆独立的清除阶段组成,所有这些你都可以运行手动——才是真正删除东西的东西,最终.

2在Git 1.8.2之前的版本中,git push将分支规则应用于标签,因此如果操作可以推送标签快进了。 1.8.2 发行说明开头为:

"git push $there tag v1.2.3" used to allow replacing a tag v1.2.3 that already exists in the repository $there, if the rewritten tag you are pushing points at a commit that is a descendant of a commit that the old tag v1.2.3 points at. This was found to be error prone and starting with this release, any attempt to update an existing ref under refs/tags/ hierarchy will fail, without "--force".