昨日のDay 11では、「Gitのブランチはたった40バイトのポインタに過ぎず、作成速度は0.01秒」という内部構造と、新標準コマンド
git switch による軽快なブランチ分岐を学びました。しかし、ブランチは「作って終わり」ではありません。機能開発やバグ修正が終わったら、分岐させたコードを安定板であるメインライン(
main)へと安全に合流させなければなりません。この合流作業こそが「マージ(git merge)」です。Subversion (SVN) などの従来型VCSを長く使ってきた方にとって、マージは「できればやりたくない最悪の難関」だったのではないでしょうか?「どのリビジョンからどのリビジョンまでマージしたかをExcelやメモ帳に記録しておき、間違えると重複マージで大惨事……」そんな苦痛の記憶をお持ちの方も多いはずです。
しかしGitのマージは、驚くほど知的でスマートです! Gitはコミットグラフを自動的に遡って「共通の祖先(Base)」を一瞬で特定し、人間がリビジョン番号を数える必要など一切ありません。
本日は第12回として、Gitのマージにおける2大基本動作である「Fast-Forward(早送りマージ)」と「3-Way Merge(3方向マージ)」のメカニズムを解剖し、C#実務での安全な統合手順やTortoiseGitでのGUI操作まで徹底的にわかりやすく伝授します!
① 導入・本日の達成目標
「ブランチを切るのは簡単になったけれど、いざmainに戻す(マージする)のが怖い……」
これはGitを学び始めた多くのエンジニアが抱える最初の心理的ハードルです。
しかし、Gitが内部で「どのように共通の先祖を探し、どうやって変更を重ね合わせているのか」という仕組みさえ知ってしまえば、マージは決して怖いものではなく、極めて安全で再現性の高い日常業務になります。
本日の講義でマスターするゴールは以下の3点です!
- なぜGitのマージはSVNより圧倒的に楽なのか? コミットグラフによる「共通祖先(Base)の自動特定」の仕組みを理解する。
- 「Fast-Forward」と「3-Way Merge」の決定的な違いを把握し、マージコミットが生成される条件と
--no-ffオプションの使い所を習得する。 - TortoiseGitでの安全なマージダイアログ操作と、C#プロジェクト(.csproj / DIコンテナ登録)における安全な合流作法をマスターする。
② 【徹底比較】SVNの手動リビジョン指定 vs Gitの自動先祖検出
まず、長年SVNを使ってきたエンジニアのトラウマを払拭するために、SVNとGitのマージの違いを整理しましょう。
両者のアーキテクチャの違いを比較表で確認してみましょう:
③ 【内部構造①】Fast-Forward(早送りマージ)の仕組み
Gitでマージを実行した際、最もシンプルに完了するパターンが「Fast-Forward(ファストフォワード / 早送り)」です。
Fast-Forwardが起こる条件は、「ブランチを切った後、マージ先(通常はmain)に新しいコミットが1件も追加されていない場合」です。
この場合、main の先端から feature の先端までは一直線につながっています。
そのため、Gitは新しい「マージコミット」を作る必要すらありません。単に main という名札(40バイトのポインタ)を、feature がいる先頭コミットへとスッと前に進める(早送りする)だけでマージが完了します!
- メリット: 余計なマージコミットが作られないため、コミット履歴が綺麗な一本道のまま保たれます。
- 注意点(--no-ff の存在): 「いつ、どの機能ブランチを合流させたか」という分岐と合流の歴史的記録を残したいチームでは、あえてFast-Forwardを禁止する
git merge --no-ffを使う運用ルールもあります。
④ 【内部構造②】3-Way Merge(3方向マージ)とマージコミット
一方、チーム開発で日常的に発生するのが、「自分がfeatureブランチで開発している間に、同僚がmainブランチに別の変更をコミットしてしまった」というケースです。
この場合、履歴は完全に二股に分岐しているため、単にポインタを早送りすることはできません。ここで登場するのが「3-Way Merge(3方向マージ)」です!
なぜ「3-Way(3方向)」と呼ばれるのでしょうか? それはGitが以下の3つの状態を比較して結合するからです:
- 共通の祖先(Baseコミット): ブランチが枝分かれした直前のコミット
- マージ先(main)の最新コミット: 同僚が加えた変更
- マージ元(feature)の最新コミット: 自分が加えた変更
Gitは「共通の祖先」と双方の先端を照らし合わせ、「Aさんが触ったファイルとBさんが触ったファイルが違えば両方を自動結合」「同じファイルでも修正行が離れていれば自動結合」と賢く処理します。
そして、両方の変更を1つに統合した特別なコミット、すなわち「親コミットを2つ持つマージコミット(Merge Commit)」を自動生成するのです!
⑤ 【コマンド実践】実務で使うマージコマンド 4選
ターミナルでマージを行う際の基本ルールは、「まずマージ先(受け入れ側)のブランチに移動してから、マージコマンドを打つ」ということです!
$ git switch main
Switched to branch 'main'
# ステップ2: 機能ブランチをmainに合流させる(基本マージ)
$ git merge feature/order-api
Updating 3a8f1b2..7d2e4f1
Fast-forward
Controllers/OrderController.cs | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
# 【実務テクニック①】Fast-Forwardが可能な場合でも、必ずマージコミットを残す
$ git merge --no-ff feature/order-api
# 【実務テクニック②】細かい試行錯誤コミットを1つにまとめてスッキリ合流(Squashマージ)
$ git merge --squash feature/order-api
# 【緊急避難】マージ中にコンフリクト等で困ったとき、マージ前の状態に完全リセットする
$ git merge --abort
⑥ 【GUI編】TortoiseGitでの安全なマージダイアログ操作
Windowsのエクスプローラー上で右クリック操作できるTortoiseGitなら、ブランチの合流も直感的なダイアログで安全に行えます。
- 作業ツリーが
mainブランチになっていることを確認します(なっていなければ「切り替え/チェックアウト」で移動)。 - プロジェクトフォルダで右クリック →「TortoiseGit」→「マージ(M)...」を選択。
- 「ブランチ(B)」のドロップダウンから、合流させたい機能ブランチ(例:
feature/order-api)を選択。 - 「マージオプション」で必要に応じて「Fast-forward if possible」または「No Fast-Forward」を選択し、[OK]をクリック!
- マージが成功すると進行ダイアログに「成功」と表示され、Visual Studioのソリューションにも即座に新コードが反映されます!
⑦ 【C#現場のリアル】.csproj と DIコンテナ登録でのマージ注意点
C#(.NET)開発の実務において、マージ時に特に意識しておきたいポイントが2点あります。
💡 C#エンジニアが知っておくべき2大マージ勘所
- SDKスタイル .csproj の恩恵: 昔の.NET Framework時代の
.csprojはファイルを追加するたびにXML行が追加され、マージのたびに競合が頻発していました。しかし現代の .NET 6/8/9 では「フォルダ内の.csファイルを自動インクルード」する仕様になったため、ファイル追加に伴うプロジェクトファイルの衝突は劇的に減少しました! - Program.cs(DIコンテナ登録)の集中管理: 複数人が同時に新サービス(
builder.Services.AddScoped<IOrderService, OrderService>()など)を追加すると、Program.csの末尾で競合が起きやすくなります。チーム内で「DI登録の記述ルール」を決めておくか、サービスごとに拡張メソッド(services.AddOrderModule())に分割しておくとマージが極めて平和になります。
⑧ 📌 本日のまとめ・理解したい最重要ポイント
第12回の講義、お疲れ様でした! 本日の最重要ポイントを振り返りましょう。
- Gitは共通の祖先(Base)を自動探索する: SVNのような人間によるリビジョン番号メモは一切不要! コミットグラフから賢く自動結合。
- Fast-Forwardは「早送り」: mainが進んでいなければ、ポインタを先頭へ進めるだけ。コミット履歴は一本道の直線のまま。
- 3-Way Mergeは「マージコミット生成」: 双方が進んでいる場合、Base・main・featureの3方向を比較して合流コミットを作成。
- まずは
mainに切り替えてからgit merge <ブランチ>: 万が一のトラブル時はgit merge --abortで安全に巻き戻せる!
コメント
コメントを投稿