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でマージを中止し、各ブランチを見直してみる。

コマンドで

  1. コンフリクトでマージがストップ
  2. git statusでコンフリクトしているファイルを確認
  3. git diff git diff --ours git diff --theirs git diff --baseでコンフリクトしている差分を確認。
  4. git checkout --ours git checkout --theirs でどちらかの変更を適用するか、ファイルを直接編集して解決する

マージツールで

git diffしながらエディタをさわるのも面倒なので、git mergetoolでマージツールを利用して解決する。

  1. コンフリクトでマージがストップ
  2. $ 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の変更がまとめられる。

Last modified 2020.05.29