Git cherry-pickのコンフリクト解決手順と実践ベストプラクティス
Git cherry-pickで発生するコンフリクトの原因から解決手順、rerereやアトミックコミットを活用したベストプラクティスまで、実例コード付きで解説します。
Git cherry-pickのコンフリクト解決は、正しい手順を押さえれば怖くありません。
cherry-pickは内部で3-way mergeを使います。
そのため通常のマージと同様のコンフリクトが発生します。
解決の流れはgit status→手動編集→git add→
git cherry-pick --continueの4ステップです。
たとえば、hotfixブランチから本番ブランチへ
特定のバグ修正だけを取り込む場面を考えましょう。
コンフリクトが起きてもCHERRY_PICK_HEADと
rerereによる自動解決を活用すれば短縮できます。
この記事では環境準備からコンフリクト解決、
マージコミットの扱い、チーム運用ルールまで網羅しています。
前提条件:cherry-pick実行前に確認すべき環境と準備
cherry-pickのコンフリクトで手間取る原因の多くは、実行前の準備不足です。 ここでは、作業を始める前に確認すべき2つのポイントを整理します。
必要なGitバージョンとworking treeのクリーン状態チェック
cherry-pickを安全に実行するには、Gitのバージョンと 作業ツリーの状態を事前に確認してください。
- Gitバージョンを確認する
rerere(後述)や--rerere-autoupdateを使うには
Git 2.x以上が必要です。以下のコマンドで確認します。
# macOS / Linux
git --version
# Windows PowerShell
git --version
出力例は次のとおりです。
git version 2.43.0
2.30以降であれば、本記事のコマンドはすべて動作します。
- working tree(作業ツリー)をクリーンにする
Git公式ドキュメントによると、 cherry-pick実行前にworking treeがクリーンである必要があります。 未コミットの変更が残っていると、コンフリクト解決時に どの差分がcherry-pick由来か判別できなくなるためです。
# macOS / Linux
git status
git stash # 未コミットの変更を一時退避
# Windows PowerShell
git status
git stash # 未コミットの変更を一時退避
git statusの出力に
nothing to commit, working tree clean
と表示されれば準備完了です。
リモートブランチのフェッチ(bad objectエラーを未然に防ぐ)
cherry-pickで最も多いエラーの一つがbad objectです。
Graphite Guidesによると、
原因は主に3パターンあります。
- コミットハッシュの誤記(タイポ)
- リモートのコミットが未フェッチ
- リポジトリの破損
中でも最頻出はリモートブランチの未フェッチです。
他のメンバーが作成したコミットを取り込む場合、
ローカルにそのコミット情報がなければbad objectになります。
cherry-pick実行前に、必ずフェッチを行ってください。
# macOS / Linux — 全リモートブランチの最新情報を取得
git fetch --all
# Windows PowerShell — 全リモートブランチの最新情報を取得
git fetch --all
特定のブランチだけ取得したい場合は以下のように指定します。
# macOS / Linux
git fetch origin feature/target-branch
# Windows PowerShell
git fetch origin feature/target-branch
フェッチ後、対象コミットが存在するか確認します。
# macOS / Linux — コミットの存在確認
git cat-file -t <コミットハッシュ>
# Windows PowerShell — コミットの存在確認
git cat-file -t <コミットハッシュ>
commitと表示されれば、そのハッシュは有効です。
fatal: Not a valid object nameと出た場合は、
ハッシュの誤記かリポジトリの破損を疑ってください。
cherry-pickでコンフリクトが起きる仕組みと典型パターン
git cherry-pickは、指定したコミットの変更を 現在のブランチに適用するコマンドです。 内部では単純なパッチ適用ではなく、3-way merge(3方向マージ)を使っています。 この仕組みを理解しておくと、コンフリクト発生時に 「なぜ競合したのか」を素早く判断できます。
3-way mergeによるパッチ適用の内部動作
cherry-pickは、以下の3つのバージョンを比較してマージを試みます。
| バージョン | 内容 |
|---|---|
| Base(共通祖先) | cherry-pick対象コミットの親コミット |
| Ours(現在側) | 現在のHEADが指すファイル状態 |
| Theirs(取込側) | cherry-pick対象コミットのファイル状態 |
Gitはまず、BaseとTheirsの差分を「パッチ」として抽出します。 次に、そのパッチをOurs(現在のブランチ)に適用します。 BaseとOursの間で同じ行が変更されていなければ、自動マージが成功します。 両方が同じ行を変更していた場合、Gitは自動判断できずコンフリクトを報告します (How git cherry-pick and revert use 3-way merge)。
コンフリクト発生時、GitはCHERRY_PICK_HEADというrefを作成します。
このrefが対象コミットを参照しており、インデックスには
最大3つのバージョン(Base/Ours/Theirs)が記録されます
(Git公式ドキュメント)。
コンフリクトが発生する3つの典型シナリオ
cherry-pickでコンフリクトが起きるパターンは、大きく3つに分類できます。
1. 同一行の並行編集
最も多いケースです。 ブランチAとブランチBで同じファイルの同じ行を変更していると、 cherry-pick時に競合します。 hotfix対応で同じ設定値を別々の値に変更した場合などが典型例です。
2. ファイル構造の変更との衝突
cherry-pick対象のコミットが編集したファイルが、 現在のブランチではリネーム・削除されているケースです。 Gitはファイルの対応関係を解決できず、コンフリクトを報告します。
3. 依存関係のあるコミットの部分取り込み
コミットAの変更を前提にコミットBが作られている場合、 コミットBだけをcherry-pickすると競合します。 たとえば、コミットAで追加した関数をコミットBが修正するケースです。 この場合はコミットAから順にcherry-pickするか、 範囲指定で両方を取り込む必要があります。
# macOS / Linux:依存関係のあるコミットを範囲指定で取り込む
git cherry-pick A^..B
# Windows PowerShell:同様の操作
git cherry-pick A^..B
ここでA^と指定するのがポイントです。
範囲指定の開始コミットは「取り込みたいコミットの1つ前」を指す必要があります
(Qiita - 複数のcommitをまとめてcherry-pick)。
コンフリクト解決の基本手順【4ステップで完了】
cherry-pick中にコンフリクトが起きても、手順は4つだけです。 Git公式ドキュメントに沿って、 落ち着いて進めれば問題ありません。
Step 1-2:git statusでの特定とコンフリクトマーカーの読み方
まずgit statusで競合ファイルを確認します。
# macOS / Linux
git status
# Windows PowerShell
git status
出力にboth modified:と表示されたファイルが対象です。
該当ファイルを開くと、以下のようなマーカーが挿入されています。
<<<<<<< HEAD
現在のブランチの内容
=======
cherry-pick元のコミットの内容
>>>>>>> (cherry-pickしたコミットハッシュ)
<<<<<<< HEADから=======までが今のブランチの状態です。
=======から>>>>>>>までがcherry-pick元の変更にあたります。
cherry-pickは内部で3-way mergeを使っています。
同じ行を両方が変更した場合にこのマーカーが出ます
(参考:Julia Evans - How git cherry-pick and revert use 3-way merge)。
マーカーを含む行をすべて削除し、最終的に残したいコードだけにしてください。
Step 3-4:git addとcherry-pick —continueで再開
編集が終わったら、ステージングして操作を再開します。
# macOS / Linux
git add path/to/conflicted-file.ts
git cherry-pick --continue
# Windows PowerShell
git add path/to/conflicted-file.ts
git cherry-pick --continue
git addは「このファイルの競合を解決しました」とGitに伝える操作です。
--continueを実行するとコミットメッセージの編集画面が開きます。
保存すればcherry-pickが完了します。
解決に自信がない場合は、以下で操作前の状態に戻せます。
# macOS / Linux — cherry-pick全体を取り消す
git cherry-pick --abort
# Windows PowerShell — cherry-pick全体を取り消す
git cherry-pick --abort
複数コミットの連続cherry-pickで中断した場合の再開フロー
範囲指定でcherry-pickする場合、構文に注意が必要です。 開始コミットは取り込みたいコミットの1つ前を指定します (参考:Qiita - 複数のcommitをまとめてcherry-pick)。
# macOS / Linux — abc1234の次からdef5678までを適用
git cherry-pick abc1234..def5678
# Windows PowerShell — abc1234の次からdef5678までを適用
git cherry-pick abc1234..def5678
途中でコンフリクトが発生すると、処理はそのコミットで一時停止します。
Gitは.git/sequencerディレクトリに残りのコミット情報を保持しています。
再開の流れは単一コミット時と同じです。
git statusで競合ファイルを確認する- マーカーを手動編集して解決する
git addでステージングするgit cherry-pick --continueで次のコミットへ進む
コンフリクトが連続する場合は、コミットごとにこの手順を繰り返します。
特定のコミットだけスキップしたいときは
git cherry-pick --skipを使ってください。
すべて中断したい場合は--abortで開始前の状態に戻ります。
マージコミットのcherry-pickと-mオプションの使い分け
通常のコミットと違い、マージコミットには「親」が2つ存在します。
そのためcherry-pick時には-mオプションで基準となる親を指定する必要があります。
ここを理解しておくと、hotfix適用時の混乱を防げます。
-m 1と-m 2の親選択ロジックを図解で理解する
マージコミットの親は、git logで確認できます。
# macOS / Linux
git log --oneline --graph --merges -5
# Windows PowerShell
git log --oneline --graph --merges -5
出力例を見てみましょう。
Merge: a1b2c3d e4f5g6h
この場合、a1b2c3dが第1親、e4f5g6hが第2親です。
親の関係をテーブルで整理します。
| オプション | 基準となる親 | 典型的な意味 |
|---|---|---|
-m 1 | 第1親(a1b2c3d) | マージ先ブランチ(main等) |
-m 2 | 第2親(e4f5g6h) | マージ元ブランチ(feature等) |
-m 1を指定すると、第1親との差分が変更内容として適用されます。
つまり「feature側で加えた変更だけを取り込む」動きです。
実務では-m 1を使うケースがほとんどでしょう。
一方-m 2は、main側の変更を基準にします。
featureブランチへmainの変更を取り込む場面で使いますが、頻度は低いです。
Git公式ドキュメントによると、 マージコミットのcherry-pickでは 指定しなかった親側の個別コミット履歴が統合されます。 履歴が失われるリスクを認識したうえで実行してください。
「no -m option was given」エラーの原因と解決策
マージコミットに対して-mなしでcherry-pickすると、
次のエラーが出ます。
error: commit <hash> is a merge but no -m option was given
解決は、-mオプションを付けて再実行するだけです。
# macOS / Linux — 第1親を基準にcherry-pick
git cherry-pick -m 1 <commit-hash>
# Windows PowerShell — 第1親を基準にcherry-pick
git cherry-pick -m 1 <commit-hash>
チーム開発では-xオプションの併用をおすすめします。
コミットメッセージに元コミットのハッシュが自動追記され、追跡が容易になります
(Git公式ドキュメント)。
# macOS / Linux — 追跡情報付きでcherry-pick
git cherry-pick -x -m 1 <commit-hash>
# Windows PowerShell — 追跡情報付きでcherry-pick
git cherry-pick -x -m 1 <commit-hash>
そもそもマージコミットのcherry-pickが頻発する場合は、 ブランチ戦略の見直しを検討してみてください。 個別のコミットを直接cherry-pickするほうが、履歴もシンプルに保てます。
よくあるエラーと具体的トラブルシューティング
cherry-pick中に遭遇するエラーは、原因さえ把握すれば対処は難しくありません。 ここでは現場で頻出する3つのパターンを取り上げます。
「bad object」エラー:原因3パターンと対処法
bad object <コミットハッシュ> と表示される場合、原因は主に3つです。
- コミットハッシュの誤記 — タイプミスやコピー漏れを確認してください
- リモートのコミットが未フェッチ — 最も多い原因です
- リポジトリの破損 — まれですが
git fsckで検証できます
Graphite Guidesによると、 最多の原因はリモート未フェッチです。以下のコマンドで解決します。
# macOS / Linux
git fetch origin
git cherry-pick <コミットハッシュ>
# Windows PowerShell
git fetch origin
git cherry-pick <コミットハッシュ>
特定ブランチだけ取得したい場合は
git fetch origin <ブランチ名> を使ってください。
「CHERRY_PICK_HEAD exists」エラーと空コミット問題の解決
このエラーは、前回のcherry-pickが中途半端な状態で残っているときに発生します。
Git公式ドキュメントによると、
CHERRY_PICK_HEADは未完了のcherry-pickを指すrefです。
対処法は2つあります。
# macOS / Linux — 前回の操作を取り消して最初からやり直す
git cherry-pick --abort
# または、状態だけクリアして既存の変更は残す
git cherry-pick --quit
# Windows PowerShell
git cherry-pick --abort
# または
git cherry-pick --quit
--abortはpre-cherry-pick状態に完全に戻します。
--quitは適用済みの変更を残したまま、
sequencer(連続操作の管理機構)の状態だけをリセットします。
また、cherry-pick対象の変更がすでに取り込み済みだと「空コミット」になります。
この場合は git cherry-pick --skip でスキップできます。
記録として残したいなら git commit --allow-empty を使ってください。
Git Hooks失敗時の挙動とGUIツール使用時の注意点
huskyなどのGit Hooksがcherry-pick時に失敗すると、 CLIでは明確なエラーが表示されます。 しかし、 GitHub Desktop Issue #16369で 報告されているとおり、一部のGUIツールはエラーを表示せず、 以前のブランチに戻ってしまうことがあります。
この問題を回避するポイントは以下の2つです。
- cherry-pickはCLIで実行する — エラーメッセージを確実に確認できます
- GUIツール使用時はHooksを一時無効化する —
--no-verifyオプションの検討も一つの手段です
なお、Gitのコマンド自体はWindows・macOS・Linuxで同一に動作します。 OS間の差異はGUIツールの実装に限定されます。 トラブル時はCLIでの再現確認をおすすめします。
コンフリクトを減らすベストプラクティスと運用ルール
cherry-pick時のコンフリクトは、事前の設計と運用ルールで大幅に減らせます。 ここではコミット設計・便利オプション・使い分けの3つの観点から、 チーム開発で実践できる方法を紹介します。
アトミックコミット設計とrerere活用で解決コストを削減する
コンフリクトの根本原因は、1つのコミットが複数の変更を抱えすぎていることです。 「1コミット=1つの論理的変更」に分割する アトミックコミット設計を徹底してください。 MoldStudの報告では、この手法により コンフリクト解決が約43%削減されたとされています (ただし測定条件は非公開)。
さらに、rerere(reuse recorded resolution)を有効にすると、 過去に解決したコンフリクトのパターンを記録・再利用できます。 設定は以下のとおりです。
# macOS / Linux
git config --global rerere.enabled true
# Windows PowerShell
git config --global rerere.enabled true
rerereを有効にすると、類似のコンフリクト解決時間を最大60%削減できるという
報告もあります。
ただし、誤った解決パターンも記録されるリスクがあります。
初回は --no-rerere-autoupdate で手動確認するのがおすすめです。
# macOS / Linux — 自動適用せず手動確認する場合
git cherry-pick --no-rerere-autoupdate <commit-hash>
# Windows PowerShell
git cherry-pick --no-rerere-autoupdate <commit-hash>
-xオプションによる履歴追跡とチーム共有のルール化
チーム開発では「どのコミットをどこにcherry-pickしたか」の追跡が欠かせません。
-x オプションを付けると、
コミットメッセージに元コミットのハッシュが自動追記されます
(Git公式ドキュメント)。
# macOS / Linux
git cherry-pick -x <commit-hash>
# Windows PowerShell
git cherry-pick -x <commit-hash>
実行すると、コミットメッセージ末尾に以下が追記されます。
(cherry picked from commit 3a4b5c6d7e8f...)
筆者の経験では、この1行があるだけで
hotfix適用漏れの調査時間が大きく短縮されます。
チームのコミット規約に「cherry-pickには必ず -x を付ける」
と明記しておくのが効果的です。
cherry-pickを避けるべきケース(merge・rebaseとの比較)
cherry-pickは万能ではありません。 以下のケースではmergeやrebaseを優先してください。
| 状況 | 推奨操作 | 理由 |
|---|---|---|
| 機能ブランチ全体の取り込み | git merge | 履歴が保存され、コンフリクトも1回で済みます |
| 連続する多数のコミット取り込み | git rebase | コミット順序を維持したまま適用できます |
| マージコミットの取り込み | git merge推奨 | cherry-pickでは -m で親指定が必要で、個別コミット履歴が失われます |
| 単発のbugfix/hotfix適用 | git cherry-pick | 必要な変更だけを正確に取り込めます |
LabExのチュートリアルでも指摘されているように、 複雑なコンフリクトが頻発する場合はmergeを検討すべきです。 cherry-pickの多用はブランチ運用自体に課題がある兆候でもあります。 チームのブランチ戦略を見直すきっかけにしてください。
まとめ:cherry-pickを「最後の手段」として正しく使いこなす
cherry-pickのコンフリクト解決は、4ステップの基本手順
(git status→手動編集→git add→--continue)を
身につければ確実に対処できます。
ただし、cherry-pickを頻繁に使う状況が続いているなら、
ブランチ戦略そのものを見直すタイミングかもしれません。
cherry-pickの多用はGit運用ルールに課題がある兆候です。
記事の内容を踏まえて、明日から実践できるアクションを3つ提案します。
rerereを今日有効にする —git config --global rerere.enabled trueを実行するだけで、 同じコンフリクトの再解決から解放されます。 ただし--no-rerere-autoupdateで手動確認を挟む運用から始めると安全です-xオプションをチームの標準にする — cherry-pick時に元コミットの追跡情報を残すことで、 「このコミットはどこから来たのか」という問い合わせが激減します- アトミックコミットを習慣化する — 1コミット1責務を徹底すると、cherry-pick対象の選定が容易になります。 コンフリクト発生率そのものが下がります
cherry-pickよりも安全な選択肢がないか、常に検討してください。
単一ファイルの特定リビジョン取得なら
git checkout <commit> -- <file>が使えます。
ブランチ全体の統合ならgit mergeやgit rebaseのほうが履歴を健全に保てます。
cherry-pickは「ピンポイントで必要なコミットだけを取り込む」
場面でこそ真価を発揮します。
Git公式ドキュメントと
Atlassian Git Tutorialを
ブックマークしておくと、オプションの詳細を確認したいときにすぐ参照できます。
参考文献
- https://git-scm.com/docs/git-cherry-pick
- https://jvns.ca/blog/2023/11/10/how-cherry-pick-and-revert-work/
- https://medium.com/@kajals909/git-advanced-rebase-cherry-pick-conflict-resolution-9d44faefb3f1
- https://moldstud.com/articles/p-best-practices-for-resolving-conflicts-while-cherry-picking-in-git
- https://medium.com/@hamida.meknassi/cherry-picking-from-a-merge-request-commit-git-cherry-pick-m-de5be4e1f286
- https://www.atlassian.com/git/tutorials/cherry-pick
- https://graphite.com/guides/resolving-the-git-error-git-cherry-pick-bad-object
- https://qiita.com/shosho/items/fb4e4eaddc944ce2c435
- https://github.com/desktop/desktop/issues/16369
- https://github.com/fork-dev/TrackerWin/issues/1471
- https://labex.io/tutorials/git-how-to-handle-conflicts-during-git-cherry-pick-417328