Gitでmasterの開発内容を開発ブランチに反映させるとき、mergeか、rebaseか

参考:

あるあるのシナリオだが、masterブランチからフォークした開発ブランチでコミットを進めている間にmasterでコミットが進む場合。

master -o--o--o--o--o
           |
           v
dev        +--o--o--o

この場合も多くの場合では特に何も考えずにmasterにマージしていいはずである。

master -o--o--o--o--o--o
           |           ^
           v           |
dev        +--o--o--o--+

マージ時にconflictがある場合、ローカルのgitでマージをしているのならマージ元(master)でconflictを解消することもできるが、GitHubのpull-requestではマージがリモートサーバで発生するので少々ややこしい (conflict解消できるのか??)

また、masterブランチの新しいコミットを開発ブランチで使いたい場合も少々厄介だ。

こういうとき、自分は極力rebaseをするようにしている。

$ git checkout dev
$ git rebase master

これにより、コミットグラフは以下のようになる。

master -o--o--o--o--o
                    |
                    v
dev                 +--o--o--o

そのため、devでmasterの新しいコミットを使うことができる。rebase時にconflictが発生する場合があるので、その場合は自力で解決する。

注意点としては、devを既にどこかのリモートブランチにpushしていた場合、rebase後のローカルdevをそのままではリモートにpushできない。push --forceで強制的にリモートブランチを書き換えることはできるが、リモートdevを複数人で共有している場合は混乱を招くので注意が必要だ。

他のやり方としては、一度masterをdevにマージするやり方がある。

$ git checkout dev
$ git merge master

これにより、グラフは

master -o--o--o--o--o--+
           |           |
           v           v
dev        +--o--o--o--o

こうなる。

個人的にはこのやり方は好きではない。理由は、masterからdevへのマージリンクがコミットグラフに生じることで、コミットグラフの構造が複雑になるからだ。masterからdevへはフォーク(branch)だけが起こり、devからmasterへはmergeだけが起こる、というルールになっている方がコミットグラフが分かりやすくなる。と思う。

ということで、masterから任意の開発ブランチへのmergeを禁止するような設定がGitでできないか少し調べた。Git Hookを使えばなんとかできそうではあるが、ちょっと微妙な作りのスクリプトを使うことになりそうだ。

上記スクリプトでは、マージコミットのコミットメッセージをスクレイピングしてマージ元のブランチ名を取得している。うーん、もう少しエレガントにできないものか。

ところで、調べていると以下のQ&Aでもう一つの方法が言及されていた。

$ git checkout master
$ git branch dev2
$ git checkout dev2
$ git merge dev

要は、こういうコミットグラフを作る。

master -o--o--o--o--o
           |        |
           |        v
dev2       |        +--o
           |           ^
           v           |
dev        +--o--o--o--+

で、devは放置してdev2で開発を進める。

これだと確かに、masterから開発ブランチへ直接マージすることはない。devからdev2へのマージは、要はmasterへのマージのconflict解消作業を手元のdev2でやるということだと考えられる。とはいえ、なんだか余計にコミットグラフが複雑になったような印象は否めない。