はじめに
皆さん、Git使ってますか?
自分は新人エンジニアとして現在の会社に入社してから初めてGitに触りました。
実際に使ってみると、これまでの変更履歴が一目で分かったり、以前のバージョンにすぐに戻せたりと開発をする上で手放せないツールだなと思います。以前エンジニアの知り合いが「Gitを使わない開発は考えられない!」と言っていた理由がよく分かります。
話は逸れましたが、今回は merge・cherry-pick・rebase の使い分けについての記事になります。
別ブランチで行った変更をmasterブランチなどに適用する際に merge・cherry-pick・rebase を使うことがありますが、正直自分はどの場面でどれを使えば良いかは理解しきれていませんでした。
そこで本記事では、それぞれの操作に対する自分なりの解釈を備忘録がてらまとめていこうと思います。
※注意
- 今回の説明はTortoiseGitを使うことを前提としています
- 操作方法の説明ではなく、各操作の結果にどのような違いがあるかの説明になります
- この記事はcommit・push・branchなどの基本操作は分かるが、merge・cherry-pick・rebaseの使い分けにはまだ自信がない、という方向けに書いています
先に結論
先に自分なりの結論を書きます。
| 機能 | 何をするか | 向いている場面 | 注意点 |
|---|---|---|---|
| merge | 別ブランチの変更を統合する | feature ブランチを master に取り込みたい | 履歴に分岐と統合が残る |
| cherry-pick | 特定コミットだけを別ブランチへ取り込む | 必要な修正だけ別ブランチへ反映したい | 同じ内容の変更が別コミットとして存在している可能性がある |
| rebase | 変更を別のベースの上に積み直す | feature ブランチを最新 master に追従させたい | 履歴を書き換えるため共有済みブランチでは注意が必要 |
短く言うと、次のような使い分けです。
- ブランチの変更をそのまま統合したいなら merge
- 必要なコミットだけ別ブランチへ取り込みたいなら cherry-pick
- 最新の土台に載せ替えて履歴を整理したいなら rebase
この3つは似た場面で登場しますが、履歴に対する意味がかなり違います。TortoiseGit では GUI で簡単に実行できる分、結果だけでなく「何をした操作なのか」を正しく理解しておくと実際に操作するときに安心だと思います。
実際に操作しながら違いを見てみよう!
文章での説明だと差がイメージしにくいと思うので、今回は小さなコンソールアプリとTortoiseGitを使いながら各操作の違いを説明していきます。
まずは操作前の準備です。
master ブランチ
初めに、masterブランチに以下のコンソールアプリの実装をコミットします。
起動時に Hello, World! を表示し、簡単なコマンドを受け付けるだけの簡易的なアプリです。
using System;
Console.WriteLine("Hello, World!");
while (true)
{
Console.Write("command> ");
var input = Console.ReadLine()?.Trim()?.ToLowerInvariant();
switch (input)
{
case "help":
Console.WriteLine("Available commands: help, exit");
break;
case "exit":
Console.WriteLine("Bye!");
return;
case "":
case null:
break;
default:
Console.WriteLine("Unknown command. Type 'help' to see available commands.");
break;
}
}
コンソールアプリを起動し、コマンドを実行すると以下のようになります。

次に、branch-a、branch-bを作成し、それぞれに追加の実装をコミットします。
branch-a
branch-a では機能追加を行います。
branch-b
branch-b では既存表示を変更します。
この構成にすると、branch-a は機能追加、branch-b は既存変更になります。
そのため、merge・cherry-pick・rebase の違いを比較しやすくなります。
ブランチ構成は以下のイメージです。
master
└─ 初期状態: Hello, World! を表示するコンソールアプリ
branch-a
├─ commit A1: greet コマンドを追加
└─ commit A2: help 表示に greet を追加
branch-b
└─ commit B1: 起動時メッセージを Hello, Git! に変更
merge
merge は、あるブランチの変更を別のブランチへ統合する機能です。機能開発用のブランチを master に取り込むような場面で使います。
操作するときは、「今チェックアウトしているブランチに、別ブランチの変更を取り込む」という向きを意識しておくと理解しやすいです。
今回は、branch-a から切った scenario-merge ブランチに、branch-b の変更を統合します。
merge前ログ
scenario-mergeにはmasterとbranch-aのコミットのみが表示されています。

操作ダイアログ
TortoiseGitを操作してscenario-mergeにbranch-bのコミットをmergeします。

merge後ログ
merge操作が完了すると、ログにbranch-bのコミットが追加されます。

merge後のアプリ画面
この結果から branch-a の機能追加と branch-b のメッセージ変更の両方が統合されていることが分かります。
ログの履歴上には分岐していたブランチを統合したという事実が残ります。後から見たときに、「どのブランチを取り込んだのか」が分かりやすいのは merge の利点だと思います。
図で表すと、merge は次のようなイメージです。
master: A---B---------M
\ /
feature: C---D-----
ここで重要なのは、merge は「ブランチ単位で統合したい」ときに自然な選択だという点です。
cherry-pick
cherry-pick は、別ブランチの特定コミットだけを現在のブランチへ取り込む機能です。ブランチ全体ではなく、この修正だけ欲しい、というときに向いています。
今回は master から切った scenario-cherry-pick ブランチに branch-a の commit A1 だけを取り込むことを考えます。
cherry-pick 前ログ
masterブランチから切った直後のため、mainプログラムのコミットだけが表示されています。

操作ダイアログ
branch-a の commit A1 を選択して cherry-pick を実行します。


cherry-pick 後ログ
cherry-pick操作が完了すると、branch-a の commit A1 のみが追加されます。

cherry-pick後のアプリ画面
この結果から、「commit A1: greet コマンドを追加」のみがscenario-cherry-pickに追加されたことが分かります。
つまり、branch-a 全体が入ったのではなく、commit A1 という特定のコミットだけが取り込まれた形です。
この例だと、「欲しいのは branch-a 全体ではなく、greet コマンド追加だけ」という判断になります。そういう場面では merge より cherry-pick の方が意図に合っています。
ただし、cherry-pick で取り込まれたコミットは、元の commit A1 と内容は同じでも、履歴上は別のコミットとして追加されます。そのため、履歴を追うときは merge よりも文脈が分かりにくくなることがあります。
図にすると、cherry-pick は次のイメージです。
master: A---B---D'
feature: C---D
ここでの D' は、元の D と内容は近くても、履歴上は別コミットとして追加されたものです。
そのため cherry-pick は便利ですが、何でもこれで済ませると履歴の見通しが悪くなる可能性があります。
rebase
rebase は、あるブランチの変更を別のベースの上に積み直す操作です。「統合する」のが merge、「土台を付け替える」のが rebase と考えると整理しやすいと思います。
今回は、branch-a から切った scenario-rebase ブランチを使います。このブランチは master に branch-b の変更を適用する前の時点で切ったブランチです。つまり、branch-a の変更は持っていますが、branch-b による起動メッセージの変更はまだ含んでいません。
この「ブランチを切った後に master が先へ進んでしまった」状況で、scenario-rebase の土台を最新の master に付け替えるのが今回の操作です。
rebase 前ログ
scenario-rebase は master に branch-b の変更を適用する前にブランチを切っているので、main → branch-a の順でコミットされています。

master の更新
操作ダイアログ
Upstream(上流)に master ブランチを選択して rebase を実行します。

rebase 後ログ
rebase前はmain → branch-aの順でコミットが並んでいましたが、rebase後はmain → branch-b → branch-aの順でコミットが並んでいます。
これはscenario-rebaseの土台が「mainのコミット」から「mainのコミット + branch-bのコミット」に変更されたからです。

rebase後のアプリ画面
実行結果だけを見ると merge 後とかなり似ていますが、履歴の意味は異なります。
merge では「分岐していたブランチを統合した」という履歴が残るのに対し、rebase では branch-a 側のコミットが、最新の master の後ろに積み直された形になります。そのため、履歴は直線的に見えやすくなります。
図にすると、rebase は次のようなイメージです。
master: A---B---C
\
feature: D---E (rebase前)
master: A---B---C
\
feature: D'--E' (rebase後)
rebase は履歴を整理しやすく便利ですが、履歴を書き換える操作であるため、mergeやcherry-pickよりも注意が必要な操作になります。特に、すでに他の人と共有しているブランチに対して使うと、周囲の履歴理解とずれが生じて混乱の原因になることがあります。「使ってはいけない操作」ではないですが、何が起きるかを理解した上で使うことが重要です。
どう使い分けるか
自分としては、迷ったときは次の順番で考えると判断しやすくなりました。
- 欲しいのはブランチ全体か、特定コミットか
- 履歴をそのまま残したいか、整理したいか
- そのブランチはすでに共有されているか
たとえば、場面ごとに書くと次のようになります。
- feature ブランチを master に取り込みたい: merge
- 別ブランチで直したバグ修正だけ本番保守ブランチへ入れたい: cherry-pick
- レビュー前に feature ブランチを最新 master に追従させたい: rebase
この切り分けをしておくと、各操作をするときに迷いにくくなると思います。
まとめ
merge・cherry-pick・rebase はどれも履歴に対して異なる意味を持っています。
自分としては、次のように整理すると分かりやすかったです。
- ブランチ全体を取り込みたいなら merge
- 必要な修正だけなら cherry-pick
- 最新の土台に載せ替えて整理したいなら rebase
今回は conflict 解消までは扱いませんでしたが、まずは「何を取り込みたいのか」「履歴に何を残したいのか」を整理するだけでも、3機能の選び方はかなり分かりやすくなると思います。
参考資料
- TortoiseGit Manual: Merging
https://tortoisegit.org/docs/tortoisegit/tgit-dug-merge.html - TortoiseGit Manual: Cherry picking
https://tortoisegit.org/docs/tortoisegit/tgit-dug-cherrypick.html - TortoiseGit Manual: Rebase
https://tortoisegit.org/docs/tortoisegit/tgit-dug-rebase.html - Pro Git 日本語版
https://git-scm.com/book/ja/v2





