Rewriting git commit message history across multiple branches

The usual way to modify a previous git commit is to rebase interactively:

git rebase -i <parent of oldest commit to edit>

This lets you modify the past commit and then it reapplies all commits in the current branch since then on top of the modified commit.

This is all you need if you only have one branch. This time, however, I had multiple branches splitting off after the commit I had to change. When I used the rebase technique, the other branches remained along with the unmodified commit they were based on.

I decided to try a different approach - I used filter-branch.

filter-branch is very powerful and has many options. In this case, I only wanted to rewrite a commit message, so I used the --msg-filter option. This pipes each message to a shell command and replaces it with the output of that command - perfect in conjunction with sed. This method, unlike rebasing onto a single edited commit, lets you programmatically edit all commit messages

One of the things I wanted to do was remove the comments that had been added by git-svn. This command did the trick:

git filter-branch -f --msg-filter 'sed "s/git-svn.*$//g"' -- --all

If you have any tags, you should add --tag-name-filter cat. This updates the tags to point to the modified commits. It’s more complicated if the tags are signed - see the git-fliter-branch man page for details.

Before this operation, git makes a backup, referred to as the original. If there is already a backup git will, be default, refuse to run the command, as doing so would overwrite the existing backup with a new one. Use -f to force it.

-- --all applies the filter to all branches. Alternatively, single branch names can be given.

The above commands have taken care of modifying our commits but they have not removed the originals, which have been kept as backups. To purge these from the repository, you need to remove all references to them and then run the garbage collector.

Edit these files to delete any lines which refer to the original commits:


.git/info/refs
.git/packed-refs

And do this:


rm -rf .git/refs/original
rm -rf .git/logs/

Now garbage collect:


git gc --prune=now