更改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.name
和git 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
您可能会遇到以下错误之一:
- 临时目录已经存在
- 已经存在以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
希望它是有用的。
- 运行
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
这将保留所有其他提交信息(包括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,如果您有上游,可能会导致问题,并且一直是意外丢失数据的好方法。
当然,如果你自己做了一件事情,而且它确实应该是别人,而且你不介意在这一点上重写历史,那么改变提交作者可能是归因的好主意(在这种情况下,我指导你到我的其他答案在这里)。
-
通过
Amend
更改提交author name & email
,然后用old-commit with new-one
replaceold-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 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/master
的master
分支,需要运行
$ 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的人会看到旧的提交。
程序是这样的:
-
find所有提交错误的电子邮件,例如使用
$ git log --author=user@wrong.email --all
-
对于每个错误的提交,创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>
-
现在你已经纠正了对象数据库中的提交,你必须告诉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循环,或者手动replace。
没有testing! 因人而异。
请注意,在使用refs/replace/
mechanism时可能会遇到一些粗糙的问题:这是新的,还没有很好的testing 。
如果您想要修复的提交是最新的提交,并且只是其中的几个,那么在configuration正确的名称和电子邮件之后,可以使用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
如果您使用EGit的Eclipse,那么有一个相当简单的解决scheme。
假设:您有一个本地分支“local_master_user_x”提交,由于用户无效,无法将其推送到远程分支“主”。
- 检出远程分支“主”
- select“local_master_user_x”包含更改的项目/文件夹/文件
- 右键单击 – replace为 – 分支 – “local_master_user_x”
- 再次提交这些更改,这次是正确的用户,并进入本地分支的“主”
- 推到远程“主”
使用交互式资料库,您可以在每次提交之后放置修改命令。 例如:
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 <branch> <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 .