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


142

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

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
    

为什么会这样呢?

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


1
提交不会在“标记中”或“在分支中”进行(尽管确实确实是后者)。实际上,标记和分支名称只是指向(一个,单个)提交。请参阅下面的答案。
torek 2013年

8
git pull --tags那时对我git push origin --tags
有用

Answers:


175

编辑,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如果正在创建标记,则该值为全零“ null” SHA-1。该new-sha1是空SHA-1,如果该标签被删除。否则,两个SHA-1值都是真实有效值。

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

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

如果您在本地删除标签,则push,您的推送不会转移标签。遥控器假定不应进行任何更改。

如果您在本地删除标签,然后创建指向新位置push的标签,则您的推将转移标签,并且遥控器将其视为标签更改,并拒绝更改,除非是强制推送。

因此,您有两个选择:

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

即使在本地删除标签并且ing无效,可以通过git push2来使用后者push。假设遥控器的名称为origin,而您要删除的标签为dev

git push origin :refs/tags/dev

这要求遥控器删除标签。dev本地存储库中标签的存在与否无关紧要;这种类型的push,有作为的Refspec,是一个纯删除推。:remoteref

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

推力更简单。如果你想确保不更新任何其他比标签,只是告诉git push推只有一个的Refspec:

git push --force origin refs/tags/dev:refs/tags/dev

(请注意:--tags如果您只推送一个标签ref-spec,则不需要)。


1当然,使用此内置钩子的原因是为了帮助强制执行同一远程回购的其他用户所期望的行为:分支不倒退,标签不移动。如果您强行推动,则应让其他用户知道您正在执行此操作,以便他们可以对其进行纠正。注意,“标签根本不会移动”是Git 1.8.2新增的;以前的版本将允许标记在提交图中“向前移动”,这与分支名称非常相似。请参阅git 1.8.2发行说明

2如果您可以登录遥控器,这很简单。只需转到那里的Git存储库并运行git tag -d dev。请注意,无论是通过删除遥控器上的标签还是使用git push删除标签的方式,都有一段时间会导致访问遥控器的任何人都发现dev标签丢失。(如果他们已经拥有旧标签,他们将继续拥有自己的旧标签,甚至可以在推送新标签之前将旧标签重新备份。)


这仅在新版本的git中发生吗?我有1.7.9.5,我没有这个问题...
尼卡比曹

2
Probalby-我记忆犹新,git push --tags只是在git的较早版本中自动更改了标签,而没有--force。我在1.8.4下对此进行了测试,但是您确实需要--force或两阶段更新技术。
2013年

2
@Johnツ:更新:根据发行说明,这是1.8.2版的新行为。我也将其编辑为脚注1。
torek

不知道我是怎么陷入这种情况的,但是这删除了一个标签,并在一个trice中重新创建了一个标签。
RiggsFolly,2015年

4
如果您没有绝地武士怎么办?
Fonix

54

在Mac SourceTree中,仅取消选中“ 推送所有标签”复选框:

在此处输入图片说明


3
哈哈哈,这么简单的人,我正在阅读被接受的答案,我以为我会犯错了
MegaManX 2016年

10
这只是为了克服它,而没有实际解决问题。这不能解决远程和本地的标签名称丢失匹配。
amalBit '16

1
也适用于Windows版本!感谢您使我们免于阅读已被广泛接受的答案,该答案忽略了不关心命令提示符中正在发生什么的sourcetree用户:)
schlingel

19

如果您使用SourceTree,非常简单

在此处输入图片说明 基本上,您只需要删除并重新添加冲突标签:

  1. 转到选项卡存储库 -> 标签 -> 删除标签
  2. 选择有冲突的标签名称
  3. 选中从所有遥控器上删除标签
  4. 删除
  5. 创建具有相同名称的新标签以正确提交
  6. 将更改推送到远程时,请确保选中“ 推送所有标签”

16

如果您想更新标签,可以说它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

完成


12

看来我在这个问题上迟到了,并且/或者它已经被回答了,但是,可以做的是:(在我的情况下,我在本地只有一个标签,所以。 :

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

然后:

git push --tags -f

这将更新远程上的所有标签。

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


1
这为我做到了!该标签只在本地而不是在远程:)
pgarciacamou

4

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

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

之所以要使用--rebase标志,是因为您希望将工作放在远程工作之上,这样可以避免其他冲突。

另外,我不明白的是为什么您要删除dev标签并重新创建它???标签用于指定软件版本或里程碑。Git标签的例子v0.1devv0.0.1alphav2.3-cr(CR -发行候选版本)等..


解决此问题的另一种方法是发出a git reflog并转到将dev标签推到远程的那一刻。复制提交IDgit reset --mixed <commmit_id_from_reflog>这样一来,您就可以知道您的标签在您按下标签的那一刻就已与远程同步,并且不会发生冲突。


例如,如果要标记当前正在生产中的提交。然后,您是否必须从特定的提交中删除旧的生产标签,并在新的生产版本发布后为该提交创建并推送新的标签。
Ville Miekk-oja

2

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

在此处输入图片说明


0

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.