Git, rewrite previous commit usernames and emails
Asked Answered
L

13

350

I've committed a bunch of commits to a project on Github, however I realized I hadn't set up the proper email and committer full name on the computer I'm currently using to make my commits and therefore the users avatar and email address are not there.

How can I rewrite all past commit email and usernames?

Licit answered 27/5, 2010 at 9:22 Comment(2)
possible duplicate of How do I change the author of a commit in git?Dimer
I experienced this after changing the email address on my GitHub account. In addition to pushing code changes from the local git repo using the git command line (and not the GitHub desktop) interface, I also edited text and managed files directly from the remote git repo using the GitHub web interface. The new email address propagated only to the commits resulting from the latter actions and not the former.Bethesda
G
411

You can add this alias:

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"

To change the author name:

git change-commits GIT_AUTHOR_NAME "old name" "new name"

or the email for only the last 10 commits:

git change-commits GIT_AUTHOR_EMAIL "[email protected]" "[email protected]" HEAD~10..HEAD

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

Gentilesse answered 1/8, 2012 at 23:6 Comment(19)
fixed for "eval: [[: not found" on ubuntu and add a confirm change-commits = "!f() { VAR1=$1; VAR='$'$1; OLD=$2; NEW=$3; echo \"Are you sure for replace $VAR $OLD => $NEW ?(Y/N)\";read OK;if [ \"$OK\" = 'Y' ] ; then shift 3; git filter-branch --env-filter \"if [ \\\"${VAR}\\\" = '$OLD' ]; then export $VAR1='$NEW';echo 'to $NEW'; fi\" $@; fi;}; f "Orthodontics
git: 'change-commits' is not a git command. See 'git --help'. Means you have not added the alias to your git config. e.g. git config -eWeatherley
This just made duplicates of all the commits with the email I wanted to change. Doesn't appear to rewrite history. @Olivier Verdier's solution worked for me.Mcnamee
Why do you use a ! in the beginning of the alias? What does that do?Affiant
Also, the change-commits = expression in your answer differs by one sign (a \ before $@) from the code in your GitHub repository. This is a mistake, right?Affiant
Git also creates a backup. I've used this to change the GIT_COMMITTER_EMAIL, which worked. However, when I try to use it again to change the GIT_AUTHOR_EMAIL, Git complains and says Cannot create a new backup. A previous backup already exists in refs/original/ Force overwriting the backup with -f. Now, I can use the -f flag, or I can remove the backup using git update-ref -d refs/original/refs/heads/<branch-name>.Affiant
Can this alias be modified to support regex matches so that multiple old variable values can be matched? For example if I have used many different email addresses for the various commits and I would like to change all of them to a new one?Affiant
I ended up just using git checkout -b temp-branch followed by for var in GIT_AUTHOR_EMAIL GIT_COMMITTER_EMAIL; do for email in email@address1 email@address2 email@address3; do git change-commits $var $email target-email@address; git update-ref -d refs/original/refs/heads/temp-branch; done; done to change all email addresses both for author and committer.Affiant
When doing git config alias.change-commits..., don't forget to add --global to make this a global, rather than project, alias.Idolum
Doing it twice in a row with different inputs leads to: Cannot create a new backup. A previous backup already exists in refs/original/Statement
My solution to Cannot create a new backup.... issue is from blog.tinned-software.net/… : $ git fetch origin $ git reset --hard origin/master $ git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin $ git reflog expire --expire=now --all $ git gc --prune=now snippet: gist.github.com/gwpl/dbf993223c8433767c81925513bef656Penult
When I do this in Windows 7 PowerShell, I get nasty errors about unrecognized commands then and fi\" \$@; }; f ". Seems to work ok in Git for Windows Bash.Hillie
A word of warning: Y/N is case sensitiveHardener
Your git backups are just files that you can delete that exist in .git/refs/original.Barnard
git: 'change-commits' is not a git command. See 'git --help'.Intertwist
@Orthodontics ’s solution using a command line instead of .gitconfig: git config --global alias.change-commits '!'"f() { VAR1=\$1; VAR='\$'\$1; OLD=\$2; NEW=\$3; echo \"Are you sure for replace \$VAR \$OLD => \$NEW ?(Y/N)\";read OK;if [ \"\$OK\" = 'Y' ] ; then shift 3; git filter-branch --env-filter \"if [ \\\"\${VAR}\\\" = '\$OLD' ]; then export \$VAR1='\$NEW';echo 'to \$NEW'; fi\" \$@; fi;}; f "Ormolu
I am getting change-commits is not a git commandLeotaleotard
As @HelloGoobye I am also wondering: Why do you use a ! in the beginning of the alias? Besides that question, thank you so much!Cleveite
I got this scary warning when I did this: 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. See the filter-branch manual page for more details; to squelch this warning, set FILTER_BRANCH_SQUELCH_WARNING=1.Koralie
E
166

See here:

git filter-branch -f --env-filter \
"GIT_AUTHOR_NAME='Newname'; GIT_AUTHOR_EMAIL='newemail'; \
GIT_COMMITTER_NAME='committed-name'; GIT_COMMITTER_EMAIL='committed-email';" HEAD
Exoenzyme answered 27/5, 2010 at 9:43 Comment(11)
wouldn't this change the author name for all the commits (entire history) of the branch?Abject
Yeah, that would change all commits to the new author info.Floater
Please mark questions as duplicates instead of copy pasting the answer.Dimer
what if I didn't specify an old name or old email? git says "empty ident <> not allowed"Civil
I ran this command and now my repo won't push to or pull from the git server.Bronze
I think this is the best answer. The most upvoted answer doesn't work for me (it errors out, but I don't recall the error right now).Idolum
how do I change the GIT_COMMITTER_NAME though?Statement
Now on my repository on github I have "X authored and Y committed commit ", but I want the author and the committer to be the sameStatement
when i tried this today, i was missing the tags in the new branch. is it possible to move them as well?Fractionize
See important warning about git filter-branch. See @Pugsley's answer instead.Aggregate
Trying this led to all commits being changed to the new author, even those from other authors not matching the one I wanted to replaceBula
F
95

If you have already pushed some of your commits to the public repository, you do not want to do this, or it would make an alternate version of the master's history that others may have used. "Don't cross the streams... It would be bad..."

That said, if it is only the commits you have made to your local repository, then by all means fix this before you push up to the server. You can use the git filter-branch command with the --commit-filter option, so it only edits commits which match your incorrect info, like this:

git filter-branch --commit-filter '
      if [ "$GIT_AUTHOR_EMAIL" = "wrong_email@wrong_host.local" ];
      then
              GIT_AUTHOR_NAME="Your Name Here (In Lights)";
              GIT_AUTHOR_EMAIL="correct_email@correct_host.com";
              git commit-tree "$@";
      else
              git commit-tree "$@";
      fi' HEAD
Floater answered 28/5, 2010 at 19:27 Comment(11)
This works perfect whereas the answer marked in green didn'tColossal
Afterwards, one might want to clear the backup with git update-ref -d refs/original/refs/heads/master, see <https://mcmap.net/q/13053/-remove-refs-original-heads-master-from-git-repo-after-filter-branch-tree-filter>.Bottomry
FYI: If you have multiple incorrect names / emails you may need to run this multiple times. If that happens it will moan at you with this error: A previous backup already exists in refs/original/ In that case, re run it, with the new email, and add a -f before the --commit-filter. Use at your own discretion. Usually -f is a dangerous thing to do without knowledge of what it's doing.Fortson
Now I got fatal: refusing to merge unrelated historiesPhenylketonuria
@Phenylketonuria The error you're seeing could be due to a Git v2.9 change to the default merge behavior... maybe look into the --allow-unrelated-histories option?Floater
what do you mean by "In Lights"?Myriagram
@Myriagram Just a joke... like a "see your name in lights" marquee.Floater
This answer is perfectly fine while the one having more votes are not fully correctBianka
This works perfectly and in about ten seconds so bravo, but jokes in answers about git just don't work because people on Stack Overflow researching git are typically in a state of panic.Beamy
I tried this, but now all the commits are double. How I can revert it back?Kame
See important warning about git filter-branch. See @Pugsley's answer instead.Aggregate
W
54

Considering use of git-filter-branch is not desired, to do the same thing in git-filter-repo (you may need to install it first with pip install git-filter-repo):

git-filter-repo --name-callback 'return name.replace(b"OldName", b"NewName")' --email-callback 'return email.replace(b"[email protected]", b"[email protected]")'

If repository is original, w/o remote, you will have to add --force to force rewrite. (You may want to create backup of your repo before doing this.)

If you do not want to preserve refs (they will be displayed in branch history of Git GUI), you will have to add --replace-refs delete-no-add.

For more advanced features, see "Filtering of names & emails".

P.S. Stolen and improved from https://mcmap.net/q/13054/-changing-git-author-info-on-all-commits-worked-on-one-of-my-repos-but-not-the-others-why.

Walter answered 23/2, 2020 at 16:9 Comment(11)
Doing it this way also avoids the time wait that the filter-branch warning imposes on at least Git 2.34.1. This answer is pretty nice and deserves more upvotes.Bernardo
I get some "name bymyoldname is not defined". Any idea why?Cindy
@Cindy not sure, but if you had bymyoldname passed instead of b"OldName" in name.replace(b"OldName", b"NewName"), then there may have been no such user name. Otherwise it's possible that API was changed, or there's some incompatibility between git and git-filter-repo. The best atarting point is to searh source code of git-filter-repo for bymyoldname and see where it leads.Walter
Ran this today, and while it worked it removed my origins. So I had to git remote add origin [email protected]:foo-repo...., and also to sync the remote I needed to git push origin +devUrian
Also, it works better on a fresh clone, and be sure to set git config user.email "[email protected]" and git config user.name "your_handle" prior, otherwise there are lingering items in git log | grep oldemailUrian
Q: Should we still need to remove backups when using git-filter-branch? See: stackoverflow.com/questions/7654822/…Hallette
Nice, modern approach :·)Winburn
The pasted command run failed in Windows Command Prompt(cmd.exe) due to single quote not correctly handled. People have to switch to Git Bash to make it work. ref: github.com/newren/git-filter-repo/issues/435Stochmal
Thanks! Note, this has made it into a lot of package managers now - for example, for apt you can just install with sudo apt install git-filter-repo -y. See github.com/newren/git-filter-repo/blob/main/…Highhanded
After installing git-filter-repo via my Linux distro's package manager, the command I had to use was git filter-repo, not git-filter-repo.Ensphere
Can also be used from PowerShell if to replace inner " with \"Electrodynamics
A
48

After applying Olivier Verdier's answer:

git filter-branch -f --env-filter \
"GIT_AUTHOR_NAME='Newname'; GIT_AUTHOR_EMAIL='newemail'; \
GIT_COMMITTER_NAME='committed-name'; GIT_COMMITTER_EMAIL='committed-email';" HEAD

...to push the changed history on the original repository use:

git push origin +yourbranch

The above command (note the plus) forces rewriting the history on the original repo as well. Use with caution!

Again, WARNING: this will make ALL commits as committed by Newname/newemail! The scenario is where you have a repo with only one author who committed using different identities by mistake and you want to fix it.

Akmolinsk answered 10/9, 2015 at 21:1 Comment(8)
Worked for me, also correctly rewrote history on origin.Brocade
This is going to rewrite ALL the commits - irrespective of who authored it. Use with caution.Andes
I did this. Is there any way of reverting this? Now all commits are mine...Arrange
I had to force it, use push -f.Ladyinwaiting
See important warning about git filter-branch. See @Pugsley's answer instead.Aggregate
I want to change the email that I used to commit. Will this do it on the entire repo or only the branch I am currently on? I'm the only one who changed my branch (the newest branch), but I don't want to change the other branches because other people did them.Black
Only the branch where you are. Moreover, this will filter by a given commiter name and email, so you will not touch other commits in any case.Akmolinsk
You can also only filter up to a specific commit or branch: git filter-branch -f --env-filter "GIT_AUTHOR_NAME='Newname'; GIT_AUTHOR_EMAIL='newemail'; GIT_COMMITTER_NAME='committed-name'; GIT_COMMITTER_EMAIL='committed-email';" HEAD branch-you-are-filtering...ref-for-commit-to-stop-at Source: https://mcmap.net/q/13055/-running-filter-branch-over-a-range-of-commitsBlack
C
31

https://help.github.jp/enterprise/2.11/user/articles/changing-author-info/

#!/bin/sh

git filter-branch --env-filter '

OLD_EMAIL="[email protected]"
CORRECT_NAME="yourName"
CORRECT_EMAIL="yourEmail"

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

this totally worked for me. After git push, make sure to see update on git's web portal. If the commit was still not linked to my account, shown default thumbnail image next to the commit and it was not reflected on my contributions timeline chart, go to the commit url and append .patch at the end of the url, and verify the name and email are correct.

Chumley answered 24/1, 2019 at 22:14 Comment(6)
Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.Sverdlovsk
This is the only one rewriting all branches.Protein
this should be the correct answer. the rest is waste of time and possible damage to the repositoryOculist
This code works and is self-documenting. Tip: make this into a script like reauthor.py, chmod +x it, and drop it into whatever local repos you have. Make sure all your changes are committed or stashed first, then run it from each affected repo.Saffron
See important warning about git filter-branch. See @Pugsley's answer instead.Aggregate
After that, one may git push -f to force push the changes to remote repository.Frutescent
A
18

First update the username and email

Update them global or in your current repository

git config --global user.name "Name"
git config --global user.email "<[email protected]>"

Note that you can update them in .git/config file, just append this section with your info

[user]
    name = new name
    email = new email

Then run this command to update all previous commits

git rebase -r --root --exec "git commit --amend --no-edit --reset-author"
Anthonyanthophore answered 3/5, 2023 at 19:19 Comment(5)
That's a good alternative if you realize it "early" like when committing changes to a new repository. Note that it will change all commits from the beginning and if you already pushed something to the origin probably a workaround will be a force pushSheikdom
Yes i think you are right force will be a good workaroundAnthonyanthophore
I don't think force pushing to a feature branch should be seen as a workaround. I think it's perfectly normal (if you're on a feature branch!)Debbiedebbra
The downside of this command is that it changes the modification dates of each commit to system time of the 'rebase'.Lipps
Ooops. Spoke too soon. How do I make this happen for a range of commits?Alger
I
13

For those that just want the easy copy paste version (aside from updating emails and names):

git config 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 "
git change-commits GIT_AUTHOR_NAME "<Old Name>" "<New Name>" -f
git change-commits GIT_AUTHOR_EMAIL <[email protected]> <[email protected]> -f
git change-commits GIT_COMMITTER_NAME "<Old Name>" "<New Name>" -f
git change-commits GIT_COMMITTER_EMAIL <[email protected]> <[email protected]> -f
Illuviation answered 18/5, 2018 at 18:16 Comment(2)
-bash: !f: event not foundSkiver
Most likely a problem with a terminal escaping things automaticallyAcquire
T
6

The answers already present are complete. But are you sure, you need those? For eg. I was facing a similar issue but the answers here were overkill for that case. My case and the solution are described below:

Assume you have two email ids, [email protected] and [email protected]. It is possible that the previous commits were through [email protected] which is not the email id that you want. In that case, one option is to simply link [email protected] to your GitHub account.

How to do it?

Find the option to add the email in the Emails section on the settings page.

enter image description here

Still lost?

The first three steps mentioned on the github page will be enough.

NOTE:

  1. Github supports multiple email ids for a single github account.
  2. Since you are not replacing the email id, but just adding a new one, commits through both the email ids are linked to your Github account.
  3. It's not necessary that [email protected] should be set as primary email id.
Troth answered 3/5, 2021 at 3:46 Comment(0)
B
5

An alternative to rewriting the history, if you mainly care about the local repo display names, is the .mailmap file, which is basically a list of names and emails. For example, placing this in a .mailmap file in the root of a repo:

Tamika Page <[email protected]>
Orlando Cervantes <[email protected]> Orlando Jackson <[email protected]>
Jared Michael <[email protected]> <jared@desktop.(none)>

will result in any commits attributed to [email protected] being shown with the name Tamika Page, regardless of the Committer Name, commits attributed to Orlando Jackson <[email protected]> being displayed as Orlando Cervantes, and the pesky <jared@desktop.(none)> commits being attributed to [email protected]. For full details, check out the git documentation for this feature - since it is built into git it should work for any reasonably new git client.

There's a big caveat here though: while it's been supported for quite a while in the official git client, support in various git implementations, notably the big web interfaces, isn't guaranteed - see this question, where consensus on whether GitHub respects it is mixed but seems negative. I threw together quick test repos on GitHub and GitLab and neither seem to pay attention to .mailmap, unfortunately.

As mentioned in @paradocslover's answer, the services have their own interfaces to do similar things, but you have to set that up per-service, and it won't affect your local copy at all. Because .mailmap is part of the repo, it will work locally for anyone who clones your repo (you can clone the repos linked above to see for yourself), but it seems it won't show in the web interfaces I tested.

For some cases you do want to rewrite the history, of course, but that can be rather invasive and comes with all the standard caveats, so for some situations it's nice to have this option, which may be sufficient, especially if rewriting isn't practical.

Beatriz answered 2/7, 2021 at 20:40 Comment(3)
I'd ask what level does this work at? I already used a fix above for my issue and when I pushed the repository up to the remote all my changes were made correctly. Does this work in that manner or is this just a local 'view-only/display-only ' fixGrassland
It is indeed display-only, the actual commit data remains unchanged. Rewriting is a more permanent solution and probably better if it is an option. But rewriting can be very disruptive for some use cases (e.g. if the repo is shared with any significant number of people), so this is an alternative for when rewriting is impossible or impractical.Beatriz
Your question did prompt me to actually check if it is supported by the big web clients, and unfortunately it seems that it is not, which is a shame. They have their own ways of handling this, but it would be nice if they used the existing standard to do so, although I suppose they have concerns around impersonation that are better solved by their own solutions, altho even their own verification only solves a small subset of those concerns.Beatriz
E
4

If you want to rewrite the author on previous commits you will also inadvertently change the commit date to the current day. Ideally, you want to keep the date of the original commits (this is useful if you want to make the history of git contributions accurate - lets say for the Github contributions in your user history overview). You should also be sure to only target commits with a specific author or email, so you don't re-write every single commit.

This is how you do that:

Step 1: Set your email & name to on the local repo

git config user.name "Your name"
git config user.email "[email protected]"

add a --global flag to set it everywhere.

Step 2: Rebase your commits and change the name & email to the current name & email in your config settings.

git rebase -r <commit-hash> \
    --exec 'if [ "$(git log -n 1 --format="%ae")" = “[email protected]” ]; then git commit --amend --no-edit --reset-author --date="$(git log -n 1 --format=%aD)"; fi'

How it works:

By rebasing on a specific commit-hash you are processing each commit starting from the commit you specified with the hash.

$(git log -n 1 --format="%ae") retrieves the name of the author from the latest commit (the one being processed) using the git log command with the --format="%ae" option, which specifies to display only the author email. You can change this part to --format="%an" to target a author names.

The conditional statement [ "$(git log -n 1 --format="%ae")" = “[email protected]” ] checks if the name of the author from the latest commit matches the specific email “[email protected]”.

If the condition is true, the subsequent git commit --amend --no-edit --reset-author --date="$(git log -n 1 --format=%aD)" command will be executed as part of the --exec option during the rebase operation. Its important to add the --date= flag with the matcher so that we don't change the original commit date during the rebase. This is selecting the most recent commit of the branch (which is the one you are currently processing) and using that commit's date thereby leaving the commit message with the same date.

But be careful, you are changing git history and you are changing the commit hashes for each node on the branch. You will have to force push the change up to a public repository (if you are changing a branch that has already been pushed up).

Using this command goes without saying - only change commit emails & author names if there has been some error in the email that was used for the commit, and you want to attribute the proper developer. You may have committed for someone else, or you may have forgot to set the proper name & email before starting your repo.

Ebneter answered 21/6, 2023 at 7:23 Comment(0)
R
1
  1. Open the terminal or command prompt.
  2. Navigate to the root directory of your Git repository.
  3. Run the following command with the -f flag to force overwrite the
    backup:
    git filter-branch -f --env-filter '
    OLD_EMAIL="[email protected]"
    CORRECT_NAME="YourCorrectName"
    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

After running the above command, you will have forced the overwrite of the backup and updated the author information for all the commits. Now, use the following command to force push the changes to update the remote repository: bash

git push --force --all
Rodolforodolph answered 5/8, 2023 at 14:51 Comment(0)
N
1

If you just did this a small number of times on a single branch, it can be fixed with a change of configuration, a new commit, and a rebase.

This is perhaps a less "proper" method than some of the others, but it's easy and uses commands you already know how to use (hopefully?)

Change your configuration settings

Assuming you want to set a new email / name, globally:

git config --global user.name "Your name"
git config --global user.email "[email protected]"

Create a new commit

// do something
git add ...
git commit -m 'new commit message'

Rebase current branch

Change <count> to required number. It is an integer value. If you don't know what it might be / supposed to be, check git log. It's somewhat a trial and error thing, sometimes (we won't go into detail as to why)

git rebase -i HEAD~<COUNT>

When in the interactive rebase editor, change pick to squash everywhere, but change pick to drop for any commits where you merged in another branch (eg master)... the last part is only relevant if you have done this...

If you don't know how interactive rebase works, this is a good time to learn - it is a very useful tool and you will be amazed when you learn about it (:

Check with git log

git log should now show a single commit, with the changed email / name

Navel answered 9/9, 2023 at 23:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.