如何更改 Git 标记的标记者名称和电子邮件

How to change the Tagger name and email of a Git Tag

长话短说,我正在编写一个脚本,将一个非常大的项目从 (gasp) Microsoft SourceSafe 迁移到 Git,我试图保留 SourceSafe 项目标签的作者(本质上是Git 中的标签)。我知道您可以修改 Git 提交的作者和提交者 name/date,但是您可以对 Git 标签做同样的事情吗?

与提交不同,您可以轻松地从远程删除标签,用您想要的作者姓名重新创建它们

git tag -d <tag-name> 
git push origin :refs/tags/<tag-name>
git config --global user.name "John Doe"
git config --global user.email johndoe@example.com
git tag <tag-name> [commit]
git push origin <tag-name>

TL;DR

使用所需的新数据重新创建标签。但是如果其他人以前有过它们,他们可能不会接受你的新的。或者他们可能!不过,这取决于 他们

描述

I know you can modify the author and committer name/date of a Git Commit

实际上,您不能,而且您不能(以及您可以做什么)这一事实在其余答案中起着重要作用。

所有 Git 对象都有一个散列 ID 作为它们的 "true name"。散列是通过计算对象内容的加密校验和形成的。这意味着您根本无法更改任何 Git 对象。1 可以 做的是构造一个 new 对象,然后说服所有拥有旧对象的人停止使用它,并改用新对象。

这就是 git commit --amend 所做的(以及 editreword 等各种交互式变基选项也可以做的)。首先我们把原来的Git对象抽取出来变成普通数据,我们可以在其中对其进行操作;然后我们进行操作并要求 Git 构造一个新对象;最后我们停止使用旧对象并开始使用新对象。

对于提示提交(参见the definition of head in the gitglossary)的提交,只要我们没有推送该提交,这一切都非常容易和顺利然而。没有其他提交引用此提示提交,因此我们创建一个新提交 "just as good",将分支名称(head)重定向到新提交,忘记我们刚刚更换的原件。 看起来我们更改了提交,但我们得到了一个新的哈希 ID。

这如何应用于标签

Git有两种标签,一种是lightweight tag and an annotated tag. The difference between these is that an annotated tag consists of a lightweight tag pointing to a tag object。它是具有标记器信息的标记对象。 (轻量级标签本身没有这样的信息,它只是直接指向提交对象。)

因此,对于 "change" 标记对象,我们必须做与 "change" 提交对象相同的事情:将其复制到 new标记对象。

没有内置命令来执行此操作,但很容易从 git cat-file -p 中构建一个 - 这可以让您将原始标签提取到普通数据中 - git mktag,它可以让你把普通数据变成一个新的标签对象。例如,Git 存储库中 Git 的 v2.2.1 标签以:

开头
$ git cat-file -p v2.2.1
object 9b7cbb315923e61bb0c4297c701089f30e116750
type commit
tag v2.2.1
tagger Junio C Hamano <...

object行是标签指向的提交:

$ git cat-file -t 9b7cbb315923e61bb0c4297c701089f30e116750
commit

因此我们可以将此标签复制到具有不同 tagger:

的新标签
$ new_hash_id=$(git cat-file -p v2.2.1 | sed -e .... | git mktag)
$ git update-ref refs/tags/$name $new_hash_id

其中 sed 执行任何必要的操作(见下文),$name 是标签的名称。然后我们将使轻量级标签 v2.2.1 指向 $new_hash_id 中的这个新标签对象。但是有两个问题(其中只有一个可能适用于您的情况)。

标签可以是 PGP 签名的

上面的标签接着说:

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

然后里面有一个PGP签名。此签名涵盖除签名本身之外的所有数据。如果您复制并修改此标签,则应完全丢弃原始签名(它将无效并且无法通过任何应用的测试);您是否可以并且应该用新签名替换它,如果可以,由您决定。

标签不应更改其目标对象

现有轻量级标签v2.2.1当前指向现有标签对象:

$ git rev-parse v2.2.1
7c56b20857837de401f79db236651a1bd886fbbb

这是我们到目前为止一直在查看的数据。

new 标记对象将具有其他一些不同的哈希 ID。当我们修改未发布的提交时,这没什么大不了的,因为 没有其他人 知道某个分支名称映射到某个特定的哈希 ID。

然而,标签非常普遍 "well known"。事实上,标签的要点——特别是 PGP 签名的带注释的标签,PGP 签名可以让你验证没有人用过标签数据——是为了保证你可以确定这个标签是 right 标签,它指向的提交对象是原始提交而不是某种特洛伊木马。如果您更改 一个现有标签,您就是在颠覆这个意图。此外,一些知道先前标签值的人会简单地拒绝接受新值:您无法能够让他们更新现有标签。不过,只要您在之前其他任何人都拥有该标签,他们永远不会知道,您会没事的。


1或者更确切地说,您不能更改 Git 对象的内容,除非您可以 break the hash. See also How does the newly found sha1 collision affect git?

一个附加细节:创建 annotated/signed 标签时,git tag 将使用 GIT_COMMITTER_NAMEGIT_COMMITTER_EMAIL 不是 GIT_AUTHOR_NAME/GIT_AUTHOR_EMAIL) 对于 “Tagger”字段。

在不更改全局 Git 配置的情况下创建具有不同 name/email 的标签:

GIT_COMMITTER_NAME="name" GIT_COMMITTER_EMAIL="email" git tag -am"1.0.0" v1.0.0 <commit>

请注意,要创建签名标签 (-s),需要指定身份的私钥(这就是签名的要点)。