git range-diff: Rebase後にコードを正確に「検証」するための秘策

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

なぜ git range-diff に注目すべきなのか?

キャリアの浅い頃、私は履歴の長いブランチを rebase するのが非常に怖かったです。コンフリクトを解消した後は、いつも冷や汗をかいていました。一番の不安は、同僚の重要なロジックを誤って消してしまったり、自分のコードが消えてしまったりすることでした。

通常、rebase 後の確認には git diff を使うことが多いでしょう。しかし、このコマンドは main ブランチからの最新コードを含む「大量の変更点」を表示してしまいます。そのため、自分が修正したコードなのか、ベースブランチから継承したコードなのかを区別するのが非常に困難です。

私の8人のチームでは、force push する前に git range-diff でチェックするフローを導入した結果、不注意な rebase によるロジックエラーが70%以上減少しました。チームメンバーも、複雑な機能の実装に自信を持って取り組めるようになりました。

従来の方法の限界

ある機能を開発中に、main ブランチに10個の新しいコミットが追加された場面を想像してください。あなたは git rebase main を実行し、コンフリクトを解消しました。この時、大きな疑問が生じます。「自分のコミット内容は、rebase 前と同じ状態に保たれているだろうか?」

  • git diff: 最終的な状態(スナップショット)のみを比較します。main からの新しいコードによって結果にノイズが混じります。
  • git log: 履歴は表示されますが、同じコミット集合の2つのバージョン間の詳細な差分を確認することはできません。

ここで git range-diff が真価を発揮します。ファイル単位で比較するのではなく、各コミットのパッチ(patches)同士を比較するのです。

Git バージョンの確認

サードパーティ製のツールをインストールする必要はありません。Git はバージョン 2.19(2018年)から this 機能を標準搭載しています。現在のほとんどの開発環境では、このバージョンを超えているはずです。

以下のコマンドで確認してみましょう:

git --version

もし 2.19 より低い場合は、すぐにアップデートすることをお勧めします。また、結果を見やすくするためにカラー表示を有効にしておきましょう:

git config --global color.ui true

実践的な使い方

このコマンドの原理は、2つのコミット範囲(commit ranges)を比較することです。具体的には、古い feature ブランチ(rebase 前)と新しい feature ブランチ(rebase 後)を比較します。

1. Rebase 前に必ずバックアップを作成する

私はいつも、古い状態をマークするために一時的なブランチやタグを作成します。これには2秒しかかかりませんが、絶体絶命のピンチから救ってくれます。

# Rebase前の状態をマークする
git branch feature-backup

# Rebaseを実行
git rebase main

# 新旧のバージョンを比較
git range-diff main feature-backup feature

上記のコマンドは Git に対して、「main から feature-backup までの変更と、main から feature までの変更を比較してくれ」と指示しています。

2. 記号の意味を理解する

結果が表示されたら、行頭の記号に注目してください。これらはコードを壊していないかを知るための鍵となります:

  • =: コミットが完全に一致. これが理想的な状態です。
  • <: 古い範囲にのみ存在するコミット(コミットを squash した場合など)。
  • >: 新しい範囲にのみ存在するコミット。
  • !: 内容が変更されたコミット。通常、コンフリクト解消時に手を加えた場合に表示されます。

例えば、auth.service.ts を修正した際に、誤ってロジックを間違えてしまった場合:

1:  a1b2c3d = 1:  e5f6g7h ログインロジックを追加
2:  i9j0k1l ! 2:  m3n4o5p メールバリデーションの修正
    @@ -10,5 +10,7 @@
     -  if (email === '')
     +  if (!validateEmail(email))
    ++  console.log('この行を消し忘れた');
        return false;

! マークと ++ の行は、誤って console.log を残してしまったことを警告しています。もし range-diff がなければ、この不要なコードは間違いなく本番環境に紛れ込んでいたでしょう。

3. バックアップを忘れた時の対処法

バックアップを作成せずに rebase してしまった場合は、git reflog を使いましょう。このツールは Git でのあらゆる操作を記録しています。rebase コマンドが始まる直前のコミットハッシュを見つければ準備完了です。

git reflog
# 見つかった古いハッシュが abc1234 だと仮定
git range-diff main abc1234 feature

ワークフローの最適化

range-diff のパワーを最大限に引き出すために、以下の2つのヒントを活用してください:

変更の要約を表示: コミット範囲が長すぎて一行ずつ読みたくない場合は、--summary フラグを追加してください。Git はコミットのリストとその対応するステータスのみを表示します。

レビュー品質の向上: ジュニアメンバーの PR をレビューする際、複雑な rebase がある場合は古いハッシュを残しておくよう依頼しています。range-diff の結果がすべて = であれば、ロジックをすべて読み直すことなく自信を持って Approve できます。

特筆すべき点は、git range-diff が非常にスマートであることです。rebase -i でコミットの順序を入れ替えたとしても、エラーを出す代わりに、対応するコミットのペアを自動的に見つけて比較してくれます。

最後に:git range-diff を顕微鏡のように考えてください。rebase で履歴を「かき回した」後、どんなに小さな変更も見逃さずに確認できます。Git の履歴を綺麗に保つために rebase を恐れる必要はありません。push --force-with-lease を実行する前に、このコマンドで確認することだけ忘れないでください。

Share: