长话短说,我正在编写一个脚本来将一个非常大的项目从(喘气)Microsoft SourceSafe 迁移到 Git,并且我正在尝试保留 SourceSafe 项目标签(本质上是 Git 中的标签)的作者。我知道你可以修改 Git 提交的作者和提交者姓名/日期,但你可以对 Git 标签做同样的事情吗?
2 回答
TL;博士
使用新的所需数据重新创建标签。但是,如果其他人以前拥有它们,他们可能不会接受您的新产品。或者他们可能!不过,这取决于他们。
描述
我知道您可以修改 Git 提交的作者和提交者姓名/日期
实际上,您不能,而您不能(以及您可以做什么)这一事实在其余答案中起着重要作用。
所有 Git 对象都有一个哈希 ID 作为它们的“真实名称”。哈希是通过计算对象内容的加密校验和形成的。这意味着您根本无法更改任何 Git 对象。1 你可以做的是构造一个新对象,然后说服所有拥有旧对象的人停止使用它,而改用新对象。
这就是git commit --amend
所做的(以及各种交互式 rebase 选项喜欢edit
并且reword
也可以做的事情)。首先我们将原始的 Git 对象提取为普通数据,我们可以在其中对其进行操作;然后我们进行操作并要求 Git 构造一个新对象;最后我们停止使用旧对象并开始使用新对象。
对于作为提示提交的提交(请参阅gitglossary中head的定义),只要我们还没有推送该提交,这一切都非常容易和顺利。没有引用此提示提交的其他提交,因此我们进行了“同样好”的新提交,将分支名称(head)重新定向到新提交,而忘记我们刚刚替换的原始提交。看起来我们更改了一个提交,但我们得到了一个新的哈希 ID。
这如何适用于标签
Git 有两种标签,一种是轻量级的标签,一种是带注释的标签。它们之间的区别在于带注释的标签由指向标签对象的轻量级标签组成。它是具有标记器信息的标记对象。(轻量级标签本身没有这样的信息,它只是直接指向提交对象。)
因此,要“更改”标签对象,我们必须做与“更改”提交对象相同的事情:将其复制到新的标签对象。
没有内置命令可以执行此操作,但很容易构建一个git cat-file -p
- 这可以让您将原始标签提取为普通数据 - 和git mktag
,它可以让您将普通数据转换为新的标签对象。例如,v2.2.1
Git 存储库中 Git 的标签以:
$ 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
这是到目前为止我们一直在查看的数据。
新标签对象将具有其他一些不同的哈希 ID。当我们修改一个未发布的提交时,这没什么大不了的,因为没有人知道某个分支名称映射到某个特定的哈希 ID。
然而,标签通常是“众所周知的”。事实上,标签的意义——尤其是 PGP 签名的带注释的标签,PGP 签名让您可以验证没有人对标签数据进行过篡改——是为了保证您可以确定这个标签是正确的标签,并且它指向的提交对象是原始提交,而不是某个特洛伊木马。如果您更改现有标签,您就是在颠覆这个意图。此外,一些知道前一个标签值的人会拒绝接受新值:您将无法让他们更新现有标签。但是,只要您在其他人拥有标签之前执行此操作,他们就永远不会知道,您会没事的。
1或者更确切地说,您不能更改 Git 对象的内容,除非您可以破坏 hash。另请参阅新发现的 sha1 冲突如何影响 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>