I was writing a simple script in the school computer, and committing the changes to Git (in a repo that was in my pendrive, cloned from my computer at home). After several c
For a single commit:
git commit --amend --author="Author Name <email@address.com>"
(extracted from asmeurer's answer)
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.
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
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.com>" -C HEAD
pick 1234 my bad commit 2
exec git commit --amend --author="New Author Name <email@address.com>" -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.
If you are the only user of this repository, you can rewrite history using either git filter-branch (as svick wrote), or git fast-export/git fast-import plus filter script (as described in article referenced in docgnome answer), or interactive rebase. But either of those would change revisions from first changed commit onwards; this means trouble for anybody that based his/her changes on your branch pre-rewrite.
RECOVERY
If other developers didn't based their work on pre-rewrite version, simplest solution would be to re-clone (clone again).
Alternatively they can try git rebase --pull
, which would fast-forward if there weren't any changes in their repository, or rebase their branch on top of re-written commits (we want to avoid merge, as it would keep pre-rewrite comits forever). All of this assuming that they do not have not comitted work; use git stash
to stash away changes otherwise.
If other developers use feature branches, and/or git pull --rebase
doesn't work e.g. because upstream is not set up, they have to rebase their work on top of post-rewrite commits. For example just after fetching new changes (git fetch
), for a master
branch based on / forked from origin/master
, one needs to run
$ git rebase --onto origin/master origin/master@{1} master
Here origin/master@{1}
is pre-rewrite state (before fetch), see gitrevisions.
Alternate solution would be to use refs/replace/ mechanism, available in Git since version 1.6.5. In this solution you provide replacements for commits that have wrong email; then anybody who fetches 'replace' refs (something like fetch = +refs/replace/*:refs/replace/*
refspec in appropriate place in their .git/config
) would get replacements transparently, and those who do not fetch those refs would see old commits.
The procedure goes something like this:
Find all commits with wrong email, for example using
$ git log --author=user@wrong.email --all
For each wrong commit, create a replacement commit, and add it to object database
$ git cat-file -p <ID of wrong commit> |
sed -e 's/user@wrong\.email/user@example.com/g' > tmp.txt
$ git hash-object -t commit -w tmp.txt
<ID of corrected commit>
Now that you have corrected commit in object database, you have to tell git to automatically and transparently replace wrong commit by corrected one using git replace command:
$ git replace <ID of wrong commit> <ID of corrected commit>
Finally, list all replacement to check if this procedure succeded
$ git replace -l
and check if replacements take place
$ git log --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.
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