How do I change the author for a range of commits?
This answer uses
git-filter-branch
, for which the docs now give this warning:git filter-branch has a plethora of pitfalls that can produce non-obvious manglings of the intended history rewrite (and can leave you with little time to investigate such problems since it has such abysmal performance). These safety and performance issues cannot be backward compatibly fixed and as such, its use is not recommended. Please use an alternative history filtering tool such as git filter-repo. If you still need to use git filter-branch, please carefully read SAFETY (and PERFORMANCE) to learn about the land mines of filter-branch, and then vigilantly avoid as many of the hazards listed there as reasonably possible.
Changing the author (or committer) would require rewriting all of the history. If you're okay with that and think it's worth it then you should check out git filter-branch. The manual page includes several examples to get you started. Also note that you can use environment variables to change the name of the author, committer, dates, etc. -- see the "Environment Variables" section of the git manual page.
Specifically, you can fix all the wrong author names and emails for all branches and tags with this command (source: GitHub help):
#!/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
For using alternative history filtering tool git filter-repo, you can first install it and construct a git-mailmap
according to the format of gitmailmap.
Proper Name <[email protected]> Commit Name <[email protected]>
And then run filter-repo with the created mailmap:
git filter-repo --mailmap git-mailmap
git push --force --tags origin 'refs/heads/*'
mentioned in the GitHub docs gives me Everything up-to-date
–
Louvar git push --force --tags origin HEAD:master
–
Louvar git push --force --tags origin 'refs/heads/*'
over git push -f origin master
? I've tried both, the results seem to be the same. –
Bussy git update-ref -d refs/original/refs/heads/master
and then check if .git/refs/original
folder structure is empty and then just remove it with rm -rf .git/refs/original
. Lastly, you can verify the new rewritten log via: git log --pretty=format:"[%h] %cd - Committer: %cn (%ce), Author: %an (%ae)"
! One more thing: .git/logs
has some log files that still have your old name! –
Canoodle .git/logs
is your reflog. It is important and cannot be hand-edited. The only way to get rid of your own name from there is to make a brand new empty repository, set your existing one as a remote, pull in your old repo to a branch, then rebase that branch onto your new repo. I just did that for a sensitive project but it's way too much work for most people (I used a variation of the two answers here: stackoverflow.com/questions/5340790/…). I also did git gc --aggressive
afterwards to clean it all up. Good luck. –
Canoodle git log -i --date=short --pretty="format:'%C(auto) (%ae, $an, $ce, %cn, %cr, %cd) %h --%s--'"
which shows the author's and committer's email and name. –
Sandberg remote: error: denying non-fast-forward refs/heads/master (you should pull first) To myserver:/path/to/repo.git ! [remote rejected] HEAD -> master (non-fast-forward) error: failed to push some refs to 'myserver:/path/to/repo.git'
Is this solution for GitHub only? –
Seleucia grep -rl "[email protected]" | xargs sed -i 's/[email protected]/[email protected]/g'
. Source: unix.stackexchange.com/questions/472476/…. –
Calcifuge git update-ref -d refs/original/refs/heads/master
followed by git -c gc.reflogExpireUnreachable=now gc --prune=now
. This removes the reference to the old chain of commits, making them orphans, which the second command will then clear all references to from the reflog and delete (prune) the actual commit blobs (check with git show <one of the old commit hashes>
) –
Sulla --all
flag, it would be interesting to know the reason for the choice of options in your answer. From the "rev-list" manual, I understand that --all
affects the remotes refs in addition to the heads (--branches
) and tags. But I am unsure if I want to change the remote refs as well... –
Sarre --branches --tags
with main..HEAD
or similar –
Landy git shortlog -sne --all
now shows both new and old emails. What am I missing? –
Halfpenny git filter-repo
not work, no any outputs. –
Orland git branch --delete --remotes -- origin/main
to remove the remote tracking branch after I had git update-ref -d refs/original/refs/heads/main
. Then I just delete and recreate the remote repository and push easily. –
Caricaria NOTE: This answer changes SHA1s, so take care when using it on a branch that has already been pushed. If you only want to fix the spelling of a name or update an old email, Git lets you do this without rewriting history using .mailmap
. See my other answer.
Using Rebase
First, if you haven't already done so, you will likely want to fix your name in git-config:
git config --global user.name "New Author Name"
git config --global user.email "<[email protected]>"
This is optional, but it will also make sure to reset the committer name, too, assuming that's what you need.
To rewrite metadata for a range of commits using a rebase, do
git rebase -r <some commit before all of your bad commits> \
--exec 'git commit --amend --no-edit --reset-author'
--exec
will run the git commit
step after each commit is rewritten (as if you ran git commit && git rebase --continue
repeatedly).
If you also want to change your first commit (also called the 'root' commit), you will have to add --root
to the rebase call.
This will change both the committer and the author to your user.name
/user.email
configuration. If you did not want to change that config, you can use --author "New Author Name <[email protected]>"
instead of --reset-author
. Note that doing so will not update the committer -- just the author.
Single Commit
If you just want to change the most recent commit, a rebase is not necessary. Just amend the commit:
git commit --amend --no-edit --reset-author
Entire project history
git rebase -r --root --exec "git commit --amend --no-edit --reset-author"
For older Git clients (pre-July 2020)
-r,--rebase-merges
may not exist for you. As a replacement, you can use -p
. Note that -p
has serious issues and is now deprecated.
git filter-branch
is a very dangerous command. This should only be used imho in the rare case where you need to delete information from the history permanently (e.g., accidentally committed password or something). –
Stave git rebase
and git commit --amend
than to use git filter-branch
. –
Stave -f .git/rebase-merge/message
:-) –
Daveta -f
when pushing if you do this, because it changes history. Note that this will screw up anyone who has pulled your code. See my other answer if you want a way to do this without changing history. –
Stave Additional Note
must be run for every commit whose author need to change (kind of boring). Also, cannot change author of the first commit with your solution (a problem when you are as dumb as me and do not notice you have global author set to a wrong email from the beginning) –
Dissertation --no-edit
option, it'll keep the message of the amended commit. –
Amund git commit --amend --reset-author
also works once user.name
and user.email
are configured correctly. –
Joust <commit>
using user.name
and user.email
from ~/.gitconfig
: run git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit'
, save, quit. No need to edit! –
Adebayo git rebase -i --root
–
Sapphire filter-branch
script from the accepted answer if you don't want to clobber the metadata. –
Vladimir --rebase-merges
(-p
is deprecated) –
Eartha git rebase -i --rebase-merges --root; while true; do git commit --amend --no-edit --author "Theodore R. Smith <[email protected]>"; git rebase --continue; done
–
Sure -i
option for the rebase
command was for. –
Bangalore unknown switch -r
. If I remove that it works fine. –
Riel --author
does not change commit date, while --reset-author
does. –
Receivable --root
and specify --author
as in Theodore R. Smith's comment –
Bathe --author
preserves author-date, but not commit-date (which is shown by default on github). see stackoverflow.com/questions/28536980/… –
Bathe git config
, then git -c rebase.instructionFormat='%s%nexec GIT_COMMITTER_DATE="%cD" GIT_AUTHOR_DATE="%aD" git commit --amend --no-edit --reset-author' rebase --committer-date-is-author-date -r --root HEAD
This resets author and preserves timestamps of all commits including the very first. Got a hint from a reddit post reddit.com/r/git/comments/jp59k5/… –
Pontus git filter-repo
. With the -r
option, any resolved merge conflicts or manual amendments in these merge commits will have to be resolved/re-applied manually. –
Miculek git rebase
produces confusing behavior, I simply reset my branch to the base commit and cherry-pick bad commits one by one manually, and execute git commit --amend --no-edit --reset-author
after each single cherry-pick, it's more clear & transparent than rebase –
Chenay git rev-list --all --tags | while read commit; do new_commit=$(git log --format="%H" -n 1 $commit); git tag -f $(git tag --contains $commit) $new_commit; done
–
Loopy git -c rebase.instructionFormat='%s%nexec GIT_COMMITTER_DATE="%cD" GIT_AUTHOR_DATE="%aD" git commit --amend --no-edit --reset-author' rebase -f --root
it will rewrite all history –
Airglow -f
in the push command. This only works if you are pushing to unprotected branches (if you own the project you can easily disable that temporarily). –
Pewit This answer uses
git-filter-branch
, for which the docs now give this warning:git filter-branch has a plethora of pitfalls that can produce non-obvious manglings of the intended history rewrite (and can leave you with little time to investigate such problems since it has such abysmal performance). These safety and performance issues cannot be backward compatibly fixed and as such, its use is not recommended. Please use an alternative history filtering tool such as git filter-repo. If you still need to use git filter-branch, please carefully read SAFETY (and PERFORMANCE) to learn about the land mines of filter-branch, and then vigilantly avoid as many of the hazards listed there as reasonably possible.
Changing the author (or committer) would require rewriting all of the history. If you're okay with that and think it's worth it then you should check out git filter-branch. The manual page includes several examples to get you started. Also note that you can use environment variables to change the name of the author, committer, dates, etc. -- see the "Environment Variables" section of the git manual page.
Specifically, you can fix all the wrong author names and emails for all branches and tags with this command (source: GitHub help):
#!/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
For using alternative history filtering tool git filter-repo, you can first install it and construct a git-mailmap
according to the format of gitmailmap.
Proper Name <[email protected]> Commit Name <[email protected]>
And then run filter-repo with the created mailmap:
git filter-repo --mailmap git-mailmap
git push --force --tags origin 'refs/heads/*'
mentioned in the GitHub docs gives me Everything up-to-date
–
Louvar git push --force --tags origin HEAD:master
–
Louvar git push --force --tags origin 'refs/heads/*'
over git push -f origin master
? I've tried both, the results seem to be the same. –
Bussy git update-ref -d refs/original/refs/heads/master
and then check if .git/refs/original
folder structure is empty and then just remove it with rm -rf .git/refs/original
. Lastly, you can verify the new rewritten log via: git log --pretty=format:"[%h] %cd - Committer: %cn (%ce), Author: %an (%ae)"
! One more thing: .git/logs
has some log files that still have your old name! –
Canoodle .git/logs
is your reflog. It is important and cannot be hand-edited. The only way to get rid of your own name from there is to make a brand new empty repository, set your existing one as a remote, pull in your old repo to a branch, then rebase that branch onto your new repo. I just did that for a sensitive project but it's way too much work for most people (I used a variation of the two answers here: stackoverflow.com/questions/5340790/…). I also did git gc --aggressive
afterwards to clean it all up. Good luck. –
Canoodle git log -i --date=short --pretty="format:'%C(auto) (%ae, $an, $ce, %cn, %cr, %cd) %h --%s--'"
which shows the author's and committer's email and name. –
Sandberg remote: error: denying non-fast-forward refs/heads/master (you should pull first) To myserver:/path/to/repo.git ! [remote rejected] HEAD -> master (non-fast-forward) error: failed to push some refs to 'myserver:/path/to/repo.git'
Is this solution for GitHub only? –
Seleucia grep -rl "[email protected]" | xargs sed -i 's/[email protected]/[email protected]/g'
. Source: unix.stackexchange.com/questions/472476/…. –
Calcifuge git update-ref -d refs/original/refs/heads/master
followed by git -c gc.reflogExpireUnreachable=now gc --prune=now
. This removes the reference to the old chain of commits, making them orphans, which the second command will then clear all references to from the reflog and delete (prune) the actual commit blobs (check with git show <one of the old commit hashes>
) –
Sulla --all
flag, it would be interesting to know the reason for the choice of options in your answer. From the "rev-list" manual, I understand that --all
affects the remotes refs in addition to the heads (--branches
) and tags. But I am unsure if I want to change the remote refs as well... –
Sarre --branches --tags
with main..HEAD
or similar –
Landy git shortlog -sne --all
now shows both new and old emails. What am I missing? –
Halfpenny git filter-repo
not work, no any outputs. –
Orland git branch --delete --remotes -- origin/main
to remove the remote tracking branch after I had git update-ref -d refs/original/refs/heads/main
. Then I just delete and recreate the remote repository and push easily. –
Caricaria One liner, but be careful if you have a multi-user repository - this will change all commits to have the same (new) author and committer.
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
With linebreaks in the string (which is possible in 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
in the end of the command? –
Hanuman git push --force --tags origin 'refs/heads/*'
after the advised command –
Dwarf $git push --force --tags origin 'refs/heads/master'
–
Ambiversion $git push --force --tags origin 'refs/heads/main'
–
Intermediary if [ "$GIT_AUTHOR_EMAIL" = "incorrect@email" ]; then
–
Youngran git push --force
–
Hinge You can also do:
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
Note, if you are using this command in the Windows command prompt, then you need to use "
instead of '
:
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
"A previous backup already exists in refs/original/ Force overwriting the backup with -f"
sorry but where the -f
-flag is going to be whene executing this script two times. Actually that is in Brian's answer, sorry about disturbance just after the filter-branch is the solution. –
Redness filter-branch
command. Nice to have if you did something wrong. –
Biron --tag-name-filter cat
switch. –
Mellifluous It happens when you do not have a $HOME/.gitconfig
initialized. You may fix this as:
git config --global user.name "you name"
git config --global user.email [email protected]
git commit --amend --reset-author
Tested with Git version 1.7.5.4.
Note that this fixes only the last commit.
--local
works too –
Mcnully git commit --amend --reset-author --no-edit
command is especially useful if you created commits with the wrong author information, then set the correct author after-the-fact via git config
. Saved my a$$ just now when I had to update my email. –
Cooky In the case where just the top few commits have bad authors, you can do this all inside git rebase -i
using the exec
command and the --amend
commit, as follows:
git rebase -i HEAD~6 # as required
which presents you with the editable list of commits:
pick abcd Someone else's commit
pick defg my bad commit 1
pick 1234 my bad commit 2
Then add exec ... --author="..."
lines after all lines with bad authors:
pick abcd Someone else's commit
pick defg my bad commit 1
exec git commit --amend --author="New Author Name <[email protected]>" -C HEAD
pick 1234 my bad commit 2
exec git commit --amend --author="New Author Name <[email protected]>" -C HEAD
save and exit editor (to run).
This solution may be longer to type than some others, but it's highly controllable - I know exactly what commits it hits.
Thanks to @asmeurer for the inspiration.
exec git commit --amend --reset-author -C HEAD
? –
Collettecolletti Someone else's commit
instead of my bad commit 1
? I just tried HEAD^^
to amend the last 2 commits, and it worked perfectly fine. –
Marisolmarissa git rebase -i HEAD^^^^^^
you can also write git rebase -i HEAD~6
–
Drunken filter-branch
is usually overkill for changing authors -- in my experience it's typically only a few commits that got messed up. And exec
is way less confusing and error-prone than manually running the command on every commit you need to change. –
Parra git -c rebase.instructionFormat="[%an <%ae>] %s" rebase -i COMMITISH
to add the author details to your rebase plan. –
Parra fatal: Needed a single revision
, try git rebase -i --root
instead –
Stig For a single commit:
git commit --amend --author="Author Name <[email protected]>"
(extracted from asmeurer's answer)
git help commit
, git commit --amend
changes the commit at the “tip of the current branch” (which is HEAD). This is normally the most recent commit, but you can make it any commit you want by first checking out that commit with git checkout <branch-name>
or git checkout <commit-SHA>
. –
Weldonwelfare author
and not the committer
–
Hanuman git rebase --exec 'git commit --amend --author="Author Name <[email protected]>"' HEAD~10
(note the commit-time will be modified) –
Ilona GitHub originally had a nice solution (broken link), which was the following shell script:
#!/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"
'
git reset --hard HEAD^
a couple of times on the other local repositories to get them to an earlier version, git pull
-ed the amended version, and here I am without any lines containing unknown <[email protected]>
(got to love git's defaulting). –
Ravelin git push -f
. Also, local repos have to be recloned after this. –
Gowk A single command to change the author for the last N commits:
git rebase -i HEAD~N -x "git commit --amend --author 'Author Name <[email protected]>' --no-edit"
NOTES
- replace
HEAD~N
with the reference until where you want to rewrite your commits. This can be a hash,HEAD~4
, a branch name, ... - the
--no-edit
flag makes sure thegit commit --amend
doesn't ask an extra confirmation - when you use
git rebase -i
, you can manually select the commits where to change the author,
the file you edit will look like this:
pick 897fe9e simplify code a little
exec git commit --amend --author 'Author Name <[email protected]>' --no-edit
pick abb60f9 add new feature
exec git commit --amend --author 'Author Name <[email protected]>' --no-edit
pick dc18f70 bugfix
exec git commit --amend --author 'Author Name <[email protected]>' --no-edit
You can then still modify some lines to see where you want to change the author. This gives you a nice middle ground between automation and control: you see the steps that will run, and once you save everything will be applied at once.
Note that if you already fixed the author information with git config user.name <your_name>
and git config user.email <your_email>
, you can also use this command:
git rebase -i HEAD~N -x "git commit --amend --reset-author --no-edit"
git rebase -i master -x ...
–
Bremble --root
instead of HEAD~N
to edit the entire history (including initial commit), and use --reset-author
to take the current committer instead of --author ...
–
Historical HEAD~4
with the reference until where you want to rewrite your commits... I'll try to make this a little clearer in my answer. As I mentioned before: beware for merge commits where you will get into complicated stuff –
Bremble As docgnome mentioned, rewriting history is dangerous and will break other people's repositories.
But if you really want to do that and you are in a bash environment (no problem in Linux, on Windows, you can use git bash, that is provided with the installation of git), use 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'
To speed things up, you can specify a range of revisions you want to rewrite:
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
is the "make it work" option. –
Tell --tag-name-filter cat
. This really should have been the default behaviour. –
Tell I should point out that if the only problem is that the author/email is different from your usual, this is not a problem. The correct fix is to create a file called .mailmap
at the base of the directory with lines like
Name you want <email you want> Name you don't want <email you don't want>
And from then on, commands like git shortlog
will consider those two names to be the same (unless you specifically tell them not to). See https://schacon.github.io/git/git-shortlog.html for more information.
This has the advantage of all the other solutions here in that you don't have to rewrite history, which can cause problems if you have an upstream, and is always a good way to accidentally lose data.
Of course, if you committed something as yourself and it should really be someone else, and you don't mind rewriting history at this point, changing the commit author is probably a good idea for attribution purposes (in which case I direct you to my other answer here).
You can use this as a alias so you can do:
git change-commits GIT_AUTHOR_NAME "old name" "new name"
or for the last 10 commits:
git change-commits GIT_AUTHOR_EMAIL "[email protected]" "[email protected]" HEAD~10..HEAD
Add to ~/.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 "
Source: https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig
Hope it is useful.
[[ ]]
with sh-compatible test [ ]
(single brackets). Besides that it works very well, thanks! –
Schadenfreude This is a more elaborated version of @Brian's version:
To change the author and committer, you can do this (with linebreaks in the string which is possible in 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
You might get one of these errors:
- The temporary directory exists already
- Refs starting with refs/original exists already
(this means another filter-branch has been run previously on the repository and the then original branch reference is backed up at refs/original)
If you want to force the run in spite of these errors, add the --force
flag:
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
A little explanation of the -- --all
option might be needed: It makes the filter-branch work on all revisions on all refs (which includes all branches). This means, for example, that tags are also rewritten and is visible on the rewritten branches.
A common "mistake" is to use HEAD
instead, which means filtering all revisions on just the current branch. And then no tags (or other refs) would exist in the rewritten branch.
When taking over an unmerged commit from another author, there is an easy way to handle this.
git commit --amend --reset-author
--no-edit
to make this even easier, as generally most people will want to update only the email address and not the commit message –
Valorize A safer alternative to git's filter-branch
is filter-repo
tool as suggested by git docs here.
git filter-repo --commit-callback '
old_email = b"[email protected]"
correct_name = b"Your Correct Name"
correct_email = b"[email protected]"
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
'
The above command mirrors the logic used in this script but uses filter-repo
instead of filter-branch
.
The code body after commit-callback
option is basically python code used for processing commits. You can write your own logic in python here. See more about commit
object and its attributes here.
Since filter-repo
tool is not bundled with git you need to install it separately.
See Prerequisties and Installation Guide
If you have a python env >= 3.5, you can use pip
to install it.
pip3 install git-filter-repo
Note: It is strongly recommended to try filter-repo
tool on a fresh clone. Also remotes are removed once the operation is done. Read more on why remotes are removed here. Also read the limitations of this tool under INTERNALS section.
Note
section. –
Miculek old_email = b\"[email protected]\"
–
Owing run
git rebase -i <sha1 or ref of starting point>
mark all commits that you want to change with
edit
(ore
)loop the following two commands until you have processed all the commits:
git commit --amend --reuse-message=HEAD --author="New Author <[email protected]>"
;git rebase --continue
This will keep all the other commit information (including the dates).
The --reuse-message=HEAD
option prevents the message editor from launching.
I use the following to rewrite the author for an entire repository, including tags and all branches:
git filter-branch --tag-name-filter cat --env-filter "
export GIT_AUTHOR_NAME='New name';
export GIT_AUTHOR_EMAIL='New email'
" -- --all
Then, as described in the MAN page of filter-branch, remove all original refs backed up by filter-branch
(this is destructive, backup first):
git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d
--tag-name-filter cat
. Otherwise your tags will remain on the original chain of commits. The other answers fail to mention this. –
Sally I adapted this solution which works by ingesting a simple author-conv-file
(format is the same as one for git-cvsimport). It works by changing all users as defined in the author-conv-file
across all branches.
We used this in conjunction with cvs2git
to migrate our repository from cvs to git.
i.e. Sample author-conv-file
john=John Doe <[email protected]>
jill=Jill Doe <[email protected]>
The script:
#!/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
I found the presented versions way to aggressive, especially if you commit patches from other developers, this will essentially steal their code.
The version below does work on all branches and changes the author and comitter separately to prevent that.
Kudos to leif81 for the all option.
#!/bin/bash
git filter-branch --env-filter '
if [ "$GIT_AUTHOR_NAME" = "<old author>" ];
then
GIT_AUTHOR_NAME="<new author>";
GIT_AUTHOR_EMAIL="<[email protected]>";
fi
if [ "$GIT_COMMITTER_NAME" = "<old committer>" ];
then
GIT_COMMITTER_NAME="<new commiter>";
GIT_COMMITTER_EMAIL="<[email protected]>";
fi
' -- --all
The fastest, easiest way to do this is to use the --exec argument of git rebase:
git rebase -i -p --exec 'git commit --amend --reset-author --no-edit'
This will create a todo-list that looks like this:
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
...
and this will work all automatically, which works when you have hundreds of commits.
-p
with --root
to change all commits in the history (The -p option is deprecated). And note that this only works after you have corrected the username and email via git config user.name <yourname>
and git config user.email <youremail>
. –
Whoreson Change commit
author name & email
byAmend
, then replacingold-commit with new-one
:$ git checkout <commit-hash> # checkout to the commit need to modify $ git commit --amend --author "name <[email protected]>" # 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
Another way
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 <[email protected]>" # change the author name & email # Save changes and exit the editor $ git rebase --continue # finish the rebase
If you are the only user of this repository, you can rewrite history using either git filter-branch
(as svick wrote), or git fast-export
/git fast-import
plus filter script (as described in article referenced in docgnome answer), or interactive rebase. But either of those would change revisions from first changed commit onwards; this means trouble for anybody that based his/her changes on your branch pre-rewrite.
RECOVERY
If other developers didn't based their work on pre-rewrite version, simplest solution would be to re-clone (clone again).
Alternatively they can try git rebase --pull
, which would fast-forward if there weren't any changes in their repository, or rebase their branch on top of re-written commits (we want to avoid merge, as it would keep pre-rewrite comits forever). All of this assuming that they do not have not comitted work; use git stash
to stash away changes otherwise.
If other developers use feature branches, and/or git pull --rebase
doesn't work e.g. because upstream is not set up, they have to rebase their work on top of post-rewrite commits. For example just after fetching new changes (git fetch
), for a master
branch based on / forked from origin/master
, one needs to run
$ git rebase --onto origin/master origin/master@{1} master
Here origin/master@{1}
is pre-rewrite state (before fetch), see gitrevisions.
Alternate solution would be to use refs/replace/ mechanism, available in Git since version 1.6.5. In this solution you provide replacements for commits that have wrong email; then anybody who fetches 'replace' refs (something like fetch = +refs/replace/*:refs/replace/*
refspec in appropriate place in their .git/config
) would get replacements transparently, and those who do not fetch those refs would see old commits.
The procedure goes something like this:
Find all commits with wrong email, for example using
$ git log [email protected] --all
For each wrong commit, create a replacement commit, and add it to object database
$ 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>
Now that you have corrected commit in object database, you have to tell git to automatically and transparently replace wrong commit by corrected one using
git replace
command:$ git replace <ID of wrong commit> <ID of corrected commit>
Finally, list all replacement to check if this procedure succeded
$ git replace -l
and check if replacements take place
$ git log [email protected] --all
You can of course automate this procedure... well, all except using git replace
which doesn't have (yet) batch mode, so you would have to use shell loop for that, or replace "by hand".
NOT TESTED! YMMV.
Note that you might encounter some rough corners when using refs/replace/
mechanism: it is new, and not yet very well tested.
Note that git stores two different e-mail addresses, one for the committer (the person who committed the change) and another one for the author (the person who wrote the change).
The committer information isn't displayed in most places, but you can see it with git log -1 --format=%cn,%ce
(or use show
instead of log
to specify a particular commit).
While changing the author of your last commit is as simple as git commit --amend --author "Author Name <[email protected]>"
, there is no one-liner or argument to do the same to the committer information.
The solution is to (temporarily, or not) change your user information, then amend the commit, which will update the committer to your current information:
git config user.email [email protected]
git commit --amend
path\to\repo\.git
. I'm not sure yet what you'd need to do to expunge it totally. Amends unfortunately (?) don't seem to erase. –
Mythos For reset ALL commits (including first commit) to current user and current timestamp:
git rebase --root --exec "git commit --amend --no-edit --date 'now' --reset-author"
If the commits you want to fix are the latest ones, and just a couple of them, you can use a combination of git reset
and git stash
to go back an commit them again after configuring the right name and email.
The sequence will be something like this (for 2 wrong commits, no pending changes):
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
One-liner with filter-repo
:
You can use the callbacks feature of git-filter-repo
(a recommended replacement for filter-branch
) to change the name and email associated with all the commits:
git filter-repo --name-callback 'return b"New Name"' --email-callback 'return b"[email protected]"'
This is more performant and potentially more reliable than solutions using filter-branch
.
Note that the above command changes the authors (and committer) of all commits, if you want to effectively "edit" a certain author, and only modify the commits of that specific author, then use the --commit-callback
option like this:
git filter-repo --commit-callback '
old_email = b"[email protected]"
new_email = b"[email protected]"
new_name = b"New Author"
if commit.author_email == old_email:
commit.author_email = new_email
commit.author_name = new_name
if commit.committer_email == old_email:
commit.committer_email = new_email
commit.committer_name = new_name
'
(Just change the old_email
, new_email
, and new_name
variables in the command above to the right values.)
If you are using Eclipse with EGit, then there is a quite easy solution.
Assumption: you have commits in a local branch 'local_master_user_x' which cannot be pushed to a remote branch 'master' because of the invalid user.
- Checkout the remote branch 'master'
- Select the projects/folders/files for which 'local_master_user_x' contains changes
- Right-click - Replace with - Branch - 'local_master_user_x'
- Commit these changes again, this time as the correct user and into the local branch 'master'
- Push to remote 'master'
Using interactive rebase, you can place an amend command after each commit you want to alter. For instance:
pick a07cb86 Project tile template with full details and styling
x git commit --amend --reset-author -Chead
;-)
. –
Fichu We have experienced an issue today where a UTF8 character in an author name was causing trouble on the build server, so we had to rewrite the history to correct this. The steps taken were:
Step 1: Change your username in git for all future commits, as per instructions here: https://help.github.com/articles/setting-your-username-in-git/
Step 2: Run the following bash script:
#!/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
Quick overview: Checkout your repository to a temp file, checkout all the remote branches, run the script which will rewrite the history, do a force push of the new state, and tell all your colleagues to do a rebase pull to get the changes.
We had trouble with running this on OS X because it somehow messed up line endings in commit messages, so we had to re-run it on a Linux machine afterwards.
Your problem is really common. See "Using Mailmap to Fix Authors List in Git"
For the sake of simplicity, I have created a script to ease the process: git-changemail
After putting that script on your path, you can issue commands like:
Change author matchings on current branch
$ git changemail -a [email protected] -n newname -m [email protected]
Change author and committer matchings on <branch> and <branch2>. Pass
-f
to filter-branch to allow rewriting backups$ git changemail -b [email protected] -n newname -m [email protected] -- -f <branch> <branch2>
Show existing users on repo
$ git changemail --show-both
By the way, after making your changes, clean the backup from the filter-branch with: git-backup-clean
chmod +x git-changemail
–
Youngran I want to add my Example too. I want to create a bash_function with given parameter.
this works in 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:-"[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;
}
If you want to (easily) change the author for the current branch I would use something like this:
# update author for everything since origin/master
git rebase \
-i origin/master \
--exec 'git commit --amend --no-edit --author="Author Name <[email protected]>"'
origin/master
just produces a noop
rebase for me. Shouldn't this be git rebase -i --root --exec 'git commit --amend --no-edit --author="Author Name <[email protected]>"'
–
Heliometer Try this out. It will do the same as above mentioned, but interactively.
bash <(curl -s https://raw.githubusercontent.com/majdarbash/git-author-change-script/master/run.sh)
Reference: https://github.com/majdarbash/git-author-change-script
For all the commits, my solution:
git rebase -i --root -x "git commit --amend --author 'bedorlan <[email protected]>' --no-edit"
-i
, it works great. –
Vaivode So, I had to edit email address for many past commits. Because I want to put the email of my main GitHub account so that it will show-up in the constribution graph, all within one account.
First make sure your remote is in sink with the local. Push and pull to make this possible. Then use rebase
git rebase -r --root --exec "git commit --amend --no-edit --reset-author"
You can check in your 'git log' that email got set appropriately. Now you need to push the updated history to the remote. Use:
git push -f
That '-f' is important otherwise you will get:
To github.com:UserName/RepoName.git
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'github.com:UserName/RepoName.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
If you are the only user of this repo or you don't care about possibly breaking the repo for other users, then yes. If you've pushed these commits and they exist where somewhere else can access them, then no, unless you don't care about breaking other people's repos. The problem is by changing these commits you will be generating new SHAs which will cause them to be treated as different commits. When someone else tries to pull in these changed commits, the history is different and kaboom.
This page http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html describes how to do it. (I haven't tried this so YMMV)
refs/replace/
mechanism. –
Decadence All the answers above rewrite the history of the repository.
As long as the name to change has not been used by multiple authors and especially if the repository has been shared and the commit is old I'd prefer to use .mailmap
, documented at https://git-scm.com/docs/git-shortlog.
It allows mapping incorrect names/emails to the correct one without modifying the repo history. You can use lines like:
Proper Name <[email protected]> <root@localhost>
For those under windows, you could also use the git-rocket-filter tool.
From the documentation:
Change commit author name and email:
git-rocket-filter --branch TestBranch --commit-filter '
if (commit.AuthorName.Contains("Jim")) {
commit.AuthorName = "Paul";
commit.AuthorEmail = "[email protected]";
}
I would like to contribute with a modification of @Rognon answer. This answer is just another alternative in case the selected answer or others don't work for you (in my particular issue that was the case):
Objective: You will fix one or more authors with a correct one in the ALL the history, and you will get a clean history without duplicates. This method works by replacing 'master' branch with a 'clean' branch (its not using merge/rebase)
NOTE: Anyone using the "master" repository may need to checkout it again (after performing these steps) before pushing, as merge may fail.
We will use a new branch named "clean" to perform the operations (assuming you want to fix "master"):
git checkout -b clean
(be sure you are in the "clean" branch: git branch
)
Modify the following script (replacing the email addresses and names). Note that this script expects two wrong emails/authors (as example), so if you only need to fix a single author, you can remove the second part of the condition or leave it like that (as it will be ignored as it won't match).
Execute the script.
#/bin/bash
git filter-branch --force --commit-filter '
if [ "$GIT_COMMITTER_EMAIL" = "[email protected]" -o "$GIT_COMMITTER_EMAIL" = "[email protected]" ];
then
export GIT_COMMITTER_NAME="John Doe";
export GIT_AUTHOR_NAME="John Doe";
export GIT_COMMITTER_EMAIL="[email protected]";
export GIT_AUTHOR_EMAIL="[email protected]";
fi;
git commit-tree "$@"
' --tag-name-filter cat -- --all
It has to report: Ref 'refs/heads/clean' was rewritten
. If it reports "unchanged", maybe the email(s) entered in the script is wrong.
Confirm the history has been corrected with: git log
If you are using github/gitlab (recommended = safe):
- create the "clean" branch in remote:
git push --set-upstream origin clean
- set "clean" branch as default branch
- remove "master" (be sure everything is as expected before doing this).
- Create a new branch "master" based in "clean" branch.
- After confirming all is good, you can remove "clean" branch now (alternative you can just rename it).
If are not using github/gitlab or you prefer doing it by command:
- Delete the master branch from local:
git branch -d master
- Rename the branch:
git branch -m clean master
- Push it (be sure you "master" is unprotected)
git push --force origin master
git rebase -i YOUR_FIRTS_COMMIT_SHA^
while true; do git commit --amend --author="Name Surname <[email protected]>" --no-edit && git rebase --continue; done
Press ^C # after the rebase is done (the loop will keep updating last commit)
I have tried the scripts above it did not work for me, this fixed my issue:
use Git's "filter-branch" command. It allows you to batch-process a (potentially large) number of commits with a script. You can run the below sample script in your repository (filling in real values for the old and new email and name):
git filter-branch --env-filter '
WRONG_EMAIL="[email protected]"
NEW_NAME="New Name Value"
NEW_EMAIL="[email protected]"
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
See more details here
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 "[email protected]"
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 == "[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 "\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.
© 2022 - 2024 — McMap. All rights reserved.
refs/replace/
mechanism. – Decadencegit reset --hard origin/master
or justgit pull origin
(which should fast-forward). If they based their change, they have to rebase usinggit rebase origin/master
or justgit pull --rebase origin
(the commands are only examples). – Decadence