编辑 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 push
2仍然可以使用后者删除标签。 假设远程服务器的名称为
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 pull --tags
,然后运行git push origin --tags
。 - sawe