重新创建git标签后出现“标签已存在于远程”错误

174
我按照以下步骤运行后出现了以下错误:
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来强制更新标签,但这很危险(例如,在标签中重新编写由错误提交的提交,而不在分支中进行更改)。


3
提交并非在标签或分支内进行(虽然后者看起来确实是这样)。事实上,标签和分支名称仅仅是指向一个单一提交的指针。请参阅下面的答案。 - torek
11
这个方法适用于我:先运行git pull --tags,然后运行git push origin --tags - sawe
请参见 https://dev59.com/5FwZ5IYBdhLWcg3wC8b4。 - icc97
9个回答

196
编辑 2016年11月24日:这个答案显然很受欢迎,因此我在这里添加一条注释。如果您在中央服务器上替换标签,任何拥有旧标签的人-任何已经拥有该标签的中央服务器存储库的克隆版本-都可以保留其旧标签。因此,虽然这告诉您如何执行此操作,请确保您确实想要执行此操作。您需要让所有已经拥有“错误”标签的人删除其“错误标签”,并将其替换为新的“正确标签”。
在Git 2.10/2.11中进行的测试表明,对于运行git fetch的客户端,保留旧标签是默认行为,并且对于运行git fetch --tags的客户端,则默认更新。
(遵循原始回答。)
当您请求推送标记时,git push --tags会向远程发送一个更新请求,形式为new-sha1 refs/tags/name(以及来自推送设置的任何提交和其他所需对象以及任何其他引用更新)。(嗯,它会发送多少:每个标签都会发一个。)
远程会修改更新请求以添加一个old-sha1(或再次为每个标签添加一个),然后将其传递给pre-receive和/或update钩子(哪个钩子存在于远程)。这些钩子可以决定是允许还是拒绝标记的创建/删除/更新。
如果正在创建标记,则old-sha1值为所有零的“null”SHA-1。如果正在删除标记,则new-sha1为null SHA-1。否则,两个SHA-1值都是真实的、有效的值。
即使没有任何钩子,仍有一种“内置钩子”也会运行:除非使用“force”标志,否则远程将拒绝移动标记(尽管“内置钩子”始终对“add”和“delete”都可以)。您看到的拒绝消息来自这个内置钩子。(顺便说一下,这个相同的内置钩子还会拒绝不是快进的分支更新。)1 但是-这是理解发生了什么的关键之一-git push步骤不知道远程现在是否具有该标记,如果有,则它具有什么SHA-1值。它只会说“这是我完整的标记列表,以及它们的SHA-1值”。远程比较这些值,如果有添加和/或更改,则对这些运行钩子。(对于相同的标记,它根本不执行任何操作。对于您没有但他们有的标记,它也不执行任何操作!)
如果您在本地删除标签,然后进行push,那么您的推送只是不传输标签。远程服务器假定不需要进行更改。
如果您在本地删除标签,然后将其创建为指向新位置,然后进行push,则您的推送会传输标签,并且远程服务器会将其视为标签更改并拒绝该更改,除非强制推送。
因此,您有两个选择:
  • 强制推送,或者
  • 在远程服务器上删除标签。
即使本地删除标签并进行push没有效果,但通过git push2仍然可以使用后者删除标签。 假设远程服务器的名称为origin,要删除的标签为dev
git push origin :refs/tags/dev

这会请求远程删除标签。你本地仓库中的dev标签的存在或不存在都不重要;这种使用:remoteref作为refspec的push是一个纯删除操作。

远程可能允许或不允许标签删除(取决于添加的任何额外钩子)。如果它允许删除,则该标签将被删除,当你拥有指向某个提交或注释标签 repo 对象的本地dev标签时,第二个git push --tags将发送你的新dev标签。在远程端,dev现在是一个新创建的标签,因此远程可能会允许推送(这还取决于添加的任何额外钩子)。

强制推送更加简单。如果想确保不更新除标签之外的任何内容,只需告诉git push仅推送那一个refspec:

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

(提示:如果您明确地只推送一个标签 ref-spec,则不需要--tags。)


1当然,内置挂钩的目的是帮助强制执行同一远程仓库的其他用户期望的行为:分支不会倒退,标签不会移动。如果您进行强制推送,应该让其他用户知道您正在这样做,以便他们可以进行更正。请注意,Git 1.8.2 新增了“标签根本不会移动”的强制规定;之前的版本会允许标签在提交图中“向前移动”,就像分支名称一样。请参见git 1.8.2 发布说明

2如果您可以登录到远程,那很容易。只需转到那里的 Git 存储库并运行git tag -d dev。请注意,在删除远程标签或使用git push删除标签时,存在一段时间,任何访问远程的人都会发现dev标签已丢失。(如果他们已经拥有自己的旧标签,则仍将拥有它们,并且甚至可能在您推送新标签之前将自己的旧标签重新推送上去。)


这只发生在新版本的git中吗?我有1.7.9.5,我没有这个问题... - Ionică Bizău
2
可能——我模糊地记得在旧版本的git中,git push --tags会自动更改标签,而不需要--force。但是我已经在1.8.4下测试过了,您确实需要--force或采用两个阶段的更新技术。 - torek
2
@Johnツ:更新:根据发行说明,这是自1.8.2以来的新行为。我也会将其编辑到脚注1中。 - torek
不知道我是怎么陷入这种情况的,但很快就删除并重新创建了一个标签。 - RiggsFolly
4
如果你不是绝地武士,如何进行强制推送? - Fonix
显示剩余6条评论

62
在Mac的SourceTree中,只需取消选中“推送所有标签”复选框:

enter image description here


5
好的,我会尽力进行翻译。以下是需要翻译的内容:"hahahah so simple man, i was reading the accepted answer and i thought i so gonna fak this up" 哈哈哈,太简单了。我正在阅读被接受的答案,但我觉得我肯定会搞砸它。 - MegaManX
12
这只是克服问题,而并非真正解决问题。这并不能解决本地和远程的标签名称不匹配的问题。 - amalBit
1
适用于Windows版本!感谢您为我们节省了阅读长篇接受答案的时间,该答案忽略了不关心命令提示符中发生了什么的Sourcetree用户 :) - schlingel
1
再次强调,采用这种方法并不是为了“避免阅读一个省略了 Sourcetree 用户的长答案”,这只是一个解决方法而非解决方案。被接受的答案之所以被接受,是因为它是一种解决方案,而这种解决方法将会在以后给你造成麻烦。 - CodeSpent

28

如果你使用 SourceTree,这就非常简单

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

  1. 进入标签页 Repository -> Tag -> Remove Tag
  2. 选择冲突的标签名称
  3. 勾选 Remove tag from all remotes
  4. 按下 Remove
  5. 创建一个新的标签,名称与正确的提交相同
  6. 在将更改推送到远程时,确保勾选 Push all tags

24

看起来我已经晚了,这个问题可能已经有答案了,但是可以做的是:(在我这种情况下,我只有一个本地标签,所以...我删除了旧标签并使用新标签重新标记:

)

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

那么:

git push --tags -f

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

可能存在危险性!请自行承担风险。


1
这对我有用!标签仅在本地而不是远程 :) - pgarciacamou

21
如果您想要更新一个标签,比如说它是`1.0.0`,请按以下步骤进行:
  1. git checkout 1.0.0
  2. 进行修改
  3. git ci -am '修改一些内容'
  4. git tag -f 1.0.0
  5. 删除Github上的远程标签:git push origin --delete 1.0.0
  6. git push origin 1.0.0

完成


6

虽然我还在想着我是如何陷入这种状态的...

但我找到了一个简单的解决方案 - 删除所有本地重复的标签,然后从远程拉取这些标签。例如:

git tag -d some-tag
git pull --tags

不再出现冲突或警告。

您可能需要考虑提前备份本地存储库。


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>,这样您就知道在推送标签时您的标签与远程同步,并且不会出现冲突。

例如,如果您想为当前正在生产中的提交打标签。那么您是否需要从特定提交中删除旧的生产标签,并为新的生产发布后的提交创建和推送新标签。 - Ville Miekk-oja

2
在Windows SourceTree中,取消选中“将所有标签推送到远程”。

enter image description here


0

这里有一些好的答案,尤其是@torek的那一个。我想补充一下一个解决方法,并为那些匆忙的人提供一些解释。

总的来说,当您在本地移动标签时,它会将标签从非空提交值更改为不同的值。但是,因为git(作为默认行为)不允许更改非空远程标签,所以您无法推送更改。

解决方法是删除标签(并选中删除所有远程标签)。然后创建相同的标签并推送。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接