Git のマージコンフリクトを構文木(AST)ベースで解決する merge driver。Git 標準は「行」しか見ないが、Mergiraf は言語の文法を理解して両者の変更を突き合わせる。
base に import json だけある状態から、2人が別々に import を足す。
# base # ブランチA # ブランチB
import json import json import json
import os import sys
Git には「同じ場所に別の行」= コンフリクト。Mergiraf は import が「順序が意味を持たない列挙」だと文法から分かるので、両方採って import json / os / sys を返す。
コンフリクトとして認知されなくなる?
ほぼその通り。ただし git が見落とすのではなく:
- git が行レベルで衝突を検出する
.gitattributes経由で merge driver(= Mergiraf)が呼ばれる- 解けたら成功として git に返す → 人間の目にはコンフリクトが出ない
- 解けなければ従来どおりマーカーを残す
握りつぶしではなく「driver が代わりに解いた」。3 は mergiraf review で後から確認できる。
base / 自分側 / 相手側(3-way マージ)
main で git merge feature したときに git が見ている3つ。
- base(
%O)= 枝分かれした地点の共通祖先 - 自分側(
%A/ ours)= いまいるmain - 相手側(
%B/ theirs)= 取り込むfeature
base と見比べて片側だけ変わっていればそちらを採る。両方が同じ場所を変えていたら base では決められない → コンフリクト。base 無しでは「追加されたのか消されたのか」も判別できないので3つ要る。
コンフリクト中は3つとも index に残っている。: の後ろの数字は index の stage 番号(単なる例ではない)。
git show :1:path/to/file # base
git show :2:path/to/file # 自分側 (ours)
git show :3:path/to/file # 相手側 (theirs)
git ls-files -u # stage 番号つきで一覧
普段 index は1パス1エントリ(stage 0 = マージ済み)。コンフリクト中だけ同じパスが3エントリに分裂する —— これが「コンフリクト状態」の正体。3つが一致すれば git が自動で stage 0 に畳む。
mergiraf review は何をしている?
前提として、Mergiraf は1回のマージで2通りの答えを作っている。
- 行ベースの結果 —— git 標準と同じやり方で出した答え
- 構文木ベースの結果 —— Mergiraf 独自の答え
両方をキャッシュに残し、採用したほうを git に返す。
review はその2つを並べるだけ。
git diff --no-index <行ベースの結果> <採用した結果>
出てくる差分 = 「git 標準ならこうなっていた」との違い = Mergiraf が独自に手を入れた箇所そのもの。行ベースをそのまま採用した場合は差分ゼロになる。
対応言語
33言語 + 11のデータ形式(Go, Python, Rust, TypeScript, Java, Ruby … / JSON, YAML, TOML, XML, HTML, go.mod …)。mergiraf languages で一覧できる。
拡張子で言語を判定し、未対応・パース失敗なら黙って行ベースにフォールバックする(= git と同じ挙動に戻るだけ)。だから * merge=mergiraf と全ファイルに当てても壊れない。
そもそも merge driver とは? デフォルトは?
git がマージ時にファイルごとに呼ぶ「解決係」。.gitattributes でパスごとに割り当てる。組み込みは3つ:
| driver | 挙動 |
|---|---|
text | デフォルト。行単位の3-wayマージ。衝突箇所に <<<<<<< を残す |
binary | マージを試みず自分側を残し、コンフリクト扱いにする(バイナリ用) |
union | 衝突した行を両方採用して並べる。順序は保証されない |