この記事では、Gitのcherry-pickコマンドを使って、特定のコミットを現在のブランチに適用する方法を詳しく解説します。cherry-pickは、別のブランチで行われた修正を簡単に取り込むことができる便利なツールです。競合が発生した場合の対処方法や、適用前に作業ディレクトリをクリーンにするための手順も含めて説明します。
現在のブランチに特定のコミットのパッチを適用 (git cherry-pick)
特定のコミットを指定して適用
git cherry-pickは別のブランチで行われたバグ修正等を現在のブランチに反映させたい場合に便利です。
以下のコミット履歴を想定します。
1 2 3 4 5 6 7 8 9 10 |
~/Proj (master) $ git log --all --oneline --graph --decorate * c359653 (b1) 8 commit * 13eba7f 7 commit * 51e74cb 6 commit | * dac7b7a (HEAD -> master) 5 commit | * 103a22d 4 commit | * 0607029 3 commit |/ * 70408ab 2 commit * 42b81ea 1 commit |
git cherry-pickで特定のコミット(複数可)を指定すると現在のブランチにそのコミットを適用させることができます。
競合が発生したら、その競合を解決するための処理に移ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
~/Proj (master) $ git cherry-pick 13eba7f Auto-merging file4.txt CONFLICT (add/add): Merge conflict in file4.txt error: 13eba7fを適用できませんでした... 7 commit hint: After resolving the conflicts, mark them with hint: "git add/rm <pathspec>", then run hint: "git cherry-pick --continue". hint: You can instead skip this commit with "git cherry-pick --skip". hint: To abort and get back to the state before "git cherry-pick", hint: run "git cherry-pick --abort". ~/Proj (master|CHERRY-PICKING) $ git mergetool -t vimdiff Merging: file4.txt Normal merge conflict for 'file4.txt': {local}: created file {remote}: created file 3 個のファイルが編集を控えています ~/Proj (master|CHERRY-PICKING) $ git cherry-pick --continue [master 6e8ccf0] 7 cherry-pick commit Date: Sun Apr 28 06:51:00 2024 +0900 1 file changed, 1 insertion(+) ~/Proj (master) $ |
git cherry-pickを解決したら現在のブランチ(masterブランチ)に指定したコミットを適用できます。コミット履歴は以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 |
~/Proj (master) $ git log --all --oneline --graph --decorate * 6e8ccf0 (HEAD -> master) 7 cherry-pick commit * dac7b7a 5 commit * 103a22d 4 commit * 0607029 3 commit | * c359653 (b1) 8 commit | * 13eba7f 7 commit | * 51e74cb 6 commit |/ * 70408ab 2 commit * 42b81ea 1 commit |
古いコミットのコミットメッセージの変更
ここでは、とても単純な形のコミット履歴でブランチが分岐しているような古いコミットのコミットメッセージを変更するような想定でgit cherry-pickを利用したいと思います。
まず、以下のようなコミット履歴を想定します。
1 2 3 4 5 6 7 8 9 10 |
~/TestProject (master) $ git log --all --oneline --graph --decorate * c359653 (b1) 8 commit * 13eba7f 7 commit * 51e74cb 6 commit | * dac7b7a (HEAD -> master) 5 commit | * 103a22d 4 commit | * 0607029 3 commit |/ * 70408ab 2 commit * 42b81ea 1 commit |
2番目のブランチが分岐するコミットのメッセージを変更したいと思います。コマンドのみを示すと以下のような流れとなります。
1 2 3 |
~/TestProject (master) $ git rebase -i 70408ab^ ~/TestProject (master|REBASE 1/4) $ git commit --amend -m '2 edit commit' ~/TestProject (master|REBASE 1/4) $ git rebase --continue |
その後にコミット履歴を確認すると2番目のコミットがコミットメッセージが異なりますが2つあるような感じになります。
1 2 3 4 5 6 7 8 9 10 11 |
~/TestProject (master) $ git log --all --oneline --graph --decorate * 1e4665d (HEAD -> master) 5 commit * 371efa4 4 commit * bcecdbb 3 commit * 6fa295f 2 edit commit | * c359653 (b1) 8 commit | * 13eba7f 7 commit | * 51e74cb 6 commit | * 70408ab 2 commit |/ * 42b81ea 1 commit |
修正したコミットに対して、チェックアウトを行い、ブランチの作成と作成したブランチへのチェックアウトを行います。
1 2 3 |
~/TestProject (master) $ git checkout 6fa295f ~/TestProject ((6fa295f...)) $ git branch b1tmp ~/TestProject ((6fa295f...)) $ git checkout b1tmp |
コミット履歴は以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 |
~/TestProject (b1tmp) $ git log --all --oneline --graph --decorate * 1e4665d (master) 5 commit * 371efa4 4 commit * bcecdbb 3 commit * 6fa295f (HEAD -> b1tmp) 2 edit commit | * c359653 (b1) 8 commit | * 13eba7f 7 commit | * 51e74cb 6 commit | * 70408ab 2 commit |/ * 42b81ea 1 commit |
次にgit cherry-pickでb1ブランチのコミットを適用させていきます。基本的に適用されるコミットの内容で競合を起こさないはずですが、作業ディレクトリに何かゴミ(適用するコミットで作成するはずの同名のファイル等)があると競合するので、cherry-pickを行う前に一度、git statusで作業ディレクトリを確認後に、作業ディレクトリの内容をgit clean等できれいにしてからcherry-pickを適用しても良いかもしれません。
また、git cherry-pickのコミットの範囲の指定はもっといい指定方法があるかもしれませんがここではコミットIDを用いて直接範囲を指定しています。
1 |
git cherry-pick 70408ab..c359653 |
コミットの範囲はダブルドットで指定した左のコミットは適用するコミットに含まれないことに注意が必要です(数学的に表すとa<x<=b, もしくは(a,b]みたいなイメージの指定といえるかもしれません)。
コミットの範囲は直前を表す(^)を用いて、適用するコミットのみを用いた
1 |
git cherry-pick 51e74cb^..c359653 |
みたいな記述をしても良いでしょう
この例でgit cherry-pickが適切に適用されると
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
~/TestProject (b1tmp) $ git cherry-pick 70408ab..c359653 [b1tmp 6a50643] 6 commit Date: Sun Apr 28 06:50:02 2024 +0900 2 files changed, 2 insertions(+) create mode 100644 file3.txt [b1tmp 9cae8ce] 7 commit Date: Sun Apr 28 06:51:00 2024 +0900 1 file changed, 1 insertion(+) create mode 100644 file4.txt [b1tmp abf8f74] 8 commit Date: Sun Apr 28 06:51:29 2024 +0900 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 file6.txt ~/TestProject (b1tmp) $ |
のようになり、きれいに処理が完了します。これで、コミット履歴を確認すると
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
~/TestProject (b1tmp) $ git log --all --oneline --graph --decorate * abf8f74 (HEAD -> b1tmp) 8 commit * 9cae8ce 7 commit * 6a50643 6 commit | * 1e4665d (master) 5 commit | * 371efa4 4 commit | * bcecdbb 3 commit |/ * 6fa295f 2 edit commit | * c359653 (b1) 8 commit | * 13eba7f 7 commit | * 51e74cb 6 commit | * 70408ab 2 commit |/ * 42b81ea 1 commit |
のようになり、ブランチの削除とリネームを行うと
1 2 3 |
~/TestProject (b1tmp) $ git branch -D b1 Deleted branch b1 (was c359653). ~/TestProject (b1tmp) $ git branch --move b1tmp b1 |
コミット済みの古いコミットメッセージを変更した形になったコミット履歴を作成できます。
1 2 3 4 5 6 7 8 9 10 |
~/TestProject (b1) $ git log --all --oneline --graph --decorate * abf8f74 (HEAD -> b1) 8 commit * 9cae8ce 7 commit * 6a50643 6 commit | * 1e4665d (master) 5 commit | * 371efa4 4 commit | * bcecdbb 3 commit |/ * 6fa295f 2 edit commit * 42b81ea 1 commit |