Git入門:バージョン管理のきほん
マージの仕組み(fast-forward と マージコミット)
この回でやること
git merge が fast-forward になる場合とマージコミットを作る場合の違いを、履歴の図で理解します。どちらが起きるかを事前に読めるようになりましょう。
- 読む 約 8 分
打つ向きを間違えると、逆向きに入る
ブランチで分かれた作業を元の 1 本に戻す操作がマージです。打ち方はいつも同じで、取り込みたい側に立って、相手を指定します。
ターミナル
$ git switch main
$ git merge featurefeature に立ったまま git merge main と打つと、main の内容が feature に入るだけです。エラーにならないので気づきにくい失敗です。マージは自分の足元に相手を引き込む操作だ、と覚えてください。
同じコマンドなのに、履歴の形が 2 通りに分かれる
git merge の結果は 2 通りあります。分かれ目はひとつだけで、分岐したあとに取り込む側が 1 つでも進んでいるかです。
プレーンテキスト
分岐後、main が進んでいない 分岐後、main も進んでいた
A---B---C feature A---B---C feature
/ / \
o---o main o---o---D---E-------M main
main の名札を C まで滑らせる 合流点 M を新しく作る
→ fast-forward → マージコミット左は、main が分岐点から動いていません。A・B・C はそのまま main の続きとして一直線に並ぶので、名札を C の位置まで滑らせれば合流は終わりです。新しいコミットは要りません。これが fast-forward です。
ターミナル
$ git merge feature
Updating 4c2b70e..8f3a91c
Fast-forward
login.html | 24 ++++++++++++++++++++++++出力の 2 行目に Fast-forward と出ているのが目印です。枝分かれの跡は残らず、最初から 1 本道だったように見えます。読みやすい代わりに、どこまでが 1 つの作業だったかは分からなくなります。
右は main が D と E の分だけ先に進んでいるので、名札を滑らせるだけでは済みません。C の内容と E の内容を両方持った状態を新しく作る必要があります。そこで Git は、親を 2 つ持つコミット M を作ります。これがマージコミットです。このときはコミットメッセージを聞くエディタが開くので、既定のまま保存して閉じれば進みます。
打つ前に、どちらになるか読んでおく
結果は事前に分かります。取り込む側が進んでいるかを見るだけです。
ターミナル
$ git log --oneline feature..main
$A..B は「B にはあるが A には無いコミット」という指定です。feature..main が空なら main は分岐点から動いていないので fast-forward、1 行でも出ればマージコミットになります。
一直線にしたくない、を伝える
Git 任せにせず、形を指定することもできます。
ターミナル
$ git merge --no-ff feature
$ git merge --squash feature--no-ff は、fast-forward できる場合でもマージコミットを作ります。「この 3 コミットは 1 つの機能追加だった」という区切りを履歴に残せるので、チーム開発では好まれます。
--squash は毛色が違います。feature の変更をまとめてステージに載せるだけで、コミットは作りません。自分で git commit して完了です。結果は普通のコミットが 1 つ増えるだけで、枝の跡は残りません。細かい試行錯誤を main に持ち込みたくないときに使います。
- マージは取り込みたい側に立って
git merge 相手と打つ - 分岐後に取り込む側が進んでいなければ fast-forward、進んでいればマージコミットになる
git log --oneline 相手..自分が空かどうかで、打つ前に結果を読める