How to change the author and committer name and e-mail of multiple commits in Git?
我在学校的计算机上写了一个简单的脚本,并将更改提交给了Git(在我的PenDrive中的一个repo中,从我家的计算机克隆而来)。几次提交之后,我意识到我是作为根用户提交东西的。
有没有办法改变这些承诺的作者的名字?
使用交互式钢筋网
你可以做到
1 | git rebase -i -p <some HEAD before all of your bad commits> |
然后在rebase文件中将所有错误提交标记为"编辑"。如果还想更改第一次提交,则必须将其手动添加为REBASE文件中的第一行(遵循其他行的格式)。然后,当git要求您修改每个提交时,请执行
1 | git commit --amend --author"New Author Name" |
编辑或关闭打开的编辑器,然后执行
1 | git rebase --continue |
继续使用钢筋。
你可以跳过在这里全部打开编辑器的步骤,附加
1 2 | git commit --amend --author"New Author Name" --no-edit && \ git rebase --continue |
单一提交
正如一些评论者所指出的,如果您只想更改最近的提交,则不需要使用rebase命令。只要做
1 | git commit --amend --author"New Author Name" |
这会将作者更改为指定的名称,但提交者将设置为您在
1 | git -c user.name="New Author Name" -c [email protected] commit --amend --reset-author |
合并提交注意事项
我最初的回答有一点瑕疵。如果当前的
我在上面的命令中添加了
更改作者(或提交者)需要重新编写所有历史。如果你认为这是值得的,那么你应该检查一下Git过滤器分支。该手册页包含几个示例,可以帮助您开始学习。还要注意,您可以使用环境变量来更改作者、提交者、日期等的名称——请参见Git手册页的"环境变量"部分。
具体来说,您可以使用此命令修复所有分支和标记的所有错误作者名称和电子邮件(来源:github帮助):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/bin/sh git filter-branch --env-filter ' OLD_EMAIL="[email protected]" CORRECT_NAME="Your Correct Name" CORRECT_EMAIL="[email protected]" 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 |
您还可以执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 | 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命令提示符中使用此命令,则需要使用
1 2 3 4 5 6 7 8 9 10 11 | 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 |
一行程序,但是如果您有一个多用户存储库,那么要小心——这将改变所有提交,使其拥有相同的(新的)作者和提交者。
1 | 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中可以使用):
1 2 3 4 5 6 | 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 |
当您没有初始化$home/.gitconfig时会发生这种情况。您可以将此修复为:
1 2 3 | git config --global user.name"you name" git config --global user.email [email protected] git commit --amend --reset-author |
使用Git版本1.7.5.4测试
对于单个提交:
1 | git commit --amend --author="Author Name" |
(摘自Asmeurer的回答)
如果只有前几位提交的作者不好,那么可以使用
1 | git rebase -i HEAD~6 # as required |
它向您提供可编辑的提交列表:
1 2 3 | pick abcd Someone else's commit pick defg my bad commit 1 pick 1234 my bad commit 2 |
然后在所有作者不好的行后加上
1 2 3 4 5 | pick abcd Someone else's commit pick defg my bad commit 1 exec git commit --amend --author="New Author Name" -C HEAD pick 1234 my bad commit 2 exec git commit --amend --author="New Author Name" -C HEAD |
保存并退出编辑器(运行)。
这个解决方案的输入时间可能比其他一些解决方案长,但它是高度可控制的——我确切知道它命中了什么提交。
感谢@asmeurer的启发。
Github有一个很好的解决方案,它是以下shell脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #!/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" ="[email protected]" ] then cn="Your New Committer Name" cm="Your New Committer Email" fi if ["$GIT_AUTHOR_EMAIL" ="[email protected]" ] 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" ' |
正如docgnome所提到的,重写历史是危险的,并且会破坏其他人的存储库。
但是,如果您真的想这样做,并且您在一个bash环境中(在Linux中没有问题,在Windows中,您可以使用git-bash,这是在安装git时提供的),请使用git filter branch:
1 2 3 4 5 | git filter-branch --env-filter ' if [ $GIT_AUTHOR_EMAIL = bad@email ]; then GIT_AUTHOR_EMAIL=correct@email; fi; export GIT_AUTHOR_EMAIL' |
要加快速度,可以指定要重写的修订范围:
1 2 3 4 5 | 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 |
当接收另一个作者未合并的提交时,有一种简单的方法来处理这一问题。
您可以将其用作别名,以便执行以下操作:
1 | git change-commits GIT_AUTHOR_NAME"old name""new name" |
或最后10次承诺:
1 | git change-commits GIT_AUTHOR_EMAIL"[email protected]""[email protected]" HEAD~10..HEAD |
添加到~/.gitconfig:
1 2 | [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
希望它有用。
这是@brian版本的更详细版本:
要更改作者和提交者,可以这样做(使用bash中可能出现的字符串中的换行符):
1 2 3 4 5 6 7 8 | 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 |
您可能会得到以下错误之一:
如果要在出现这些错误的情况下强制运行,请添加
1 2 3 4 5 6 7 8 | 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 |
可能需要对
一个常见的"错误"是使用
循环以下两个命令,直到处理完所有提交:
这将保留所有其他提交信息(包括日期)。
我使用以下代码重写整个存储库的作者,包括标记和所有分支:
1 2 3 4 | git filter-branch --tag-name-filter cat --env-filter" export GIT_AUTHOR_NAME='New name'; export GIT_AUTHOR_EMAIL='New email' " -- --all |
然后,如filter branch的手册页所述,删除
1 2 | git for-each-ref --format="%(refname)" refs/original/ | \ xargs -n 1 git update-ref -d |
我修改了这个解决方案,它通过摄取一个简单的
我们将它与
即样品
1 2 |
剧本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #!/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 |
我发现所呈现的版本有攻击性,特别是如果您提交来自其他开发人员的补丁,这将从本质上窃取他们的代码。
下面的版本对所有分支都有效,并分别更改作者和Comitter以防止这种情况发生。
为所有选项向LeiF81致敬。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #!/bin/bash git filter-branch --env-filter ' if ["$GIT_AUTHOR_NAME" ="" ]; then GIT_AUTHOR_NAME="<new author>"; GIT_AUTHOR_EMAIL="<[email protected]>"; fi if ["$GIT_COMMITTER_NAME" ="" ]; then GIT_COMMITTER_NAME="<new commiter>"; GIT_COMMITTER_EMAIL="<[email protected]>"; fi ' -- --all |
由
1 2 3 4 5 6 7 8 | $ git checkout <commit-hash> # checkout to the commit need to modify $ git commit --amend --author"name" # change the author name and email $ git replace <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 # remove the replacement for cleanliness $ git push -f origin HEAD # force push |
另一种方法是
1 2 3 4 5 6 7 8 9 | $ 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" # change the author name & email # Save changes and exit the editor $ git rebase --continue # finish the rebase |
我要指出的是,如果唯一的问题是作者/电子邮件与您通常使用的不同,那么这不是问题。正确的解决方法是在目录的基部创建一个名为
1 | Name you want Name you don't want |
从那时起,像
这有其他所有解决方案的优点,因为您不必重写历史,如果您有上游,这可能会导致问题,并且始终是意外丢失数据的好方法。
当然,如果你像你自己一样承诺了某件事情,而且它真的应该是另一个人,此时你不介意重写历史,那么改变提交作者可能是一个很好的归因方法(在这种情况下,我会把你引向我的另一个答案)。
如果您是该存储库的唯一用户,则可以使用
恢复
如果其他开发人员的工作没有基于预重写版本,最简单的解决方案是重新克隆(再次克隆)。
或者,他们可以尝试
如果其他开发人员使用功能分支,和/或
1 | $ git rebase --onto origin/master origin/master@{1} master |
这里,
另一种解决方案是使用refs/replace/mechanism,从1.6.5版开始就可以在git中使用。在这个解决方案中,您为有错误电子邮件的提交提供替换;然后任何获取"替换"引用的人(如
程序是这样的:
查找包含错误电子邮件的所有提交,例如使用
1 | $ git log [email protected] --all |
对于每个错误的提交,创建一个替换提交,并将其添加到对象数据库中。
1 2 3 4 | $ git cat-file -p <ID of wrong commit> | sed -e 's/user@wrong\.email/[email protected]/g' > tmp.txt $ git hash-object -t commit -w tmp.txt <ID of corrected commit> |
既然已经在对象数据库中更正了提交,那么就必须告诉Git使用
1 | $ git replace <ID of wrong commit> <ID of corrected commit> |
最后,列出所有替换项以检查此过程是否成功
1 | $ git replace -l |
检查是否更换
1 | $ git log [email protected] --all |
当然,您可以自动化这个过程…好吧,除了使用没有(还没有)批处理模式的
没有测试过!YMMV。
注意,在使用
如果要修复的提交是最新的,并且只有两个,则可以在配置正确的名称和电子邮件之后,使用
顺序如下(对于2个错误提交,没有挂起的更改):
1 2 3 4 5 6 7 8 | 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 |
假设您要更改最后n个提交的作者:
1 | git rebase -i HEAD~4 -x"git commit --amend --author 'Author Name ' --no-edit" |
笔记
--no-edit 标志确保git commit --amend 不要求额外确认。- 使用
git rebase -i 时,可以手动选择提交更改作者的位置。
您编辑的文件将如下所示:
1 2 3 4 5 6 | pick 897fe9e simplify code a little exec git commit --amend --author 'Author Name ' --no-edit pick abb60f9 add new feature exec git commit --amend --author 'Author Name ' --no-edit pick dc18f70 bugfix exec git commit --amend --author 'Author Name ' --no-edit |
然后,您仍然可以修改一些行,以查看要更改作者的位置。这在自动化和控制之间给了你一个很好的中间地带:你看到了将要运行的步骤,一旦你保存了,所有的东西都会立即应用。
如果您使用Eclipse和eGit,那么有一个非常简单的解决方案。假设:您在本地分支"本地主用户"x中进行了提交,由于用户无效,无法将其推送到远程分支"主用户"。
你的问题很普遍。参见"使用mailmap修复Git中的作者列表"
为了简单起见,我创建了一个脚本来简化这个过程:git changemail
将该脚本放到路径上后,可以发出如下命令:
更改当前分支上的作者匹配
1$ git changemail -a [email protected] -n newname -m [email protected]更改
和 上的作者和提交者匹配。将 -f 传递给filter branch以允许重写备份1$ git changemail -b [email protected] -n newname -m [email protected] -- -f <branch> <branch2>在回购上显示现有用户
1$ git changemail --show-both
另外,在进行更改之后,用:git backup clean从filter分支清除备份
我们今天遇到了这样一个问题:作者名中的utf8字符在构建服务器上造成了问题,因此我们必须重写历史记录以更正此问题。所采取的步骤是:
步骤1:根据下面的说明,为将来的所有提交更改您在Git中的用户名:https://help.github.com/articles/setting-your-username-in-github/
步骤2:运行以下bash脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #!/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="[email protected]" 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 |
快速概述:将存储库签出到一个临时文件,签出所有远程分支,运行将重写历史记录的脚本,强制推送新状态,并告诉所有同事执行一次重新平衡拉取以获取更改。
我们在OSX上运行它时遇到了问题,因为它在提交消息中以某种方式破坏了行尾,所以我们不得不在之后的Linux机器上重新运行它。
请注意,Git存储了两个不同的电子邮件地址,一个用于提交者(提交更改的人),另一个用于作者(编写更改的人)。
提交者信息在大多数地方都没有显示,但是您可以通过
虽然更改上一次提交的作者和
解决方案是(暂时或不)更改您的用户信息,然后修改提交,这会将提交者更新为您的当前信息:
1 2 | git config user.email [email protected] git commit --amend |
使用交互式REBASE,您可以在每次要更改的提交之后放置一个修正命令。例如:
1 2 | pick a07cb86 Project tile template with full details and styling x git commit --amend --reset-author -Chead |
最快、最简单的方法是使用git-rebase的--exec参数:
1 | git rebase -i -p --exec 'git commit --amend --reset-author --no-edit' |
这将创建如下所示的待办事项列表:
1 2 3 4 5 6 7 | 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 ... |
这将自动工作,当您有数百个提交时,这将工作。
如果您是该回购协议的唯一用户,或者您不关心是否可能破坏其他用户的回购协议,那么是的。如果你推动了这些承诺,并且它们存在于其他地方可以访问它们的地方,那么就不会,除非你不关心破坏其他人的回购。问题是,通过更改这些提交,您将生成新的SHA,这将使它们被视为不同的提交。当其他人试图拉入这些改变的承诺时,历史就不同了。
本页http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html介绍了如何进行验证。(我没试过,所以YMMV)
我还想添加我的示例。我想用给定的参数创建一个bash_函数。
这在mint-linux-17.3中有效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # $1 => email to change, $2 => new_name, $3 => new E-Mail function git_change_user_config_for_commit { # defaults WRONG_EMAIL=${1:-"[email protected]"} NEW_NAME=${2:-"your name"} NEW_EMAIL=${3:-"[email protected]"} git filter-branch -f --env-filter" 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; } |
试试这个。它将和上面提到的一样,但是是交互的。
1 | bash <(curl -s https://raw.githubusercontent.com/majdarbash/git-author-change-script/master/run.sh) |
参考:https://github.com/majdarbash/git-author-change-script
1 2 3 | git rebase -i YOUR_FIRTS_COMMIT_SHA^ while true; do git commit --amend --author="Name Surname" --no-edit && git rebase --continue; done |
完成钢筋网后按^C(循环将继续更新上一次提交)
这不是你问题的答案,而是你将来可以用来避免这个问题的一个脚本。它利用自Git 2.9版以来可用的全局挂钩,根据您在以下位置的目录检查您的电子邮件配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #!/bin/sh PWD=`pwd` if [[ $PWD == *"Ippon"* ]] # 1) then EMAIL=$(git config user.email) if [[ $EMAIL == *"Work"* ]] # 2) then echo""; else echo"Email not configured to your Work email in the Work directory."; git config user.email"[email protected]" echo"Git email configuration has now been changed to "$(git config user$ echo" Please run your command again..." echo '' exit 1 fi; elif [[ $PWD == *"Personal"* ]] then EMAIL=$(git config user.email) if [[ $EMAIL =="[email protected]" ]] then echo""; else echo"Email is not configured to your personal account in the Personal di$ git config user.email"[email protected]" echo"Git email configuration has now been changed to "$(git config user$ echo" Please run your command again..." echo '' exit 1; fi; fi; |
它检查您当前的工作目录,然后验证您的git是否配置为正确的电子邮件。如果没有,它会自动更改。请参阅此处的完整详细信息。