分岐した履歴の解決:Git Merge と Rebase の戦略

分岐したブランチ、競合処理、共有履歴、チームセーフなワークフローの選択における git merge と git rebase の比較。

分岐した履歴の解決:Git Merge と Rebase の戦略

Git merge と rebase はどちらも、ブランチが別のブランチから分岐した場合に役立ちます。違いは履歴の保持方法にあり、間違った方を選ぶとコラボレーションが必要以上に難しくなることがあります。

すべての変更に完璧な履歴は必要ありません。必要なのは、チームが理解し、共有ブランチを安定に保つ戦略です。

分岐した履歴とは

分岐した履歴は、共通の開始点以降に2つのブランチが異なるコミットを持つ場合に発生します。例えば、月曜日に main から feature/log-cleanup を作成したとします。火曜日に誰かがセキュリティ修正を main にマージしました。すると、あなたのブランチと main はそれぞれ相手が持っていないコミットを持つようになります。

これは次のように確認できます:

git log --oneline --graph --decorate --all

また、Git があなたのブランチとリモートブランチが分岐したことを伝える場合もあります。これは両側に固有のコミットがあることを意味します。Git はそれらをどのように結合するかをあなたに決定させます。

通常のツールは merge と rebase の2つです:

git merge main

または:

git rebase main

どちらも最終的なファイル内容は同じになります。履歴は異なって見えます。

Git Merge の仕組み

Merge は既存のコミットを書き換えずに履歴を結合します。あなたのブランチと main の両方が進んだ場合、Git は2つの作業ラインを結びつけるマージコミットを作成します。

典型的な流れは次のようになります:

git switch feature/log-cleanup
git fetch origin
git merge origin/main

競合がなければ、Git はマージコミットを作成するか、可能な場合は fast-forward します。競合がある場合、Git は一時停止し、解決を求めます。

Merge は、コラボレーションの真の形状を保持したい場合に適しています。作業が並行して行われ、後で結合されたことを示します。これは長期ブランチ、リリースブランチ、共有フィーチャーブランチに役立ちます。

トレードオフとして、頻繁なマージはノイズを増やす可能性があります。「main を feature にマージ」というコミットが多いブランチは、スキャンが難しくなることがあります。それが間違っているわけではありませんが、履歴が乱雑になる可能性があります。

Merge を使用すべき場合:

  • ブランチが他の開発者と共有されている場合。
  • コミット履歴の書き換えを避けたい場合。
  • チームが統合ポイントの正確な記録を重視する場合。
  • リリースブランチや保護されたブランチを更新している場合。

すでにプッシュされ、他の人が使用しているブランチでは、通常 merge がより安全なデフォルトです。

Git Rebase の仕組み

Rebase は、ブランチのコミットを新しいベースコミットの上に移動させます。分岐した2つのラインがマージコミットで結合されるのではなく、ブランチの履歴が線形になります。

典型的な流れは次のようになります:

git switch feature/log-cleanup
git fetch origin
git rebase origin/main

Git はあなたのコミットを1つずつ最新の main の上に再生します。競合が発生した場合、Git は再生できないコミットで停止します。競合を修正した後、次のコマンドで続行します:

git add <修正したファイル>
git rebase --continue

Rebase が混乱した場合は、元に戻すことができます:

git rebase --abort

Rebase はローカルでプライベートなブランチに便利です。履歴が読みやすくなるためです。レビュアーは、あなたのコミットが最初から最新の main の上に構築されたかのように見ることができます。

Rebase を使用すべき場合:

  • ブランチが自分専用で共有されていない場合。
  • クリーンで線形なコミット履歴を希望する場合。
  • プルリクエストを開く前にブランチを準備している場合。
  • チームが明示的にリベースされたフィーチャーブランチを好む場合。

主なルールはシンプルです:チームが期待しない限り、公開コミットをリベースしないでください。Rebase はコミットハッシュを書き換えます。他の人があなたの古いコミットに基づいて作業していた場合、あなたの書き換えが混乱を引き起こす可能性があります。

より日常的な Git 履歴ツールについては、プロジェクト履歴の探索 を参照してください。

Merge または Rebase 中の競合処理

競合は、Git が変更を自動的に結合できない場合に発生します。デプロイメントマニフェスト、依存関係ロックファイル、共有設定ファイルなど、頻繁に変更されるファイルでよく発生します。

まずステータスを確認します:

git status

競合ファイルを開き、競合マーカーを探します:

<<<<<<< HEAD
カレントブランチのバージョン
=======
受信バージョン
>>>>>>> ブランチ名

あなたの仕事は、マークされたブロックを実際に必要な最終内容に置き換えることです。マーカーは残さないでください。

編集後、ファイルをステージします:

git add path/to/file

Merge の場合は、次のコマンドで完了します:

git commit

Rebase の場合は、次のコマンドで続行します:

git rebase --continue

競合解決後はテストを実行するか、少なくとも関連する検証コマンドを実行してください。ファイルは構文的にクリーンでも論理的に間違っている可能性があります。例えば、2つの Kubernetes YAML 変更が競合なくマージされても、重複したポートや不一致のラベルが定義されることがあります。

チーム戦略の選択

最良の戦略は、プロジェクトに予測可能な履歴を作成するものです。多くのチームは、プライベートなフィーチャーブランチには rebase を、プルリクエストにはマージコミットを使用します。他のチームはすべてのフィーチャー作業を1つのコミットにスカッシュします。一部のインフラチームは、ブランチがいつ統合されたかを正確に示すため、マージコミットを好みます。

ルールを決めて文書化してください。シンプルなポリシーは次のようになります:

  • レビュー前に自分のフィーチャーブランチをリベースする。
  • main、リリースブランチ、共有ブランチを決してリベースしない。
  • 承認されたプルリクエストにはマージコミットを使用する。
  • 小さな単一目的の修正にはスカッシュマージを使用する。

これにより、緊急作業中に Git スタイルの議論を防げます。また、CI、リリースノート、デプロイメントツールが一貫した履歴に依存できるため、自動化も容易になります。

助けを求めるべき時

他の人が使用する可能性のあるブランチでリベース後に force-push する前に尋ねてください。また、競合がセキュリティ設定、データベースマイグレーション、本番デプロイメントファイル、または理解できない生成されたロックファイルに影響する場合も尋ねてください。

マージやリベースが複雑になった場合は、git status で状態を確認して停止してください。通常は git merge --abort または git rebase --abort で中断して以前の状態に戻せます。これは、ワーキングツリーがきれいに見えるまでランダムなコマンドを試すより良い方法です。

Merge と rebase は同じ大きな問題を解決しますが、異なるストーリーを語ります。Merge は作業がどのようにまとまったかを保持します。Rebase はよりクリーンなコミットラインを作成します。それぞれを適切な場面で使用すれば、Git 履歴はリスクの原因ではなく、有用なものになります。