Renaming a Git Project for Real

I was midway through working on a project I had been calling commentate, but then one of my coworkers suggested a much better name, ahnnotate. Instead of a single large commit to rename each file and each class name, I thought it'd be cool to rewrite history and pretend that it had the cool name all along. But I have to admit, it wasn't quite as straightforward as I had expected.

To fully rename this project, we'll need to take the following steps:

  1. ⚠️ ⚠️ ⚠️ MAKE A BACKUP! ⚠️ ⚠️ ⚠️ (like with cp -r)
  2. Delete branches, remotes, tags, and .gitignored files
  3. Update the file contents
  4. Update the file/directory names
  5. Update the commit message contents
  6. Check if it worked

We'll mostly be using git filter-branch. It's kinda similar to git rebase in that it allows you to rewrite history, but it's a bit of a "sharper" tool. I found this article quite helpful in understanding exactly what it does.

By the way, I'm using macOS, but I'm using gsed which takes the same arguments as Linux's sed.

⚠️ ⚠️ ⚠️ One more warning! I'm very freely using "force" flags and deleting git's automatic backups. Be careful, and don't run stuff without a decent understanding of what's happening! Make sure you make a backup! ⚠️ ⚠️ ⚠️


Delete branches, remotes, tags, and .gitignored files

Before starting, it's a bit easier if you have no remotes, no tags, and only one branch.

git branch --list
git tag --list
git remote -v

You'll also need to delete whatever files are ignored via .gitignore. According to the git filter-branch docs, --tree-filter will add whatever files are present in the working directory, even if they're ignored.

Update the file contents

First, we'll replace commentate with ahnnotate.

git filter-branch --tree-filter 'ag -l -s commentate | xargs gsed -i "s/commentate/ahnnotate/g"' HEAD

Next, we'll replace Commentate with Ahnnotate. But git will complain something about a backup already existing, so we need to add the -f flag. You can also optionally delete the backup with rm -rf .git/refs/original.

git filter-branch -f --tree-filter 'ag -l -s Commentate | xargs gsed -i "s/Commentate/Ahnnotate/g"' HEAD

We'll need to do this over and over for each capitalization variation you care about. One thing that kinda bit me though is that I used commentate as a verb, commentating. I had to do an additional search/replace for that since the i broke the pattern.

Update the file/directory names

I found this resource quite helpful, and this following example is basically copied from there.

git filter-branch -f --index-filter 'git ls-files -s | sed "s/commentate/ahnnotate/g" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD

Update the commit message contents

Lastly, we'll update the commit message contents. You'll need to repeat this for whatever capitalization combinations you have.

git filter-branch -f --msg-filter 'sed s/commentate/ahnnotate/g' HEAD

Check if it worked

Cool! Hopefully we're done now. Now we just need to check.

We'll know we're done once the following commands return the same result:

# Lists the number of commits in master
git rev-list --count master
# Lists the number of commits in the entire repository
git rev-list --all --count

An easy first step is to delete the .git/refs/original directory. Hopefully, this will be the only step you have to do. For some reason, I had a bunch of lines in .git/info/refs and had to delete all the lines except the one containing refs/heads/master.

I also found git reflog expire --expire-unreachable=now --all and git gc --prune=now helpful in removing commits that weren't reachable through the master branch.

Last was the actual verification. None of the following commands should give you any output. If it does, you'll probably need to go back and re-run some commands.

# Search file contents
git grep commentate $(git rev-list --all)

# Search file/directory names
git log --all --full-history --oneline -- '*commentate*'

# Search commit message contents
git log --all --grep='commentate'

References

Posted on Thursday, February 14, 2019 at 11:12 AM
Zach Ahn
hello(at)zachahn(dot)com
© Copyright 2008–2019 Zach Ahn. All Rights Reserved.