git merge
コマンドの概要
複数のブランチに分岐して管理されたコミットを統合して1つのブランチにするコマンド。
コミットは親子関係で把握されており、共通のご先祖さまにさかのぼってそこからの変更が統合される。
masterブランチでEをコミットしてgit switch -c topic
とし、topicブランチではAからCのコミット、masterブランチではF、Gのコミットをした場合、以下のようになる。
A - B - C [topic]
/
D - E - F - G [HEAD:master]
ここで、git merge topic
とすると
A - B - C [topic]
/ \
D - E - F - G --- H [HEAD:master]
となり、Hというマージコミットが作成され、topicブランチとmasterブランチの共通のご先祖さまであるEを始点として、そこからの変更であるコミットA,B,Cをmasterブランチに統合する。
masterブランチが指し示すコミットHは、コミットAからFまでの内容を含んだものとなり、topicブランチはCを指したまま。git merge
してもtopicブランチは削除されない。マージによりtopicブランチがもう不要となったら、git branch -d topic
でブランチを削除する。
A - B - C
/ \
D - E - F - G --- H [HEAD:master]
Cのポインタであった[topic]
が削除されるだけで、コミットA,B,Cが削除されるわけではない。
コンフリクト
各ブランチで同じ箇所を変更してしまった場合、Gitがどちらを採用すべきか判断できず、マージがストップしてしまうので、手動で解決する。
コンフリクトするはずじゃなかった、というときはgit merge --abort
でマージを中止し、各ブランチを見直してみる。
コマンドで
- コンフリクトでマージがストップ
git status
でコンフリクトしているファイルを確認git diff
git diff --ours
git diff --theirs
git diff --base
でコンフリクトしている差分を確認。git checkout --ours
git checkout --theirs
でどちらかの変更を適用するか、ファイルを直接編集して解決する
マージツールで
git diff
しながらエディタをさわるのも面倒なので、git mergetool
でマージツールを利用して解決する。
- コンフリクトでマージがストップ
$ git mergetool
vim diff を使用する場合、
| LOCAL | BASE | REMOTE|
| MERGED |
という画面構成。ウインドウ間の移動は ctrl+w hjkl
MERGEDを直接編集してコンフリクトを解決し、:xa
で保存、全ウインドウを終了。
- vim の
:wq
と:x
どちらも保存して終了だが、:wq
はかならず保存して終了するが、:x
は変更があった場合だけ保存して終了する。(:ZZ
は:x
と同じ)
ファストフォワード
マージしたいブランチが現在のブランチより先に進んでおり、現在のブランチがコミットしていないとき、git merge
をすると、そのままHEADがマージしたいブランチが指しているコミットに移動する。
コミットE でgit branch -c topic
とし、masterではコミットなし、topicでA、B、C のコミットをしたとき
A - B - C [topic]
/
D - E [HEAD:master]
ここで、git merge topic
とすると
D - E - A - B - C [HEAD:master][topic]
となる。
git merge --no-ff topic
とし、ファストフォワードさせずにマージコミットを作ることもできる。
A - B - C [topic]
/ \
D - E ----------- F [HEAD][master]
ファストフォワードありがデフォルトになっており、設定可能。
ただし、git pull
時にはファストフォワードしたいので(ファストフォワードしないとgit pull
のたびにマージコミットができちゃう)、その設定もあわせて。
// ファストフォワードせずにマージコミットを作る、ただし git pull のときはつくらない
$ git config --global merge.ff false
$ git config --global pull.ff only
ブランチをまとめてひとつのコミットにする
ファストフォワードができる状況で、マージしたいブランチのコミットをまとめてひとつのコミットにすることもできる。
コミットE でgit branch -c topic
とし、masterではコミットなし、topicでA、B、C のコミットをしたとき
A - B - C [topic]
/
D - E [HEAD][master]
ここで、git merge --squash topic
とするとコミットA、B、Cにおける変更がインデックスにステージされる。そのままgit commit
すると
$ git merge --squash topic
$ git commit -m"merge branch topic"
A - B - C [topic]
/
D - E - F [HEAD][master]
コミットFにコミットA、B、Cの変更がまとめられる。