Graham McGregor

Formatting A Whole Git Branch

September 08, 2018

Have you ever gotten to the end of a bunch of work on a branch, and realize you didn't have the auto-formatter turned on for the last 5 commits?

Or perhaps your team just introduced a new formatter on the master branch, and now you need to deal with the merge conflicts of a entirely reformatted codebase.

Whatever the reason, we're going to walk through how to solve these problems with minimal pain. We're going to re-write the history of your branch so that formatting is applied at each step along the way.

Find Your Formatter

First, we need to make sure we have the formatter installed that we want to use. For this example we'll use Prettier.

npm install -g prettier

Rewrite History

Now open the terminal to the project directory, and check out the branch we want to format.

cd best-project/
git checkout super-cool-feature-X

Before we get started, let's create a branch as a backup of the current one.

git branch backup1 super-cool-feature-X

To start, we need to find the commit where this branch was created. We'll store it in a variable for later.

export BRANCH_START=$(git merge-base super-cool-feature-X origin/master)
echo $BRANCH_START
# 85938af7938b83c87afb8c9e0d4887bd49462007

Next we're going to add an empty commit to the start of our branch. When we rewrite the branch history, this is where all the formatting changes, if any, will end up.

# create a temporary branch at the commit where the feature branch was created
git checkout --branch temp1 $BRANCH_START
# create the empty commit
git commit --allow-empty --message "Format code"
# go back to our feature branch
git checkout super-cool-feature-X
# replay our branch on top of the temporary one
git rebase temp1
# delete the temporary branch
git branch -D temp1

Now we're going to rewrite the branch history and format the code. The --prune-empty flag will drop any empty commits. So if no formatting changes happen to the code in its' original state, the "Format code" commit we created will disappear.

git filter-branch --prune-empty --tree-filter "\
  prettier --write '**/*.{js,jsx}' \
  " -- $BRANCH_START..HEAD

Update the Server

Almost done! Assuming we pushed the branch at some point before this, we're going to have to force-push to update the server's copy of our branch. Do a first pass with the --dry-run flag to confirm that we're going to overwrite the correct branch.

git push --dry-run --force-with-lease
# To github.com:YourName/best-project.git
#    1603684..d800942  super-cool-feature-X -> super-cool-feature-X

The name on the right hand side of the arrow -> is where we're pushing to. If this isn't the branch we expected, update it with:

git branch --set-upstream-to=origin/super-cool-feature-X

Once we've confirmed this branch will go where we want, run the push command for real.

git push --force-with-lease
# To github.com:YourName/best-project.git
#    1603684..d800942  super-cool-feature-X -> super-cool-feature-X

We're all done. Enjoy your nicely formatted code!


If you want to see more things from Graham McGregor in the future, you should follow him on twitter!