← 解説「マージ — なぜポインタの付け替えで済むことがあるのか」に戻る
サンプルコードで身につける: マージ — なぜポインタの付け替えで済むことがあるのか
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1fast-forward を実機で観察する
mainに新しいコミットが無いときのマージです。新しいコミットは作られず、ブランチファイルの40文字が書き換わるだけであることを、オブジェクト数と出力で確認します。
# main は動いておらず、feature だけが進んでいる状態
$ git log --all --graph --oneline
* e4d92c8 (feature-login) ログイン処理を追加
* dd2818b (HEAD -> main) READMEに使い方を追加
$ git rev-list --objects --all | wc -l
11
$ git merge feature-login
Updating dd2818b..e4d92c8
Fast-forward
src/index.js | 2 +-
# オブジェクトは1つも増えていない
$ git rev-list --objects --all | wc -l
11
# 変わったのはブランチファイルの中身だけ
$ cat .git/refs/heads/main
e4d92c88aa54eae7a1b1acb811fae2cde621683b
2どちらのマージになるかを事前に判定する
fast-forwardになるか、マージコミットができるかは「相手が自分の子孫か」だけで決まります。git merge-base --is-ancestor で事前に判定できます。予測できると身構えずに済みます。
# main が feature の祖先か? = fast-forwardできるか?
$ git merge-base --is-ancestor main feature-login && echo 'FFできる' || echo '分岐している'
FFできる
# 共通の祖先(merge base)を調べる
$ git merge-base main feature-login
dd2818b6f87da96757d1cd70f43e293660aece49
# 分岐後、両方が進むと判定が変わる
$ git merge-base --is-ancestor main feature-login && echo 'FFできる' || echo '分岐している'
分岐している
# 履歴を残したいときは、FFできる場合でも明示的にマージコミットを作れる
$ git merge --no-ff feature-login
3マージコミットは親を2つ持つ
分岐していた場合に作られるマージコミットの実物です。parent 行が2つ並んでいるのが、履歴がグラフになる瞬間の証拠です。これ以降 git log の表示が枝分かれします。
$ git cat-file -p HEAD
tree bb8ae4bd21c330573ab1645b75a0f9577407a012
parent 0361ab2aa5b694109622d757dfb1c17c564b0f73 ← 自分側(main)
parent e4d92c88aa54eae7a1b1acb811fae2cde621683b ← 相手側(feature-login)
author Tanaka <tanaka@example.com> 1700000400 +0900
committer Tanaka <tanaka@example.com> 1700000400 +0900
Merge branch 'feature-login'
# 履歴が合流している様子
$ git log --graph --oneline
* 9f3c1a2 Merge branch 'feature-login'
|\
| * e4d92c8 ログイン処理を追加
* | 0361ab2 ログアウト処理を追加
|/
* dd2818b READMEに使い方を追加
43-way merge の判定を自分で実装する
共通祖先・自分側・相手側の3つを比べるだけの単純な規則です。実装してみると、どういうときに自動でマージでき、どういうときに競合するのかが規則として頭に入ります。
Python
5同じファイルを触っても競合しない場合
「同じファイルを2人が編集したら必ず競合する」は誤解です。Gitは行単位で見るため、離れた場所の変更なら自動で統合されます。実際に別々の行を編集した場合の出力です。
# 元のファイル(3行)
$ cat config.yml
host: localhost
port: 8080
debug: false
# main では1行目を、feature では3行目を編集した
$ git merge feature-config
Auto-merging config.yml
Merge made by the 'ort' strategy.
# 両方の変更が入っている
$ cat config.yml
host: production.example.com ← mainの変更
port: 8080
debug: true ← featureの変更
# → 競合するのは「同じ行の付近」を両方が変えたときだけ