重新创建git标记后,“标记已存在于远程”错误中

时间:2013-10-10 14:29:37

标签: git repository git-tag

运行以下步骤后出现以下错误:

To git@provider.com:username/repo-name.git
 ! [rejected]        dev -> dev (already exists)
error: failed to push some refs to 'git@provider.com:username/repo-name.git'
hint: Updates were rejected because the tag already exists in the remote.
  1. 创建了存储库
  2. 在本地计算机上克隆了回购。
  3. 修改了README文件,提交了更改并推送了提交。
  4. 已创建代码devgit tag dev
  5. 推送标签:git push --tags
  6. 修改了README文件,提交了更改并推送了提交。
  7. 已删除标记dev,再次创建标记并推送标记:

    git tag -d dev
    git tag dev
    git push --tags
    
  8. 为什么会这样?

    我在Mac上。我使用Linux(Ubuntu)的朋友没有这个问题。我知道我可以使用git push --tags -f强制标记更新,但这很危险(例如,只在标记中重写错误提交,而不是在分支中)。

8 个答案:

答案 0 :(得分:157)

编辑,2016年11月24日:这个答案显然很受欢迎,所以我在这里添加一个注释。如果您替换中央服务器上的标记,那么拥有标记的任何人 - 已经拥有该标记的该中央服务器存储库的任何克隆 - 都可以保留它的旧标签。因此,虽然这告诉你如何做到这一点,但要确保你想要这样做。您需要让已经拥有“错误”标记的所有人删除他们的“错误标记”并将其替换为新的“正确标记”。

在Git 2.10 / 2.11中进行测试表明,保留旧标记是运行git fetch的客户端的默认行为,而更新是运行git fetch --tags的客户端的默认行为。

(原始答案如下。)


当您要求推送标签时,git push --tags将(以及所需的任何提交和其他对象以及推送设置中的任何其他参考更新)发送到远程的new-sha1 refs/tags/name形式的更新请求。 (好吧,它发送的很多:每个标签都有一个。)

远程修改更新请求以添加 old-sha1 (或者再次为每个标记添加一个),然后将其传递到预接收和/或更新挂钩(以较为者为准)挂钩存在于遥控器上)。这些钩子可以决定是允许还是拒绝标签创建/删除/更新。

如果正在创建标记,则 old-sha1 值为全零“空”SHA-1。如果要删除标记, new-sha1 是空SHA-1。否则,两个SHA-1值都是实数,有效值。

即使没有挂钩,也有一种“内置挂钩”也会运行:遥控器会拒绝移动标签,除非你使用“强制”标志(尽管“内置挂钩”始终是单击“添加”和“删除”都可以。您看到的拒绝消息来自此内置挂钩。 (顺便说一下,这个相同的内置钩子也拒绝不快进的分支更新。) 1

但是 - 这是理解发生了什么的关键之一 - git push步骤不知道遥控器现在是否具有该标签,如果是,那么它具有什么SHA-1值。它只是说“这是我完整的标签列表,以及它们的SHA-1值”。远程比较值,如果有添加和/或更改,则在那些上运行挂钩。 (对于相同的标签,它什么都不做。对于你没有的标签,它也什么也没做!)

如果您在本地删除了代码,那么push,您的推送根本就不会传输代码。遥控器假定不应该进行任何改变。

如果你在本地删除标签,然后创建它指向一个新的地方,然后push,你的推送传输标签,遥控器将其视为标签更改并拒绝更改,除非它是一个力推动。

因此,您有两种选择:

  • 执行强制推送,或
  • 删除遥控器上的标签。

后者 可能通过git push 2 ,即使在本地删除标记并且push没有效果。假设遥控器的名称为origin,您要删除的标签为dev

git push origin :refs/tags/dev

这要求遥控器删除标签。您本地存储库中是否存在标记dev无关紧要;这种push:remoteref作为refspec,是纯删除推送。

遥控器可能允许或不允许删除标签(取决于添加的任何额外挂钩)。如果它允许删除,那么标记将会消失,而第二个git push --tags,当您有一个指向某个提交或带注释的标记repo对象的本地dev标记时,会发送新的{{1} } 标签。在遥控器上,dev现在将是一个新创建的标签,因此遥控器可能允许推送(这又取决于添加的任何额外挂钩)。

强制推动更简单。如果您想确保不更新任何其他而不是标记,请告诉dev仅推送一个refspec:

git push

(注意:如果您明确只推送一个代码参考规范,则不需要git push --force origin refs/tags/dev:refs/tags/dev


1 当然,这个内置钩子的原因是为了帮助强制执行同一个远程仓库的其他用户所期望的行为:那些分支不是重绕,标签不动。如果你强制推送,你应该让其他用户知道你这样做,以便他们可以纠正它。请注意,Git 1.8.2新强制执行“标签根本不移动”;以前的版本将允许标记在提交图中“前进”,就像分支名称一样。请参阅git 1.8.2 release notes

2 如果您可以登录遥控器,这是微不足道的。只需转到那里的Git存储库并运行--tags。请注意,无论是删除遥控器上的标签,还是使用git tag -d dev删除标签,都会有一段时间访问遥控器的任何人都会发现git push标签丢失。 (他们将继续拥有他们自己的旧标签,如果他们已经拥有它,他们甚至可能会推送他们的旧标签,然后再推新标签。)

答案 1 :(得分:48)

在Mac SourceTree中,只取消选中推送所有标记复选框:

enter image description here

答案 2 :(得分:14)

如果您使用 SourceTree 非常简单

enter image description here 基本上你只需要删除并重新添加冲突标记:

  1. 转到标签存储库 - > 标记 - > 删除标记
  2. 选择有冲突的标记名称
  3. 检查从所有遥控器中删除标签
  4. 删除
  5. 为正确的提交创建与新名称相同的新标记
  6. 确保在将更改推送到远程
  7. 时检查推送所有代码

答案 3 :(得分:10)

如果您想更新标签,请说出1.0.0

  1. git checkout 1.0.0
  2. 进行更改
  3. git ci -am 'modify some content'
  4. git tag -f 1.0.0
  5. 删除github上的远程标记:git push origin --delete 1.0.0
  6. git push origin 1.0.0
  7. 完成

答案 4 :(得分:8)

我似乎已经迟到了这个问题和/或它已经得到了解答,但是,可以做的是: (在我的情况下,我在本地只有一个标签所以..我删除了旧标签并将其重新标记为:

git tag -d v1.0
git tag -a v1.0 -m "My commit message"

然后:

git push --tags -f

这将更新遥控器上的所有标签。

可能很危险!使用风险自负。

答案 5 :(得分:4)

您获得拒绝的原因是您的代码与远程版本失去同步。这与分支的行为相同。

通过git pull --rebase <repo_url> +refs/tags/<TAG>与远程标记同步,同步后,您需要管理冲突。 如果您安装了diftool(例如meld)git mergetool meld,请使用它来同步远程并保留您的更改。

您使用 - rebase 标记进行拉动的原因是您希望将您的工作置于远程工作之上,以避免其他冲突。

另外,我不明白为什么你会删除dev标签并重新创建它?标签用于指定软件版本或里程碑。 git标记示例v0.1devv0.0.1alphav2.3-cr(cr - 候选版本)等等。

您可以解决此问题的另一种方法是发出git reflog并转到您在遥控器上推送dev标签的那一刻。复制提交ID git reset --mixed <commmit_id_from_reflog>,这样您就知道您的标签在推送时与遥控器同步,并且不会产生任何冲突。

答案 6 :(得分:2)

在Windows SourceTree中,取消选中Push all tags to remotes

enter image description here

答案 7 :(得分:0)

一些好的答案。特别是the one by @torek。我以为我会添加此变通方法,并为匆忙中的人员提供一些解释。

总而言之,发生的事情是,当您在本地移动标签时,它会将标签从非null提交值更改为另一个值。但是,由于git(作为默认行为)不允许更改非null的远程标签,因此您不能推送更改。

解决方法是删除标签(并勾选删除所有遥控器)。然后创建相同的标签并推送。