How do I change the author and committer name/email for multiple commits?
Asked Answered
O

42

3047

How do I change the author for a range of commits?

Oregano answered 15/4, 2009 at 3:9 Comment(10)
Question: does using git filter-branch preserve the SHA1's for previous tags, versions and objects? Or will changing the author name force change the associated SHA1's as well?Stribling
Or you can try to use refs/replace/ mechanism.Meow
After rewrite, if they didn't base their work on history pre-rewrite, just git reset --hard origin/master or just git pull origin (which should fast-forward). If they based their change, they have to rebase using git rebase origin/master or just git pull --rebase origin (the commands are only examples).Meow
Hashes will change yesDistefano
Tangentially, I created a small script which finally fixed the root cause for me. gist.github.com/tripleee/16767aa4137706fd896cMedick
@Medick I really enjoy your link/response, but this question is about 5 years old at this point. This could become a great Q&A post to make here for other users (answer your own question). It would be more useful, descriptive, and visible than a mere comment here.Douse
@impinball The age of the question is hardly relevant. Creating a new duplicate question is out of the question. I suppose I could create a question which begs this particular answer but I'm not altogether convinced it would get all that much visibility. It's not like there is a shortage of Git questions here... Glad I could help, anyway.Medick
this answer is much simpler @Flávio Amieiro https://mcmap.net/q/12496/-how-do-i-change-the-author-and-committer-name-email-for-multiple-commitsPropensity
The github script that @TimurBernikovich mentioned is great and works for me. But that github url has changed: docs.github.com/en/enterprise/2.17/user/github/using-git/…Imperative
The answers might be overkill. First check whether this satisfies your usecase - https://mcmap.net/q/12740/-git-rewrite-previous-commit-usernames-and-emailsMiler
S
1563

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
Salinasalinas answered 15/4, 2009 at 3:16 Comment(33)
After executing the script you may remove the backup branch by executing "git update-ref -d refs/original/refs/heads/master".Recapitulation
@rodowi, it duplicates all my commits.Shonda
@RafaelBarros the author info (just like anything else in the history) is part of the commit's sha key. Any change to the history is a rewrite leading to new id's for all commits. So don't rewrite on a shared repo or make sure all users are aware of it ...Gouache
Make sure to checkout a bare repository before running the github script (--bare option). Otherwise it'll flatten your branches.Burkhard
I'm working in a multi-user repository but I see that my commits were never hooked to my account. Will this safely amend only my commits which are currently submitted as "Unknown author"(but it does have a name) to my Github account?Hawaii
Is there any way to fix the duplicated commits to have only ones from fixed author's email?Refraction
You can use https://mcmap.net/q/12496/-how-do-i-change-the-author-and-committer-name-email-for-multiple-commits to just affect say the last ten commitsReluctant
For the first time it works perfect. But second time I got this error: Cannot create a new backup. A previous backup already exists in refs/original/ Force overwriting the backup with -fGroark
This works, locally. But what if I want to push the changes to a remote? The command git push --force --tags origin 'refs/heads/*' mentioned in the GitHub docs gives me Everything up-to-dateLindi
Solved using git push --force --tags origin HEAD:masterLindi
@rodowi Why have they recommended the use of git push --force --tags origin 'refs/heads/*' over git push -f origin master? I've tried both, the results seem to be the same.Pubescent
Beware, I used this script on a very simple branch that I used to create a PR and it completely messed up the historySemiyearly
IMPORTANT!!! Before executing the script, set your user.name and user.email git config parameter properly! And after executing the script you'll have some duplicate backup history called "original"! Delete it via 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!Biforked
By the way: .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.Biforked
I've created an interactive script based on this answer - to avoid reediting the script if you have multiple authors to modify gist.github.com/yaronuliel/8157d7318de988f4399f561d466e12f3Mucosa
Got me 90% of the way there but didn't rewrite my tags with new commit SHAs. I found this example which, when I followed it, updated the commit SHA on my tag.Issiah
I also wrote a convenient script to replace author/committer name/email. It can be found here: github.com/frz-dev/utilities/blob/master/git/…Uchida
Just a comment to say that once you have used one of the answers below, before updating remote, use the following command to see the updated info: 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.Finnegan
This (i.e. the GitHub solution) does not work on a mac or debian. In both cases, I am unable to subsequently push the changes. I always get the following: 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?Busty
For anyone getting the error in my previous comment, this was the issue for me: How to force push a reset to remote repository?Busty
Even after running this script, some of the metadata sitting around in your project's .git folder may still contain the old email address, which can be confusing when grepping the folder. To find and replace the old email address in that metadata with the new email address, use this command: grep -rl "[email protected]" | xargs sed -i 's/[email protected]/[email protected]/g'. Source: unix.stackexchange.com/questions/472476/….Paperweight
please pay attention that OLD_EMAIL is case sensitiveIkon
for some reason the old gh help article you linked was removed. script still works but its odd.Dowery
I think I've found a (seemingly safe) way to delete the old entries in the reflog and the original (now orphaned) commits which still contain the old author details. First run the script as described in this answer, then run 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>)Incorrect
The "filter-branch" manpage simply uses the --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...Golgotha
The answers might be overkill. First check whether this satisfies your usecase - https://mcmap.net/q/12740/-git-rewrite-previous-commit-usernames-and-emailsMiler
I had many existing branches, so "git update-ref -d refs/original/refs/heads/master" alone was not sufficient to clean up the backup refs (under .git/refs/original). What did work was: "ls .git/refs/original/refs/heads/ | xargs -I {} git update-ref -d refs/original/refs/heads/{}"Iva
This is useful for me when I clone a new repo and forget to specify my work email. To run it only on a branch, replace --branches --tags with main..HEAD or similarBoarhound
After doing all this, as well as cleaning the backup, the git shortlog -sne --all now shows both new and old emails. What am I missing?Bittersweet
Can this script be combined with commit signature -S flag?Overdevelop
git filter-repo not work, no any outputs.Layla
This preserves timestamtp!Jackstraw
Was lucky in my case that the remote repository still had no other contributors, so I just execute 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.Desalvo
A
2210

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.

Article answered 24/8, 2009 at 3:8 Comment(52)
That would take forever if you had even as few as a hundred commits to pick through.Swafford
Great for the odd commit though - useful if you're pairing and forget to change the authorSuspender
+1 for mentioning the usecase for the typical one-mistake fix: git commit --amend --author=usernameBogbean
This is perfect, my most common usecase is that I sit down at another computer and forget to set up author and thus usually have < 5 commits or so to fix.Scratchboard
@Mike, I totally agree. 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).Article
So even if you want to streamline it for multiple commits, it would be better to script git rebase and git commit --amend than to use git filter-branch.Article
I needed to use git push -f in order to push this to my repository host (git push -f -u origin master)Meteorograph
Yes, this rewrites history! See my other response if you want to avoid that.Article
Instead of specifying an explicit author name and email, you could just use the option --reset-author when amending (assuming you want to use the values from your git configuration), i.e. 'git commit --amend --rebase-author'Morpheus
you can do more automation in this by reusing the commit message in the file .git/rebase-merge/message. this would prevent opening the editor altogether using -f .git/rebase-merge/message :-)Ory
I get stackoverflow.com/questions/19864934/… . How do I push the edited commit to replace the already pushed one?Montenegro
You have to use -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.Article
+1, although the command in 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)Mayoralty
That would be even simpler if using the --no-edit option, it'll keep the message of the amended commit.Wintertime
git commit --amend --reset-author also works once user.name and user.email are configured correctly.Meleager
Just FYI, I got "malformed --author parameter" when I first tried this. Tried again without my email address and it worked (and inserted my email address into the actual commit).Sophiesophism
Rewrite author info on all commits after <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!Coimbatore
But the typical use-case here is to set the author as something other than the author in .gitconfig.Article
you should not that this of course changes the author on all commits since the commit you're rebasing from. Not just your ownEngineman
To include the first commit you can use git rebase -i --rootOutofdoors
Both solutions create new commits, but github's one (see accepted answer) keeps the rest of the metadata (e.g. commit date) intact.Cabinetmaker
What if there is no e-mail set? I have been committing and pushing to a repo and now, on GitHub the account which says that committed, doesn't have any avatar or mail. Could you help me, please?Chemnitz
@TomaRadu-Petrescu you will have better luck getting an answer if you ask a new question. There's not enough space in the comments to give a full answer to your question.Article
Note that this resets the commit dateRod
To re-iterate what nandilugio and Jorjon said: This overwrites all of the commit's timestamps. Use the filter-branch script from the accepted answer if you don't want to clobber the metadata.Proximate
This will change commits SHA1s. So you keep it in mindGallagher
@Gallagher yes, I've added a note at the top and a link to my other answer which shows how to just fix name spellings or alternate emails without rewriting history (using .mailmap).Article
after all do "git push -f"Alaynaalayne
Use --rebase-merges (-p is deprecated)Staffman
I guess push is missingHitt
Dude! You're a lifesaver, I just used your instructions step by step and they saved me a whole bunch of time (I had accidentally pushed several commits with my job key into a public personal library I have and had seriously contemplated copying and pasting again all that work). THANKS!Ida
One line: git rebase -i --rebase-merges --root; while true; do git commit --amend --no-edit --author "Theodore R. Smith <[email protected]>"; git rebase --continue; doneWeevily
@TheodoreR.Smith does this execute without the need for user interaction? I assumed that's what the -i option for the rebase command was for.Asclepiadaceous
The answers might be overkill. First check whether this satisfies your usecase - https://mcmap.net/q/12740/-git-rewrite-previous-commit-usernames-and-emailsMiler
I'm getting unknown switch -r. If I remove that it works fine.Rundle
note that --author does not change commit date, while --reset-author does.Gussi
@Article How to amend including the very first commit, not just the commits after it. Also if possible, how to preserve timestamps while doing it?Bruit
CAREFUL this changes also the commit date! which is not what I wanted. I ruined all my commits history with the solution aboveHereon
@Bruit use --root and specify --author as in Theodore R. Smith's commentHydrastis
@transang --author preserves author-date, but not commit-date (which is shown by default on github). see stackoverflow.com/questions/28536980/…Hydrastis
@Hydrastis I found solution to my comment above: First set new author using 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/…Bruit
@Bruit at that point of command length, I might as well used the git-filter-branch strategy which I have used multiple times without any issuesHydrastis
@Hydrastis yes, didn't know it could be done like that also. Seems like a neat solutionBruit
I think this is not perfect as the 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.Lotze
in some cases where merge commit presents, 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 rebaseIndent
Please add a disclaimer that all the dates will be reset with this answer. Wish I had seen that first, but it was in a collapsed comment.Hampshire
I tried this and after the rebase all my release tags were left pointing to the old commits. In order to reapply the tags on the new commits I had to do this: 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; doneStichous
this is work for me thanks @Article and StackOverflow :)Alita
As mentioned by DharmaTurtle you can preserve timestamps with this command 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 historyJackstraw
what Versions are "pre-July 2020" can you specify this?Whitherward
I like this answer for its simplicity, but it overwrites the commit dates to the current date. To preserve dates, there's a one liner that I found very useful.Cuprite
Only issue is, as noted, with the commit dates (although this can be great if you want to show high activity in your profile :D). One can push the changes by using -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).Engud
S
1563

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
Salinasalinas answered 15/4, 2009 at 3:16 Comment(33)
After executing the script you may remove the backup branch by executing "git update-ref -d refs/original/refs/heads/master".Recapitulation
@rodowi, it duplicates all my commits.Shonda
@RafaelBarros the author info (just like anything else in the history) is part of the commit's sha key. Any change to the history is a rewrite leading to new id's for all commits. So don't rewrite on a shared repo or make sure all users are aware of it ...Gouache
Make sure to checkout a bare repository before running the github script (--bare option). Otherwise it'll flatten your branches.Burkhard
I'm working in a multi-user repository but I see that my commits were never hooked to my account. Will this safely amend only my commits which are currently submitted as "Unknown author"(but it does have a name) to my Github account?Hawaii
Is there any way to fix the duplicated commits to have only ones from fixed author's email?Refraction
You can use https://mcmap.net/q/12496/-how-do-i-change-the-author-and-committer-name-email-for-multiple-commits to just affect say the last ten commitsReluctant
For the first time it works perfect. But second time I got this error: Cannot create a new backup. A previous backup already exists in refs/original/ Force overwriting the backup with -fGroark
This works, locally. But what if I want to push the changes to a remote? The command git push --force --tags origin 'refs/heads/*' mentioned in the GitHub docs gives me Everything up-to-dateLindi
Solved using git push --force --tags origin HEAD:masterLindi
@rodowi Why have they recommended the use of git push --force --tags origin 'refs/heads/*' over git push -f origin master? I've tried both, the results seem to be the same.Pubescent
Beware, I used this script on a very simple branch that I used to create a PR and it completely messed up the historySemiyearly
IMPORTANT!!! Before executing the script, set your user.name and user.email git config parameter properly! And after executing the script you'll have some duplicate backup history called "original"! Delete it via 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!Biforked
By the way: .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.Biforked
I've created an interactive script based on this answer - to avoid reediting the script if you have multiple authors to modify gist.github.com/yaronuliel/8157d7318de988f4399f561d466e12f3Mucosa
Got me 90% of the way there but didn't rewrite my tags with new commit SHAs. I found this example which, when I followed it, updated the commit SHA on my tag.Issiah
I also wrote a convenient script to replace author/committer name/email. It can be found here: github.com/frz-dev/utilities/blob/master/git/…Uchida
Just a comment to say that once you have used one of the answers below, before updating remote, use the following command to see the updated info: 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.Finnegan
This (i.e. the GitHub solution) does not work on a mac or debian. In both cases, I am unable to subsequently push the changes. I always get the following: 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?Busty
For anyone getting the error in my previous comment, this was the issue for me: How to force push a reset to remote repository?Busty
Even after running this script, some of the metadata sitting around in your project's .git folder may still contain the old email address, which can be confusing when grepping the folder. To find and replace the old email address in that metadata with the new email address, use this command: grep -rl "[email protected]" | xargs sed -i 's/[email protected]/[email protected]/g'. Source: unix.stackexchange.com/questions/472476/….Paperweight
please pay attention that OLD_EMAIL is case sensitiveIkon
for some reason the old gh help article you linked was removed. script still works but its odd.Dowery
I think I've found a (seemingly safe) way to delete the old entries in the reflog and the original (now orphaned) commits which still contain the old author details. First run the script as described in this answer, then run 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>)Incorrect
The "filter-branch" manpage simply uses the --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...Golgotha
The answers might be overkill. First check whether this satisfies your usecase - https://mcmap.net/q/12740/-git-rewrite-previous-commit-usernames-and-emailsMiler
I had many existing branches, so "git update-ref -d refs/original/refs/heads/master" alone was not sufficient to clean up the backup refs (under .git/refs/original). What did work was: "ls .git/refs/original/refs/heads/ | xargs -I {} git update-ref -d refs/original/refs/heads/{}"Iva
This is useful for me when I clone a new repo and forget to specify my work email. To run it only on a branch, replace --branches --tags with main..HEAD or similarBoarhound
After doing all this, as well as cleaning the backup, the git shortlog -sne --all now shows both new and old emails. What am I missing?Bittersweet
Can this script be combined with commit signature -S flag?Overdevelop
git filter-repo not work, no any outputs.Layla
This preserves timestamtp!Jackstraw
Was lucky in my case that the remote repository still had no other contributors, so I just execute 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.Desalvo
L
761

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
Libreville answered 15/4, 2009 at 3:22 Comment(12)
Minor point, the export is actually superfluous although it does no harm. e.g. git-filter-branch --env-filter "GIT_AUTHOR_NAME='New name';GIT_AUTHOR_EMAIL='New email'" HEAD.Tsuda
Why does it rewrite all commits if you specify HEAD in the end of the command?Quirinus
This does not work for my bitbucket repository, any idea ? I do a git push --force --tags origin 'refs/heads/*' after the advised commandKerrison
I spent hours looking for a solution that worked in PowerShell and this finally solved it for me. I would +1, but apparently, I already did. :)Frigidarium
The push command for this is : $git push --force --tags origin 'refs/heads/master' Alicealicea
Neat; this keeps the old timestamps too.Portia
@HARSHNILESHPATHAK Note that for recently created repositories the branch master has been renamed main, so the command becomes $git push --force --tags origin 'refs/heads/main'Irrigate
All you have to do is to pass if [ "$GIT_AUTHOR_EMAIL" = "incorrect@email" ]; thenKordofanian
The answers might be overkill. First check whether this satisfies your usecase - https://mcmap.net/q/12740/-git-rewrite-previous-commit-usernames-and-emailsMiler
Coming back to this a few years later, I got scared off this answer based on the warning. I eventually formulated my own answer which keeps the old author and committer timestamps.Portia
To push you need to force with: git push --forceParamount
Sincerely thank you! The multi-liners were failing for me for whatever reason. This worked perfectly. Thanks a bunch!Venusberg
C
621

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
Cannula answered 15/5, 2009 at 19:15 Comment(15)
Isn't using env-filter the easier solution? Not sure why this is getting more votes, then.Mordecai
Then link is broken. How do we push these changes to another repository?Pyrometallurgy
env-filter will change all the commits. This solution allows a conditional.Deposal
"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.Pinnati
+1 to this answer, works perfectly for me out-of-the-box. On my local GIT setup in my work I had used my GitHub email for commits. I wanted my corporate email to be present in my work GIT repo commits because my GitHub and work Git were entirely separate projects altogether. This script worked great for this purpose.Erse
There is a problem with this solution. It duplicates previous commits. So, if you had 200 commits after running this command you will get 400 commits.Hinterland
To those who find themselves having duplicates in their commit history, please read refer to this post: https://mcmap.net/q/12746/-git-duplicate-commit-issueHinterland
It's not duplicates, it's backup from running the filter-branch command. Nice to have if you did something wrong.Mordecai
@Deposal env-filter also allows a conditional; look at my answer :-)Mordecai
If you've got tags, you probably want to use --tag-name-filter cat switch.Lariat
Use ref..head to only run the rewrite on a certain collection of commits. You rarely want to rewrite the entire history.Lorola
these modifications can be pushed with the -f --force swithCruller
Is it possible to adapt this to rename multiple emails in a single pass? I asked a new question about this at stackoverflow.com/questions/33866185/…Namara
To prevent duplications and be safe, I added an answer based in this one (with minor modifications and other explanations replacing the master branch with a clean one).Qualifier
The answers might be overkill. First check whether this satisfies your usecase - https://mcmap.net/q/12740/-git-rewrite-previous-commit-usernames-and-emailsMiler
B
250

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.

Baseless answered 16/2, 2012 at 9:46 Comment(3)
That works really well on the last commit. Nice and simple. Doesn't have to be a global change, using --local works tooPentstemon
This one was the big winner for me! The 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.Burhans
The answers might be overkill. First check whether this satisfies your usecase - https://mcmap.net/q/12740/-git-rewrite-previous-commit-usernames-and-emailsMiler
S
207

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.

Saporous answered 8/12, 2011 at 17:5 Comment(14)
Definitely awesome. Can you shorten it by setting user.name and user.email in the repo's local config, and then each line is onlyexec git commit --amend --reset-author -C HEAD ?Cohn
The canonical answer, to use filter-branch, just deleted refs/heads/master for me. So +1 to your controllable, editable solution. Thanks!Newel
Why do you start with 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.Propinquity
In place of git rebase -i HEAD^^^^^^ you can also write git rebase -i HEAD~6Behindhand
Please note that this changes the timestamp of the commits. See https://mcmap.net/q/12748/-git-rebase-without-changing-commit-timestamps for reverting to the correct timestampsDin
I think we can also make our lives easier if we defined an alias for the exec commandShorts
I tried many other solutions but this one was the one that worked the best for me :)Mercado
This needs to be higher. 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.Demodena
Also, by default rebase doesn't show which commits have which authors, so if wrong authors are mixed with right authors this might be a little annoying to get right. In that case, you can use git -c rebase.instructionFormat="[%an <%ae>] %s" rebase -i COMMITISH to add the author details to your rebase plan.Demodena
This indeed did the trick for me nicely and quickly when I only have a few commits done with a wrong identity.Kagera
For anyone else struggling with the same problem as me, if you are trying to include the initial commit and you get fatal: Needed a single revision, try git rebase -i --root insteadUnconsidered
The answers might be overkill. First check whether this satisfies your usecase - https://mcmap.net/q/12740/-git-rewrite-previous-commit-usernames-and-emailsMiler
@Miler your referenced question and answer only apply to github, the website. The OPs question is clearly not about github, so your comment is not appropriate on this question.Saporous
It's just that Github has the most number of users. And it is highly probable that anyone who lands on this page is a GitHub user (Like me). That's why I mentioned, "whether this satisfies your use-case".Miler
L
193

For a single commit:

git commit --amend --author="Author Name <[email protected]>"

(extracted from asmeurer's answer)

Lobation answered 26/4, 2010 at 22:50 Comment(6)
but that's only if it's the most recent commitMarijn
According to 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>.Tishtisha
But if you do that, all of the commits that already have that commit as a parent will be pointing to the wrong commit. Better to use filter-branch at that point.Inae
@JohnGietzen: You can rebase the commits back onto the one that's changed to fix that. However, if you're doing >1 commit, then as mentioned, filter-branch is probably going to be a lot easier.Afeard
Note that this changes only commit author and not the committerQuirinus
And to correct the top 10 commits: git rebase --exec 'git commit --amend --author="Author Name <[email protected]>"' HEAD~10 (note the commit-time will be modified)Pernick
U
123

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"
'
Usually answered 7/10, 2010 at 9:54 Comment(6)
Worked perfectly. Just had to 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).Prolate
I cannot push after this. Do I have to use "-f"?Coincident
I did git push -f. Also, local repos have to be recloned after this.Coincident
If you need to run the shell script on a specific branch you can change the last line into: "' master..your-branch-name" (assuming you branched of master).Teodoro
Click on the link <nice solution> as the script has been updatedFaubourg
The link is broken and not in the web archive. :/Wampler
R
102

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 the git 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"
Roti answered 25/1, 2019 at 10:14 Comment(8)
I used HEAD~8 and it shows way more than the last 8 commits.Prolactin
@BryanBryce if there are merge commits involved, things get complicated :)Roti
@ChrisMaes Ah, I see what's going on. I don't want to mess with those, just on the branch I'm on.Prolactin
In that case, supposed you branched from master, you could: git rebase -i master -x ...Roti
You use --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 ...Latialatices
My use case was that I had to change all past commits in some private repositories because my pushes were under a different username with no email attached. The first bit allowed me to change the author and email for the first N commits but it did not preserve the commit timestamps, those got updated along with it. I solved this by using this script. It is nice and clean and allows me to change the entire commit history to a single username and email while preserving the commit timestamps.Ule
For some reason this just edited my 5 last commits email (from 142), got different results on multiple repositoriesAffected
@PedroHenrique: you need to replace 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 stuffRoti
G
88

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
Germanophobe answered 4/8, 2010 at 0:52 Comment(4)
Do note that this will leave any tags pointing at the old commits. --tag-name-filter cat is the "make it work" option.Asteriated
@romkyns any idea on how to change tags as well?Quirinus
@NickVolynkin Yes, you specify --tag-name-filter cat. This really should have been the default behaviour.Asteriated
The answers might be overkill. First check whether this satisfies your usecase - https://mcmap.net/q/12740/-git-rewrite-previous-commit-usernames-and-emailsMiler
A
66

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).

Article answered 28/2, 2012 at 23:57 Comment(3)
Actually this is a very interesting answer. In my case I made some commits from home and it may be confusing an extra author so this is all I needed.Maldonado
Also, notice this does not works for web side on Gitea.Maldonado
@iuliu.net I'm not sure. This question stackoverflow.com/questions/53629125/… seems to suggest it does, but I haven't confirmed it. Certainly if they don't then they ought to, because it's a standard part of git.Article
P
54

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.

Propensity answered 1/8, 2012 at 23:9 Comment(5)
"git: 'change-commits' is not a git command. See 'git --help'."Portfolio
After this command & sync with master all commits in the history are duplicated! Even of other users :(Ethics
@Ethics that is expected, please study about changing history in gitPropensity
For me it seems to run in /bin/sh, so I had to replace the bash-specific test [[ ]] with sh-compatible test [ ] (single brackets). Besides that it works very well, thanks!Hunch
@Portfolio You need this: git config --global 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"Prunella
M
50

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:

  1. The temporary directory exists already
  2. 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.

Mordecai answered 9/12, 2011 at 10:23 Comment(2)
Kudos for supplying a procedure that changes commits on all refs/branches.Northward
Doesn't work on Windows fatal: $GIT_COMMITTER_NAME: no such path in the working tree.Hudibrastic
S
49

When taking over an unmerged commit from another author, there is an easy way to handle this.

git commit --amend --reset-author

Scrophulariaceous answered 23/3, 2016 at 22:23 Comment(5)
For a single commit, and if you wanna put your username, this is most easy way.Mannino
You can add --no-edit to make this even easier, as generally most people will want to update only the email address and not the commit messageConclude
Can you guys please share the git command for just to update last commit's email/username with the new oneAmphora
Did you try this? That should be a side effect of this, if not https://mcmap.net/q/12496/-how-do-i-change-the-author-and-committer-name-email-for-multiple-commits looks like a good path.Scrophulariaceous
Note that this changes the author dateIdeal
R
42

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.

Ruthenious answered 22/4, 2020 at 15:3 Comment(4)
This seems to be the new kid on the block and I cherish this answer like gold. remember the fields have to be binary and then remove the == lines, and You can unconditionally change everything before pushing. Did I say I like this answer? It should be the accepted one.Squabble
Thank you for sharing the links in the Note section.Lotze
To get this to work on Windows, I had to escape all the double quotes: old_email = b\"[email protected]\"Julianjuliana
Thanks a lot, this solved my case 🙏Jeannettajeannette
S
28
  1. run git rebase -i <sha1 or ref of starting point>

  2. mark all commits that you want to change with edit (or e)

  3. 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.

Significance answered 4/10, 2012 at 2:22 Comment(1)
This doesn't update the committer. If you want to update the author and committer while keeping the dates, you may be interested in my answerPortia
W
25

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
Whitewing answered 16/2, 2011 at 15:27 Comment(1)
It's very important to use --tag-name-filter cat. Otherwise your tags will remain on the original chain of commits. The other answers fail to mention this.Anacreon
B
21

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
Bemused answered 4/2, 2011 at 22:46 Comment(1)
Thanks, I wonder why this is not core git (or git-svn) functionality. This can be done with a flag for git svn clone, but not in git filter-branch...Ignominious
D
19

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
Decade answered 23/4, 2012 at 8:46 Comment(0)
M
19

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.

Marylnmarylou answered 25/10, 2018 at 16:19 Comment(2)
You can replace -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>.Arrest
I have a repository that I've been working on with another contributor. I want to change all my commits' credentials. Is your suggestion safe to use in this case, to avoid any modification on the other contributor's commits?Addia
C
18
  1. Change commit author name & email by Amend, then replacing old-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 
    
  2. 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
    
Convert answered 14/12, 2016 at 15:1 Comment(1)
Very nice answer. I like that the changes are wrapped up from the very update to even cleaning up the git commitsHostel
U
9

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:

  1. Find all commits with wrong email, for example using

    $ git log [email protected] --all
    
  2. 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>
    
  3. 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>
    
  4. 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.

Unscrupulous answered 4/8, 2010 at 9:41 Comment(0)
E
8

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
Export answered 5/12, 2013 at 21:21 Comment(1)
Note that the old value is still in a few places in 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.Jaws
S
8

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"
Sension answered 11/12, 2021 at 15:22 Comment(1)
this will work only for the current branch.Roti
A
6

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
Abey answered 30/9, 2011 at 18:4 Comment(0)
K
6

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.)

Karykaryl answered 4/11, 2022 at 10:20 Comment(0)
S
5

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.

  1. Checkout the remote branch 'master'
  2. Select the projects/folders/files for which 'local_master_user_x' contains changes
  3. Right-click - Replace with - Branch - 'local_master_user_x'
  4. Commit these changes again, this time as the correct user and into the local branch 'master'
  5. Push to remote 'master'
Shrew answered 24/8, 2011 at 17:54 Comment(0)
T
5

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
Terraqueous answered 26/2, 2013 at 13:19 Comment(1)
The problem with this is that other commit metadata (e.g. date and time) is also amended. I just found that out the hard way ;-).Nertie
T
5

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.

Tophet answered 22/10, 2014 at 3:32 Comment(0)
T
5

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 &lt;branch> &lt;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

Tilda answered 3/1, 2015 at 12:19 Comment(2)
when i run your command, it says "fatal: cannot exec 'git-changemail': Permission denied"Rhodos
@Rhodos You need to set the execute permission for the script chmod +x git-changemailKordofanian
H
4

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;
}
Hale answered 28/9, 2017 at 14:15 Comment(0)
A
4

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]>"'
Azeria answered 5/5, 2020 at 14:51 Comment(1)
Running this against 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]>"'Hawser
O
3

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

Organogenesis answered 5/6, 2018 at 20:4 Comment(0)
H
3

For all the commits, my solution:

git rebase -i --root -x "git commit --amend --author 'bedorlan <[email protected]>' --no-edit"
Higa answered 27/9, 2021 at 16:56 Comment(1)
Without -i, it works great.Appoggiatura
B
3

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.
Bundestag answered 11/2 at 4:52 Comment(0)
P
2

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)

Precious answered 4/8, 2010 at 0:40 Comment(3)
So, there is no safe way to rewrite the user.email. Without blowing up everyone else. I knew that rewriting history was a bad idea, I just thought that there might be a clean way to do it safely. Thanks.Moberg
@mediaslave: Try refs/replace/ mechanism.Meow
meta.stackexchange.com/a/8259/184684 -- aka, sum links to make them into answers.Jaws
P
2

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>
Pastypat answered 30/8, 2019 at 20:19 Comment(0)
K
2

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]"; 
    }
Killer answered 30/10, 2019 at 10:8 Comment(0)
Q
2

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):

  1. create the "clean" branch in remote:

git push --set-upstream origin clean

  1. set "clean" branch as default branch
  2. remove "master" (be sure everything is as expected before doing this).
  3. Create a new branch "master" based in "clean" branch.
  4. 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:

  1. Delete the master branch from local:

git branch -d master

  1. Rename the branch:

git branch -m clean master

  1. Push it (be sure you "master" is unprotected)

git push --force origin master

Qualifier answered 4/1, 2020 at 4:14 Comment(0)
F
1
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)

Fledgy answered 7/12, 2016 at 22:53 Comment(0)
P
1

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

Paresh answered 24/10, 2021 at 13:8 Comment(0)
P
-1

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.

Pepperandsalt answered 15/12, 2016 at 15:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.