JavaScript
Git
prettier

prettierを使って過去のコミットをすべてフォーマットする

困ったことに

古い古い自社ライブラリのお話です。はじめのコードはインデントが4で作成されていました。後から入ってきた私はJavaScriptのファイルのインデントは2と聞かされておりましたので、IDEの設定で2にしておいたのです。

古いライブラリなので改修の頻度は少ないですが、新規の機能を作成しました。新しく作成されたファイルはインデントが2で書かれました。気づかないレビュー者。見かねてEslintを入れてくれました。EditorConfigも入れてくれました。EslintとEditorConfigでインデントが食い違っています。2なのか4なのか、混乱するIDE。私は悲しみました。

eslint --fixで直るよ!

直ります!しかし、こんな話が上がりました。

Aさん > git annotateで見れるのが、全てフォーマット修正のコミットになるのがつらいかも...
私 > 確かに!!

古いライブラリで実装を理解している人間も少ないため、修正についてはコミットを追う必要がありました。私もデグレードが起きないように行単位でコミットを追っていました。

歴史を変えるしかない...

事前準備

グローバルにprettierをインストールしておいてください。

$ yarn global add prettier

本題

git annotateを変更しないためには、過去に存在する全てのコミットに対して、フォーマットを行った状態で再度コミットするという必要があります。
そこで、git filter-branchを使用します。このコマンドで調べると、「コミットしてしまった秘密鍵を削除する」「コミットのEmail, Authorを変更する」という内容が多く出てきます。歴史を変えるための最強のコマンドです。

# 現状のブランチの全てのコミット
$ git filter-branch -f --tree-filter 'prettier --no-config --print-width=120 --write "src/**/**.js" || echo "Error"' --
# 全てのブランチの全てのコミット
$ git filter-branch -f --tree-filter 'prettier --no-config --print-width=120 --write "src/**/**.js" || echo "Error"' -- --all
# 特定のブランチの特定のコミットからコミットまで
# コミット間の指定が面倒なので、ブランチを作成して指定している
$ git filter-branch -f --tree-filter 'prettier --no-config --print-width=120 --write "src/**/**.js" || echo "Error" from_branch..to_branch

これらのコマンドによって全てのコミットのインデントが2に修正されているはずです。

一つ問題点

インデント以外もフォーマットされるので、正直何が起こるか予測が難しいかも。
特定のルールのみ修正するのであれば、Eslintで行うのがいいかもしれないです。

$ git filter-branch -f --tree-filter 'eslint --fix --no-eslintrc --rule "indent: [\"error\", 2]" --parser-options "ecxaVersion: 2017" --env "es6" "src/**/**.js" || echo "Error"' --

コミットの数分だけ実行するので、実行速度がprettierよりも遅いのが懸念点ではあります。

最後に

規模のでかいリポジトリであれば、Eslintの実行に時間がかかるでしょう。filter-branchを使用した後はブランチの取り直し、面倒であればgit cloneを行う必要があります。
元も子もないですが、コミットのログを気にしないのであれば、普通にフォーマットしてコミットしてあげればいいと思います。