Pull Requestのマージオプションあれこれ

2017年7月11日(火)
池田 尚史

今回はPull Requestのマージ時に選べる3つのオプションについてお話したいと思います。

本題の前に: GitHub Satellite

本題に入る前に、さる5月22日、ロンドンにて開催された弊社カンファレンスGitHub Satelliteについて簡単に触れたいと思います。

GitHub Satelliteというのは、GitHub社が定期的に開催しているカンファレンスシリーズの1つです。1番上位のカンファレンスが毎年秋にサンフランシスコで開催される"Universe"(宇宙)、2番目が春開催の"Satellite"(衛星)です。昨年はアムステルダム、今年はロンドンでの開催となりました。なお、今年からさらに3番目の"Constellation"(星座)が東京で開催されることとなり、実際に開催されました。

さてそのGitHub Satelliteにおいて、プラットフォームとしてのアップデートを中心に、次のような発表がされました。

GraphQL API、GitHub Apps、GitHub Marketplace

まず1つ目はEarly Access扱いであったGraphQL APIのプロダクションリリースです。従来のREST API v3のあとを継ぐものとして、GraphQL API v4と名付けられています。GraphQLはFacebookが開発した新しいクエリ言語で、2016年にはRFCのワーキングドラフトとして公開されているものです。RESTのようにAPIエンドポイントを細く分ける代わりに、単一のエンドポイントにPOSTでクエリを送信する形です。クエリを柔軟に記述できるため、RESTの場合複数回の呼び出しになりがちだったケースでも、GraphQLを使うと一回の呼び出しで望みのデータを取得できるのが特徴です。スキーマ定義もあり、型チェックなどのサポートも期待できる仕様になっています。 SPA(Single Page Application)や、モバイルアプリケーションからのAPI呼び出しのニーズが増えてきたことで、GitHub.comへのAPI呼び出しが大変な割合になってきたのもGraphQL採用の1つのモチベーションとなっています。

利用ガイドも同時にリリースしていますので、下記をご覧ください。
https://developer.github.com/v4/guides/

2つめはGitHub Appsです。これは従来Integrationsと呼んでいたものを改善したものになります。 従来のOAuth AppsがGitHubをOAuthのプロバイダーとしてユーザーを認証し、そのユーザーに成り代わってAPIコールを行うものだったのに対し、GitHub AppsはOrganizationに直接インストールができるイメージになります。組み込みのWebhookを持ち、APIコールにユーザーアカウントを必要としません。従来のように特定の個人ユーザーに紐付いてしまうこともなく、またそれを避けるためにBotアカウントなどをわざわざ用意しなくとも良くなっています。

GitHub AppsとOAuth Apsの違いについては次の記事もぜひ参照してください。
https://developer.github.com/apps/building-integrations/setting-up-a-new-integration/about-choosing-an-integration-type/

3つ目はGitHub Marketplaceです。
https://github.com/marketplace

これは従来Integrations Directoryと言っていたものをさらに拡張したもので、個人または企業ユーザーが自由に3rdパーティーのアプリケーションを購入してインストールできるようにしたものです。現状はTravisCIやCircleCI、ZenHubやWaffleなどが並んでいます。もちろん従来のように各社Freeプランも用意しています。

もしGitHubと連携するサービスを作っているのであれば、ご自身のサービスをMarketplaceに出すことも可能です。手順については次もご覧ください。
https://developer.github.com/apps/adding-integrations/listing-apps-on-github-marketplace/

概要については次のビデオもぜひご覧ください。日本語字幕もあります。

GitHub Constellation Tokyo

Satelliteの次は6月6日にGitHub Constellation Tokyoがありました。

当日はGitHub Enterprise2.10のリリースが発表されました。また同日に米国で開催されたAppleのWWDCにおいて、XcodeプロジェクトをGitHubのUIから直接クローンできる機能の発表もありました。

私自身もGitHubの使いこなしについてセッションを持たせていただきました。ご興味あればぜひ次のリンクからスライドをご覧ください。
https://speakerdeck.com/ikeike443/xiao-guo-de-nigithubwoshi-utameni

今回の発表内容についても皆さんの参考になる部分があるかと思います。いずれこの連載内でより詳細に解説できればと思います。

マージオプションについて

さて、今回の記事の本題です。今回はPull Request利用時に選択できるマージオプションについてお話したいと思います。

GitHub.comもGitHub Enterpriseも、現在はPull Requestのマージ時に3つのマージオプションが選べるようになっています。

これらオプションにはそれぞれどのような違いがあり、どのようなユースケースに向いているのでしょうか。

今回は、次の例をベースに説明をしたいと思います。

この例では、ikeike443という開発者がmasterブランチからfeatureブランチを作り、それに対して2つのコミットを追加しています。"日本語版を追加"と"テストも修正"の2つです。

この2つのコミットがfeatureブランチにコミットされる間に、masterブランチには別の人によって"関係ないコミット"が追加されている状態です。

このような状況で、それぞれ3つのマージオプションはどのように動作するでしょうか。

従来のマージ(--no-ffマージ)

先程の画像で一番上に表示されていた"Create a merge commit"というオプションがこちらです。 従来、GitHubではこのオプションだけを長年サポートしてきました。

これは、マージ時にマージコミットと呼ばれるコミットを作り、2つのブランチのコミットを結びつける形で履歴を作成します。Gitコマンドとしてはデフォルトの挙動そのままですが、あえてオプションを明示的に付ける場合、 --no-ff というオプションを使ってマージする形式になります。結果として、次の図のような形になります。

"Merge pull request..."というメッセージがついているのがマージコミットです。このコミットを介して2つの異なるブランチの変更が1つの歴史としてマージされます。コミットは時系列順に並び、featureブランチにあったコミットもSHA値を維持したままmasterにマージされ、元の歴史が残ります。

最も単純で理解しやすいマージであり、featureブランチ上で行ったコミットもすべて維持されるので後から振り返りやすいというメリットがあります。

デメリットをあえてあげるなら、featureブランチにあまりに多くの細かなコミットがあった場合、masterブランチのノイズになる可能性があることでしょうか。

ですが、大抵の場合には過去の履歴がすべて参照可能な形で残っていたほうが何かと便利ですし、またPull Requestをベースに作業していれば、前回お話したとおりRevertも容易ですので、あまりmasterの履歴を気にすることもないのが実情です。ほとんどの場合にはこちらをおすすめしています。

Squashマージ

Squashマージはそれに対して、featureブランチのコミットを1つのコミットにまとめなおして、masterブランチに新しいコミットとして作成することでマージする方法です。Gitコマンドとしては --squash オプションを利用したのと同じ動きになります。次の図のようなイメージです。

こちらはマージコミットを作らずに、新しいコミットをmasterに作るイメージになります。"日本語対応"というコミットがマージの際にできるコミットであり、このコミットはfeatureブランチの"日本語版を追加"と"テストも修正"という2つのコミットを合わせたものと内容的には全く同じですが、SHA値は異なっており、全く別のコミットとして作られます。

このオプションは特に大きなOSSコミュニティなどで求められることが多いようです。大量のPull Requestが頻繁にマージされるとmasterブランチのノイズが多くなってしまうため、それを少しでも減らしてmasterをきれいにしようという志向を持つコミュニティに向いています。

また、Squashマージオプションを使うユースケースとして次のようなものも考えられます。 featureブランチ上ではできるだけ細く、何か作業するたびに保存の意味合いでコミットを作っておき、masterブランチにマージする際にSquashしてきれいに1つにまとめ直す、というケースです。こうしておけば、作業中の保存はこまめにできるので万が一の際も安心であり、かつmasterに入れる時にあとできれいに整理できるという安心感もあります。

メリットとしてはmasterブランチをシンプルに保てる点が挙げられます。 デメリットとしては、featureブランチで行っていた作業の履歴がmasterには残らないため、後から振り返る時にわかりにくいケースがあるかもしれない点でしょうか。また、master上のコミット1つ1つが肥大化しがちです。

Rebaseマージ

こちらのマージは、Gitコマンドとしてはfeatureブランチ上で git rebase master を行った後、masterブランチ上で --ff オプションを使ってマージしたのと近い動きになります。featureブランチのコミットが、masterのHEAD(一番最新のコミット)の次に発生したかのように、コミットのベースを付け替えます。次の図のようになります。

いわゆる git rebase した場合と違う点もあります。GitHubのこのRebaseマージを行った場合、featureブランチのコミットはすべてコミッター情報とSHA値が変わります。

また、このオプションは、結果としてファイル競合が起きることがない場合にのみ成功します。ですので、長期間メンテしているブランチからのマージの場合には、上手くいかないケースが多いことが考えられます。

メリットとしては、featureブランチ上の歴史を維持したままマージコミットを作らず、きれいでストレートな歴史をmasterブランチに残すことができる点です。デメリットとしては、リベースをするため、どうしても競合が起こる場合があり、競合が起きた場合にはコマンドラインで直す必要が出てくる点です。またリベース自体できない場合も出てきますので、その場合もローカルでコマンドラインを使ってリベースし直す必要が出てきます。

リベースは初心者の人には難しいオプションですので、このマージオプションを選択される場合にはチーム内の習熟度のバランスも考える必要が出てきます。

設定方法

なお、これら3つのオプションはデフォルトではどれも選択できるようになっていますが、設定にて一部をオフにすることも可能です。

チーム内で利用すべきでないと判断されたオプションはオフにしておくことをおすすめします。

まとめ

以上、今回はマージオプションについて見てみました。

GitHubはわかりやすいWebUIを付与することで、masterブランチにノイズがあっても、Pull Requestをベースに履歴を追ったり、Blameをビジュアルに使いやすくしています。そのためデフォルトでは --no-ff オプションが優先的に選ばれるようになっていますが、チームによってSquashやRebaseが望ましい場合には、その他のオプションも選べるようにしています。

ぜひチーム内で話し合って、最適なオプションを見出してみましょう!

ソフトウェア開発者。大学卒業後、ITコンサルタントとしてキャリアをスタート。その後コンサルタントからプログラマーに転身し、パッケージソフトウェア開発、Webサービス開発を経て、2015年現在GitHubにてソリューションエンジニアとして働いている。著書に『チーム開発実践入門』(2014年 技術評論社)がある。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています