158

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

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. 在本地机器上克隆了 repo。
  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来强制更新标签,但这很危险(例如,只在标签中重写错误的提交,而不是在分支中)。

4

8 回答 8

185

编辑,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 值都是真实有效的值。

即使没有钩子,也会运行一种“内置钩子”:除非您使用“强制”标志,否则遥控器将拒绝移动标签(尽管“内置钩子”对两者都适用“添加”和“删除”)。您看到的拒绝消息来自这个内置的钩子。(顺便说一下,这个内置的钩子也拒绝不是快进的分支更新。)1

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

如果您在本地删除标签,那么push您的推送根本不会传输标签。遥控器假定不应进行任何更改。

如果你在本地删除标签,然后创建它指向一个新的地方,那么push,你的推送会传输标签,远程会将此视为标签更改并拒绝更改,除非它是强制推送。

因此,您有两个选择:

  • 做一个力推,或
  • 删除遥控器上的标签。

即使在本地删除标签并且ing 没有效果,后者可以通过git push2实现。push假设遥控器的名称是origin,并且您希望它删除的标签是dev

git push origin :refs/tags/dev

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

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

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

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

--tags(注意:如果您只明确推送一个标签参考规范,则不需要)。


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

2能远程登录就很简单了。只需转到那里的 Git 存储库并运行git tag -d dev. 请注意,无论哪种方式——删除遥控器上的标签,或使用git push删除它——在一段时间内,任何访问遥控器的人都会发现dev标签丢失。(如果他们已经拥有旧标签,他们将继续拥有自己的旧标签,他们甚至可能会在您推送新标签之前将旧标签推回原处。

于 2013-10-10T15:30:06.713 回答
59

在 Mac SourceTree 中,仅取消选中Push all tags复选框:

在此处输入图像描述

于 2015-12-02T09:59:34.523 回答
22

如果您使用的是SourceTree,这非常简单

在此处输入图像描述 基本上你只需要删除并重新添加冲突的标签:

  1. 转到选项卡存储库->标签->删除标签
  2. 选择有冲突的标签名称
  3. 检查从所有遥控器中删除标签
  4. 删除
  5. 为正确的提交创建具有相同名称的新标签
  6. 将更改推送到远程时,请确保选中推送所有标签
于 2016-12-15T14:06:46.143 回答
20

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

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

然后:

git push --tags -f

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

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

于 2016-08-26T13:57:08.647 回答
19

如果你想更新一个标签,让我们说出来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

完毕

于 2016-04-29T04:35:37.307 回答
4

您被拒绝的原因是您的标签与远程版本失去了同步。这与分支的行为相同。

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

你使用--rebase标志的原因是你想把你的工作放在远程工作之上,这样你就可以避免其他冲突。

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


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

于 2015-07-26T07:37:45.540 回答
2

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

在此处输入图像描述

于 2016-02-22T12:35:07.683 回答
0

这里有一些很好的答案。尤其是@torek 的那个。我想我会为那些匆忙的人添加这个变通方法并稍微解释一下。

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

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

于 2020-05-26T23:54:28.683 回答