如何在修改git提交时更新作者信息但保留原日期?

35
如果提交已经完成并推送到存储库,且想要更改某个特定提交的作者,可以按照以下方式进行操作:
git commit --amend --reset-author

但是,那将改变原先的提交日期。

我如何重置作者,但保留原始的提交日期?


术语细节:文档中记录的日期格式被称为“作者-日期”和“提交者-日期”。回答合理地假设“最初提交日期”指的是“作者-日期”。我想更新问题以澄清,但审核队列已满。 - sampi
7个回答

45

以下是如何使用 rebase 保留提交日期和作者日期的方法:

git -c rebase.instructionFormat='%s%nexec GIT_COMMITTER_DATE="%cD" GIT_AUTHOR_DATE="%aD" git commit --amend --no-edit --reset-author' rebase -f <commit/branch before wrong author and email, or --root to rebase all>

基于这个 Reddit 帖子


4
最棒的!回答!无敌! - undefined
3
最棒的!回答!曾经! - DeltaIV
顺便说一下,您可以删除“-i”标志,这样就不会显示编辑器了。即:git -c rebase.instructionFormat ='%s%nexec GIT_COMMITTER_DATE =“%cD”GIT_AUTHOR_DATE =“%aD”git commit --amend --no-edit --reset-author' rebase <错误作者和电子邮件之前的提交>。此外,如果希望更改从第一个提交开始的所有提交,则只需提供一个提交,而不是传递“--root”标志。 - Alex Lomia
如果我省略 -i,会收到类似 当前分支 foo 已经是最新的。 的消息,但是添加 -f 就可以正常工作了。--root 的效果也符合预期!谢谢,我会更新我的答案! - DharmaTurtle

32

正如其他答案中提到的,您可能会使用:

git commit --amend --reset-author --no-edit --date="<old-date>"

虽然这样可以实现目标,但手动复制或输入旧日期的工作量很大。您可能希望通过获取日志中最后一条记录的日期来自动获取日期:

然而,这个过程非常繁琐,需要手动复制或打字才能得到旧日期。您可能想要自动获取日期,只需获取日志中最后一个条目的日期:

git log -n 1 --format=%aD

结合两者并使用一些shell技巧:

git commit --amend --reset-author --no-edit --date="$(git log -n 1 --format=%aD)"

这将自动将日志中最后一次提交的日期设置为要修改的新提交的日期,并更改作者。

如果要更改较大量的提交的作者,例如因为在克隆的git库中忘记设置作者,则交互式变基是您的好朋友:

git rebase -i <commit before wrong author and email>

然后,您需要将想要调整的所有提交从 pick 更改为 edit 并保存文件。Git 在每个要编辑的提交上停止,然后重新运行:

git commit --amend --reset-author --no-edit --date="$(git log -n 1 --format=%aD)" && \
    git rebase --continue

如果提交的数量比较小,您可以使用shell的上箭头键重复执行此命令,直到rebase完成。如果有更多的提交,那么输入上箭头+ 回车键变得过于繁琐,您可能需要创建一个小型的shell脚本,重复执行上述命令,直到rebase完成。


4
不需要重复那个命令。您可以使用“-x <cmd>”在待办事项列表的每一行后追加“exec <cmd>”。当然,在您不想编辑的提交后,您应该删除“exec <cmd>”。 - DungSaga
1
@DungSaga 所以整个命令应该是 git rebase -i <commit> -x 'git commit --amend --reset-author --no-edit --date="..." && git rebase --continue' - template boy
4
非常有帮助!但是需要注意的是:--date 命令只会更新作者的日期。如果你想要同时更新提交者的日期(在 GitHub 上显示的日期),你需要修改这个脚本,将 GIT_COMMITTER_DATE 存储到每次修订中:export GIT_COMMITTER_DATE=$(git log -n 1 --format=%aD) && git commit --amend --reset-author --no-edit --date="$GIT_COMMITTER_DATE" && git rebase --continue - RCB
3
我没有花太多时间研究这个问题,所以我相信这里的某个人可以进一步优化它。但是,如果对其他人有帮助的话,我将其添加为我的.gitconfig中的git别名:update-user-keep-date = "!export GIT_COMMITTER_DATE=$(git log -n 1 --format=%aD);git commit --amend --reset-author --no-edit --date=\"$GIT_COMMITTER_DATE\";git rebase --continue"现在只需在每次提交修订时运行 git update-user-keep-date 即可轻松更新提交。 - RCB
是的,这不会更新提交日期。 - qwr

10
  1. 如果你正在进行 rebase,请使用 committer-date-is-author-date 选项以保持日期与之前相同。

$ git commit --amend --committer-date-is-author-date
对于正常修改,请复制原提交者时间并使用--date标志覆盖修改时的时间。
$ git log                     # copy the 'original-committer-time'
$ git commit --amend --reset-author --date="<original-committer-time>"

# e.g. git commit --amend --date="Fri Dec 23 18:53:11 2016 +0600"

1
尝试使用 committer-date-is-author-date 标志。 - Sajib Khan
8
好的,顺便说一下,--committer-date-is-author-date 是在执行变基操作时使用的,而不是提交时使用的。在使用 git commit --committer-date-is-author-date 时,它会报告为未知命令。在这两种情况下都无法正常工作。 - Aleks
运行 git log 后未看到任何 'original-committer-time' - 2540625
@2540625 original-committer-time实际上是你想为该提交设置的“日期”! - Sajib Khan
10
git commit命令提示unknown option \committer-date-is-author-date'`。 - darw
显示剩余5条评论

10

嗯,在最后,我找到了一个更容易的解决方案。我在包含git项目的目录中创建了一个脚本gitrewrite.sh,并修改了其权限以便可以执行:

$ chmod 700 gitrewrite.sh

然后我将以下内容放入shell脚本中:

#!/bin/sh

git filter-branch --env-filter '
NEW_NAME="MyName"
NEW_EMAIL="my-name@my-domain.com"
if [ "$GIT_COMMIT" = "afdkjh1231jkh123hk1j23" ] || [ "$GIT_COMMIT" = "43hkjwldfpkmsdposdfpsdifn" ]
then
    export GIT_COMMITTER_NAME="$NEW_NAME"
    export GIT_COMMITTER_EMAIL="$NEW_EMAIL"
    export GIT_AUTHOR_NAME="$NEW_NAME"
    export GIT_AUTHOR_EMAIL="$NEW_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags

然后在终端中运行该脚本:

$ ./gitrewrite.sh

就这样,历史记录已经被重写。

将代码推送到代码库并添加 force 标志。

$ git push -f

重要提示:

对于其他人阅读此内容,请记住这将在 Git 历史记录中创建新的引用,因此请仅在私有存储库或尚未与他人共享的存储库中执行此操作,否则可能会导致引用失效!

我的是私有存储库,所以那部分不用担心。如果是公共存储库,则也许使用其他建议的答案可能是更好的选择。


哇!这是一个复杂的解决方案,可以避免输入七个退格和自己的姓名和电子邮件... - sampi
@sampi 我甚至会说 - 这是一个不可能的解决方法,因为没有人要求输入七个退格键。如果是六个退格键,那还可以,但是七个。天哪,不行。 - Aleks

8
我们可以使用rebase autosquash来实现所需的行为:
git commit --fixup HEAD
git rebase --autosquash --committer-date-is-author-date HEAD~2

第一个命令在当前HEAD提交上创建一个新的fixup提交。fixup提交意味着如果以后使用--autosquash重新进行变基,这个新提交将在旧提交上被修复(与交互式变基中的fixup相同)。第二个命令触发一个变基到HEAD~2——意味着HEAD的第2级父级,或原始HEAD的父级(在我们添加fixup之前)。这将只触发包含2个提交的变基。由于我们添加了--autosquash,所以这些提交将被合并,添加--committer-date-is-author-date意味着它将使用原始撰写提交的日期而不是创建新提交的当前日期。
警告:如果提交已经被推送,并且如果您以任何方式重写了推送的提交并强制推送,您将重写发布历史,这通常被认为是非常糟糕的事情。如果没有人将自己的工作基于您的提交,则不会有问题。

3
我不确定这有什么帮助?您能否解释一下步骤,以便我们和普通的SO用户知道他在做什么? - Aleks
@Aleks 有点晚了,但是我添加了一些澄清。虽然还需要一些关于git rebase的背景知识,但总比没有好。 - avmohan
1
我喜欢这种方法。我经常会修改我刚创建的提交,由于某些原因(我不想深入讨论),我们的CI工具在CommitterDate和AuthorDate不同时存在问题。因此,我需要手动编辑CommitterDate,这很麻烦。这个解决方案很好,因为我可以创建一个别名,基本上相当于git commit --amend --no-edit,而不会改变提交者日期。 - mbargiel
我刚试了这个解决方案,不确定它是否仍适用于现代Git。我已经安装了2.35.1版本,显然--autosquash只在交互式变基中起作用。我找到了一个解决方法,将第二行更改为:GIT_SEQUENCE_EDITOR=: git rebase -i --autosquash --committer-date-is-author-date HEAD~2然后它会加载“变基交互”机制(即应用--autosquash选项),但直接应用变基指令而不打开文本编辑器。 - mbargiel
此方法不会更新原提交的作者。回答命令中没有任何与问题中的--reset-author相对应的内容。 - sampi

5
错误的做法是在使用--author时使用--reset-author,像这样就足够了:
git commit --amend --author 'My Name <email@example.com>'

从上面链接的选项的文档中可以看出,重置状态的选项:
这也会更新作者的时间戳。
而仅覆盖作者的选项(自v1.5.0以来一直可用)并未提及任何此类副作用。
要修改多个提交或整个提交历史,可以使用以下命令:
git rebase -r <commit/branch before wrong author and email or '--root' for all commits> --exec "git commit --amend --no-edit --author 'My Name <email@example.com>'"

0

非常类似于@Aleks 答案,我在git-tower上找到了最适合我的解决方案:

https://www.git-tower.com/learn/git/faq/change-author-name-email

您可以根据需要在以下脚本中设置WRONG_EMAILNEW_NAMENEW_EMAIL的值,并从git项目内运行它:

git filter-branch --env-filter '
WRONG_EMAIL="wrong@example.com"
NEW_NAME="New Name Value"
NEW_EMAIL="correct@example.com"

if [ "$GIT_COMMITTER_EMAIL" = "$WRONG_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$NEW_NAME"
    export GIT_COMMITTER_EMAIL="$NEW_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$WRONG_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$NEW_NAME"
    export GIT_AUTHOR_EMAIL="$NEW_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags

警告:这将更改所有提交中出现错误电子邮件地址的作者和电子邮件。

引用自https://git-scm.com/docs/git-filter-branch

git filter-branch 存在大量陷阱[...]。这些安全和性能问题无法向后兼容地修复,因此不建议使用它。请使用其他历史记录过滤工具,例如 git filter-repo。

如果您仍然搞砸了,希望您知道如何使用 git reflog。正如 @Aleks 已经说过的:只在您独自工作的私有存储库上执行此类操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接