2974

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

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Flávio Amieiro
  • 41,644
  • 8
  • 32
  • 24
  • 17
    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? – AndyL Aug 03 '10 at 14:13
  • Or you can try to use `refs/replace/` mechanism. – Jakub Narębski Aug 04 '10 at 09:42
  • 1
    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). – Jakub Narębski Aug 04 '10 at 09:44
  • 48
    Hashes will change yes – Not Available Oct 14 '10 at 15:16
  • 3
    Tangentially, I created a small script which finally fixed the root cause for me. https://gist.github.com/tripleee/16767aa4137706fd896c – tripleee May 30 '14 at 08:51
  • @tripleee 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. – Claudia Sep 01 '14 at 05:24
  • 2
    @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. – tripleee Sep 01 '14 at 14:50
  • this answer is much simpler @Flávio Amieiro https://stackoverflow.com/a/11768870/670229 – brauliobo May 31 '18 at 14:18
  • 3
    The github script that @TimurBernikovich mentioned is great and works for me. But that github url has changed: https://docs.github.com/en/enterprise/2.17/user/github/using-git/changing-author-info#changing-the-git-history-of-your-repository-using-a-script – Kaiwen Sun Oct 13 '20 at 04:14
  • The answers might be overkill. First check whether this satisfies your usecase - https://stackoverflow.com/a/67363253/8293309 – paradocslover May 03 '21 at 03:48

41 Answers41

2095

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@address.example>"

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@address.example>" 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.

Alex R
  • 11,364
  • 15
  • 100
  • 180
asmeurer
  • 86,894
  • 26
  • 169
  • 240
  • 2
    That would take *forever* if you had even as few as a hundred commits to pick through. – Matthew Iselin Aug 24 '09 at 03:14
  • 33
    Great for the odd commit though - useful if you're pairing and forget to change the author – mloughran Sep 25 '09 at 11:14
  • 37
    +1 for mentioning the usecase for the typical one-mistake fix: git commit --amend --author=username – Nathan Kidd Mar 15 '10 at 20:03
  • 15
    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. – Zitrax Aug 21 '10 at 11:34
  • @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). – asmeurer Feb 28 '12 at 23:59
  • 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`. – asmeurer Feb 29 '12 at 00:00
  • 3
    I needed to use git push -f in order to push this to my repository host (git push -f -u origin master) – falstaff Nov 22 '12 at 11:38
  • Yes, this rewrites history! See my other response if you want to avoid that. – asmeurer Nov 22 '12 at 19:43
  • 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' – Pete Nov 08 '13 at 09:28
  • 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` :-) – Avinash R Jan 24 '14 at 11:47
  • I get http://stackoverflow.com/questions/19864934/git-your-branch-and-origin-master-have-diverged-how-to-throw-away-local-com . How do I push the edited commit to replace the already pushed one? – user1236048 Feb 10 '14 at 09:41
  • 1
    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. – asmeurer Feb 10 '14 at 15:04
  • +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) – ThanksForAllTheFish Feb 19 '14 at 23:09
  • 1
    That would be even simpler if using the `--no-edit` option, it'll keep the message of the amended commit. – bric3 Mar 10 '14 at 23:58
  • 72
    `git commit --amend --reset-author` also works once `user.name` and `user.email` are configured correctly. – pts Jul 04 '14 at 16:56
  • 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). – thepeer Aug 05 '14 at 07:14
  • 30
    Rewrite author info on all commits after `` using `user.name` and `user.email` from `~/.gitconfig`: run `git rebase -i --exec 'git commit --amend --reset-author --no-edit'`, save, quit. No need to edit! – ntc2 Mar 06 '15 at 23:47
  • But the typical use-case here is to set the author as something *other* than the author in .gitconfig. – asmeurer Mar 09 '15 at 16:08
  • you should not that this of course changes the author on all commits since the commit you're rebasing from. Not just your own – spankmaster79 May 21 '15 at 09:06
  • 9
    To include the first commit you can use `git rebase -i --root` – Alessandro Resta Jan 03 '17 at 17:31
  • 5
    Both solutions create new commits, but github's one (see accepted answer) keeps the rest of the metadata (e.g. commit date) intact. – nandilugio Mar 01 '17 at 18:31
  • 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? – Toma Radu-Petrescu May 09 '17 at 21:26
  • @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. – asmeurer May 09 '17 at 22:40
  • 10
    Note that this resets the commit date – Jorjon May 02 '18 at 11:43
  • 11
    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. – bitsmack May 11 '18 at 22:07
  • This will change commits SHA1s. So you keep it in mind – danielrvt Nov 29 '19 at 14:26
  • 1
    @danielrvt 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). – asmeurer Nov 29 '19 at 18:42
  • 1
    after all do "git push -f" – Shivaraja HN Feb 19 '20 at 17:23
  • Use `--rebase-merges` (`-p` is deprecated) – DylanYoung Jul 30 '20 at 17:07
  • I guess push is missing – PHP Avenger Oct 24 '20 at 03:13
  • 1
    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! – Mauricio Chirino Dec 05 '20 at 11:12
  • One line: `git rebase -i --rebase-merges --root; while true; do git commit --amend --no-edit --author "Theodore R. Smith "; git rebase --continue; done` – Theodore R. Smith Dec 27 '20 at 17:11
  • @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. – bit Jan 08 '21 at 21:36
  • The answers might be overkill. First check whether this satisfies your usecase - stackoverflow.com/a/67363253/8293309 – paradocslover May 03 '21 at 03:48
  • I'm getting `unknown switch -r`. If I remove that it works fine. – David G Jun 01 '22 at 15:30
  • 2
    note that `--author` does not change commit date, while `--reset-author` does. – Sang Jun 06 '22 at 19:40
  • @asmeurer How to amend *including* the very first commit, not just the commits after it. Also if possible, how to preserve timestamps while doing it? – Joy Jun 14 '22 at 08:03
  • 2
    CAREFUL this changes also the commit date! which is not what I wanted. I ruined all my commits history with the solution above – Kristi Jorgji Jul 07 '22 at 16:31
  • @Joy use `--root` and specify `--author` as in Theodore R. Smith's comment – qwr Jul 23 '22 at 00:42
  • 1
    @transang `--author` preserves author-date, but not commit-date (which is shown by default on github). see https://stackoverflow.com/questions/28536980/git-change-commit-date-to-author-date – qwr Jul 23 '22 at 00:54
  • 2
    @qwr 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 https://www.reddit.com/r/git/comments/jp59k5/rebase_without_changing_commit_timestamps/ – Joy Aug 04 '22 at 08:32
  • 1
    @Joy at that point of command length, I might as well used the git-filter-branch strategy which I have used multiple times without any issues – qwr Aug 04 '22 at 08:36
  • @qwr yes, didn't know it could be done like that also. Seems like a neat solution – Joy Aug 04 '22 at 08:47
  • 1
    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. – hustnzj Aug 31 '22 at 09:23
  • 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 rebase – TingQian LI Mar 20 '23 at 08:32
  • 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. – jjj Apr 19 '23 at 04:45
  • 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; done` – Mig82 May 26 '23 at 11:12
  • this is work for me thanks @asmeurer and StackOverflow :) – msrajwat298 May 28 '23 at 14:35
  • As mentioned by [DharmaTurtle](https://stackoverflow.com/questions/750172/how-do-i-change-the-author-and-committer-name-email-for-multiple-commits?page=1&tab=trending#comment129474745_750191) 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 history – Madacol May 29 '23 at 23:44
  • what Versions are "pre-July 2020" can you specify this? – J-Eibe Jul 26 '23 at 06:29
  • 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](https://stackoverflow.com/a/73314337/5362795) that I found very useful. – Nagev Aug 23 '23 at 11:16
1538

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="your-old-email@example.com"
CORRECT_NAME="Your Correct Name"
CORRECT_EMAIL="your-correct-email@example.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
    export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
    export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags

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 <proper@email.xx> Commit Name <commit@email.xx>

And then run filter-repo with the created mailmap:

git filter-repo --mailmap git-mailmap
Skully
  • 2,882
  • 3
  • 20
  • 31
Pat Notz
  • 208,672
  • 30
  • 90
  • 92
  • 48
    After executing the script you may remove the backup branch by executing "git update-ref -d refs/original/refs/heads/master". – D.R. Aug 14 '13 at 16:47
  • 13
    @rodowi, it duplicates all my commits. – Rafael Barros Jun 17 '14 at 17:43
  • 12
    @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 ... – johannes Jun 11 '15 at 13:52
  • 1
    Make sure to checkout a bare repository before running the github script (--bare option). Otherwise it'll flatten your branches. – Shumoapp Aug 24 '15 at 17:02
  • 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? – Tariq Sep 04 '15 at 17:44
  • Is there any way to fix the duplicated commits to have only ones from fixed author's email? – haxpor Mar 12 '16 at 08:31
  • You can use http://stackoverflow.com/a/11768870/200987 to just affect say the last ten commits – oligofren Mar 15 '16 at 07:39
  • 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 -f – Matrosov Oleksandr Oct 31 '16 at 21:54
  • 1
    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-date` – mcont Nov 13 '16 at 11:43
  • 32
    Solved using `git push --force --tags origin HEAD:master` – mcont Nov 13 '16 at 11:50
  • 1
    @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. – olfek Dec 10 '16 at 15:23
  • Beware, I used this script on a very simple branch that I used to create a PR and it completely messed up the history – Daniel Serodio Dec 21 '16 at 20:50
  • 24
    **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! –  Feb 03 '17 at 22:23
  • 2
    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: http://stackoverflow.com/questions/5340790/easiest-way-to-replay-commits-on-new-git-repository). I also did `git gc --aggressive` afterwards to clean it all up. Good luck. –  Feb 03 '17 at 22:54
  • 3
    I've created an interactive script based on this answer - to avoid reediting the script if you have multiple authors to modify https://gist.github.com/yaronuliel/8157d7318de988f4399f561d466e12f3 – Yaron U. Jun 02 '17 at 14:34
  • 1
    Got me 90% of the way there but didn't rewrite my tags with new commit SHAs. I found [this example](https://www.mattshelton.net/2015/08/12/changing-the-author-of-a-single-tag-in-git/) which, when I followed it, updated the commit SHA on my tag. – vhs Jul 08 '18 at 12:14
  • I also wrote a convenient script to replace author/committer name/email. It can be found here: https://github.com/frz-dev/utilities/blob/master/git/git-author-rewrite.sh – FedFranz Jul 30 '19 at 11:18
  • 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. – Ashutosh Jindal Jan 06 '20 at 11:38
  • 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? – Dave Jan 10 '20 at 00:29
  • For anyone getting the error in my previous comment, this was the issue for me: [How to force push a reset to remote repository?](https://stackoverflow.com/questions/10544139/how-to-force-push-a-reset-to-remote-repository) – Dave Jan 10 '20 at 00:44
  • 1
    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 "old_email@gmail.com" | xargs sed -i 's/old_email@gmail.com/new_email@gmail.com/g'`. Source: https://unix.stackexchange.com/questions/472476/grep-global-find-replace/472482#472482. – Gabriel Staples Mar 20 '20 at 00:52
  • please pay attention that OLD_EMAIL is case sensitive – Alexey Sh. Apr 24 '20 at 19:33
  • for some reason the old gh help article you linked was removed. script still works but its odd. – notacorn Nov 11 '20 at 04:53
  • 3
    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 `) – Edward Millen Nov 24 '20 at 01:50
  • 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... – PlasmaBinturong Mar 23 '21 at 10:36
  • The answers might be overkill. First check whether this satisfies your usecase - stackoverflow.com/a/67363253/8293309 – paradocslover May 03 '21 at 03:48
  • 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/{}" – John King May 27 '21 at 20:38
  • 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 similar – CervEd Nov 30 '21 at 09:32
  • 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? – Meglio Oct 24 '22 at 00:28
  • Can this script be combined with commit signature -S flag? – Pawel Cioch Jan 14 '23 at 22:35
  • `git filter-repo` not work, no any outputs. – huang Jan 25 '23 at 23:22
  • This preserves timestamtp! – Madacol May 29 '23 at 23:26
744

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
kenorb
  • 155,785
  • 88
  • 678
  • 743
Brian Gianforcaro
  • 26,564
  • 11
  • 58
  • 77
  • 1
    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. – Alec the Geek Jan 06 '10 at 06:55
  • 8
    Why does it rewrite all commits if you specify `HEAD` in the end of the command? – Nick Volynkin Jun 18 '15 at 03:26
  • 5
    This does not work for my bitbucket repository, any idea ? I do a `git push --force --tags origin 'refs/heads/*'` after the advised command – Olórin Oct 05 '16 at 21:46
  • 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. :) – Dan Friedman Jun 05 '18 at 07:19
  • 10
    The push command for this is : `$git push --force --tags origin 'refs/heads/master' ` – HARSH NILESH PATHAK Jun 08 '18 at 22:07
  • 13
    Neat; this keeps the old timestamps too. – DharmaTurtle Mar 06 '20 at 21:19
  • 4
    @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'` – Alex Gisi Jan 20 '21 at 22:30
  • All you have to do is to pass `if [ "$GIT_AUTHOR_EMAIL" = "incorrect@email" ]; then` – H Aßdøµ Mar 19 '21 at 00:42
  • The answers might be overkill. First check whether this satisfies your usecase - stackoverflow.com/a/67363253/8293309 – paradocslover May 03 '21 at 03:48
  • 2
    Coming back to this a few years later, I got scared off this answer based on [the warning](https://git-scm.com/docs/git-filter-branch#_warning). I eventually formulated [my own answer](https://stackoverflow.com/a/73314321/625919) which keeps the old author and committer timestamps. – DharmaTurtle Aug 11 '22 at 01:52
  • 1
    To push you need to force with: `git push --force` – Save Jan 08 '23 at 12:27
  • Sincerely thank you! The multi-liners were failing for me for whatever reason. This worked perfectly. Thanks a bunch! – rbleattler May 12 '23 at 02:12
616

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
Rognon
  • 6,317
  • 2
  • 15
  • 6
  • 5
    Isn't using env-filter the easier solution? Not sure why this is getting more votes, then. – stigkj Dec 09 '11 at 09:21
  • 3
    Then link is broken. How do we push these changes to another repository? – Russell Feb 18 '12 at 23:21
  • 30
    env-filter will change all the commits. This solution allows a conditional. – user208769 Apr 11 '12 at 15:29
  • 6
    `"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. – hhh May 04 '12 at 22:11
  • 1
    +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. – therobyouknow Aug 06 '12 at 09:21
  • 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. – Mike Nov 29 '12 at 11:24
  • To those who find themselves having duplicates in their commit history, please read refer to this post: http://stackoverflow.com/questions/1983101/git-duplicate-commit-issue – Mike Nov 29 '12 at 12:55
  • 1
    It's not duplicates, it's backup from running the `filter-branch` command. Nice to have if you did something wrong. – stigkj Apr 11 '13 at 08:51
  • 2
    @user208769 env-filter also allows a conditional; look at my answer :-) – stigkj Apr 11 '13 at 08:52
  • If you've got tags, you probably want to use `--tag-name-filter cat` switch. – Craig McQueen Jun 27 '13 at 06:01
  • 1
    Use ref..head to only run the rewrite on a certain collection of commits. You rarely want to rewrite the entire history. – Antony Stubbs Nov 15 '13 at 22:12
  • 1
    these modifications can be pushed with the -f --force swith – laplasz Dec 10 '13 at 23:22
  • Is it possible to adapt this to rename multiple emails in a single pass? I asked a new question about this at https://stackoverflow.com/questions/33866185/rename-multiple-names-and-emails-in-a-single-pass-of-git-filter-branch – sashoalm Nov 23 '15 at 08:08
  • To prevent duplications and be safe, [I added an answer](https://stackoverflow.com/a/59587820/196507) based in this one (with minor modifications and other explanations replacing the master branch with a clean one). – lepe Jan 04 '20 at 04:19
  • The answers might be overkill. First check whether this satisfies your usecase - stackoverflow.com/a/67363253/8293309 – paradocslover May 03 '21 at 03:48
248

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 you@domain.example
git commit --amend --reset-author

Tested with Git version 1.7.5.4.

Note that this fixes only the last commit.

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
lrkwz
  • 6,105
  • 3
  • 36
  • 59
  • 11
    That works really well on the last commit. Nice and simple. Doesn't *have* to be a global change, using `--local` works too – Ben May 30 '12 at 23:24
  • 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. – ecbrodie Feb 08 '19 at 19:36
  • The answers might be overkill. First check whether this satisfies your usecase - stackoverflow.com/a/67363253/8293309 – paradocslover May 03 '21 at 03:48
204

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@address.example>" -C HEAD
pick 1234 my bad commit 2
exec git commit --amend --author="New Author Name <email@address.example>" -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.

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Alex Brown
  • 41,819
  • 10
  • 94
  • 108
  • 30
    Definitely awesome. Can you shorten it by setting user.name and user.email in the repo's local config, and then each line is only`exec git commit --amend --reset-author -C HEAD` ? – Andrew Nov 30 '12 at 11:07
  • 2
    The canonical answer, to use filter-branch, just deleted refs/heads/master for me. So +1 to your controllable, editable solution. Thanks! – jmtd Jun 17 '14 at 20:30
  • 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. – fredoverflow Mar 16 '15 at 12:14
  • 3
    In place of `git rebase -i HEAD^^^^^^` you can also write `git rebase -i HEAD~6` – Patrick Schlüter Jun 09 '15 at 13:09
  • 4
    Please note that this changes the timestamp of the commits. See https://stackoverflow.com/a/11179245/1353267 for reverting to the correct timestamps – Samveen Oct 10 '17 at 11:50
  • I think we can also make our lives easier if we defined an alias for the exec command – Peter Chaula Oct 15 '18 at 15:05
  • I tried many other solutions but this one was the one that worked the best for me :) – Alex Aug 10 '19 at 22:56
  • 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. – Soren Bjornstad Nov 08 '19 at 22:51
  • 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. – Soren Bjornstad Nov 08 '19 at 23:08
  • This indeed did the trick for me nicely and quickly when I only have a few commits done with a wrong identity. – planetmaker Sep 29 '20 at 16:11
  • 2
    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` instead – DJMcMayhem Nov 23 '20 at 18:44
  • The answers might be overkill. First check whether this satisfies your usecase - stackoverflow.com/a/67363253/8293309 – paradocslover May 03 '21 at 03:48
  • 1
    @paradocslover 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. – Alex Brown May 04 '21 at 05:30
  • 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". – paradocslover May 04 '21 at 06:20
192

For a single commit:

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

(extracted from asmeurer's answer)

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
blueyed
  • 27,102
  • 4
  • 75
  • 71
  • 19
    but that's only if it's the most recent commit – Richard Jan 17 '12 at 23:24
  • 4
    According to [`git help commit`](http://schacon.github.com/git/git-commit.html), `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](http://schacon.github.com/git/git-checkout.html) that commit with `git checkout ` or `git checkout `. – Rory O'Kane Apr 25 '12 at 19:33
  • 12
    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. – John Gietzen Jul 11 '12 at 21:02
  • 3
    @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. – Thanatos Oct 24 '13 at 20:35
  • 13
    Note that this changes only commit `author` and not the `committer` – Nick Volynkin Jun 18 '15 at 03:39
  • And to correct the top 10 commits: `git rebase --exec 'git commit --amend --author="Author Name "' HEAD~10` *(note the commit-time will be modified)* – Luke Usherwood Dec 29 '20 at 23:31
124

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" = "your@email.to.match.example" ]
then
    cn="Your New Committer Name"
    cm="Your New Committer Email"
fi
if [ "$GIT_AUTHOR_EMAIL" = "your@email.to.match.example" ]
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"
'
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Olivier Verdier
  • 46,998
  • 29
  • 98
  • 90
99

A single command to change the author for the last N commits:

git rebase -i HEAD~N -x "git commit --amend --author 'Author Name <author.name@mail.example>' --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 <author.name@mail.example>' --no-edit
pick abb60f9 add new feature
exec git commit --amend --author 'Author Name <author.name@mail.example>' --no-edit
pick dc18f70 bugfix
exec git commit --amend --author 'Author Name <author.name@mail.example>' --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"
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Chris Maes
  • 35,025
  • 12
  • 111
  • 136
  • I used HEAD~8 and it shows way more than the last 8 commits. – Bryan Bryce Jan 17 '20 at 00:05
  • 2
    @BryanBryce if there are merge commits involved, things get complicated :) – Chris Maes Jan 17 '20 at 08:04
  • @ChrisMaes Ah, I see what's going on. I don't want to mess with those, just on the branch I'm on. – Bryan Bryce Jan 17 '20 at 21:49
  • In that case, supposed you branched from master, you could: `git rebase -i master -x ...` – Chris Maes Jan 18 '20 at 12:14
  • 5
    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 ...` – Allan Deamon Feb 25 '21 at 12:26
  • 2
    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](https://gist.github.com/bgromov/a1905055a8b9cdbeb1d2a87e70920cc8). 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. – Ajay Pillay Jun 07 '21 at 12:18
  • For some reason this just edited my 5 last commits email (from 142), got different results on multiple repositories – Pedro Henrique Sep 07 '21 at 21:45
  • 1
    @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 stuff – Chris Maes Sep 08 '21 at 06:37
86

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
svick
  • 236,525
  • 50
  • 385
  • 514
59

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

Skully
  • 2,882
  • 3
  • 20
  • 31
asmeurer
  • 86,894
  • 26
  • 169
  • 240
  • 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. – Òscar Raya Sep 08 '20 at 10:31
  • 2
    Also, notice this does not works for web side on Gitea. – Òscar Raya Sep 08 '20 at 10:39
  • @iuliu.net I'm not sure. This question https://stackoverflow.com/questions/53629125/does-github-consider-mailmap-for-contribution-graph 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. – asmeurer Jan 11 '21 at 09:20
51

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 "old@email.com" "new@email.com" 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.

Borzh
  • 5,069
  • 2
  • 48
  • 64
brauliobo
  • 5,843
  • 4
  • 29
  • 34
  • 4
    "git: 'change-commits' is not a git command. See 'git --help'." – Native_Mobile_Arch_Dev Apr 03 '17 at 02:05
  • After this command & sync with master all commits in the history are duplicated! Even of other users :( – Vladimir Feb 26 '19 at 13:00
  • @Vladimir that is expected, please study about changing history in git – brauliobo Feb 28 '19 at 10:26
  • 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! – Steffen Schwigon Jun 05 '20 at 10:22
  • @Native_Mobile_Arch_Dev 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" – Amir Hajiha Mar 29 '21 at 07:45
49

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

git commit --amend --reset-author

Ryanmt
  • 3,215
  • 3
  • 22
  • 23
44

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.

Lucas
  • 523
  • 2
  • 10
  • 20
stigkj
  • 1,439
  • 14
  • 10
38

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"your-old-email@example.com"
  correct_name = b"Your Correct Name"
  correct_email = b"your-correct-email@example.com"
  
  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.

Wenfang Du
  • 8,804
  • 9
  • 59
  • 90
Saurabh P Bhandari
  • 6,014
  • 1
  • 19
  • 50
  • 1
    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. – user2692263 Feb 27 '21 at 09:08
  • Thank you for sharing the links in the `Note` section. – hustnzj Aug 31 '22 at 09:28
  • To get this to work on Windows, I had to escape all the double quotes: `old_email = b\"your-old-email@example.com\"` – RapidIce Sep 01 '22 at 06:46
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 <new@author.email>" ; 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.

Community
  • 1
  • 1
sporsh
  • 377
  • 4
  • 3
  • 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 answer](https://stackoverflow.com/a/73314321/625919) – DharmaTurtle Aug 11 '22 at 01:44
24

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
Ton van den Heuvel
  • 10,157
  • 6
  • 43
  • 82
  • 2
    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. – jeberle Mar 30 '14 at 17:22
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 <john.doe@hotmail.com>
jill=Jill Doe <jill.doe@hotmail.com>

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
Leif Gruenwoldt
  • 13,561
  • 5
  • 60
  • 64
  • 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... – Daniel Hershcovich Feb 15 '12 at 13:36
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="<youmail@somehost.ext>";
fi
if [ "$GIT_COMMITTER_NAME" = "<old committer>" ];
then
    GIT_COMMITTER_NAME="<new commiter>";
    GIT_COMMITTER_EMAIL="<youmail@somehost.ext>";
fi
' -- --all
drahnr
  • 6,782
  • 5
  • 48
  • 75
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 <author@email.com>" # change the author name and email
    
    $ git replace <old-commit-hash> <new-commit-hash>      # replace the old commit by new one
    $ git filter-branch -- --all                           # rewrite all futures commits based on the replacement                   
    
    $ git replace -d <old-commit-hash>     # remove the replacement for cleanliness 
    $ git push -f origin HEAD              # force push 
    
  2. 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 <author@email.com>"  # change the author name & email
    
    # Save changes and exit the editor
    
    $ git rebase --continue                # finish the rebase
    
Sajib Khan
  • 22,878
  • 9
  • 63
  • 73
  • 2
    Very nice answer. I like that the changes are wrapped up from the very update to even cleaning up the git commits – Aleks May 07 '17 at 20:41
18

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.

Lie Ryan
  • 62,238
  • 13
  • 100
  • 144
  • You can replace `-p` with `--root` to change _all_ commits in the history (The [-p option](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt--p) is deprecated). And note that this only works after you have corrected the username and email via `git config user.name ` and `git config user.email `. – Wim Deblauwe Mar 06 '21 at 10:15
  • 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? – bbasaran Oct 26 '22 at 21:12
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 --author=user@wrong.email --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/user@example.com/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 --author=user@wrong.email --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.

Community
  • 1
  • 1
Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
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@example.com>", 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 my_other_email@example.com 
git commit --amend
Sir Athos
  • 9,403
  • 2
  • 22
  • 23
  • 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. – ruffin Oct 08 '14 at 15:02
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"
mixalbl4
  • 3,507
  • 1
  • 30
  • 44
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
djromero
  • 19,551
  • 4
  • 71
  • 68
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
j16r
  • 189
  • 2
  • 7
  • 4
    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 `;-)`. – halfer Jul 07 '13 at 20:31
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="me@something.com"
CORRECT_NAME="New Me"

if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
fi
' --tag-name-filter cat -- --branches --tags

# Force push the rewritten branches + tags to the remote
git push -f

# Remove all knowledge that we did something
rm -rf ${REPO_DIR}

# Tell your colleagues to `git pull --rebase` on all their local remote tracking branches

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.

Miloš Ranđelović
  • 444
  • 1
  • 5
  • 13
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 old@email.com -n newname -m new@email.com
    
  • Change author and committer matchings on <branch> and <branch2>. Pass -f to filter-branch to allow rewriting backups

    $ git changemail -b old@email.com -n newname -m new@email.com -- -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

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
albfan
  • 12,542
  • 4
  • 61
  • 80
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'
paphko
  • 51
  • 1
  • 1
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:-"you_wrong_mail@hello.world"}
 NEW_NAME=${2:-"your name"}
 NEW_EMAIL=${3:-"new_mail@hello.world"}

 git filter-branch -f --env-filter "
  if [ \$GIT_COMMITTER_EMAIL = '$WRONG_EMAIL' ]; then
    export GIT_COMMITTER_NAME='$NEW_NAME'
    export GIT_COMMITTER_EMAIL='$NEW_EMAIL'
  fi
  if [ \$GIT_AUTHOR_EMAIL = '$WRONG_EMAIL' ]; then
    export GIT_AUTHOR_NAME='$NEW_NAME'
    export GIT_AUTHOR_EMAIL='$NEW_EMAIL'
  fi
 " --tag-name-filter cat -- --branches --tags;
}
stephanfriedrich
  • 563
  • 5
  • 20
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 <author.name@email.co.uk>"'
Chisel
  • 91
  • 5
  • 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 "'` – John Pancoast Sep 18 '22 at 05:52
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

3

For all the commits, my solution:

git rebase -i --root -x "git commit --amend --author 'bedorlan <bedorlan@gmail.com>' --no-edit"
bedorlan
  • 55
  • 1
  • 5
3

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"newemail@gmail.com"'

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"oldemail@gmail.com"
new_email = b"newemail@gmail.com"
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.)

Arad Alvand
  • 8,607
  • 10
  • 51
  • 71
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)

baudtack
  • 29,062
  • 9
  • 53
  • 61
  • 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. – manumoomoo Aug 04 '10 at 00:43
  • @mediaslave: Try `refs/replace/` mechanism. – Jakub Narębski Aug 04 '10 at 21:22
  • http://meta.stackexchange.com/a/8259/184684 -- aka, sum links to make them into answers. – ruffin Oct 08 '14 at 15:05
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 <proper@email.xx> <root@localhost>
marco
  • 488
  • 4
  • 9
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 = "paul@company.com"; 
    }
Philippe
  • 28,207
  • 6
  • 54
  • 78
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" = "wrong1@example.com" -o "$GIT_COMMITTER_EMAIL" = "wrong2@example.com" ];
        then
                export GIT_COMMITTER_NAME="John Doe";
                export GIT_AUTHOR_NAME="John Doe";
                export GIT_COMMITTER_EMAIL="correct@example.com";
                export GIT_AUTHOR_EMAIL="correct@example.com";
        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

lepe
  • 24,677
  • 9
  • 99
  • 108
1
git rebase -i YOUR_FIRTS_COMMIT_SHA^

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

Press ^C # after the rebase is done (the loop will keep updating last commit)

kayess
  • 3,384
  • 9
  • 28
  • 45
Vojtech Vitek - golang.cz
  • 25,275
  • 4
  • 34
  • 40
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="wrong@example.com"
NEW_NAME="New Name Value"
NEW_EMAIL="correct@example.com"

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

DINA TAKLIT
  • 7,074
  • 10
  • 69
  • 74
0

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

#!/bin/sh
PWD=`pwd`
if [[ $PWD == *"Ippon"* ]] # 1)
then
  EMAIL=$(git config user.email)
  if [[ $EMAIL == *"Work"* ]] # 2)
  then
    echo "";
  else
    echo "Email not configured to your Work email in the Work directory.";
    git config user.email "youremail@youremail.com"
    echo "Git email configuration has now been changed to \"$(git config user$
    echo "\nPlease run your command again..."
    echo ''
    exit 1
  fi;
elif [[ $PWD == *"Personal"* ]]
then
  EMAIL=$(git config user.email)
  if [[ $EMAIL == "youremail@youremail.com" ]]
  then
    echo "";
  else
    echo "Email is not configured to your personal account in the Personal di$
    git config user.email "youremail@youremail.com"
    echo "Git email configuration has now been changed to \"$(git config user$
    echo "\nPlease run your command again..."
    echo ''
    exit 1;
  fi;
fi; 

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

Jake Henningsgaard
  • 704
  • 1
  • 6
  • 16