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

在运行以下步骤之后,我得到以下错误:

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. 创建标记 dev: git tag dev
  5. 推送标签: git push --tags
  6. 修改 README 文件,提交更改并推送提交。
  7. 删除标签 dev,再次创建并推送标签:

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

Why is this happening?

I am on Mac. My friends that use Linux (Ubuntu) don't have this problem. I know that I can use git push --tags -f to force the tag update, but this is dangerous (e.g. rewriting a commit made by mistake only in the tag, not in the branch).

185629 次浏览

编辑,2016年11月24日: 这个答案显然是受欢迎的,所以我在这里添加一个说明。如果您在中央服务器上使用 更换标记,那么任何拥有 老了标记的人ーー任何已经拥有该标记的中央服务器存储库的克隆ーー都可以使用 保留了原来的标签。因此,虽然这告诉你如何做到这一点,一定要确保你 想要做到这一点。你需要让每个已经有“错误”标签的人删除 他们的“错误标签”,并用新的“正确标签”替换它。

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

(原答案如下)


当您要求推送标记时,git push --tags会向远程发送一个表单 new-sha1 refs/tags/name的更新请求(同时发送所需的任何提交和其他对象以及推送设置中的任何其他 ref 更新)。(好吧,它发送多少个: 每个标签一个。)

远程程序修改更新请求,添加一个 old-sha1(或者每个标记一个) ,然后传递给预接收和/或更新挂钩(无论远程程序上存在哪个挂钩)。这些钩子可以决定是否允许或拒绝标记 create/delete/update。

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

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

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

如果您在本地删除标记,那么 push,您的推送根本不会传输标记。远程设备假定不应该进行任何更改。

如果您在本地删除标记,然后创建指向新位置的标记,然后 push,您的推送传输标记,远程将其视为标记更改并拒绝更改,除非这是一个强制推送。

因此,你有两个选择:

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

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

git push origin :refs/tags/dev

这会要求远程删除标记。在本地存储库中是否存在标记 dev是无关紧要的; 这种 push(使用 :remoteref作为 refspec)是纯粹的删除推送。

远程可能允许也可能不允许删除标记(取决于添加的任何额外挂钩)。如果它允许删除,那么标记将不复存在,当有一个本地 dev标记指向某个提交或带注释的标记 repo 对象时,发送新的 dev标记。在远程上,dev现在将是一个新创建的标记,因此远程可能会允许推(同样,这取决于添加的任何额外挂钩)。

推力更简单。如果您想确保不更新除标记以外的任何 其他,只需告诉 git push只推送一个 refspec:

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

(注意: 如果只显式地推送一个标记 ref-spec,则不需要 --tags)。


1 当然,这个内置钩子的 原因是为了帮助强制执行同一个远程回购的其他用户所期望的行为: 分支不会重绕,标签不会移动。如果您强制推送,您应该让其他用户知道您正在执行此操作,以便他们可以对此进行更正。注意,Git1.8.2最近强制“标记根本不移动”; 以前的版本允许标记在提交图中“向前移动”,就像分支名称一样。看看 Git1.8.2版本说明

如果你可以登录到远程系统,那就没什么了不起了。只需转到那里的 Git 存储库并运行 git tag -d dev。请注意,无论哪种方式ーー删除远程控制器上的标记,或者使用 git push来删除ーー有一段时间,任何访问远程控制器的人都会发现 dev标记不见了。(如果他们已经有了 他们自己的旧标签,那么他们将继续使用旧标签,他们甚至可能在您推新标签之前将 他们的旧标签推回。)

您获得 被拒绝了的原因是您的标记与远程版本失去了同步。这与树枝的行为是一样的。

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

您使用 —— rebase标志的原因是您希望将您的工作放在远程标志的上面,这样您就可以避免其他冲突。

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


解决这个问题的另一种方法是发出一个 git reflog,然后回到在远程上按下 dev标记的那一刻。通过这种方式复制 确认身份git reset --mixed <commmit_id_from_reflog>,你就可以知道你的标签在你按下它的那一刻是与远程同步的,而且不会产生任何冲突。

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

enter image description here

在 WindowsSourceTree 中,取消选中 Push all tags to remotes

enter image description here

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

搞定

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

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

然后:

git push --tags -f

这将更新远程 所有标记。

可能很危险,使用后果自负。

如果你使用 源树,它就是 很简单

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

  1. 转到选项卡 仓库-> 标签-> 删除标签
  2. 选择冲突的标记名称
  3. 检查 从所有遥控器上删除标记
  4. 拿开
  5. 为正确的提交创建具有相同名称的新标记
  6. 将更改推到远程时,请确保检查 推出所有标签

这里有些不错的答案。特别是 作者:@torek。我想我应该加上这个解决方案,并对那些匆忙的事情做一个小小的解释。

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

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

我还在想我是怎么进入这种状态的。

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

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

不再有冲突或警告。

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