1

开个玩笑,我将笔记本电脑的用户帐户命名为 Jon Skeet。我已经将每个存储库的选项配置为 call me wizzwizz4,但是当我查看我的提交时,我看到了这个:

Author: Jon Skeet <jon@myLaptop>  2018-12-21 22:07:11
Committer: wizzwizz4 <wizzwizz4@users.noreply.github.com>  2018-12-21 22:12:07
Parent: 39c31f5aebe43cdddbe00432207e4bb2cc6a777e (Initial commit)
Branches: master
Follows: 
Precedes: 

当我的存储库设置清楚地表明我的意图时,为什么它会继续这样做?我不希望 Jon 因我的代码而获得荣誉!从命令行提交具有预期的结果。

4

1 回答 1

6

有多种可能发生的方式。在我们了解所有这些之前,我们需要介绍适当的背景信息。还值得强调一个关键项:任何现有提交中的任何数据都不能更改,甚至不能更改。 只要39c31f5aebe43cdddbe00432207e4bb2cc6a777e您的存储库中存在提交,它将继续具有相同的信息。(请注意,这是您显示的提交的级。您没有显示提交本身的实际哈希 ID,所以我不能使用那个。)

在非常具体的 Git-GUI ( git-gui.sh,我从不使用) 的情况下,它从源代码看起来好像有一个功能,使用“修改”读取HEAD提交的作者信息并复制它。它通常应该在您选择“修改”时执行此操作(如上所述,这是一个善意的谎言),并且在不修改时不应执行此操作。与 command-line 不同git commit,似乎没有 Git-GUI 旋钮可以在不保留作者的情况下进行修改。如果它意外地将作者保留应用于所有新提交,那只是一个错误。

有关更多信息,请继续阅读。

背景

每个提交都有一些与之关联的元数据。原始提交对象中有两个相关的元数据行,称为authorcommitter。这两个通常但不一定相同,正如从 Git 存储库中 Git 本身的各种提交中可以看到的那样。例如:

$ git cat-file -p 5d826e972970a784bd7a7bdf587512510097b8c7
tree c790c47fe551d5ed812cfefdac243eb972c1fde3
parent b5796d9a3263b26a8ef32eeca76b3c1d62fcedc5
author Junio C Hamano <gitster pobox.com> 1544328981 +0900
committer Junio C Hamano <gitster pobox.com> 1544328981 +0900

Git 2.20

Signed-off-by: Junio C Hamano <gitster pobox.com>

(我已替换@可能减少垃圾邮件的收集)。但:

$ git cat-file -p 6fcbad87d476d7281832af843dd448c94673fbfc
tree aa05bc7af6e92f3db5d5d738adf0d0b1b3dd23b6
parent b00bf1c9a8dd5009d5102aef7af9e2b886b1e5ad
author Johannes Sixt <j6t kdbg.org> 1543858489 +0100
committer Junio C Hamano <gitster pobox.com> 1543891852 +0900

rebase docs: fix incorrect format of [... snip]

请注意,这两个字段实际上都包含三个部分:全名、<尖括号> 中的电子邮件地址和带区域偏移的时间戳。

当您使用 进行新提交时git commit,Git通常将 author 和 committer 设置为相同的三个字符串。但是许多 Git 命令会将一些现有的提交复制到一个新的和改进的替代品中。根据定义,新提交有一个新的和不同的哈希 ID,但它是用来代替旧的。对于这些情况,Git 通常会保留原始作者信息并将您(现在)设置为提交者。

作为参考,保留作者的提交复制命令git commit带有/--amend-c/-C选项;git cherry-pick; 和git rebase。该git am命令旨在将通过电子邮件发送的补丁转换为提交:它接受提交以外的其他内容作为输入,因此我们可以说它是保留作者的,但是我们必须定义作者的意思。在这种情况下,git am通过解析邮箱格式的消息来猜测作者信息。

每个领域的机制

有一个底层 Git 命令,git commit-tree其他命令要么使用,要么内置在其中。这实际上构建了提交对象,其中包含上述元数据。可以使用各种指令单独设置每个字段。如果某些字段设置,git commit-tree则可以从某处获取默认值。

由于每个作者和提交者有六个部分——姓名、电子邮件地址和时间戳——有六个地方可以获取特定指令,还有很多地方——这次不是六个!——获取默认值。不过,首先,让我们列举一下前六名。

文档中所述,从环境变量git commit-tree中获取这六个项目,而不是使用命令行选项:

GIT_AUTHOR_NAME
GIT_AUTHOR_EMAIL
GIT_AUTHOR_DATE
GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL
GIT_COMMITTER_DATE

如果您设置任何或所有这些变量,则会设置将进入所有后续新提交的值(直到您取消设置变量或您与此环境的会话到期,取消设置变量)。

如果不是,那么文档继续说:

如果(某些)这些环境变量未设置,则从配置项 user.name 和 user.email 获取信息,或者,如果不存在,则从环境变量 EMAIL 获取,或者,如果未设置,则从系统用户获取name 和用于传出邮件的主机名(/etc/mailname当该文件不存在时,取自并回退到完全限定的主机名)。

这是一个善意的谎言,因为所采用的实际代码路径取决于编译时选项,因此不同的 Git 安装可以有不同的自定义默认值。但总体思路是正确的:如果您没有用各种环境变量覆盖其中一个或两个,Git 将首先使用您的user.name和设置,对于作者和提交者。user.email

当然,默认时间戳只是您自己的计算机对当前时间的想法。相对较新的user.useConfigOnly设置告诉现代 Git 不要猜测user.name/或user.email. 在旧版本的 Git 中,Git 没有猜测:如果这些没有设置,git commit-tree并且git commit会失败并显示错误消息,说它不知道你是谁。

git commit前端命令也接受--author和作为--date参数。这些参数可以指定在新提交中使用的用户名、电子邮件地址和/或时间戳;git commit通过在提交操作期间设置GIT_AUTHOR_*变量来有效地实现这些。

当使用git commit带有标志的前端时--amend——尽管它的名字,它实际上并没有改变提交;它只是创建一个新的来使用而不是当前的,这意味着——该--reset-author标志告诉前端不要保留原始提交的作者信息。

结论

如果提交得到了错误的作者,而得到了正确的提交者,则必须满足以下两种情况之一:

  • 你正在使用--author. 停止!

  • 您拥有GIT_AUTHOR_NAMEGIT_AUTHOR_EMAIL设置在您的环境中。停止设置它们!

如果您有一些现有的提交,并且您尝试通过 替换为新的和改进的提交git commit --amend,但它保留了其作者设置,只需添加--reset-author. 当然,这只适用于命令行。如果您正在使用其他东西,请查看它是否有类似的选项。

如果某些现有的提交有错误的作者,你就会被困住。您可以复制现有的、不太好的提交到新的和改进的提交,并尝试说服拥有同一存储库副本的其他所有人(克隆)选择并使用新的和改进的提交而不是旧的。显然取决于其他用户的顽固程度,以及提交的位置。

位于其分支顶端且不在任何其他分支上的提交很容易换出,使用git commit --amend. 历史上更远的提交更加困难:您可以使用交互式变基,或git replace,或,在特别丑陋的情况下,git filter-branch将它们交换出来(有时结合这些技术)。对旧提交的任何“更改”都必然会在设计上影响到其所有后代,1因此这种更改可能会造成很大的破坏。但是,如果它正在“改变”——实际上是在取代其他人从未见过的历史,那它就足够安全了。


1 “错误”提交的直接子代包含错误提交的父哈希 ID。因此,要让孩子参考替换,我们也必须替换孩子。这意味着我们必须替换他们的孩子,以此类推,一直到每个受影响分支的尖端提交。

于 2018-12-21T23:52:51.653 回答