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

时间:2016-04-29 20:30:35

标签: git git-tag

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

[3, 1]

这有效,改名,但我也想修改相应的消息,所以我做了这个:

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

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

1)如何解决这个问题,以便保留新的名称/消息并推送标签而不会出现故障?

2)首先要做的更好的方法是什么?

谢谢

2 个答案:

答案 0 :(得分:1)

简短回答

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

讨论

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

这就是更改标记时必须使用-f(强制)标记的原因:

  

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

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

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

Digression:普通与带注释的标签

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

master这样的分支名称通常通过commit-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 对象,它表示一个标记(称为标记对象带注释的标记对象 ,取决于谁打电话以及他们想要多么明确)。包含消息的标记或您使用-a创建的标记会生成其中一个带注释的标记

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

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

$ 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。

(除此之外:尝试git cat-file -p原始ID,包括提交ID。您会看到包含tree ID的内容;请尝试使用git cat-file -p,可能会通过{{1} }或less因为它们可能相当长。另外,尝试在哈希值more上运行git rev-parse,同时缩短它们,例如3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243。所有这些都很有启发性。)

对象一旦被创建,就永远不会被改变 - 不是一点点,而不是一个比特。它们可以完全删除, 1 但不会更改。因此,要移动带注释的标记,您必须删除旧标记,然后创建一个新标记,它将获得一个新的不同的哈希ID。 git rev-parse 3ad15标志使得git只需一步即可完成。

回到原来的问题

由于标签不是假设要移动,Git(现在 2 )会检查并确保它们不会移动。要移动现有的轻量级或带注释的标记,您必须使用--force--force。对于轻量级标记,这至少有效 - 删除标记(但不是提交),然后将新标记附加到新对象。对于带注释的标记,这将删除轻量级标记和基础带注释的标记对象,然后创建新的基础带注释的标记对象,并将新的轻量级标记附加到新的带注释的标记对象。

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

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

1 删除对象实际上相当复杂。 Git是围绕添加对象构建的,而不是删除它们。 垃圾收集器-f - 内部由一系列独立的清道夫阶段组成,所有这些都可以手动运行 - 最终真正删除了东西。

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

开头
  

“git push $ there tag v1.2.3”用于替换标签v1.2.3   已存在于存储库$中,如果重写的标记   您正在提交作为提交后代的提交   老标签v1.2.3指向。发现这很容易出错   并从此版本开始,任何更新现有的尝试   refs / tags / hierarchy下的ref将失败,没有“--force”。

答案 1 :(得分:0)

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

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

现在将标签推送到遥控器:

git push --follow-tags