重新创buildgit标签后,“标签已经存在于远程”错误

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

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. 创build了存储库
  2. 克隆本地机器上的回购。
  3. 修改README文件,提交更改并推送提交。
  4. 创build标签devgit tag dev
  5. 推送标签: git push --tags
  6. 修改README文件,提交更改并推送提交。
  7. 删除标签dev ,再次创build并推送标签:

     git tag -d dev git tag dev git push --tags 

为什么发生这种情况?

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

编辑,2016年11月24日:这个答案显然很受欢迎,所以我在这里添加一个注释。 如果在中央服务器上replace标签,则任何拥有标签的人(即已经拥有标签的中央服务器存储库的任何克隆)都可以保留其旧标签 。 所以,虽然这告诉你如何去做,确定你想要做到这一点。 您需要让每个已经有“错误”标签的人删除他们的 “错误标签”,并用新的“正确标签”代替。

在Git 2.10 / 2.11中testing显示,保留旧标记是运行git fetch客户端的默认行为,更新是运行git fetch --tags客户端的默认行为。

(原始答案如下。)


当你要求推送标签时, git push --tags发送(和任何需要的提交和其他对象以及推送设置中的任何其他ref的更新)到远程的new-sha1 refs/tags/ nameforms的更新请求。 (好吧,它发送很多:每个标签的其中之一。)

远程修改更新请求以添加一个old-sha1 (或者每个标签一个),然后传递给预接收和/或更新挂钩(无论哪个挂钩存在于远程)。 这些挂钩可以决定是否允许或拒绝标签的创build/删除/更新。

如果正在创build标记,那么old-sha1值是全零“空”SHA-1。 如果标签被删除, new-sha1是空的SHA-1。 否则,两个SHA-1值都是真实的有效值。

即使没有挂钩,也有一种“内置挂钩”也在运行:远程将拒绝移动标签,除非使用“强制”标志(尽pipe“内置挂钩”对于两个“添加”和“删除”)。 你看到的拒绝信息是来自这个内置的钩子。 (顺便说一句,这个相同的内置钩子也拒绝不是快进的分支更新

但是,这是了解发生了什么的关键之一 – git push步骤不知道远程设备是否有该标签,如果是的话,它具有什么样的SHA-1值。 它只是说“这是我的完整标签列表,以及他们的SHA-1值”。 远程比较值,如果有添加和/或更改,运行这些钩子。 (对于相同的标签,它什么都不做,对于你没有的标签,它也没有任何作用!)

如果您在本地删除标签,然后push ,您的推送根本不会传送标签。 遥控器不应该做任何改变。

如果你在本地删除标签,然后创build它指向一个新的地方,然后push ,你的推转移标签,和远程看到这是一个标签变化,并拒绝改变,除非它是一个强制推。

因此,你有两个select:

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

后者可以通过git push 2,即使在本地删除标签, push ing也没有效果。 假设远程的名字是origin ,你想要删除的标签是dev

 git push origin :refs/tags/dev 

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

遥控器可能允许或不允许删除标签(取决于添加的任何额外的挂钩)。 如果它允许删除,那么标签将不见了,第二个git push --tags ,当你有一个本地dev标签指向一些提交或注释的标签回购对象,发送你的新的dev标签。 在远程, dev现在将成为一个新创build的标签,所以远程可能会允许推送(同样取决于添加的额外钩子)。

推力更简单。 如果你想确保不更新标签以外的任何东西,只要告诉git push 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如果您可以在远程login,这是微不足道的。 只要到那里的Git仓库运行git tag -d dev 。 请注意,无论是删除远程标签,还是使用git push删除标签,都有一段时间访问远程的人会发现dev标签丢失。 (如果他们已经拥有旧标签,他们将继续拥有自己的旧标签,甚至可能会推回旧标签,然后才能推出新标签。)

在Mac SourceTree中,只取消选中全部标签checkbox:

在这里输入图像说明

如果你使用SourceTree,很简单

在这里输入图像说明 基本上你只需要删除并重新添加冲突的标签:

  1. 转到标签 – > 标签 – > 删除标签
  2. select冲突的标签名称
  3. 检查从所有遥控器中删除标签
  4. 删除
  5. 用相同的名字创build新的标签到正确的提交
  6. 确保在将更改推送到远程时检查推送所有标签

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

DONE

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

通过git pull --rebase <repo_url> +refs/tags/<TAG>与远程的标记同步,同步之后,需要pipe理冲突 。 如果你已经安装了difftool(例如meld), git mergetool meld使用它来同步远程并保持你的改变。

你用–rebase标志拉的原因是你想把你的工作放在远程的顶端,这样你就可以避免其他冲突。

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


你可以解决这个问题的另一个方法是发出一个git reflog ,然后到你把dev标签放在远程的那一刻。 复制commit idgit reset --mixed <commmit_id_from_reflog>这样你就知道你的标签在你推送的时候与远程同步,并且不会产生冲突。

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

在这里输入图像说明

看来我在这个问题上迟到了,或者已经得到了答复,但是可以做的是:(在我的情况下,我只有一个本地标签,所以我删除了旧的标签并用:

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

然后:

 git push --tags -f 

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

可能是危险的! 使用风险自负。