如何在 Git 中更改多个提交的作者和提交者姓名和电子邮件?
Posted
技术标签:
【中文标题】如何在 Git 中更改多个提交的作者和提交者姓名和电子邮件?【英文标题】:How to change the author and committer name and e-mail of multiple commits in Git? 【发布时间】:2011-03-25 00:34:01 【问题描述】:我正在学校计算机上编写一个简单的脚本,并将更改提交到 Git(在我的笔式驱动器中的一个存储库中,从我家的计算机克隆)。几次提交后,我意识到我是以 root 用户身份提交的东西。
有没有办法将这些提交的作者更改为我的名字?
【问题讨论】:
问题:使用 git filter-branch 会保留以前标签、版本和对象的 SHA1 吗?或者更改作者姓名是否会强制更改相关的 SHA1? 哈希会改变是 切线地,我创建了一个小脚本,最终为我解决了根本原因。 gist.github.com/tripleee/16767aa4137706fd896c @impinball 问题的年龄几乎不相关。创建一个新的重复问题是不可能的。我想我可以创建一个问题来寻求这个特定的答案,但我并不完全相信它会获得如此多的知名度。并不是说这里缺少 Git 问题……很高兴我能帮上忙。 @TimurBernikovich 提到的 github 脚本很棒并且对我有用。但是那个 github url 已经改变了:docs.github.com/en/enterprise/2.17/user/github/using-git/… 【参考方案1】:注意:此答案会更改 SHA1,因此在已推送的分支上使用它时要小心。如果您只想修复名称的拼写或更新旧电子邮件,git 允许您这样做,而无需使用 .mailmap
重写历史记录。见my other answer。
使用交互式变基
你可以的
git rebase -i -p <some HEAD before all of your bad commits>
然后在 rebase 文件中将所有错误提交标记为“编辑”。如果您还想更改第一次提交,则必须手动将其添加为 rebase 文件的第一行(遵循其他行的格式)。然后,当 git 要求您修改每个提交时,请执行
git commit --amend --author "New Author Name <email@address.com>"
编辑或关闭打开的编辑器,然后执行
git rebase --continue
继续变基。
您可以通过附加 --no-edit
来跳过此处完全打开编辑器
这样命令将是:
git commit --amend --author "New Author Name <email@address.com>" --no-edit && \
git rebase --continue
单次提交
正如一些评论者所指出的,如果您只想更改最近的提交,则不需要 rebase 命令。做吧
git commit --amend --author "New Author Name <email@address.com>"
这会将作者更改为指定的名称,但提交者将设置为您在git config user.name
和git config user.email
中配置的用户。如果您想将提交者设置为您指定的内容,这将同时设置作者和提交者:
git -c user.name="New Author Name" -c user.email=email@address.com commit --amend --reset-author
关于合并提交的注意事项
我原来的回复有一点小瑕疵。如果当前的HEAD
和你的<some HEAD before all your bad commits>
之间有任何合并提交,那么git rebase
会将它们展平(顺便说一下,如果你使用 GitHub 拉取请求,你的历史)。这通常会导致非常不同的历史记录(因为重复的更改可能会“重新定位”),在最坏的情况下,它可能会导致 git rebase
要求您解决困难的合并冲突(可能已经在合并提交)。解决方案是对git rebase
使用-p
标志,这将保留您历史记录的合并结构。 git rebase
的联机帮助页警告说,使用 -p
和 -i
可能会导致问题,但在 BUGS
部分它说“编辑提交并重新编写提交消息应该可以正常工作。”
我已将-p
添加到上述命令中。对于您只是更改最近提交的情况,这不是问题。
现代 git 客户端更新(2020 年 7 月)
使用--rebase-merges
而不是-p
(-p
已被弃用并且存在严重问题)。
【讨论】:
非常适合奇怪的提交 - 如果您正在配对并且忘记更改作者,则很有用 +1 用于提及典型的错误修复的用例: git commit --amend --author=username 这很完美,我最常见的用例是我坐在另一台电脑前忘记设置作者,因此通常有git commit --amend --reset-author
在 user.name
和 user.email
配置正确后也可以工作。
在<commit>
之后使用user.name
和user.email
从~/.gitconfig
重写所有提交的作者信息:运行git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit'
,保存,退出。无需编辑!【参考方案2】:
这个答案使用
git-filter-branch
,the docs 现在给出这个警告:git filter-branch 有很多陷阱,可能会对预期的历史重写产生不明显的破坏(并且由于它的性能如此糟糕,因此您几乎没有时间调查此类问题)。这些安全和性能问题不能向后兼容修复,因此不建议使用它。请使用替代的历史过滤工具,例如git filter-repo。如果您仍然需要使用 git filter-branch,请仔细阅读SAFETY(和PERFORMANCE)了解 filter-branch 的地雷,然后尽可能合理地避免其中列出的许多危害。
更改作者(或提交者)需要重写所有历史记录。如果您对此感到满意并认为值得,那么您应该查看git filter-branch。手册页包含几个帮助您入门的示例。另请注意,您可以使用环境变量来更改作者姓名、提交者、日期等。请参阅git man page 的“环境变量”部分。
具体来说,您可以使用此命令修复所有分支和标签的所有错误作者姓名和电子邮件(来源:GitHub help):
#!/bin/sh
git filter-branch --env-filter '
OLD_EMAIL="your-old-email@example.com"
CORRECT_NAME="Your Correct Name"
CORRECT_EMAIL="your-correct-email@example.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_NAME="$CORRECT_NAME"
export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_NAME="$CORRECT_NAME"
export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags
如果要使用替代的历史过滤工具git filter-repo,可以先安装它并按照gitmailmap的格式构造一个git-mailmap
。
Proper Name <proper@email.xx> Commit Name <commit@email.xx>
然后使用创建的邮件映射运行 filter-repo:
git filter-repo --mailmap git-mailmap
【讨论】:
执行脚本后,您可以通过执行“git update-ref -d refs/original/refs/heads/master”删除备份分支。 @rodowi,它复制了我所有的提交。 @RafaelBarros 作者信息(就像历史上的其他任何内容一样)是提交的 sha 密钥的一部分。对历史记录的任何更改都会导致所有提交的新 id 的重写。所以不要重写共享仓库或确保所有用户都知道它...... 使用git push --force --tags origin HEAD:master
解决
重要!!! 在执行脚本之前,请正确设置您的 user.name 和 user.email git config 参数!执行脚本后,您将拥有一些称为“原始”的重复备份历史记录!通过git update-ref -d refs/original/refs/heads/master
删除它,然后检查.git/refs/original
文件夹结构是否为空,然后使用rm -rf .git/refs/original
将其删除。最后,您可以通过git log --pretty=format:"[%h] %cd - Committer: %cn (%ce), Author: %an (%ae)"
验证新的重写日志!还有一件事:.git/logs
有一些日志文件仍然使用您的旧名称!【参考方案3】:
一个班轮,但如果您有一个多用户存储库,请小心 - 这会将 所有 提交更改为具有相同的(新)作者和提交者。
git filter-branch -f --env-filter "GIT_AUTHOR_NAME='Newname'; GIT_AUTHOR_EMAIL='new@email'; GIT_COMMITTER_NAME='Newname'; GIT_COMMITTER_EMAIL='new@email';" HEAD
字符串中有换行符(在 bash 中是可能的):
git filter-branch -f --env-filter "
GIT_AUTHOR_NAME='Newname'
GIT_AUTHOR_EMAIL='new@email'
GIT_COMMITTER_NAME='Newname'
GIT_COMMITTER_EMAIL='new@email'
" HEAD
【讨论】:
如果在命令末尾指定HEAD
,为什么会重写所有提交?
这不适用于我的 bitbucket 存储库,知道吗?我在建议的命令之后执行git push --force --tags origin 'refs/heads/*'
推送命令是:$git push --force --tags origin 'refs/heads/master'
整洁;这也保留了旧的时间戳。
@HARSHNILESHPATHAK 请注意,对于最近创建的存储库,分支 master 已重命名为 main,因此命令变为 $git push --force --tags origin 'refs/heads/main'
【参考方案4】:
你也可以这样做:
git filter-branch --commit-filter '
if [ "$GIT_COMMITTER_NAME" = "<Old Name>" ];
then
GIT_COMMITTER_NAME="<New Name>";
GIT_AUTHOR_NAME="<New Name>";
GIT_COMMITTER_EMAIL="<New Email>";
GIT_AUTHOR_EMAIL="<New Email>";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD
注意,如果您在 Windows 命令提示符下使用此命令,则需要使用"
而不是'
:
git filter-branch --commit-filter "
if [ "$GIT_COMMITTER_NAME" = "<Old Name>" ];
then
GIT_COMMITTER_NAME="<New Name>";
GIT_AUTHOR_NAME="<New Name>";
GIT_COMMITTER_EMAIL="<New Email>";
GIT_AUTHOR_EMAIL="<New Email>";
git commit-tree "$@";
else
git commit-tree "$@";
fi" HEAD
【讨论】:
使用 env-filter 不是更简单的解决方案吗?不知道为什么这会获得更多选票。 然后链接被破坏。我们如何将这些更改推送到另一个存储库? env-filter 将更改所有提交。此解决方案允许有条件的。"A previous backup already exists in refs/original/ Force overwriting the backup with -f"
抱歉,但是当你执行此脚本两次时,-f
-flag 将在哪里。实际上,这在 Brian 的回答中,对于解决方案后过滤器分支之后的干扰感到抱歉。
@user208769 env-filter 也允许有条件的;看看我的回答:-)【参考方案5】:
当您没有初始化 $HOME/.gitconfig
时会发生这种情况。您可以将其修复为:
git config --global user.name "you name"
git config --global user.email you@domain.com
git commit --amend --reset-author
使用 git 版本 1.7.5.4 测试。
请注意,这只修复了最后一次提交。
【讨论】:
最后一次提交时效果很好。很好很简单。 必须是全局更改,使用--local
也可以
这个是我的大赢家!如果您使用错误的作者信息创建提交,然后通过git config
在事后设置正确的作者,git commit --amend --reset-author --no-edit
命令特别有用。刚刚在我必须更新电子邮件时节省了我的 a$$。
答案可能有点矫枉过正。首先检查这是否满足您的用例 - ***.com/a/67363253/8293309【参考方案6】:
如果只有前几个提交的作者不好,您可以使用exec
命令和--amend
提交在git rebase -i
内完成所有操作,如下所示:
git rebase -i HEAD~6 # as required
它为您提供可编辑的提交列表:
pick abcd Someone else's commit
pick defg my bad commit 1
pick 1234 my bad commit 2
然后在所有作者不好的行之后添加exec ... --author="..."
行:
pick abcd Someone else's commit
pick defg my bad commit 1
exec git commit --amend --author="New Author Name <email@address.com>" -C HEAD
pick 1234 my bad commit 2
exec git commit --amend --author="New Author Name <email@address.com>" -C HEAD
保存并退出编辑器(运行)。
这个解决方案的打字时间可能比其他一些解决方案要长,但它是高度可控的 - 我确切地知道它命中了什么提交。
感谢@asmeurer 的启发。
【讨论】:
绝对很棒。你可以通过在 repo 的本地配置中设置 user.name 和 user.email 来缩短它,然后每一行是 onlyexec git commit --amend --reset-author -C HEAD
吗?
规范答案,使用过滤器分支,只是为我删除了 refs/heads/master。所以 +1 为您的可控、可编辑的解决方案。谢谢!
代替git rebase -i HEAD^^^^^^
你也可以写git rebase -i HEAD~6
请注意,这会更改提交的时间戳。请参阅***.com/a/11179245/1353267 以恢复到正确的时间戳
对于和我一样遇到同样问题的其他人,如果您尝试包含初始提交并且收到fatal: Needed a single revision
,请尝试使用git rebase -i --root
【参考方案7】:
对于单个提交:
git commit --amend --author="Author Name <email@address.com>"
(摘自 asmeurer 的回答)
【讨论】:
但这只是最近的提交 根据git help commit
,git commit --amend
更改“当前分支的尖端”(即 HEAD)处的提交。这通常是最近的提交,但您可以通过第一个 checking out 使用 git checkout <branch-name>
或 git checkout <commit-SHA>
提交任何您想要的提交。
但是如果你这样做,所有已经有该提交作为父提交的提交都将指向错误的提交。此时最好使用 filter-branch。
@JohnGietzen:您可以将提交重新设置为已更改以解决该问题的提交。但是,如果您进行 >1 次提交,那么如前所述,filter-branch 可能会容易得多。
请注意,此更改仅提交 author
而不是 committer
【参考方案8】:
Github 有一个nice solution,也就是下面的 shell 脚本:
#!/bin/sh
git filter-branch --env-filter '
an="$GIT_AUTHOR_NAME"
am="$GIT_AUTHOR_EMAIL"
cn="$GIT_COMMITTER_NAME"
cm="$GIT_COMMITTER_EMAIL"
if [ "$GIT_COMMITTER_EMAIL" = "your@email.to.match" ]
then
cn="Your New Committer Name"
cm="Your New Committer Email"
fi
if [ "$GIT_AUTHOR_EMAIL" = "your@email.to.match" ]
then
an="Your New Author Name"
am="Your New Author Email"
fi
export GIT_AUTHOR_NAME="$an"
export GIT_AUTHOR_EMAIL="$am"
export GIT_COMMITTER_NAME="$cn"
export GIT_COMMITTER_EMAIL="$cm"
'
【讨论】:
完美运行。只需在其他本地存储库上git reset --hard HEAD^
几次以将它们升级到早期版本,git pull
-ed 修改后的版本,这里我没有任何包含 unknown <stupid-windows-user@.StupidWindowsDomain.local>
的行(必须喜欢 git 的默认设置) .
这之后我无法推动。我必须使用“-f”吗?
我做了git push -f
。此外,在此之后必须重新克隆本地存储库。
如果您需要在特定分支上运行 shell 脚本,您可以将最后一行更改为:“' master..your-branch-name”(假设您是 master 的分支)。
脚本已更新,点击链接正如 docgnome 所提到的,重写历史是危险的,并且会破坏其他人的存储库。
但是如果你真的想这样做并且你在一个 bash 环境中(在 Linux 上没问题,在 Windows 上你可以使用 git bash,它是 git 的安装提供的),使用git filter-branch:
git filter-branch --env-filter '
if [ $GIT_AUTHOR_EMAIL = bad@email ];
then GIT_AUTHOR_EMAIL=correct@email;
fi;
export GIT_AUTHOR_EMAIL'
为了加快速度,您可以指定要重写的修订范围:
git filter-branch --env-filter '
if [ $GIT_AUTHOR_EMAIL = bad@email ];
then GIT_AUTHOR_EMAIL=correct@email;
fi;
export GIT_AUTHOR_EMAIL' HEAD~20..HEAD
【讨论】:
请注意,这将留下任何指向旧提交的标签。--tag-name-filter cat
是“让它工作”选项。
@romkyns 知道如何更改标签吗?
@NickVolynkin 是的,您指定 --tag-name-filter cat
。这确实应该是默认行为。
答案可能有点矫枉过正。首先检查这是否满足您的用例 - ***.com/a/67363253/8293309【参考方案10】:
单个命令更改最后 N 次提交的作者:
git rebase -i HEAD~N -x "git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit"
注意事项
用引用替换HEAD~N
,直到你想重写你的提交。这可以是哈希、HEAD~4
、分支名称、...
--no-edit
标志确保git commit --amend
不要求额外确认
当您使用git rebase -i
时,您可以手动选择更改作者的提交,
您编辑的文件将如下所示:
pick 897fe9e simplify code a little
exec git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit
pick abb60f9 add new feature
exec git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit
pick dc18f70 bugfix
exec git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit
然后您仍然可以修改一些行以查看要更改作者的位置。这为您在自动化和控制之间提供了一个很好的中间地带:您会看到将要运行的步骤,并且一旦您保存所有内容就会立即应用。
注意,如果你已经用git config user.name <your_name>
和git config user.email <your_email>
修复了作者信息,你也可以使用这个命令:
git rebase -i HEAD~N -x "git commit --amend --reset-author --no-edit"
【讨论】:
我使用了 HEAD~8,它显示的次数比最后 8 次提交要多。 @BryanBryce 如果涉及合并提交,事情会变得复杂:) 您使用--root
而不是HEAD~N
来编辑整个历史记录(包括初始提交),并使用--reset-author
来获取当前提交者而不是--author ...
我的用例是我必须更改一些私人存储库中所有过去的提交,因为我的推送使用不同的用户名,没有附加电子邮件。第一个位允许我更改前 N 次提交的作者和电子邮件,但它没有保留提交时间戳,这些时间戳随之更新。我解决了这个by using this script。它既漂亮又干净,允许我将整个提交历史更改为单个用户名和电子邮件,同时保留提交时间戳。
@PedroHenrique:您需要将HEAD~4
替换为引用,直到您要重写提交的位置......我会尝试在我的回答中更清楚地说明这一点。正如我之前提到的:当心合并提交,你会遇到复杂的事情【参考方案11】:
您可以将其用作别名,以便您可以:
git change-commits GIT_AUTHOR_NAME "old name" "new name"
或最近 10 次提交:
git change-commits GIT_AUTHOR_EMAIL "old@email.com" "new@email.com" HEAD~10..HEAD
添加到 ~/.gitconfig:
[alias]
change-commits = "!f() VAR=$1; OLD=$2; NEW=$3; shift 3; git filter-branch --env-filter \"if [[ \\\"$`echo $VAR`\\\" = '$OLD' ]]; then export $VAR='$NEW'; fi\" $@; ; f "
来源:https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig
希望有用。
【讨论】:
"git: 'change-commits' 不是 git 命令。参见 'git --help'。" 在此命令和与 master 同步之后,历史记录中的所有提交都被复制了!甚至其他用户:( @Vladimir 是意料中事,请研究一下 git 中的历史变化 对我来说它似乎在 /bin/sh 中运行,所以我不得不用 sh 兼容测试[ ]
(单括号)替换特定于 bash 的测试 [[ ]]
。除此之外,它工作得很好,谢谢!
@Native_Mobile_Arch_Dev 你需要这个: git config --global alias.change-commits '!'"f() VAR=\$1; OLD=\$2; NEW=\$3; shift 3; git filter-branch --env-filter \"if [[ \\\"\$`echo \$VAR`\\\" = '\$OLD' ]];然后导出 \$VAR='\$NEW'; fi\" \$@; ; f"【参考方案12】:
当从其他作者那里接管一个未合并的提交时,有一种简单的方法来处理这个问题。
git commit --amend --reset-author
【讨论】:
对于一次提交,如果你想输入你的用户名,这是最简单的方法。 您可以添加--no-edit
以使这更容易,因为通常大多数人只想更新电子邮件地址而不是提交消息
你们能否分享一下 git 命令,以便用新的更新上次提交的电子邮件/用户名
你试过了吗?这应该是一个副作用,如果不是***.com/a/2717477/654245 看起来像一条好路。【参考方案13】:
这是@Brian 版本的更详细的版本:
要更改作者和提交者,您可以这样做(在字符串中使用换行符,这在 bash 中是可能的):
git filter-branch --env-filter '
if [ "$GIT_COMMITTER_NAME" = "<Old name>" ];
then
GIT_COMMITTER_NAME="<New name>";
GIT_COMMITTER_EMAIL="<New email>";
GIT_AUTHOR_NAME="<New name>";
GIT_AUTHOR_EMAIL="<New email>";
fi' -- --all
您可能会遇到以下错误之一:
-
临时目录已经存在
以 refs/original 开头的参考文献已经存在
(这意味着之前已经在存储库上运行了另一个过滤器分支,然后原始分支引用备份在 refs/original)
如果您想在出现这些错误的情况下强制运行,请添加 --force
标志:
git filter-branch --force --env-filter '
if [ "$GIT_COMMITTER_NAME" = "<Old name>" ];
then
GIT_COMMITTER_NAME="<New name>";
GIT_COMMITTER_EMAIL="<New email>";
GIT_AUTHOR_NAME="<New name>";
GIT_AUTHOR_EMAIL="<New email>";
fi' -- --all
可能需要对-- --all
选项进行一点解释:它使过滤器分支适用于所有引用(包括所有分支)的所有修订。这意味着,例如,标签也被重写并且在重写的分支上可见。
一个常见的“错误”是改用HEAD
,这意味着只过滤当前分支上的所有修订。然后在重写的分支中将不存在任何标签(或其他参考)。
【讨论】:
感谢提供一个更改所有引用/分支上的提交的过程。【参考方案14】:我应该指出,如果唯一的问题是作者/电子邮件与您平时不同,这不是问题。正确的解决方法是在目录的底部创建一个名为 .mailmap
的文件,其中包含类似
Name you want <email you want> Name you don't want <email you don't want>
从那时起,git shortlog
之类的命令将认为这两个名称相同(除非您明确告诉他们不要这样做)。请参阅http://schacon.github.com/git/git-shortlog.html 了解更多信息。
这具有此处所有其他解决方案的优势,因为您不必重写历史记录,如果您有上游可能会导致问题,并且始终是意外丢失数据的好方法。
当然,如果您以自己的身份提交了某些内容,并且确实应该是其他人,并且此时您不介意重写历史记录,那么更改提交作者可能是出于归因目的的好主意(在这种情况下,我指示你给我的另一个答案here)。
【讨论】:
其实这是一个非常有趣的答案。就我而言,我在家里做了一些提交,这可能会让额外的作者感到困惑,所以这就是我所需要的。 另外,请注意这不适用于 Gitea 的 web 端。 @iuliu.net 我不确定。这个问题***.com/questions/53629125/… 似乎表明确实如此,但我尚未确认。当然,如果他们不这样做,那么他们应该这样做,因为它是 git 的标准部分。【参考方案15】:运行git rebase -i <sha1 or ref of starting point>
用edit
(或e
)标记您要更改的所有提交
循环以下两个命令,直到处理完所有提交:
git commit --amend --reuse-message=HEAD --author="New Author <new@author.email>"
;
git rebase --continue
这将保留所有其他提交信息(包括日期)。
--reuse-message=HEAD
选项阻止消息编辑器启动。
【讨论】:
【参考方案16】:我使用以下内容来重写整个存储库的作者,包括标签和所有分支:
git filter-branch --tag-name-filter cat --env-filter "
export GIT_AUTHOR_NAME='New name';
export GIT_AUTHOR_EMAIL='New email'
" -- --all
然后,如MAN page of filter-branch 中所述,删除所有由filter-branch
备份的原始引用(这是破坏性的,先备份):
git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d
【讨论】:
使用--tag-name-filter cat
非常重要。否则,您的标签将保留在原始提交链上。其他答案没有提到这一点。【参考方案17】:
git docs here 建议的 filter-repo
工具是 git 的 filter-branch
更安全的替代品。
git filter-repo --commit-callback '
old_email = b"your-old-email@example.com"
correct_name = b"Your Correct Name"
correct_email = b"your-correct-email@example.com"
if commit.committer_email == old_email :
commit.committer_name = correct_name
commit.committer_email = correct_email
if commit.author_email == old_email :
commit.author_name = correct_name
commit.author_email = correct_email
'
上述命令反映了this 脚本中使用的逻辑,但使用filter-repo
而不是filter-branch
。
commit-callback
选项之后的代码主体基本上是用于处理提交的 python 代码。你可以在这里用 python 编写你自己的逻辑。详细了解commit
对象及其属性here。
由于 filter-repo
工具没有与 git 捆绑,您需要单独安装它。
见Prerequisties和Installation Guide
如果你有一个python env >= 3.5,你可以使用pip
来安装它。
pip3 install git-filter-repo
注意:强烈建议在新克隆上尝试filter-repo
工具。操作完成后,遥控器也会被移除。阅读更多关于为什么删除遥控器here。另请阅读INTERNALS 部分下此工具的限制。
【讨论】:
这似乎是新来的孩子,我像黄金一样珍惜这个答案。请记住字段必须是二进制的,然后删除 == 行,并且您可以在推送之前无条件地更改所有内容。我说过我喜欢这个答案吗?它应该是被接受的。【参考方案18】:我改编了这个solution,它通过摄取一个简单的author-conv-file
(格式与git-cvsimport 的格式相同)来工作。它通过在所有分支中更改author-conv-file
中定义的所有用户来工作。
我们将它与cvs2git
结合使用,将我们的存储库从 cvs 迁移到 git。
即示例author-conv-file
john=John Doe <john.doe@hotmail.com>
jill=Jill Doe <jill.doe@hotmail.com>
脚本:
#!/bin/bash
export $authors_file=author-conv-file
git filter-branch -f --env-filter '
get_name ()
grep "^$1=" "$authors_file" |
sed "s/^.*=\(.*\) <.*>$/\1/"
get_email ()
grep "^$1=" "$authors_file" |
sed "s/^.*=.* <\(.*\)>$/\1/"
GIT_AUTHOR_NAME=$(get_name $GIT_COMMITTER_NAME) &&
GIT_AUTHOR_EMAIL=$(get_email $GIT_COMMITTER_NAME) &&
GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL &&
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
export GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
' -- --all
【讨论】:
谢谢,我想知道为什么这不是核心 git(或 git-svn)功能。这可以通过 git svn clone 的标志来完成,但不能在 git filter-branch...【参考方案19】:我发现提供的版本过于激进,特别是如果您提交其他开发人员的补丁,这实际上会窃取他们的代码。
以下版本确实适用于所有分支,并分别更改作者和提交者以防止这种情况发生。
感谢 leif81 的 all 选项。
#!/bin/bash
git filter-branch --env-filter '
if [ "$GIT_AUTHOR_NAME" = "<old author>" ];
then
GIT_AUTHOR_NAME="<new author>";
GIT_AUTHOR_EMAIL="<youmail@somehost.ext>";
fi
if [ "$GIT_COMMITTER_NAME" = "<old committer>" ];
then
GIT_COMMITTER_NAME="<new commiter>";
GIT_COMMITTER_EMAIL="<youmail@somehost.ext>";
fi
' -- --all
【讨论】:
【参考方案20】:将提交author name & email
更改为Amend
,然后替换old-commit with new-one
:
$ git checkout <commit-hash> # checkout to the commit need to modify
$ git commit --amend --author "name <author@email.com>" # change the author name and email
$ git replace <old-commit-hash> <new-commit-hash> # replace the old commit by new one
$ git filter-branch -- --all # rewrite all futures commits based on the replacement
$ git replace -d <old-commit-hash> # remove the replacement for cleanliness
$ git push -f origin HEAD # force push
另一种方式Rebasing
:
$ git rebase -i <good-commit-hash> # back to last good commit
# Editor would open, replace 'pick' with 'edit' before the commit want to change author
$ git commit --amend --author="author name <author@email.com>" # change the author name & email
# Save changes and exit the editor
$ git rebase --continue # finish the rebase
【讨论】:
非常好的答案。我喜欢从更新到清理 git 提交的更改【参考方案21】:最快、最简单的方法是使用 git rebase 的 --exec 参数:
git rebase -i -p --exec 'git commit --amend --reset-author --no-edit'
这将创建一个如下所示的待办事项列表:
pick ef11092 Blah blah blah
exec git commit --amend --reset-author --no-edit
pick 52d6391 Blah bloh bloo
exec git commit --amend --reset-author --no-edit
pick 30ebbfe Blah bluh bleh
exec git commit --amend --reset-author --no-edit
...
这将自动工作,当您有数百次提交时,它会工作。
【讨论】:
您可以将-p
替换为--root
以更改历史记录中的所有 个提交(-p option 已弃用)。请注意,这仅在您通过 git config user.name <yourname>
和 git config user.email <youremail>
更正用户名和电子邮件后才有效。【参考方案22】:
如果您是此存储库的唯一用户,您可以使用git filter-branch
(如svick wrote)或git fast-export
/git fast-import
加上过滤器脚本(如所述在docgnome answer 中引用的文章中,或互动rebase。但是其中任何一个都会从第一次更改提交开始更改修订;这对任何基于你的分支预重写的人来说都意味着麻烦。
恢复
如果其他开发人员的工作不是基于预重写版本,最简单的解决方案是重新克隆(再次克隆)。
或者他们可以尝试git rebase --pull
,如果他们的存储库中没有任何更改,它会快进,或者在重写的提交之上重新建立他们的分支(我们希望避免合并,因为它会保持 pre -rewrite 永远提交)。所有这一切都假设他们没有提交工作;否则使用git stash
隐藏更改。
如果其他开发人员使用功能分支,和/或 git pull --rebase
不起作用,例如因为上游没有设置,他们必须rebase他们在重写后提交之上的工作。例如,在获取新更改 (git fetch
) 之后,对于基于/从origin/master
分叉的master
分支,需要运行
$ git rebase --onto origin/master origin/master@1 master
这里origin/master@1
是预重写状态(在获取之前),参见gitrevisions。
另一种解决方案是使用 refs/replace/ 机制,该机制在 Git 1.6.5 版本中可用。在此解决方案中,您可以替换具有错误电子邮件的提交;那么任何获取“替换”参考的人(类似于fetch = +refs/replace/*:refs/replace/*
refspec 在他们的 .git/config
中的适当位置)都会透明地获得替换,而那些不获取这些参考的人会看到旧的提交。
过程如下:
查找所有带有错误电子邮件的提交,例如使用
$ git log --author=user@wrong.email --all
对于每个错误的提交,创建一个替换提交,并将其添加到对象数据库中
$ git cat-file -p <ID of wrong commit> |
sed -e 's/user@wrong\.email/user@example.com/g' > tmp.txt
$ git hash-object -t commit -w tmp.txt
<ID of corrected commit>
现在您已经更正了对象数据库中的提交,您必须告诉 git 使用git replace
命令通过更正的提交来自动透明地替换错误的提交:
$ git replace <ID of wrong commit> <ID of corrected commit>
最后,列出所有替换以检查此过程是否成功
$ git replace -l
并检查是否发生替换
$ git log --author=user@wrong.email --all
你当然可以自动化这个过程......好吧,除了使用git replace
之外,它还没有批处理模式,所以你必须使用 shell 循环,或者替换“手动”。
未经测试! YMMV。
请注意,在使用refs/replace/
机制时可能会遇到一些问题:它是新的,尚未经过很好的测试。
【讨论】:
【参考方案23】:请注意,git 存储 两个 不同的电子邮件地址,一个用于 committer(提交更改的人),另一个用于 作者(更改的人)。
提交者信息不会在大多数地方显示,但您可以通过git log -1 --format=%cn,%ce
看到它(或使用show
而不是log
来指定特定的提交)。
虽然更改上一次提交的作者就像 git commit --amend --author "Author Name <email@example.com>"
一样简单,但没有单行或参数可以对提交者信息做同样的事情。
解决方案是(暂时或不)更改您的用户信息,然后修改提交,这会将提交者更新为您当前的信息:
git config user.email my_other_email@example.com
git commit --amend
【讨论】:
请注意,旧值仍在path\to\repo\.git
中的几个地方。我还不确定你需要做什么才能完全删除它。不幸的是,修正 (?) 似乎没有被删除。【参考方案24】:
如果您要修复的提交是最新的,并且只是其中的几个,您可以使用git reset
和git stash
的组合在配置正确的名称和电子邮件后再次提交它们。
序列将是这样的(对于 2 次错误提交,没有待处理的更改):
git config user.name <good name>
git config user.email <good email>
git reset HEAD^
git stash
git reset HEAD^
git commit -a
git stash pop
git commit -a
【讨论】:
【参考方案25】:如果您将 Eclipse 与 EGit 一起使用,那么有一个非常简单的解决方案。 假设:您在本地分支“local_master_user_x”中有提交,由于用户无效,无法将其推送到远程分支“master”。
-
签出远程分支“master”
选择“local_master_user_x”包含更改的项目/文件夹/文件
右键 - 替换为 - 分支 - 'local_master_user_x'
再次提交这些更改,这次以正确的用户身份进入本地分支“master”
推送到远程“master”
【讨论】:
【参考方案26】:使用交互式变基,您可以在每次要更改的提交之后放置一个修改命令。例如:
pick a07cb86 Project tile template with full details and styling
x git commit --amend --reset-author -Chead
【讨论】:
这个问题是其他提交元数据(例如日期和时间)也被修改了。我刚刚发现很困难;-)
。【参考方案27】:
我们今天遇到了一个问题,作者姓名中的 UTF8 字符在构建服务器上造成了问题,因此我们不得不重写历史记录来纠正这个问题。采取的步骤是:
第 1 步:按照此处的说明,在 git 中更改您的用户名以进行所有未来的提交: https://help.github.com/articles/setting-your-username-in-git/
第 2 步:运行以下 bash 脚本:
#!/bin/sh
REPO_URL=ssh://path/to/your.git
REPO_DIR=rewrite.tmp
# Clone the repository
git clone $REPO_URL $REPO_DIR
# Change to the cloned repository
cd $REPO_DIR
# Checkout all the remote branches as local tracking branches
git branch --list -r origin/* | cut -c10- | xargs -n1 git checkout
# Rewrite the history, use a system that will preseve the eol (or lack of in commit messages) - preferably Linux not OSX
git filter-branch --env-filter '
OLD_EMAIL="me@something.com"
CORRECT_NAME="New Me"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_NAME="$CORRECT_NAME"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_NAME="$CORRECT_NAME"
fi
' --tag-name-filter cat -- --branches --tags
# Force push the rewritten branches + tags to the remote
git push -f
# Remove all knowledge that we did something
rm -rf $REPO_DIR
# Tell your colleagues to `git pull --rebase` on all their local remote tracking branches
快速概览:将存储库签出到临时文件,签出所有远程分支,运行将重写历史记录的脚本,强制推送新状态,并告诉所有同事执行 rebase pull 以获取变化。
我们在 OS X 上运行它时遇到了麻烦,因为它以某种方式弄乱了提交消息中的行尾,因此我们不得不在 Linux 机器上重新运行它。
【讨论】:
【参考方案28】:你的问题很常见。见“Using Mailmap to Fix Authors List in Git”
为了简单起见,我创建了一个脚本来简化这个过程:git-changemail
将该脚本放在您的路径上后,您可以发出如下命令:
更改当前分支上的作者匹配项
$ git changemail -a old@email.com -n newname -m new@email.com
更改 -f
传递给 filter-branch 以允许重写备份
$ git changemail -b old@email.com -n newname -m new@email.com -- -f <branch> <branch2>
在 repo 上显示现有用户
$ git changemail --show-both
顺便说一句,在进行更改后,请使用以下命令从过滤器分支中清除备份:git-backup-clean
【讨论】:
当我运行你的命令时,它说“致命:无法执行'git-changemail':权限被拒绝” @Govind 需要设置脚本的执行权限chmod +x git-changemail
【参考方案29】:
如果您想(轻松)更改当前分支的作者,我会使用如下内容:
# update author for everything since origin/master
git rebase \
-i origin/master \
--exec 'git commit --amend --no-edit --author="Author Name <author.name@email.co.uk>"'
【讨论】:
【参考方案30】:将所有提交(包括第一次提交)重置为当前用户和当前时间戳:
git rebase --root --exec "git commit --amend --no-edit --date 'now' --reset-author"
【讨论】:
这仅适用于当前分支。以上是关于如何在 Git 中更改多个提交的作者和提交者姓名和电子邮件?的主要内容,如果未能解决你的问题,请参考以下文章