更改Git中多个提交的作者和提交者名称和电子邮件

我在学校的电脑上写了一个简单的脚本,并对Git进行了修改(在我的电脑上,从家里的电脑上克隆了一个回购)。 几次提交后,我意识到我是作为root用户提交的东西。

有没有办法将这些提交的作者改为我的名字?

改变作者(或提交者)将需要重写所有的历史。 如果你没有问题,认为这是值得的,那么你应该检查出git filter-branch 。 主页面包括几个例子,让你开始。 另外请注意,您可以使用环境variables来更改作者,提交者,date等名称 – 请参阅git手册页的“环境variables”部分。

具体来说,您可以使用此命令修复所有分支和标签的错误作者姓名和电子邮件(来源: GitHub帮助 ):

#!/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 

使用交互式Rebase

你可以做

 git rebase -i -p <some HEAD before all of your bad commits> 

然后在rebase文件中将所有不良提交标记为“edit”。 如果你还想改变你的第一次提交,你必须手动添加它作为底线文件中的第一行(按照其他行的格式)。 然后,当git要求你修改每个提交时,就这样做

  git commit --amend --author "New Author Name <email@address.com>" 

编辑或closures打开的编辑器,然后执行

 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.namegit config user.email设置为您的已configuration用户。 如果您想将提交者设置为您指定的内容,则会同时设置作者和提交者:

  git -c user.name="New Author Name" -c user.email=email@address.com commit --amend --reset-author 

关于合并提交的说明

我最初的回应有一个小小的缺陷。 如果<some HEAD before all your bad commits> ,当前HEAD和你的<some HEAD before all your bad commits>之间有任何合并提交,那么git rebase会使它们git rebase平(顺便说一下,如果你使用GitHub pull请求,将会有大量的合并提交你的历史)。 这往往会导致非常不同的历史(因为重复的更改可能会“重新启动”),并且在最坏的情况下,可能会导致git rebase要求您解决困难的合并冲突(在合并提交中很可能已经解决)。 解决scheme是使用-p标志来git rebase ,这将保留您的历史的合并结构。 git rebase的man页面警告说使用-p-i可能会导致问题,但是在BUGS部分中,它表示“编辑提交并重新提交它们的提交消息应该可以正常工作”。

我已经添加了-p到上面的命令。 对于你只是改变最近的提交的情况,这不是一个问题。

你也可以这样做:

 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 

一个class轮,但要小心,如果你有一个多用户库 – 这将改变所有提交有相同的(新)作者和提交者。

 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 

用string中的换行符(在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 

发生这种情况时,您没有$ 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进行testing

对于单个提交:

 git commit --amend --author="Author Name <email@address.com>" 

(从asmeurer的答案中提取)

如果只有最顶层的提交者有不好的作者,你可以使用exec命令和--amend commit来完成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 

保存并退出编辑器(运行)。

这种解决scheme可能比其他一些types的打字时间要长,但它的可控性非常高 – 我确切地知道它打的是什么。

感谢@asmeurer的灵感。

Github有一个很好的解决scheme ,就是下面的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" ' 

正如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 

当接pipe另一个作者的未提交的提交时,有一个简单的方法来处理这个问题。

git commit --amend --reset-author

这是@ 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 

您可能会遇到以下错误之一:

  1. 临时目录已经存在
  2. 已经存在以refs / original开始的参考文献
    (这意味着另一个filter分支已经在仓库上运行,然后原始的分支参考以refs / original进行备份)

如果您想强制运行而不pipe这些错误,请添加--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选项做一点解释:它使得filter-branch在所有refs (包括所有分支)上的所有修订版本上工作。 这意味着,例如,标签也被重写,并在重写的分支上可见。

一个常见的“错误”是使用HEAD ,这意味着只对当前分支进行所有修订。 然后在重写的分支中将不存在标签(或其他参考)。

你可以使用这个别名,所以你可以这样做:

 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 

别名:

 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

希望它是有用的。

  1. 运行git rebase -i <sha1 or ref of starting point>
  2. edit标记你想改变的所有提交(或e
  3. 循环以下两个命令,直到处理完所有提交:

    git commit --amend --reuse-message=HEAD --author="New Author <new@author.email>" ; git rebase --continue

这将保留所有其他提交信息(包括date)。 --reuse-message=HEAD选项可防止消息编辑器启动。

我调整了这个解决scheme ,通过摄取一个简单的author-conv-file (格式与git-cvsimport相同 )。 它通过在所有分支上更改author-conv-file定义的所有用户来工作。

我们使用这个和cvs2git一起把我们的仓库从cvs迁移到git。

即Sample 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 filter-branch --tag-name-filter cat --env-filter " export GIT_AUTHOR_NAME='New name'; export GIT_AUTHOR_EMAIL='New email' " -- --all 

然后,如filter分支的MAN页面中所述,删除所有由filter-branch备份的原始参考(这是破坏性的,先备份):

 git for-each-ref --format="%(refname)" refs/original/ | \ xargs -n 1 git update-ref -d 

我发现所提供的版本方式是积极的,特别是如果您从其他开发人员提交补丁,这将基本上窃取他们的代码。

下面的版本可以在所有分支上工作,并分别更改作者和comitter,以防止这种情况。

荣誉leif81全部选项。

 #!/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 

我应该指出,如果唯一的问题是作者/电子邮件与平时不同,这不是问题。 正确的解决方法是在目录的底部用类似于的线创build名为.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

这有所有其他解决scheme的优势在于,您不必重写历史logging,如果您有上游,可能会导致问题,并且一直是意外丢失数据的好方法。

当然,如果你自己做了一件事情,而且它确实应该是别人,而且你不介意在这一点上重写历史,那么改变提交作者可能是归因的好主意(在这种情况下,我指导你到我的其他答案在这里)。

  1. 通过Amend更改提交author name & email ,然后用old-commit with new-onereplaceold-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 
  2. 另一种方式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 filter-branch (如svick写的 )或git fast-export / git fast-import plusfilter脚本(如docgnome answer中引用的文章中所述 )来重写历史logging ,或交互式重新分配 。 但是这两者中的任何一个都会改变第一次更改提交的修订版本; 这意味着对于任何基于他/她的分支的更改预先重写的麻烦。

复苏

如果其他开发者没有将他们的工作基于预重写版本,最简单的解决scheme是重新克隆(再次克隆)。

或者他们可以尝试git rebase --pull ,如果他们的版本库没有任何改变,就会快进,或者在重写提交的基础上重新分支他们的分支(我们希望避免合并,重写comits永远)。 所有这一切都假设他们没有完成工作; 使用git stash来储存变化,否则。

如果其他开发人员使用function分支,和/或git pull --rebase无法工作,例如因为没有设置上游,他们必须重写他们的工作之上的重写后提交。 例如,在获取新的更改( git fetch )之后,对于基于/从origin/mastermaster分支,需要运行

 $ git rebase --onto origin/master origin/master@{1} master 

这里origin/master@{1}是pre-rewrite状态(在fetch之前),请参阅gitrevisions 。


替代解决scheme是使用自1.6.5版以来的Git中可用的refs / replace / mechanism。 在这个解决scheme中,您提供了具有错误电子邮件的提交的replace; 那么任何取代'ref'(就像fetch = +refs/replace/*:refs/replace/* refspec在他们的 .git/config中的适当位置)的人将会透明地取代replace,而那些不会取得这些refs的人会看到旧的提交。

程序是这样的:

  1. find所有提交错误的电子邮件,例如使用

     $ git log --author=user@wrong.email --all 
  2. 对于每个错误的提交,创build一个replace提交,并将其添加到对象数据库

     $ 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> 
  3. 现在你已经纠正了对象数据库中的提交,你必须告诉git使用git replace命令自动并透明地更换错误的提交。

     $ git replace <ID of wrong commit> <ID of corrected commit> 
  4. 最后,列出所有更换,以检查此过程是否成功

     $ git replace -l 

    并检查更换是否发生

     $ git log --author=user@wrong.email --all 

你当然可以自动化这个过程…所有的,除了使用git replace (没有)批处理模式,所以你将不得不使用shell循环,或者手动replace。

没有testing! 因人而异。

请注意,在使用refs/replace/ mechanism时可能会遇到一些粗糙的问题:这是新的,还没有很好的testing

如果您想要修复的提交是最新的提交,并且只是其中的几个,那么在configuration正确的名称和电子邮件之后,可以使用git resetgit 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 

如果您使用EGit的Eclipse,那么有一个相当简单的解决scheme。
假设:您有一个本地分支“local_master_user_x”提交,由于用户无效,无法将其推送到远程分支“主”。

  1. 检出远程分支“主”
  2. select“local_master_user_x”包含更改的项目/文件夹/文件
  3. 右键单击 – replace为 – 分支 – “local_master_user_x”
  4. 再次提交这些更改,这次是正确的用户,并进入本地分支的“主”
  5. 推到远程“主”

使用交互式资料库,您可以在每次提交之后放置修改命令。 例如:

 pick a07cb86 Project tile template with full details and styling x git commit --amend --reset-author -Chead 

请注意,git存储两个不同的电子邮件地址,一个用于提交者 (进行更改的人),另一个用于作者 (编写更改的人)。

提交者信息在大多数地方不显示,但是你可以用git log -1 --format=%cn,%ce (或者用show来代替log来指定一个特定的提交)来看到它。

在更改最后一次提交的作者时,就像git commit --amend --author "Author Name <email@example.com>"一样简单,但是对于提交者信息没有任何一行或者参数。

解决scheme是(暂时或不是)更改您的用户信息,然后修改提交,这将更新提交者到您当前的信息:

 git config user.email my_other_email@example.com git commit --amend 

今天我们遇到了一个问题,那就是作者名中的UTF8字符在构build服务器上造成了麻烦,所以我们不得不重写历史logging来纠正这个问题。 采取的步骤是:

第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 

快速浏览:检查你的版本库到一个临时文件,检查所有的远程分支,运行脚本,这将重写历史logging,强制推新状态,并告诉你的所有同事进行重新拉动以获得更改。

我们在OS X上运行时遇到了麻烦,因为它在提交消息中以某种方式混淆了行尾,所以我们不得不在Linux机器上重新运行它。

你的问题真的很常见。 请参阅“ 使用Mailmap修复Git中的作者列表 ”

为了简单起见,我创build了一个脚本来简化这个过程: git-changemail

将脚本放在path上后,可以发出如下命令:

  • 更改当前分支上的作者匹配

     $ git changemail -a old@email.com -n newname -m new@email.com 
  • 更改<分支>和<分支2>上的作者和提交者匹配。 通过-f筛选分支以允许重写备份

     $ git changemail -b old@email.com -n newname -m new@email.com -- -f &lt;branch> &lt;branch2> 
  • 在回购中显示现有用户

     $ git changemail --show-both 

顺便说一下,在进行更改之后,使用以下命令清理filter-branch中的备份 : git-backup-clean

如果你是这个回购的唯一用户,或者你不关心可能打破其他用户的回购,那么是的。 如果你推动了这些提交,并且存在于其他地方可以访问它们的地方,那么不行,除非你不关心打破别人的回购。 问题是通过改变这些提交你将会生成新的SHA,这将会导致它们被视为不同的提交。 当别人试图提交这些改变的提交时,历史是不同的,kaboom。

这个页面http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html描述了如何做到这一点。; (我没有尝试过,所以YMMV)

 git rebase -i YOUR_FIRTS_COMMIT_SHA^ while true; do git commit --amend --author="Name Surname <email@example.com>" --no-edit && git rebase --continue; done 

rebase完成后按^ C#(循环将不断更新上次提交)

我也想添加我的例子。 我想用给定的参数创build一个bash_function。

这工作在mint-linux-17.3

 # $1 => email to change, $2 => new_name, $3 => new E-Mail function git_change_user_config_for_commit { # defaults WRONG_EMAIL=${1:-"you_wrong_mail@hello.world"} NEW_NAME=${2:-"your name"} NEW_EMAIL=${3:-"new_mail@hello.world"} 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; } 

This isn't an answer to your question, but rather a script you can use to avoid this in the future. It utilizes global hooks available since Git version 2.9 to check your email configuration based on the directory your in:

 #!/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 "youremail@youremail.com" echo "Git email configuration has now been changed to \"$(git config user$ echo "\nPlease run your command again..." echo '' exit 1 fi; elif [[ $PWD == *"Personal"* ]] then EMAIL=$(git config user.email) if [[ $EMAIL == "youremail@youremail.com" ]] then echo ""; else echo "Email is not configured to your personal account in the Personal di$ git config user.email "youremail@youremail.com" echo "Git email configuration has now been changed to \"$(git config user$ echo "\nPlease run your command again..." echo '' exit 1; fi; fi; 

It checks your current working directory, then verifies your git is configured to the correct email. If not, it changes it automatically. See the full details here .