ブログトップ 記事一覧 ログイン 無料ブログ開設

idesaku blog

2009-12-30

[][]git-cherry-pickを掘り下げる

Gitgit-cherry-pickという、知らなくてもなんとかなるが知っていると便利なコマンドがある。このコマンドを少し掘り下げてみた。

git-cherry-pick

git-cherry-pickは、狙ったコミットの変更内容だけを現在のブランチに取り込む操作である。

例えば、つぎのような履歴を想定する。

---A---B---C [master]
        \
         \
           ---X---Y [temp]

ここで、YはCの後にコミットするほうが適切であることに気づいた。このとき、masterブランチで次のようにすると目的は達成される*1

$ git cherry-pick Y

コミットYの変更内容だけをmasterのHEADに適用する、という操作である。このときXの変更内容は適用されない点がgit-mergeとは異なる。

---A---B---C---Y' [master]
        \
         \
           ---X---Y [temp]

もはやYは必要なくなるので、git-resetしてしまう。

$ git checkout temp
$ git reset --hard HEAD^

これで期待する履歴になった。

---A---B---C---Y' [master]
        \
         \
           ---X [temp]

疑問点

当然思いつくことだと思うのだが、Yの変更内容がXに依存する場合はどうなるのか?

例えば、git-revert*2したものだった場合は?

$ (edit)
$ git commit -am 'すごいハック!'
$ git revert HEAD
---A---B---C [master]
        \
         \
           ---X---^X [temp]

ここで^Xをgit-cherry-pickした場合、^X単体で見れば変更を行っているのだが、実際はXを取り消しているだけなのでBとの差分は存在しない。つまり、git-cherry-pickには何も変更しないで欲しいわけだが…。

$ git checkout master
$ git cherry-pick ^X
Finished one cherry-pick.
# On branch master
nothing to commit (working directory clean)

変更しない!賢い!

では、これほど単純ではなくて、git-cherry-pickの対象コミットに直前のコミットの一部が含まれる場合は?

$ (edit...)
$ git commit -am 'かっこいい処理を追加した!'
$ (edit...)
$ git commit -am 'かっこいい処理にちょっとした間違いがあったから手直しした!'
---A---B---C [master]
        \
         \
           ---X---XY [temp]

どれどれ…

$ git checkout master
$ git cherry-pick XY
Automatic cherry-pick failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>' or 'git rm <paths>' and commit the result.
When commiting, use the option '-c 007c861' to retain authorship and message.

衝突した。

ちなみに、git-mergeすると自動的にマージされるので、通常の意味での変更の衝突は無いはずである。

$ git merge XY
Auto-merging awesome.rb
Merge made by recursive.
 awesome.rb |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

Xを修正するコミットであるXYを、XY単体で取り込むとはどういう意味か?そんなのコマンドを叩いた人間でないとわからない。だからgit-cherry-pickはそれを問い合わせてくるのだろう。そしてこうした妙なgit-cherry-pickの要請を、内部的には"衝突"として検知しているわけだ。

ともあれ、少なくとも妙なコミットが自動的に適用される心配はしなくていいようだ。

まとめ

git-cherry-pickは単にパッチを当てるよりもずっと賢くコミットを取り込んでくれる。安全のために積極的に使っていこう。

しかしまぁなんですか、他のコミットの変更内容を引きずっているコミットをgit-cherry-pickする機会はないと思うので、おそらくこのあたりを気にする必要は無いと思う。 それなりに発生しえる事態であるそうなので、いざというときのために正しく使えるようになっておこう。コメント欄参照。id:jch2355 さん、ご指摘ありがとうございます。

*1:もちろん、変更が衝突する場合もあるので、そのときは通常の手続きで衝突を解消する。

*2:指定したコミットを取り消すコミットを作成するコマンド。

jch2355jch2355 2010/02/01 15:51 よく使うのは、次期リリース版用のバグ修正の積りですでに master に適用したけれど、後になってみて、それは実は昔のパージョンからずっとあったバグの修正で、同じ内容の変更がメンテナンスブランチ maint にも欲しい、という状況です。だいぶたっちゃった後になって、「masterの先頭から7番目のやつ、実は maint にも当ててやらないと」というときに、

git checkout maint
git cherry-pick master~7

何てやるわけです。モトモトから注意深くやっていれば、バグ修正を maint に当てておいて、その結果を master にマージする、っていう方が格好良いコトは言うまでもありません。

mmmm 2013/02/20 10:40 参考になりました。
$ git co master と書かれている箇所がありますが、
$ git checkout master ですよね?

idesakuidesaku 2013/02/20 10:56 おっしゃるとおりです。ご指摘ありがとうございます。coにaliasしているのでつい。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証