過去のコミットから、変更内容を抜き出す「git cherry-pick」。その機能自体はシンプルなコマンドですが、「複数のコミットを一度に取り込む」など、特定のユースケースに対応するには、仕様と使い方を知っておく必要あります。
目次
git cherry-pick の概要
git cherry-pick は、一つ以上の既存のコミットを指定して、そのコミットが持つ変更を含む新しいコミットを作成するためのコマンドです。新しいコミットは現在の作業ブランチの先頭に追加されます。
そもそも、英語の「cherry-pick (チェリーピック)」とは、「良いものだけを選ぶ/選り好みする/つまみ食いする」といった意味があります。Git においても、過去のコミットの中から、必要なものだけを選んで指定して取り込む事を意味しています。
git cherry-pick の使い方
git cherry-pick コマンドの使い方についてです。通常は単一のコミットを取り込みますが、いくつかの複雑なケースも存在します。
指定したコミットを取り込む
もっとも基本的な、単一のコミットの取り込みです。<commit>には、コミット識別子(SHA-1)を指定しても、ブランチ名などの参照でも構いません。
// 特定のコミットをピックする
git cherry-pick <commit>
範囲を指定し、複数のコミットを取り込む
もっともシンプルな cherry-pick は、コミットを一つだけ指定しますが、複数のコミットを指定するため、コミットの「範囲」を指定する事ができます。例えば、「COMMIT_A..COMMIT_B」と指定すれば、AからBまでのコミットを指定することができます。AのほうがBより古い点に注意指定下さい。また、ピックされるコミットは「COMMIT_A の次のコミット」から「COMMIT_B まで」となりますので、注意が必要です。
// 「37a1d2」の次のコミットから「a3jb9ad」までをピックする git cherry-pick 37a1d2..a3jb9ad
わかり易さのため、COMMIT_A 自体を含めるには、そのコミットの直前を表す「^」を利用して下記のように、記述すると、やや直感的に指定できます。
// 特定のブランチをピックする git cherry-pick 37a1d2^..a3jb9ad
コンフリクトの発生と解決
cherry-pick を行うと変更同士のコンフリクト(競合)が発生することがあります。
cherry-pick に指定した過去のコミットは、「過去のある『時点』でのソースコードに対して加えられた変更」を含んでいます。その「時点」のコード状態から、現在のブランチの先頭のコード状態までに、別の変更が加えられているのであれば、cherry-pick しようとしている変更との間でコンフリクト(競合)が発生し、どちらの変更を優先するのかを選ぶ処理が必要です。これは git merge におけるコンフリクトと同じ原理です。
複数のコミットを指定している場合、古いコミットから順番に取り込んで行きますので、コンフリクトが発生すれば、その時点で処理を中断します。コンフリクトを解決したコミットを作成したあと、「git cherry-pick --continue」とすれば、中断していた先程の処理を再開することができます。
コンフリクトの解決に関しては、こちらをご一読下さい。
マージコミットの cherry pick
通常、マージコミットは2つ以上の複数の親コミットを持つことになりますので、それぞれの親の立場から見た「変更内容」は、どちらの親を基準にした変更なのか、選択が必要になります。
通常は、マージコミットを指定すると、このようなエラーメッセージが表示されます。
// マージコミットを指定すると、エラーメッセージ
$ git cherry-pick f741c86
error: commit f741c8627cb799c14f706a6d54547b09729c5062 is a merge but no -m option was given.
fatal: cherry-pick failed
この場合、オプションに「-m <親番号>」「--mainline <親番号>」を加えて、親コミットを指定することになります。親番号は、git log とすると、「Merge: 」と書かれた行に、親のコミットが記載されているのがわかります。
// 該当コミットのログを表示
$ git log f741c86
// 表示結果
commit f741c8627cb799c14f706a6d54547b09729c5062
Merge: 91f6913 b104f69
Author: Www Taro <taro.www@gmail.com>
Date: Sat Feb 24 20:10:26 2018 +0900
Merge branch 'master' into prod
「Merge」行の左から親番号が1、2,・・と数えます。この番号を指定します。
// マージコミットのcherry-pick は、親番号を指定する
git cherry-pick --m 1
git cherry-pick のオプション
コミットメッッセージを編集する「-e, --edit」
通常 git cheery-pick から作成されるコミットは、コミットメーセージも指定したコミットから自動的に引き継ぎます。-e オプションにより、コミットメッセージを編集する事ができます。
空のコミットも作成する「--allow-empty」
git cherry-pick では、空のコミット(変更内容が何もないコミット)はエラーとして、失敗します。このオプションにより、空のコミットを作成することができます。
重複する変更からもコミットを作成「--keep-reduntant-commit」
git cherry-pick で指定した変更内容が、HEADが持つ履歴にすでに含まれている場合、指定した変更を取り込むことができません。そのような場合、Git は自動的にそのコミットの取り込みをスキップしますが、このオプションを指定すれば、空のコミット(=変更を含まないコミット)として、コミットを作成することができます。(上記の --allow-empty オプションを追加する必要はありません)
参考情報
Git 公式ドキュメントです。