299

I have made a series of commits in Git and I realise now that I forgot to set my user name and user email properties correctly (new machine). I have not yet pushed these commits to my repository, so how can I correct these commits before I do so (only the 3 latest commits on the master branch)?

I have been looking at git reset and git commit -C <id> --reset-author, but I don't think I'm on the right track.

Chris Maes
  • 35,025
  • 12
  • 111
  • 136
pauldoo
  • 18,087
  • 20
  • 94
  • 116
  • 2
    Another reason you might want to change the email property is this github error: `remote: error: GH007: Your push would publish a private email address.` ... ` ! [remote rejected] master -> master (push declined due to email privacy restrictions)`. – craq Sep 18 '18 at 01:20
  • Also see https://stackoverflow.com/q/750172/1340631. – scai Apr 20 '20 at 07:35

8 Answers8

324

Warning: now deprecated in favor of filter-repo.

Rebase/amend seems inefficient, when you have the power of filter-branch at your fingertips:

git filter-branch --env-filter 'if [ "$GIT_AUTHOR_EMAIL" = "incorrect@email" ]; then
     GIT_AUTHOR_EMAIL=correct@email;
     GIT_AUTHOR_NAME="Correct Name";
     GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL;
     GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; fi' -- --all

(split across lines for clarity, but not necessary)

Be sure to inspect the result when you're done, to make sure that you didn't change anything you didn't mean to!

TeNNoX
  • 1,899
  • 3
  • 16
  • 27
Cascabel
  • 479,068
  • 72
  • 370
  • 318
  • 1
    mind explaining this a bit more? not sure what filter branch is – max pleaner Aug 23 '14 at 19:34
  • 1
    @maxpleaner `git filter-branch --help` is pretty straightforward :) – alediaferia Jul 10 '15 at 12:58
  • 3
    see also https://help.github.com/articles/changing-author-info/, which also adds `--tag-name-filter cat` to the `filter-branch` in order to migrate tags to the new history. It also uses `--branches --tags` instead of `--all`, which only rewrites branch and tag history and leaves other `refs` alone (though that probably doesn't make much of a difference unless e.g. you're using [`git-notes`](https://git-scm.com/docs/git-notes)) – Tobias Kienzler Feb 23 '17 at 08:39
  • Note that it's important to use single quotes and not backticks around the command! – Jeff Paquette May 19 '17 at 14:52
  • 21
    to perform this on *just* the last two commits, I replaced `-- --all` with `HEAD~1..HEAD` – nmz787 Mar 30 '18 at 03:11
  • It works in local branch but when I am trying to push in origin I cannot. When I am trying to push, git is telling me to pull. When I am pulling I am getting the following error: "fatal: refusing to merge unrelated histories" – ahadcse Jan 09 '19 at 11:16
  • @nmz787 last two commits is `HEAD~2..HEAD` – Jona Mar 19 '19 at 14:38
  • @Jona I'm pretty sure that would yield the last 3 commits... HEAD, HEAD-1, and HEAD-2... that's 3 total – nmz787 Mar 19 '19 at 23:39
  • 1
    @nmz787 How many logs are shown if you do `git log HEAD~2..HEAD` ? – Jona Mar 20 '19 at 09:59
  • Remember to use `git push -f` afterwards to push the changes to the origin. – AdamHurwitz Feb 05 '20 at 00:42
  • The `git filter-branch` command now comes with a warning, and `filter-repo` (which is third-party) is recommended instead: https://github.com/newren/git-filter-repo. The alternative command would be along the lines of `echo ' ' > .mailmap ; git filter-repo --use-mailmap`. – msleigh Sep 19 '20 at 10:45
  • And don't forget to force the push `git push --force` – H Aßdøµ Mar 19 '21 at 00:15
  • From the docs: 'WARNING! The rewritten history will have different object names for all the objects and will not converge with the original branch. You will not be able to easily push and distribute the rewritten branch on top of the original branch. Please do not use this command if you do not know the full implications, and avoid using it anyway, if a simple single commit would suffice to fix your problem. (See the "RECOVERING FROM UPSTREAM REBASE" section in git-rebase[1] for further information about rewriting published history.)'. Careful using `--all`! – jv-k Aug 11 '21 at 12:59
  • 1
    Added an [answer](https://stackoverflow.com/a/69947947/3761234) that uses filter-repo as recommended by git. – charsi Nov 12 '21 at 20:45
  • Submitted an edit to warn about this: https://stackoverflow.com/suggested-edits/5320642 – TeNNoX Jan 21 '22 at 14:52
288

The interactive rebase approach is pretty nice when used in conjunction with exec. You can run any shell command against a specific commit or all commits in the rebase.

First set your git author settings

git config --global user.name "John Doe"
git config --global user.email johndoe@example.com

Then to reset the author for all commits after the given BASE_SHA:

git rebase -i BASE_SHA -x \
  "git commit --amend --author 'John Doe <johndoe@example.com>' -CHEAD"

This will pop up your editor to confirm the changes. All you need to do here is save and quit and it will go through each commit and run the command specified in the -x flag.

Devin Rhode
  • 23,026
  • 8
  • 58
  • 72
Alex
  • 34,776
  • 10
  • 53
  • 68
  • 4
    Thank you for introducing me to the -x option. Its pretty awesome! for the -i option I used HEAD~4 to fix my email address on my last 4 commits. worked like a charm. – Brad Hein Feb 09 '15 at 19:18
  • 4
    This is much simpler than `filter-branch` if you just want to fix your last commits :). Note however, that this changes the timestamp of the commits. – luator Oct 27 '15 at 08:44
  • As `filter-branch` is not that straight-forward, I also give my love to this one. Almost everything you usually do messing up your history is related to `rebase -i`, and this option fits this scheme perfectly. – Piotr Zierhoffer Dec 21 '15 at 13:50
  • 50
    To change the author but maintain the original timestamps, use `git rebase -i YOUR_SHA -x "git commit --amend --author 'New Name ' -CHEAD"` – Dave Apr 25 '16 at 19:15
  • This did not work for me. tried both the short and the long sha for the commit I wanted changed and it remained unchanged when I ran `git log` – Connor Jul 05 '18 at 18:34
  • Just tried the second version (the one that keeps the original timestamps) and worked like a charm. Thank you so much! This should definitely be the accepted answer – Bruno 82 Jul 07 '18 at 15:02
  • 2
    @Connor `git log` also showed old authorship for me, but git status correctly identified new commits and after force push they were as I intended. – Dan M. Aug 30 '18 at 10:09
  • 22
    To rebase all commits including the root use: `git rebase -i --root …` instead of passing a SHA. – gfullam Apr 15 '19 at 15:14
  • Thank you so much! ```git rebase -i YOUR_SHA -x "git commit --amend --author 'New Name ' -CHEAD"``` Worked like a charm! – smmehrab Sep 14 '20 at 10:38
  • I was confused by "all you need to do is save and quit" since there was no "Save" option. For me, just ctrl-X to exit was all I had to do, and it executed correctly. – David Burson Oct 15 '20 at 18:58
  • If you're a Windows user and the command opens the changes in vim, press Esc Z Z to save and quit. (Note the capital Zs) – a_hardin Feb 04 '21 at 00:34
  • 2
    Just tried @Dave answer but the original timestamps are not maintained (you can check it here https://github.com/oncet/coel/commits/temp). Using git 2.25.1. – Camilo Apr 11 '21 at 01:36
  • @Camilo Same for me (git version 2.28.0.windows.1) – René K Dec 27 '21 at 00:04
  • 2
    To amend while keeping the date: `git rebase -i YOUR_SHA -x 'git commit --amend --reset-author --no-edit --date="$(git log -n 1 --format=%aD)"'` – Steven Feb 17 '22 at 05:58
  • 1
    what is my SHA? – nougako Jul 24 '22 at 03:49
158

To change the author only for the last commit:

git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit

Suppose you only want to change the author for the last N commits:

git rebase -i HEAD~4 -x "git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit"

Change the committer as well:

as noted by @sshine and @Hermann.Gruber, the previous commands change the author only. To change the committer as well, you can use the solution proposed by @Hermann.Gruber:

git rebase -i HEAD~4 -x "GIT_COMMITTER_NAME='Author Name' GIT_COMMITTER_EMAIL='author.name@gmail.com' git commit --amend --author 'Author Name <author.name@gmail.com>' --no-edit"

NOTES

  • 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
pick abb60f9 add new feature
exec git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit
pick dc18f70 bugfix
Chris Maes
  • 35,025
  • 12
  • 111
  • 136
  • 3
    for all commits from root. git rebase -i --root UPTO_COMMIT_SHA -x "git commit --amend --author 'NEW_CHANGE' --no-edit" – Ashish Negi Oct 30 '17 at 17:16
  • 3
    I recommend to add `--rebase-merges` (short `-r`) option, to keep the topology of your branch intact if it contains some merges. – donquixote Apr 05 '20 at 19:06
  • 11
    @ChrisMaes: Please note that **`git commit --amend --author ...` does not change the committer, only the author!** So while it may *appear* that your email was changed, there is, in fact, metadata in the repository that says who the old committer is. While the `filter-branch` (or `filter-repo`) methods are more crude, they actually change both. Proof: `curl -s https://api.github.com/repos/sshine/author-committer/commits | jq '.[0].commit | { author, committer }'` -- I did a `git commit --amend --author="John Doe ..."` here, and you can see that the committer is not John Doe. – sshine Feb 03 '22 at 00:44
  • 1
    As a note, you can use `--root` in place of `HEAD` if trying to specify every commit of the rreo. – matttm Jun 28 '22 at 00:37
  • 1
    @ChrisMaes @sshine to change also the committer information of the 4 latest commits, you can use ```git rebase -i HEAD~4 -x \ "GIT_COMMITTER_NAME='Author Name' GIT_COMMITTER_EMAIL='Author Name ' git commit --amend --author 'Author Name ' --no-edit"``` – Hermann.Gruber Mar 22 '23 at 15:06
  • 1
    @Hermann.Gruber I have added this in my answer. Thanks for the proposal, which I have tweaked a little. – Chris Maes Mar 22 '23 at 20:26
47

The highest voted answer here is now out of date. Git shows this scary warning when using git filter-branch -

WARNING: git-filter-branch has a glut of gotchas generating mangled history
         rewrites. Hit Ctrl-C before proceeding to abort, then use an
         alternative filtering tool such as 'git filter-repo'
         (https://github.com/newren/git-filter-repo/) instead.

filter-repo is not (yet) part of git and needs to be installed separately.

# Requires git v2.22+ and python v3.5+. Check with -
git --version && python3 --version
    
# Install using pip
pip3 install git-filter-repo    
    

To replace only the email in previous commits run the the command like this -

git filter-repo --email-callback '
    return email if email != b"incorrect@email" else b"correct@email"
' 

To replace both, email and author name in the previous commits run the the command like this -

git filter-repo --commit-callback '
    if commit.author_email == b"incorrect@email":
        commit.author_email = b"correct@email" 
        commit.author_name = b"Correct Name"
        commit.committer_email = b"correct@email" 
        commit.committer_name = b"Correct Name"
' 

Make sure the indents are there when you paste the command in your terminal. The callback uses python syntax so indents are important.

Read more about filter-repo callbacks in the docs.

charsi
  • 2,917
  • 22
  • 40
  • 5
    Thank you. It worked! If you're getting `zsh: command not found: pip` on a Mac, try pip3 - `pip3 install git-filter-repo`. – Nelu Jan 28 '22 at 10:23
  • on Windows (in `ConEmu` terminal), in a `venv`, I had to swap quotes and make it a one-liner, and add `--force` (because I made commits since last pushing): `git filter-repo --force --email-callback " return email if email != b'wrong@email' else b'correct@email'"` – nmz787 Feb 04 '22 at 02:07
14

This method was documented by GitHub for this very purpose (though GitHub has since removed it). The steps are:

  1. Open the terminal and make a bare clone of your repo
git clone --bare https://github.com/user/repo.git
cd repo
  1. Edit the following script (replacing OLD_EMAIL, CORRECT_EMAIL, and CORRECT_NAME)
#!/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
  1. Copy/paste the script into your terminal and press enter to run it.
  2. Push your changes with git push --force --tags origin 'refs/heads/*' and you're done!
PtrJsn
  • 19
  • 5
stevec
  • 41,291
  • 27
  • 223
  • 311
  • I followed the same instructions on GitHub that you referenced, and GitHub looks right now. However, I'm a Git newb and not sure how to sync my local repo back up after that. When I pull I get the same "refusing to merge unrelated histories" error mentioned in another answer. I think I need to rebase against that new commit history, but I'd very much appreciate more specific steps. – enigment Apr 20 '19 at 23:59
  • @enigment if you are happy with the repo as it is on github, you can delete (or perhaps move to another location) the folder you have locally and simply clone from github – stevec Jun 08 '19 at 03:42
  • Thanks, I know, but that doesn't seem like the idiomatic GitHub/Git way. – enigment Jun 09 '19 at 14:53
2

I believe what you are looking for is git rebase --interactive

It allows you to go reset to an specific commit and then go throw the history changing adding or grouping commits

Here you have an explanation https://web.archive.org/web/20100213104931/http://blog.madism.org/index.php/2007/09/09/138-git-awsome-ness-git-rebase-interactive

smottt
  • 3,272
  • 11
  • 37
  • 44
Fernando Diaz Garrido
  • 3,995
  • 19
  • 22
2

If you're looking for a script, this one came handy for me.

  1. Download the script from GitHub and save it to an easily-accessible location.

  2. Change the permissions of the script file to allow it to execute:

    chmod +x changeauthor.sh

  3. Navigate into the repository with the incorrect commit history

    cd path/to/repo

  4. Run the script (with or without flags)

    ../path/to/changeauthor.sh --old-email kaka.ruto@example.com \
        --new-email ruto.kaka@example.com --new-name "Kaka Ruto" --remote origin
    

Be careful as this will rewrite all history in your current dir repository! Good thing is the script give you warnings and info about what you're about to do

Read more here https://www.adamdehaven.com/blog/update-commit-history-author-information-for-git-repository/

Kaka Ruto
  • 4,581
  • 1
  • 31
  • 39
0

As suggested in this answer git-filter-repo is preferred for the task.

However to simply change author name and/or email one can use --mailmap or --use-mailmap instead of callbacks.

You need to create a mailmap file according to the format (see git-filter-repo/docs or git-scm.com/docs)

Then simply run

git filter-repo --mailmap .mailmap
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
mr. Y
  • 187
  • 1
  • 5