Git Cherry-pickをマスターする:正確にコミットを「摘み取る」方法とコンフリクト解消のコツ

Git tutorial - IT technology blog
Git tutorial - IT technology blog

ブランチ全体をマージする代わりに、特定のコミットを「摘み取る」必要があるのはどのような時でしょうか?

例えば、feature-xyz ブランチで一週間かけて新機能を開発しているとします。そんな中、突然お客様から本番環境(Production)で緊急の修正が必要な重大なバグの報告が入りました。幸いなことに、あなたは開発中の機能ブランチの中で、そのバグを修正するコミットを既に一つ作成していたことに気づきます。

この時、機能全体のコードはまだテストが終わっていないため、ブランチ全体を main にマージすることはできません。無理にマージしてデプロイすれば、システムがダウンしてしまうリスクがあります。また、手動でコードをコピー&ペーストするのも、ミスが発生しやすく危険です。ここで Git Cherry-pick が強力な武器となります。このコマンドを使えば、そのバグ修正コミットだけを正確に選び出し、main ブランチに綺麗に反映させることができるのです。

実務において、10〜20人規模の大型プロジェクトでは、Cherry-pickは「バックポート」(新しいバージョンで行った修正を古いバージョンにも適用すること)によく使われます。これにより、チーム全体のコミット履歴を乱すことなく、コードの流れを制御できるようになります。

Cherry-pick、Merge、Rebaseの比較

適切に使い分けるために、Cherry-pickと他の2つの主要な操作(MergeとRebaseの違い)を理解しておきましょう。

  • Git Merge: あるブランチの全履歴を別のブランチに統合します。これは、バケツの水を大きな樽に一気に流し込むようなもので、時には不要なものまで混ざってしまうことがあります。
  • Git Rebase: あるブランチのコミットを別のブランチの先端に繋ぎ変えることで、履歴を書き換えます。履歴は一直線で綺麗になりますが、既にサーバーにプッシュしたコードに対して行うと非常にリスクが高い操作です。
  • Git Cherry-pick: 特定の1つ、または数個のコミットだけを選択します。これは、がれきの山の中から気に入ったレンガを1つだけ拾ってきて、自分の家を建てるのに使うようなものです。

クイック比較表:

特徴 Merge Rebase Cherry-pick
範囲 ブランチ全体 ブランチ全体 単一のコミット
Git履歴 マージコミットを作成 直線的でクリーン 新しいコミットを作成(新しいハッシュ)
リスク 非常に高い

なぜCherry-pickを使うべき(あるいは避けるべき)なのか?

メリット:

  • 絶対的なコントロール: 必要なものだけを取り込み、不要なコードを完全に排除できます。
  • 本番環境の救急対応: 他の機能の完成を待たずに、ホットフィックス(Hotfix)を迅速にデプロイできます。
  • マルチバージョン管理: v1.0とv2.0を並行運用している場合、同じ修正を二度書くことなく両方に適用できます。

デメリット:

  • 重複の発生: Cherry-pickは新しいハッシュ値を作成します。多用すると、git log が内容は同じなのに識別子が異なるコミットで溢れてしまいます。
  • コンフリクトのしやすさ: 取り込もうとしているコミットが、まだ移行していない古いコードに依存している場合、確実にコンフリクトが発生します。

Git Cherry-pickを導入する3つのステップ

まず、取り込みたいコミットのハッシュ値を以下のコマンドで確認します。

git log --oneline

ここでは、対象のコミットハッシュを a1b2c3d とします。

1. 1つのコミットをCherry-pickする

ターゲットとなるブランチ(例:main ブランチ)に切り替えて、コマンドを実行します:

git checkout main
git cherry-pick a1b2c3d

2. 複数のコミットを一度に取り込む

離れた場所にある複数のコミットを取り込むには、以下の構文を使用します:

git cherry-pick hash1 hash2 hash3

コミットAからBまでの範囲(Aは含まない)を取り込みたい場合は A..B を使います。Aも含める場合は A^..B を使います。

3. オリジナルの追跡を残すコツ

実戦での重要なアドバイスは、常に -x オプションを付けることです。これにより、このコミットがどこから「ピックアップ」されたのかがコミットメッセージに自動的に記録されます。

git cherry-pick -x a1b2c3d

これは、後でバグを追跡したりシステムをデバッグしたりする際に非常に役立ちます。

コンフリクトへの対処:パニックにならないで!

コンフリクトは、取り込もうとしているコード行がターゲットブランチですでに変更されている場合に発生します。エラーメッセージが表示されても、落ち着いて以下の手順で対応しましょう:

  1. エラーが発生しているファイルを開き、<<<<<<<======= といった記号を探して手動で修正します。
  2. 正しいコードを選択してファイルを保存します。
  3. git add <ファイル名> を実行して、修正が完了したことをマークします。
  4. git cherry-pick --continue コマンドで処理を続行します。

もし複雑すぎて元の状態に戻したい場合は、git cherry-pick --abort と入力するだけで中断できます。

実戦エピソード:Cherry-pickがプロジェクトを救った話

以前、非常に厳格なGit Flowを採用しているアウトソーシングプロジェクトに携わっていた時のことです。ある同僚が、翌朝に顧客へのデモを控えた develop ブランチに、誤って開発中の新機能のコードを直接コミットしてしまいました。その機能のコードはまだ書きかけで、アプリをクラッシュさせる可能性がありました。

影響を受けた15以上のファイルを手動で修正する代わりに、私は5分で解決しました:

  1. Cherry-pickを使って、その誤ったコミットを正しい feature ブランチにコピーしました。
  2. develop ブランチに戻り、git revert を使って変更を打ち消しました。

その結果、デモは大成功に終わりました。新機能のコードも失われることなく、個別のブランチで開発を継続できました。もしCherry-pickを使っていなければ、チーム全員で徹夜して混乱を片付けることになっていたでしょう。

最後のアドバイス:

  • Cherry-pickは本当に必要な時だけ使いましょう。Mergeの代わりとして常用してはいけません。
  • 一度に10個以上のコミットをCherry-pickしなければならない場合は、チームのワークフローを見直してください。おそらく方向性が間違っています。
  • コミットを取り込んだ後は、すべてが正常に動作することを確認するために、必ずすぐにユニットテストを実行してください。

Share: