ブランチを使用する

Git のマージ戦略のオプションとサンプル

作業とテストが完了し、開発のメインラインにマージする準備ができたら、チームで検討すべきことがあります。それは、チームにおけるマージ戦略をどうするかということです。この記事では、さまざまなマージ戦略を探りながら、アトラシアンではどのような戦略を採っているかを紹介します。この記事を読み終わるころには、チームに最適な戦略を見つけることができているでしょう。

Git でのマージの進め方

マージとは、2 つのブランチを結合することです。Git では 2 つ以上のコミットポインターを取って、ポインター間で共通するベースコミットを見つけようとします。Git にはベースコミットを見つけるためのさまざまな手法があり、これらの手法は「マージ戦略」と呼ばれます。共通するベースコミットが見つかったら、指定したマージコミットの変更を結合する新しい「マージコミット」が作成されます。技術的な観点から見ると、マージコミットは単に 2 つの親コミットを持つ通常のマージということになります。

明示的に指定しないかぎり、git merge ではマージ戦略が自動的に選択されます。git merge コマンドと git pull コマンドには -s (strategy の「s」) オプションを渡すことができます。-s オプションでは使用するマージ戦略の名前を追加できます。明示的に指定しなかった場合は、指定したブランチに最適なマージ戦略が自動的に選択されます。使用可能なマージ戦略の一覧は以下のとおりです。

Recursive

git merge -s recursive branch1 branch2

このマージ戦略では 2 つの HEAD に対応します。Recursive はデフォルトのマージ戦略で、1 つのブランチのプルまたはマージを実行します。また、名前変更に関するマージの検出と操作ができますが、現時点では検出したコピーを活用する方法はありません。こちらが 1 つのブランチのプルまたはマージを実行する際のデフォルトのマージ戦略になります。

解決

git merge -s resolve branch1 branch2

この戦略では三方向マージアルゴリズムを使った、2 つの HEAD の解決のみを実行できます。十字マージの曖昧さを慎重に検出する戦略で、一般的には安全かつ高速だとみなされています。

Octopus

git merge -s octopus branch1 branch2 branch3 branchN

3 つ以上の HEAD に対応する際のデフォルトのマージ戦略です。複数のブランチが渡されると、自動的に Octopus がマージ戦略として選択されます。手動による解決が必要なマージの競合がある場合、マージは却下されます。基本的には類似するフィーチャーブランチ HEAD を結合するときに使用します。

Ours

git merge -s ours branch1 branch2 branchN

Ours 戦略は複数の任意の数のブランチに対応しています。この戦略のマージ結果は現在のブランチの HEAD のものになります。「Ours」という単語は、他のすべてのブランチにおけるすべての変更を実質的に無視することを意味しています。類似するフィーチャーブランチの履歴を結合する際に使用します。

Subtree

git merge -s subtree branchA branchB

Subtree は Recursive 戦略の拡張版です。A と B をマージしようとしていて、B が A の子サブツリーである場合、まず B が更新されて A のツリー構造が反映されます。この更新は A と B で共有されている共通の祖先ツリーにも行われます。

Git マージ戦略のタイプ

明示的なマージ

明示的なマージはデフォルトのマージタイプです。この場合の「明示的」とは、新しいマージコミットが作成されることを意味します。これによってコミット履歴が変更され、マージが実行された箇所が明示的に示されます。マージコミットのコンテンツも明示的で、どのコミットがマージコミットの親かが表示されます。マージコミットではほぼ間違いなくプロジェクト履歴に「ノイズ」が追加されるため、明示的なマージを使わないチームもあります。

リベースマージまたは早送りマージでトリガーされる暗黙のマージ

明示的なマージではマージコミットが作成されるのに対して、暗黙のマージではそれが作成されません。暗黙のマージでは指定したブランチ HEAD から一連のコミットを取得し、ターゲットブランチの先頭にそれらを適用します。暗黙のマージはリベースイベントまたは早送りマージでトリガーされます。暗黙のマージでは、指定したブランチでどのコミットを選択するかをその都度決定します。

コミットをまとめてマージする (通常は明示的なマージで使用しない)

暗黙のマージには Squash というものもあります。Squash は対話式リベース中に実行することができます。Squash マージではターゲットブランチのコミットを取得して結合するか、1 つのコミットにまとめます。まとめられたコミットはマージベースブランチの HEAD に追加されます。Squash は通常、マージ中に「クリーンな履歴」を維持するために使用します。マージ対象ブランチには頻出するコミットの詳細な履歴を含めることができます。まとめてマージしたターゲットブランチのコミット履歴は、単一の統合された「ブランチコミット」になります。この手法はフィーチャーブランチを利用する git ワークフローで威力を発揮します。

Git マージの Recursive 戦略で使えるオプション

さきほど紹介した「Recursive」戦略には他にも独自の操作オプションのサブセットがあります。

ours

このオプションを Ours マージ戦略と混同しないようにしてください。このオプションでは「Ours」の結果を利用することで競合を自動的かつスムーズに解決します。競合がない場合、「Theirs」側の変更は自動的に組み込まれます。

theirs

「Ours」戦略とは反対に、「Theirs」オプションでは競合の解決時に外部のマージ対象ツリーを対象にします。

patience

このオプションではじっくりと時間をかけて、条件に一致する重要ではない行のマージエラーを回避します。マージ対象のブランチの分岐が激しいときにこのオプションを使います。

diff-algorithim

このオプションでは明示的に diff アルゴリズムを指定することができます。git diff コマンドで使用できるのと同じ diff アルゴリズムを指定します。

ignore-*

    ignore-space-change
    ignore-all-space
    ignore-space-at-eol
    ignore-cr-at-eol

これらは空白文字を対象とするオプションです。渡されたオプションのサブセットに一致するすべての行が無視されます。

renormalize

このオプションではすべての git ツリーでチェックアウトとチェックインが実行されるとともに、三方向マージを解決します。このオプションは、異なるチェックイン/チェックアウト状態のマージ対象ブランチで使用します。

no-normalize

このコマンドを実行すると標準化オプションが無効になり、merge.renormalize の構成変数が上書きされます。

no-renames

このオプションを指定すると、名前を変更したファイルをマージ中に無視します。

find-renames=n

これはデフォルトの動作です。Recursive マージではファイルの名前変更が尊重されます。n パラメーターを使って名前変更の類似性のしきい値を渡すことができます。デフォルトの n の値は 100% です。

subtree

このオプションは Subtree 戦略から流用したものです。マージ戦略を 2 つのツリーで実行し、同じ祖先に一致させる方法を修正する場合、このオプションではツリーのパスメタデータを操作して祖先に一致させます。

アトラシアンの Git マージポリシー

アトラシアンでは明示的なマージの使用が強く推奨されています。理由はいたってシンプルです。明示的なマージは、マージ対象のフィーチャーのトレーサビリティとコンテンツが非常にわかりやすくなっています。レビュー用にフィーチャーブランチを共有する前にローカル履歴のクリーンアップリベースを行うことを強く推奨しますが、これによってポリシーが変更されるようなことは一切なく、単純にポリシーが強化されます。

ブランチを試す準備が整いましたか?

この対話式のチュートリアルをお試しください。

今すぐ始める