Rewrite your Git history (and fast)

In cleaning up my repositories on GitHub, I came across old projects, some of which are ancient. Let’s not dwell on the shame one can feel when looking at code written almost 10 years ago; it’s inevitably very ugly…​

But one thing caught my attention: many commits are not associated with my username or do not display my profile picture. Strange, since the most recent ones seem to correspond to my username and show my picture.

What changed in the meantime?

The answer came quite quickly: I have often changed my email address over the years (school, first job, new personal address, and so on). Now, I know that you can modify the author of a commit, their username, or email address. But with hundreds of commits across dozens of repositories, well…​ life is too short, you know?

In fact, it’s all about having the right tools.

By hand ... it's long
By hand ... it's long
With the right tools ... it's better
With the right tools ... it's better

I found the right tool on Stack Overflow^ (note that this answer appears on dozens of posts). It all lies in the filter-branch command, which is capable of traversing all the commits in the project and applying modifications to them.

Can you sense the potential? Yes, it could destroy your history, so let’s proceed with caution, shall we?

In my case, I needed to modify the commits written with a certain email address and replace it with my current email address and username to bring everything into harmony. The command line looks like this:

git filter-branch -f --env-filter '
  OLD_EMAIL="old.email@example.com"
  CORRECT_NAME="New Name"
  CORRECT_EMAIL="new.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

Once these modifications are made locally, you still need to push them to the remote repository:

git push -f --tags origin HEAD:main

And clean up the local references:

git update-ref -d refs/original/refs/heads/main

You will then see that the history correctly displays the commits as ours:

Excerpt from an old Java 7 project
Excerpt from an old Java 7 project

And here’s a little boost to the ego: suddenly, there are more commits counted by GitHub on your profile page! 😉