In this little post I am going to show you why you sometimes need to rewrite the author or committer history, how you do it and where it will not work as expected.
The complete source including command-generation can be found at github:
Why would I rewrite the git author history?
First things first, why would you ever want to rewrite the git author history? Think of the following scenario:
You are going to do a very urgent fix directly on the server deployed git repository. It seems to work fine and you commit it to the local repository and push it to remote. But wait… You totally forgot that you acted as root and when you issue a
git log it will show the following:
commit a0925c315107fcdcfb7a3b2dcd435995c8216ad2 Author: root <root@localhost> Date: Sat Oct 10 21:39:43 2015 +0200 fixed xss on index.php
Damn’ that was a mistake. You should have taken the time and have done it on your local machine, pushed to remote and checked out on the server as your seniors always told you. Don’t worry, this can be fixed.
Rewriting the author history
This time you better do it locally. Make sure your local repository is up to date with the remote and quickly list all git authors:
$ git log --pretty=full | grep -E '(Author|Commit): (.*)$' | sed 's/Author: //g' | sed 's/Commit: //g' | sort -u Junior Dev <firstname.lastname@example.org> Senior Dev <email@example.com> root <root@localhost>
OK, you need to get rid of
root and rewrite it to your git account
Junior Dev. The command for this case will look like this:
$ git filter-branch --env-filter ' if [ "$GIT_COMMITTER_EMAIL" = "root@localhost" ]; then export GIT_COMMITTER_NAME="Junior Dev" export GIT_COMMITTER_EMAIL="firstname.lastname@example.org" fi if [ "$GIT_AUTHOR_EMAIL" = "root@localhost" ]; then export GIT_AUTHOR_NAME="Junior Dev" export GIT_AUTHOR_EMAIL="email@example.com" fi ' --tag-name-filter cat -f -- --all
OK, you have done it, let’s double check by listing the authors again:
$ git log --pretty=full | grep -E '(Author|Commit): (.*)$' | sed 's/Author: //g' | sed 's/Commit: //g' | sort -u Junior Dev <firstname.lastname@example.org> Senior Dev <email@example.com>
OK, its gone. Now get it to the remote.
Rewriting the remote author history
Once you have rewritten the local git author history, you will only have to make a forced push of all refs including tags:
$ git push --force --tags origin 'refs/heads/*'
Where does it come short?
Be aware that if any of the commits or tags of an author you want to change are
signed, it will mess up the commit message in the history. It will take the gpg signature of the commit and prepend it to the commit message and will look something like this:
commit 4ac785bf03d4b4f814fa9d139db9b9c7b53df733 Author: cytopia <firstname.lastname@example.org> Date: Fri Jul 10 16:03:09 2015 +0200 iQIcBAABCgAGBQJVn9CdAAoJEKrf9Tsovxef+7AP/0rHHruS1o1P28iHPJS7JEuw FKg1a2MG8DeqwfA93O+chkyl2KfvPEG6yrf369XLAdgR4WkmpCDvK9qYNBxde8NZ X5S7rGVMKvgdH031o7hYExhMKo1QJrXWzCa7KoEas0/SIAKaqWHojBEpQJO9nBw/ 60XMpwxo7MY+Y4a/W603yL3ZgPYgHFHun3pb5sxlFyL4uhdPJXPhPUhAkXdlI4TE vGt/DwJJ2puDfaQsiOe3Iz6VEYk5s5L22OcYZjBnkgeM14Db9FHAK/KGVgJg0Md0 64HRSyNDCO92CEwCMdv7cqP8cnm2zsk0TvEYpvp7zz1rWkvXK2VAiMfSy8RrUTP8 rE6qDwL7mF27aFAPuVohtddwYMKdcDijffQ/hT7tWeW8zKiI8NcbSENqJ6/SQbqP mn2WC3vyl2X33WCF3j01BPzq+02B2gMxTr0vnnxewpVDgWKPYGxhxRHXcapo9dva t8nC6jOXktzHSffWGYFVX/nK1iVoeIrGEPi4n5w3tMX5+HgZUNQCltXRTuUCGCot LYGMPCRRbSJ8i5KarRHxvxQp5YisQe/pED3NZMg0gEtD2jx9P4Yv7tZwt7CQYnwW 5S336fVLkpJfinrwoaebRgdzd6/rbjsBPd92XPQTYSYG4q5m2EJ6JoaorUCE1H0U 0svoJ7gt7KVVc4urtCiH =o0wB -----END PGP SIGNATURE----- Redirect Errors and Warnings to stderr
The rewriting has created an unsigned multiline commit of the previously signed single-line commit.
Keep that in mind before going to rewrite signed commits.