GitHubのMerge、Squash and Merge、Rebase and Mergeを理解する

 

GitHubの新バージョンでは、merge、Squash and merge、Rebase and mergeの3種類のマージに対応するようになりました。マージ方式によってコミット履歴が異なるため、どのような場合にどのマージを使用するのがよいか、共有したいと思います。

グラフで表すと

各マージをグラフで表してみます。

  • merge(a、b、cをreferするmコミットノード作成、mはparentにInit、cを持つ)

  • Squash and merge(a、b、cを合わせて新しいコミットを作り、ターゲットブランチに追加。’ a,b,c ‘コミットはparentをInitだけ持つ)

  • Rebase and merge(a、b、cをシームレスにマージ対象ブランチに追加。各コミットはすべてparentを1つずつ持つ)

メインブランチの観点から

メインブランチのコミットでマージされたコミットがどのような形状を持つか比べてみました。

  • merge:コミットmから後ろへ戻りながら親をすべて検索し、ブランチを構成する。コミットmは親としてc、Initを持っており、cはb、bはa、aはInitを再び親として持つ。この形状をすべてバックトレースして、Init -> a -> b -> c -> mという構造を作成し、履歴として残す。
  • Squash and merge:コミット ‘a,b,c’ はInitだけ親に持つ単一コミット。作業したブランチのa、b、cコミットはマージ後のメインブランチコミットInit、’a,b,c’ とは何の関連も持たない。

  • Rebase and merge:コミットa、b、cの関係を維持したまま、メインブランチに追加する。コミットaは親としてコミットeを持つ。Rebase and merge作業後は、作業したブランチのa、b、cコミットは、マージ後、メインブランチのInit、d、e、a、b、cコミットと関連を持たない。

使用例

Git Flowに従うとき、以下のように整理できます。

  • develop – featureブランチ間マージ:Squash and mergeが便利。featureの複雑で面倒なコミット履歴をすべてまとめ、完全に新しいコミットでdevelopブランチに加え、developブランチで独自に管理できる。一般的に、マージ後にfeatureブランチを削除してしまうことを考えると、featureブランチのコミット履歴をすべてdevelopブランチに関連付けて残す必要がない。
  • master – developブランチ間マージ:Rebase and mergeが便利。developの内容をmasterに追加するとき、別途新しいコミットを作成する理由がないため。
  • hotfix – develop、hotfix – masterブランチ間マージ:mergeまたはSquash and mergeが便利。TOPに合わせて選択して使うとよいだろう。hotfixブランチ作業の各コミット履歴をすべて残す必要がある場合はmerge、不要な場合はSquash and mergeを使用すればよい。

おまけ

2つのブランチ間のRebaseは上記のような観点で使用できますが、cliのinteractive rebase(ex. git rebase -i HEAD~5)を使うと、単一ブランチ内でrebaseを使ってコミット履歴を整理できます。

  • rewordキーワードを使うと、コミットメッセージを変更できる
  • squashキーワードを使うと、さまざまなコミットを1つにまとめられる

たとえば、以下のようにcliで作業対象ブランチにgit rebase -i HEAD~5を入力して、reword、squashを適切に使用すると


グラフで表すと以下のようになります。


既存のコミット履歴は


以下のように変更されます。(reword、squashすべてコミットメッセージを任意に変更できます。)


つまり、rebaseを他のブランチ間のマージで使用する以外にも、単一ブランチの取りまとめに使用できます。

TOAST Meetup 編集部

TOASTの技術ナレッジやお得なイベント情報を発信していきます
pagetop