git commit –fixup と rebase –autosquash:手動修正なしにコミット履歴を自動整理する

Git tutorial - IT technology blog
Git tutorial - IT technology blog

午前2時。コードレビューが終わり、PRを作ろうとしたその時、レビュアーからメッセージが来た:「お前のコミット履歴、ゴミみたいじゃないか。修正してから『fix typo』、『fix again』、『ok done now』って5個もコミット追加して…ちゃんと片付けてくれ。」

心当たりはありませんか?自分も何度も同じ状況を経験してきました。いつもの対応はgit rebase -i HEAD~10を開いて、一行ずつ手動でスカッシュする。順番を間違えやすいし、特に深夜2時は頭が全然働かない。

幸いなことに、Gitにはあまり知られていない2つの機能があります:--fixup--autosquashです。この組み合わせは、手動でスカッシュしていた作業を自動化してくれます — 正しいコミット、正しい順番で、ハッシュを確認する必要もなし。

問題:ぐちゃぐちゃなコミット履歴

「Add user authentication」というフィーチャーを開発しているとします。数時間のコーディングと何度かの小さな修正を経て、git logはこのようになっています:

$ git log --oneline
a3f9c12 ok done
b7e2d45 fix typo in login form
c1a8b33 forgot to add validation
d4f7e22 Add user authentication
e9b1c55 Update README

最初の3つのコミットは実際にはd4f7e22の小さな修正です。論理的には一緒に属していますが、履歴は散らばっています。レビュアーから見ると混乱するし、後でgit bisectも難しくなり、git blameは意味のあるコミットではなく「ok done」というコミットを返してしまいます。

手動の解決策:git rebase -i HEAD~4で各行のpicksquashに変更する。時間がかかり、間違えやすく、疲れている時は正しくできないことの方が多い。

基本概念

git commit –fixup とは?

git commit --fixup <commit-hash>は、fixup! <元のメッセージ>というフォーマットで自動的にメッセージが設定された新しいコミットを作成します。これはGitにリベース時にこのコミットを元のコミットに統合させるための「ラベル」です。

例えば、コミットd4f7e22(メッセージ:「Add user authentication」)に属するバグ修正をマークしたい場合:

$ git add auth/login.py
$ git commit --fixup d4f7e22
# Gitが自動でコミットを作成します。メッセージ: "fixup! Add user authentication"

メッセージを考える必要も、後で元のコミット名を覚えておく必要もありません — fixup!というプレフィックスが十分な情報を保持しています。

git rebase –autosquash は何をするのか?

git rebase -i --autosquashは、fixup!プレフィックスを持つコミットをリベースリスト内の元のコミットの直後に自動的に並べ替え、アクションをfixupに変更します。エディタを開いた時には全て正しい位置に配置済み — 保存して終了するだけです。

実践的な使い方

ステップ1:通常のコミット

paymentフィーチャーを開発していて、2つのコミットから始めます:

$ git add payment/checkout.py
$ git commit -m "Add checkout flow"
# Hash: abc1234

$ git add payment/cart.py
$ git commit -m "Add cart calculation"
# Hash: def5678

ステップ2:バグ発見 — 雑なコミットの代わりに –fixup を使う

テスト中にcheckout.pyでバリデーションが不足していることを発見しました。修正後:

# 代わりに: git commit -m "fix validation in checkout"  ← 履歴が汚れる
# こちらを使う:
$ git add payment/checkout.py
$ git commit --fixup abc1234

# git log --oneline:
# f9e3a12 fixup! Add checkout flow   ← 自動でメッセージが設定される
# def5678 Add cart calculation
# abc1234 Add checkout flow

cart側でもバグが見つかり、同じように対処します:

$ git add payment/cart.py
$ git commit --fixup def5678

# git log --oneline:
# 7b2c891 fixup! Add cart calculation
# f9e3a12 fixup! Add checkout flow
# def5678 Add cart calculation
# abc1234 Add checkout flow

ステップ3:プッシュ前に整理する

$ git rebase -i --autosquash HEAD~4

Gitはリストが自動で並べ替えられた状態でエディタを開きます — 何も変更する必要がありません:

pick abc1234 Add checkout flow
fixup f9e3a12 fixup! Add checkout flow
pick def5678 Add cart calculation
fixup 7b2c891 fixup! Add cart calculation

保存してエディタを終了すると、Gitが自動でマージします。結果:

$ git log --oneline
a8d3f21 Add cart calculation
c4b7e92 Add checkout flow

履歴がきれいになり、4つのごちゃごちゃなコミットの代わりに2つのすっきりしたコミットになりました。レビュアーも喜び、ぐっすり眠れます。

デフォルト設定 — 一度設定すれば永続的に使える

毎回--autosquashと入力する代わりに、グローバル設定で有効化しましょう:

$ git config --global rebase.autoSquash true

その後はgit rebase -i HEAD~Nと入力するだけで、autosquashが自動的に有効になります。

–squash vs –fixup:どちらを選ぶ?

--fixupはコミットをまとめて子コミットのメッセージを破棄し、元のメッセージのみを保持します。--squashはまとめますが、両方のメッセージを保持して、リベース時にエディタで編集できるようにします。

# 元のコミットにメッセージ内容を追加したい場合は --squash を使う
$ git commit --squash abc1234
# 作成: "squash! Add checkout flow" — リベース時にGitがエディタを開いてメッセージを編集できる

「コミット後の小さな修正」の95%のケースでは、--fixupで十分です。--squashが必要なのは、元のコミットメッセージに情報を追加したい場合だけです。

履歴の深いところにあるコミットを Fixup する

--autosquashの優れた点は、元のコミットが履歴のどこにあっても正確に見つけ出すことです。10個のコミットを作成していて、3番目のコミットをfixupしたい場合 — 普通に実行して、その後rebase -i HEAD~11とするだけです。Gitがfixup!を元のコミットの直後の正しい位置に自動で配置してくれます。数える必要はありません。

チームでの実践

以前所属していた8人のチームで、このフローをレビュープロセスに導入しました:全メンバーがレビューコメント後の修正に--fixupを使用し、mainにマージする前にautosquashを実行する。履歴が明確になったことでマージコンフリクトが大幅に減り、レビュアーも変更を追いやすくなりました — 7つの「fix」コミットを読んでフィーチャーの内容を把握する必要がなくなったのです。

チームのルール:

  • コーディング中:自由にコミットし、修正には--fixupを使ってマークする
  • リモートにプッシュする前:rebase --autosquashを実行してローカルを整理する
  • 共有ブランチにプッシュ済みのコミットは絶対にリベースしない(他の人のリポジトリでdivergenceが起きる)

リベース前の確認

リモートにどのコミットをプッシュしたか分からない場合:

# リモートにまだないコミットを確認する
$ git log origin/feature-branch..HEAD --oneline
# これらのコミットのみをリベースする — すでにプッシュしたものは触らない

まとめ

git commit --fixup + git rebase -i --autosquashは、コミット履歴をきれいに保つために自分が知っている中で最も時間を節約できる組み合わせです。疲れていて間違えやすい時にインタラクティブリベースで手動スカッシュするのではなく、コミット時に修正をマークしておき、プッシュ前に1回リベースするだけで完了します。

rebase.autoSquash trueをグローバル設定に一度追加するだけです。次からはPRの履歴に「fix typo」、「ok done now」、「actually fixed now」といったコミットがなくなります — そしてレビュアーから深夜2時にメッセージが来ることもなくなります。

Share: