← ブログに戻る

Git cherry-pick 完全ガイド:使うべき場面と避けるべき場面

tutorial git

Git Cherry-Pick とは?

重要なバグ修正が開発用 branch に入ったばかり -- しかし同じバグは本番環境でも発生しています。開発用 branch 全体をリリース branch に merge することはできません。作りかけの機能だらけだからです。本当に必要なのは、その 1 つの commit だけを取り出して別の場所に適用すること。それこそが git cherry-pick の役割です。

cherry-pick は、既存の commit が導入した変更を取り出し、現在の branch 上にまったく新しい commit として再現します。新しい commit は同じ diff と (デフォルトでは) 同じメッセージを持ちますが、親が異なるため SHA も異なります。この点は見た目以上に重要です。Git は commit を SHA で識別するので、cherry-pick の後、リポジトリには同じ変更を導入する 2 つの別個の commit が存在することになります。これを覚えておいてください -- cherry-pick がなぜこれほど便利なのか、そしてなぜ使いすぎると後で痛い目に遭うのか、その両方の理由がここにあります。

cherry-pick が正しい選択になる場面

hotfix をリリース branch にバックポートする

これが典型的なケースです。main でバグを修正したものの、ユーザーが使っているのはバージョン 2.3 で、修正は今すぐ必要です。修正をリリース branch に cherry-pick しましょう:

git switch release/2.3
git cherry-pick a1b2c3d

リリース branch にはその修正だけが入り、main の他の変更は一切ついてきません。

commit を間違った branch に積んでしまった

フィーチャー branch に commit するつもりが、main にいた -- そんなときも cherry-pick がきれいに解決してくれます。commit があるべき branch に切り替えてそこへ cherry-pick し、その後、本来あるべきでない branch から削除します。削除の手順に不安があれば、Git の commit を取り消す方法のガイドで各選択肢を詳しく解説しています。

破棄予定の branch から修正を 1 つだけ救出する

実験がうまくいかなかったものの、branch の途中の 1 つの commit が本物のバグを修正していた、ということがあります。行き止まりの branch を merge する代わりに、価値のあるその commit だけをアクティブな branch に cherry-pick し、残りの実験は安心して削除しましょう。

基本的な使い方

最もシンプルな形は、commit を 1 つ指定するだけです:

git cherry-pick a1b2c3d

Git はその commit の変更を適用し、元のメッセージを再利用して、現在の branch 上にすぐに新しい commit を作成します。複数の commit を一度に渡すこともでき、Git は指定した順序で 1 つずつ適用します:

git cherry-pick a1b2c3d f4e5d6c

知っておくべき実用的なオプション

-x: commit の出どころを記録する

バックポートでは、後からその commit がどこ由来なのか知りたくなるものです。-x オプションは、新しい commit のメッセージに (cherry picked from commit a1b2c3d...) のような行を追記します:

git cherry-pick -x a1b2c3d

長期間運用される公開 branch 間の cherry-pick では、これを習慣にしてください。半年後、この 1 行だけで、修正がどのリリースに入ったのか、どこから来たのかが即座にわかります。

commit の範囲を指定する

ドット 2 つの構文で範囲全体を cherry-pick できます:

git cherry-pick A..B

注意: これは A の次以降の commit を B まで (B を含む) 適用します。A 自体は含まれません。A を含めたい場合は次のように書きます:

git cherry-pick A^..B

^ は「A の親」を意味し、これで A が範囲に含まれるようになります。この 2 つの構文の取り違えは、cherry-pick で最もよくあるミスの 1 つです。

--no-commit: commit せずに適用する

変更だけが欲しくて、自動 commit はいらない場合もあります -- たとえば複数の commit を 1 つにまとめたいときや、commit 前に結果を調整したいときです:

git cherry-pick --no-commit a1b2c3d

変更は作業ディレクトリと staging エリアに入り、準備ができた時点で自分で commit を作成します。

cherry-pick 中のコンフリクトに対処する

cherry-pick は merge と同じ理由でコンフリクトを起こすことがあります。取り込もうとしている commit は、現在の branch とは異なる状態のコードに対して書かれたものだからです。コンフリクトが起きると、Git は操作の途中で停止し、コンフリクトしたファイルにマークを付けます:

error: could not apply a1b2c3d... fix login timeout
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue"

ここからの出口は 3 つあります:

  • git cherry-pick --continue -- コンフリクトしたファイルを編集して git add で staging に追加した後、これで cherry-pick を完了し、commit を作成します。
  • git cherry-pick --abort -- 操作全体をキャンセルし、開始前とまったく同じ状態に branch を戻します。何も失われず、何も適用されません。
  • git cherry-pick --skip -- 複数の commit を取り込んでいる途中で、現在の commit を飛ばして次へ進みます。範囲内のある commit がすでに適用済みだったり、不要だったりした場合に便利です。

コンフリクトの解消そのものは、merge のコンフリクト解消とまったく同じです。コンフリクトした各ファイルを開き、競合する hunk のどちらかを選ぶ (または組み合わせる) 、コンフリクトマーカーを削除し、ファイルを staging に追加します。コンフリクトマーカーがまだ怖いと感じるなら、Git の merge コンフリクトをビジュアルに解決する解説は cherry-pick にもそのまま当てはまります。

cherry-pick でやってはいけないこと

branch 全体を cherry-pick しない

1 つの branch から別の branch へ、5 個、10 個、20 個と commit を cherry-pick している自分に気づいたら、手を止めてください。それは merge と rebase の仕事です。どちらも branch 間の関係を Git に理解させたまま作業を移動させるので、その後の操作がシンプルでコンフリクトのないものになります。どちらが自分のワークフローに合うか迷ったら、rebase vs merge の比較記事でトレードオフを解説しています。

重複した履歴は後で牙をむく

思い出してください。cherry-pick は新しい SHA を持つ新しい commit を作り、Git は 2 つの commit が「同じ変更」であるという正式な記録を一切残しません。フィーチャー branch から main へ commit を cherry-pick し、後からそのフィーチャー branch をやはり merge することになった場合、Git は同じ変更の 2 つのコピーを突き合わせる必要があります。多くの場合は何事もなく成功します。しかし、どちらかのコピーが後から変更されていると、両側がほぼ同一に見える紛らわしいコンフリクトが発生します -- しかも履歴には同じ変更が 2 つの日付と 2 つの SHA で 2 回現れ、git loggit bisect といったツールでの追跡が難しくなります。

良い経験則: cherry-pick は例外を移動するためのものです -- hotfix、置き場所を間違えた commit、救出した修正。branch 間で作業を移動する日常的な手段になってしまったら、それは履歴が「branch 戦略を見直すべきだ」と教えてくれているサインです。

GitSquid での cherry-pick

GitSquid では、cherry-pick は期待どおりの場所にあります。commit グラフ上の任意の commit を右クリックし、コンテキストメニューから cherry-pick を選ぶだけ。ログとターミナルの間で SHA をコピーして回る必要はありません。

より大きな利点は、何が待ち受けているかを事前に知れることです。GitSquid の Conflict Predictor は -- 全ユーザー無料で -- merge、rebase、cherry-pick を実行する前に、どのファイルがコンフリクトするかを正確に表示します。hunk のプレビューとシンタックスハイライト付きです。操作の途中でコンフリクトに遭遇する代わりに、事前に把握した上で、続行するか、別の commit を選ぶか、落ち着いて解決の準備をするかを決められます。

そして実際にコンフリクトが起きたときは、「Resolve in scratch worktree」が一時的な worktree を新しいタブとして開くので、アクティブな checkout には一切手を触れずに、そこですべてを解決できます。

クイックリファレンス

コマンド 動作
git cherry-pick <sha> 1 つの commit を現在の branch に適用
git cherry-pick <sha1> <sha2> 複数の commit を順番に適用
git cherry-pick -x <sha> 元の SHA を commit メッセージに追記
git cherry-pick A..B A の次から B までの commit を適用 (A は除外)
git cherry-pick A^..B A を含む範囲を適用
git cherry-pick --no-commit <sha> commit せずに変更だけ適用
git cherry-pick --continue コンフリクト解消後に pick を完了
git cherry-pick --abort キャンセルして branch を元の状態に戻す
git cherry-pick --skip 現在の commit を飛ばして残りを続行

cherry-pick はメスであって、シャベルではありません。たまの精密な抽出 -- hotfix のバックポート、置き場所を間違えた commit、救出した修正 -- に使えば、Git で最も気持ちのいいコマンドの 1 つです。merge の代用品として使えば、履歴は静かに重複だらけになり、後であなたを苦しめます。この違いを理解し、バックポートでは -x を付け、非常口として --abort を頭の片隅に置いておきましょう。

始める前に、どのファイルがコンフリクトするかを見てみませんか? GitSquid を無料でダウンロードして、次の cherry-pick から当て推量を Conflict Predictor に追い出してもらいましょう。