はじめに
開発作業中に
- 他ブランチのあのコミットだけこっちにもマージしたい...!!
- 他ブランチのあのファイルだけこっちにもマージしたい...!!
なんてことはないでしょうか?
今日はそんな方法を紹介してみたいと思います
1. git cherry-pick で他ブランチのコミットを取り込む
1つめの方法は git cherry-pick
コマンドを使う方法です。
git cherry-pick とは?
既存の特定のコミットを自分のブランチに取り込むことができる というコマンドです。
書式はこうです。
$ git cherry-pick {コミットのハッシュID}
サンプル
例えば、以下のように、proj/updateTestText ブランチで test.txt を更新したコミットと、新しく test2.text を作ったコミットが存在する という状態があったとします。
~/mywork/test-product (proj/updateTestText)
$ git log --oneline
2784fd2 (HEAD -> proj/updateTestText) create test2.txt
a8706e9 update text.txt
6a539d7 (master) create test.txt
このとき、「新しく作った test2.txt だけ欲しいなー、でも test.txt の更新はまだ欲しくないなー」 という一見無茶ぶりに見える要望に、git cherry-pick を使うと容易く応えることができるのです。
というわけで先ほど紹介した書式にしたがって実際にコマンドを実行してみますとこのとおり test2.txt があなたのお手元に作成されます
$ git checkout master # master ブランチに戻って
$ git cherry-pick 2784fd2 # test2.txt を作ったコミットだけを cherry-pick!!!
[master 055cafa] create test2.txt
Date: Mon May 18 22:57:18 2020 +0900
1 file changed, 1 insertion(+)
create mode 100644 test2.txt
もちろん、まだマージしたくなかった test.txt の更新コミットは反映されていません。
$ git log --oneline
055cafa (HEAD -> master) create test2.txt
6a539d7 create test.txt
git cherry-pick を行う際は上記のようにコミットの粒度が細かいほどやりやすいので、そのあたりも含めてコミットの粒度を考えて作業を進めると誰かがちょっと幸せになれるかもしれません。
2. git checkout で他ブランチの特定ファイルだけを取り込む
2つめは git checkout を使った方法を紹介します。
「は?git checkout はブランチを切り替えるコマンドだろ?」と思った方、実はそれだけではないんです。
git checkout とは
公式 からの引用ですが、git checkout コマンドとは、
- ブランチの切り替え
- 作業ツリーファイルの復元
この2点の機能を持っています。
前者の機能は広く知られているかとおもいますが、ここではこの後者の機能をつかって、別ブランチの特定ファイルを自分のブランチに復元する という流れで、別ブランチからの作業取り込みを実現してみたいと思います。
というわけでやってみましょう
書式は以下のとおりです。
$ git checkout origin/feature/targetBranch -- path/to/targetFile.extension
以下は先ほどの cherry-pick の実行前と一見同じ状態ですが、test.textの更新とtest2.txtの作成を同じコミットで行ってしまっています。 これだと test2.txt だけを取り込みたい場合は cherry-pick コマンドでは対応できません。
~/mywork/test-product (proj/updateTestText)
$ git log --oneline
d5416a0 (HEAD -> proj/updateTestText) update test.txt and create create test2.txt
3a5d667 (master) create test.txt
そこで checkout コマンドの出番です。こちらを実行すると、なんと master ブランチに test2.txt がそのまま復元されました。
$ git checkout proj/updateTestText -- test2.txt
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: test2.txt
あとはこの新しく作成されたファイルをコミットするだけです。
$ git commit
[master aec7110] create test2.txt on master
1 file changed, 1 insertion(+)
create mode 100644 test2.txt
checkout はさらにここがスゴイ
さて、勘の良い方はこう思いませんでしたでしょうか?
「proj/updateTestTextブランチをmasterにマージしたらコンフリクトしないの?」 と。
結論から言います。しないんです。
実際やってみましょう。
$ git merge --no-ff proj/updateTestText
Merge made by the 'recursive' strategy.
test.txt | 1 +
1 file changed, 1 insertion(+)
このとおり。
所感
毎日使うような操作ではないですが、こちらを覚えておくと非常時にけっこう役にたちます。
機会があれば試してみてください。
また、公式ドキュメントを見るとどちらのコマンドにもいろんなオプションがあります。筆者もまだ全てに目を通しきれてはいませんが、何か面白そうだったり、特に役にたちそうなオプションがあればまた改めて紹介したいと思います。
はばないすでい。