cherry-pick とは
ブランチ間で特定のコミットをマージしたいが、全部はマージしたくない場合git cherry-pick
が役に立ちます。
このコマンドはコミットをつまみ食いして現在のブランチに追加します。
例えば上図のような場面でブランチaにブランチbのコミットA,Bのみを取り込みたいとします。
この時、
git checkout a
git merge b
としてしまうと、ブランチbの全てのコミットを取り込むことになります。
cherry-pickを使えば、以下のようにしてブランチbの特定のコミットをブランチaに取り込むことができます。
git checkout a
git cherry-pick Aのコミットid Bのコミットid
実際に使用した場面
cherry-pickを使用した具体的な場面について参考程度に話します。
開発の際に以下のようなフローを取っていました。
ことの発端は、開発を終えたブランチをpushしてstagingにプルリクエストを作成した際にコンフリクトを起こしたことです。
GitHub上で該当箇所を確認すると、1行修正すれば解消されるものであったため、GitHub上で直接コンフリクトを解消して、プルリクエストを送り直すことができる機能を使用しました。
その後、検証でバグが見つかったため、再度開発環境でコードを修正し、pushしたところリモートとローカルでコミットに差異があるためpullしてからpushするよう怒られます。
コンフリクトした際にGitHub上でコードを修正したことが原因なので、ここは指示に従いpullしてmergeしました。
しかし、ここでmergeした内容を確認してみると、自分がGitHub上で修正したファイル以外のファイルに関する変更が大量に含まれていることが発覚しました。
コミットログを辿ってみると、どうやらGitHub上でコンフリクトを修正した際に、一度stagingをmergeして、競合箇所を編集しプルリクエストを作成する
という処理が行われていたようです。
GitHub上ではGUIで操作を行っていたため不注意な私はポチポチ押してしまい気づかない間にstagingをmergeしてしまっていました。
stagingをmergeすると何が困るかというと、stagingで検証中であった別のブランチでの変更を大量に取り込んでしまい、開発の際に思わぬバグが発生する可能性があります。
この時、開発をしていたブランチのコミットログは自分のコミットと他人のコミットが混在して以下の図のようになっていました。
git revert
やら何やらをして元の状態に戻すことも考えたのですが、コミットログが荒れそうだったため(実際にはもっと混在していました)必要なコミットだけピックアップしてしまいたい...
ここでcherry-pickの出番です。
まずは開発環境でmasterからブランチを切り直します。
git checkout master
git checkout -b develop-hoge-new
次に問題のブランチ(ここではdevelop-hogeとします。)に移動してコミットログを確認します。
git checkout develop-hoge
git log
必要なコミットのidを確認して、新しいブランチでcherry-pickします。
git checkout develop-hoge-new
git cherry-pick Aのコミットid Bのコミットid Eのコミットid
とするとdevelop-hoge-newブランチに必要なコミットのみを取り込むことができます。
あとはpushし直すだけです。
コンフリクトした箇所については競合した相手のブランチがリリースされた後にローカルのmasterブランチにてmasterをpullしてdevelop-hoge-newにmergeして解消しましょう。