2017年07月02日

git ブランチを合流するマージ

ブランチ間で変更を共有するための操作が "merge" 。

$ git merge <ブランチ名>



ここで簡易な説明をしたが、もう少しマージについて掘り下げてみる。

直接マージ


一般的なマージのイメージ。ブランチ全体を別ブランチへ合流させる。

"new_branch" ブランチを "master" ブランチにマージする場合、
"master" ブランチをチェックアウトした状態で実行する。

$ git merge new_branch



git_branch10.png

コミットログをグラフ表示にして確認する。
$ git log --graph
* commit 674649a321b87801de0afea9389a162a66cac60d
|\ Merge: d7af236 5230eda
| | Author: hoge hogehoge
| | Date: San Jun 25 20:00:00 2017 +0900
| |
| | Merge branch 'new_branch'
| |
| | Merge new_branch -> master
| |
| * commit 5230eda9e7070b084e250852ec1f29bcc65e0870
| | Author: hoge hogehoge
| | Date: San Jun 25 19:00:00 2017 +0900
| |
| | new_branch 追加2
| |
| * commit 71950fae9396a54bbf255b8466f3f15e93e76051
| | Author: hoge hogehoge
| | Date: San Jun 25 18:00:00 2017 +0900
| |
| | new_branch 追加1
| |
* | commit d7af236ea4c844862eaafb38fbd99e951a34dda3
| | Author: hoge hogehoge
| | Date: San Jun 25 18:30:00 2017 +0900
| |
| | master branch 追加2
| |
* | commit 8673bdfb53790b7dc42a8a9ecf24491641c868f9
|/ Author: hoge hogehoge
| Date: San Jun 25 11:00:00 2017 +0900
|
| master branch 追加1
|
* commit 76eb1f66a10cea1ac9e3b3b1f59b5fe946a29467
| Author: hoge2
| Date: San Jun 25 10:10:00 2017 +0900
|
| master branch.
|
* commit b396e3f7adef3c4cedbda55df20ceac837750e0b
Author: hoge hogehoge
Date: Sat Jun 24 16:00:00 2017 +0900

Add new file.

git_branch11.png
ログを見ると "Merge: d7af236 5230eda" と、
"d7af236" "5230eda" の両方の変更履歴を取り込んだマージコミットが作成されている。

"master" ブランチは、このマージコミットを指している。





ブランチの削除


機能開発でもバグ修正でも、ブランチを切って作業し、作業が終わりブランチをマージしたのであれば、このブランチはもう役目が終わったことになる。

役目がなくなったブランチが乱立していると全体の見通しが悪くなるし、作業のケアレスミスを誘うので、削除してしまう。ブランチで作業した履歴経過は、マージしたことで別のブランチに取り込まれているので大丈夫。

"-d" オプションで削除できる。"master" ブランチをチェックアウトした状態で実行する。

$ git branch -d new_branch



もし、まだマージが終わってないのであれば、以下のようにエラーがでる。
$ git branch -d new_branch
error: The branch 'new_branch' is not fully merged.
If you are sure you want to delete it, run 'git branch -D new_branch'.

それでも強制的に削除したいのであれば、"-D" オプションを指定すればブランチが削除される。






fast-forward マージ


直接マージでの例では、"new_branch" の分岐元に変更があったが、もし、分岐元に変更がなければどうなるか?
git_branch08.png

ブランチの分岐元で変更がなければ、
単純に "master"ブランチ(= ポインタ)が移動するだけ。
git_branch09.png

この場合、マージ作業が行われない。
コミットログを見ても、マージした内容がでてこない。
$ git log --graph
* commit 693e08561ab3e6eef1055cc043644f04a75e0a32
| Author: hoge hogehoge
| Date: San Jun 25 12:20:00 2017 +0900
|
| new_branch 追加2
|
* commit 51f70ad5da7a8a73f46aa5da0cdc3798c7a4bfe4
| Author: hoge hogehoge
| Date: San Jun 25 12:15:00 2017 +0900
|
| new_branch 追加1
|
* commit 76eb1f66a10cea1ac9e3b3b1f59b5fe946a29467
| Author: hoge2
| Date: San Jun 25 10:10:00 2017 +0900
|
| Update another user.
|
* commit b396e3f7adef3c4cedbda55df20ceac837750e0b
Author: hoge hogehoge
Date: Sat Jun 24 16:00:00 2017 +0900

Add new file.



git側からすると処理が簡易になるが、作業者側からすると「ここまで作業した内容を、このタイミングで本流ブランチへマージする」という意図を残せなくなってしまう。
こんな時は、マージの実行時にオプションで、non-fast-forwardマージを指定することで、"fast-forward" マージ状態の場合でも必ずコミットを作成することができる。

$ git merge --no-ff new_branch



git_merge01.png
コミットログを確認する。
$ git log --graph
* commit de92ffc8b5239d46b3ffb6da53e746862802d7ea
|\ Merge: 76eb1f6 3394694
| | Author: hoge hogehoge
| | Date: San Jun 25 14:55:00 2017 +0900
| |
| | Merge branch 'new_branch'
| |
| * commit 3394694890bd9611876afd0c327d3d1e7aafa5a3
| | Author: hoge hogehoge
| | Date: San Jun 25 14:50:00 2017 +0900
| |
| | new_branch 追加2
| |
| * commit 6bf906ae7c607b1fec53560c4a4001aff4dbb151
|/ Author: hoge hogehoge
| Date: San Jun 25 14:40:00 2017 +0900
|
| new_branch 追加1
|
* commit 76eb1f66a10cea1ac9e3b3b1f59b5fe946a29467
| Author: hoge2
| Date: San Jun 25 10:10:00 2017 +0900
|
| Update another user.
|
* commit b396e3f7adef3c4cedbda55df20ceac837750e0b
Author: hoge hogehoge
Date: Sat Jun 24 16:00:00 2017 +0900

Add new file.

"non-fast-forward(--no-ff)" を指定したことで、マージコミットが作成できた。
"fast-forward" マージかどうか考えるのが煩わしいのであれば、マージするときは常に "--no-ff" を指定するルールで運用すればよい。





複数ブランチをマージ


マージの操作は、1つのブランチを指定するだけではなく、複数のブランチを指定することが可能

$ git merge <ブランチ1> <ブランチ2> ...



偽t
git_merge02.png
$ git log --graph
*-. commit ffa171ba45291cf2af0acd4be40cc11a1bc5b349
|\ \ Merge: 76eb1f6 3737881 51b322c
| | | Author: hoge hogehoge
| | | Date: San Jun 25 16:15:00 2017 +0900
| | |
| | | Merge branches 'new_branch' and '2nd_branch'
| | |
| | * commit 51b322cb869656e033037d451745591e73122416
| |/ Author: hoge hogehoge
|/| Date: San Jun 25 16:00:00 2017 +0900
| |
| | 2nd_branch 追加1
| |
| * commit 37378818d3cdce35f83950f13208e9822565b691
| | Author: hoge hogehoge
| | Date: San Jun 25 15:55:00 2017 +0900
| |
| | new_branch 追加 2
| |
| * commit a2458950a047450e1c07a54d109cc0f9b06b6196
|/ Author: hoge hogehoge
| Date: San Jun 25 15:50:00 2017 +0900
|
| new_branch 追加 1
|
* commit 76eb1f66a10cea1ac9e3b3b1f59b5fe946a29467
| Author: hoge2
| Date: San Jun 25 10:10:00 2017 +0900
|
| Update another user.
|
* commit b396e3f7adef3c4cedbda55df20ceac837750e0b
Author: hoge hogehoge
Date: Sat Jun 24 16:00:00 2017 +0900

Add new file.

ログを見ると "Merge: 76eb1f6 3737881 51b322c" と、
"76eb1f6" "3737881" "51b322c" の3つの変更履歴を取り込んだマージコミットが作成されている。






圧縮コミット


作業者にとって作業の履歴は必要なものだが、ときには途中経過は必要なく、最終結果があれば良いときもある。バグ修正のブランチで、作業の区切りが良いタイミングでコミットしていた場合など。
こういうときに履歴を圧縮(squash)してコミットできる。
git_merge02.png
"new_branch"ブランチでのログを確認してみる。
$ git log --graph
* commit a5d864ba614bee81b2a1f4e9a659b6c06950439c
| Author: hoge hogehoge
| Date: San Jun 25 16:30:00 2017 +0900
|
| new_branch 修正
|
* commit d45c9c87f3c59ffcac25af2e87607a016c6a2672
| Author: hoge hogehoge
| Date: San Jun 25 16:25:00 2017 +0900
|
| new_branch 追加 2
|
* commit c6b3d18719ced8097ecbc3dec1ccb51ee717491e
| Author: hoge hogehoge
| Date: San Jun 25 16:20:00 2017 +0900
|
| new_branch 追加 1
|
* commit 76eb1f66a10cea1ac9e3b3b1f59b5fe946a29467
| Author: hoge2
| Date: San Jun 25 10:10:00 2017 +0900
|
| Update another user.
|
* commit b396e3f7adef3c4cedbda55df20ceac837750e0b
Author: hoge hogehoge
Date: Sat Jun 24 16:00:00 2017 +0900

Add new file.

"new_branch" ブランチには3つのコミットが存在している。

これを "master" ブランチへ圧縮コミットする。

$ git merge --squash new_branch

Updating 76eb1f6..a5d864b
Fast-forward
Squash commit -- not updating HEAD
index.html | 4 ++++
1 file changed, 4 insertions(+)


"master" ブランチでステータスを確認してみる。
$ git status
branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)

modified: index.html

ステージされた状態になっている。
これは、圧縮コミットを実行しただけではマージコミットが作られないから。

ブランチ先でのすべてのコミットを1つにまとめて、チェックアウトしているブランチにステージするまで。コミットを再度実行する必要があることに注意。

$ git commit
[master 4f0b63b] Squashed commit of the following:
1 file changed, 4 insertions(+)

$ git log --graph
* commit 4f0b63b9131b3e50a9621502a18ae81e89bfc2be
| Author: hoge hogehoge
| Date: San Jun 25 20:40:00 2017 +0900
|
| Squashed commit of the following:
|
| commit a5d864ba614bee81b2a1f4e9a659b6c06950439c
| Author: hoge hogehoge
| Date: San Jun 25 16:30:00 2017 +0900
|
| new_branch 修正
|
| commit d45c9c87f3c59ffcac25af2e87607a016c6a2672
| Author: hoge hogehoge
| Date: San Jun 25 16:25:00 2017 +0900
|
| new_branch 追加 2
|
| commit c6b3d18719ced8097ecbc3dec1ccb51ee717491e
| Author: hoge hogehoge
| Date: San Jun 25 16:20:00 2017 +0900
|
| new_branch 追加 1
|
* commit 76eb1f66a10cea1ac9e3b3b1f59b5fe946a29467
| Author: hoge2
| Date: San Jun 25 10:10:00 2017 +0900
|
| Update another user.
|
* commit b396e3f7adef3c4cedbda55df20ceac837750e0b
Author: hoge hogehoge
Date: Sat Jun 24 16:00:00 2017 +0900

Add new file.


git_merge04.png





チェリーピック


特定機能のためブランチを切って進めていたが、このうちの一部を取り込む必要が出てきた。その一部は完成しているが、このブランチの状態とし不完全でマージすることができない。このような場合に一部のコミットだけ取り込むことができるのがチェリーピック(気に入ったものをつまみ食い)。

コミットIDを使うので、予め、gitログから取り込みたいコミットのIDを取得しておく。
git_merge05.png
$ git log
commit 27ea9f6315aa874c49917ae7295b83eac0dd5d53
Author: hoge hogehoge
Date: San Jun 25 12:55:00 2017 +0900

new_branch 追加 3

commit 516b29f44ebff0b18fe1d7dc3aab15d17a942d62
Author: hoge hogehoge
Date: San Jun 25 12:50:00 2017 +0900

new_branch 追加 2

commit 47210350a1abbd59a0e538ff2904b5ae45e07d73
Author: hoge hogehoge
Date: San Jun 25 12:45:00 2017 +0900

new_branch 追加 1

commit 76eb1f66a10cea1ac9e3b3b1f59b5fe946a29467
Author: hoge2
Date: San Jun 25 10:10:00 2017 +0900

Update another user.

commit b396e3f7adef3c4cedbda55df20ceac837750e0b
Author: hoge hogehoge
Date: Sat Jun 24 16:00:00 2017 +0900

Add new file.


コミットID:516b29f をチェリーピックでマージしてみる。
"master" ブランチをチェックアウトした状態で実行する。
git_merge06.png

$ git cherry-pick 516b29f

error: could not apply 516b29f... new_branch 追加 2
ヒント: after resolving the conflicts, mark the corrected paths
ヒント: with 'git add ' or 'git rm '
ヒント: and commit the result with 'git commit'

コンフリクトが発生した。とりあえずステータスを確認する。
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
You are currently cherry-picking commit 516b29f.
(fix conflicts and run "git cherry-pick --continue")
(use "git cherry-pick --abort" to cancel the cherry-pick operation)

Unmerged paths:
(use "git add ..." to mark resolution)

both modified: index.html

no changes added to commit (use "git add" and/or "git commit -a")


コンフリクトを修正する。
(fix conflicts and run "git cherry-pick --continue")

修正したら、ファイルをステージし、継続実行する。

ここでコミットログには、しっかりとチェリーピックを実施したことを残しておく。
マージのようにログにチェリーピックを実行したことは自動的には残らないので、どこからチェリーピックしたのかなど残しておくこと。後から作業履歴が追えなくなる。

$ git add index.html
$ git cherry-pick --continue

$ git log --graph
* commit 88299c730a7f61e3b12a5b74c4f4bcf5777fb1a2
| Author: hoge hogehoge
| Date: San Jun 25 12:50:00 2017 +0900
|
| new_branch 追加 2
|
| cherry-pick :new_branch ID:516b29f -> master
|
* commit 76eb1f66a10cea1ac9e3b3b1f59b5fe946a29467
| Author: hoge2
| Date: San Jun 25 10:10:00 2017 +0900
|
| Update another user.
|
* commit b396e3f7adef3c4cedbda55df20ceac837750e0b
Author: hoge hogehoge
Date: Sat Jun 24 16:00:00 2017 +0900

Add new file.


今回はコンフリクトを修正したが、やっぱり「チェリーピックは無しで」と思ったなら、
(use "git cherry-pick --abort" to cancel the cherry-pick operation)

$ git cherry-pick --abort

とすれば、チェリーピックの操作がキャンセルされる。





別のパターンでもチェリーピックしてみる。
$ git log
commit 51068aa82fd5e49945882d52635860f20f423ee5
Author: hoge hogehoge
Date: San Jun 25 13:15:00 2017 +0900

new_branch index.html変更4

commit 510ada971c99f12e849f05ee1fa5ff1e08a57dea
Author: hoge hogehoge
Date: San Jun 25 12:35:00 2017 +0900

new_branch index.html変更3, readme.txt変更2

commit 9d118d841fe194b89133d357db3d2b46e3c6e9ab
Author: hoge hogehoge
Date: San Jun 25 12:30:00 2017 +0900

new_branch readme.txt変更1

commit d72b4934eea9f324159fc4afd63e8c1622e40486
Author: hoge hogehoge
Date: San Jun 25 12:25:00 2017 +0900

new_branch index.html変更2, readme.txt追加

commit c88270579d1b1ecc78659e4199d7585d36d1ea26
Author: hoge hogehoge
Date: San Jun 25 12:20:00 2017 +0900

new_branch index.html変更1

commit 76eb1f66a10cea1ac9e3b3b1f59b5fe946a29467
Author: hoge2
Date: San Jun 25 10:10:00 2017 +0900

Update another user.

commit b396e3f7adef3c4cedbda55df20ceac837750e0b
Author: hoge hogehoge
Date: Sat Jun 24 16:00:00 2017 +0900

Add new file.

git_merge07.png


今回は、コミットID:d72b493, 9d118d8, 510ada9 の一連となる3つをチェリーピックしてみる。このような複数コミットのチェリーピックは、

$ git cherry-pick [cherry-pick の始点となるコミット]..[cherry-pick の終点となるコミット]


とすることで、始点~終点までの一連のコミットをチェリーピックできる。

ただし、注意点がある。
始点に指定するのは、取り込みたいコミットの1つ前を指定すること。
git_merge08.png


"master" ブランチをチェックアウトした状態で実行。

$ git cherry-pick c882705..510ada9

error: could not apply d72b493... new_branch index.html変更2,  readme.txt追加
ヒント: after resolving the conflicts, mark the corrected paths
ヒント: with 'git add ' or 'git rm '
ヒント: and commit the result with 'git commit'

コンフリクト発生。まずはステータスを確認する。
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
You are currently cherry-picking commit d72b493.
(fix conflicts and run "git cherry-pick --continue")
(use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:

new file: readme.txt

Unmerged paths:
(use "git add ..." to mark resolution)

both modified: index.html

コンフリクトしたファイルを修正し、ステージしたら、チェリーピックを継続する。

$ git add index.html
$ git cherry-pick --continue

[master cc35b51] new_branch index.html変更2,  readme.txt追加
Date: San Jun 25 12:25:00 2017 +0900
2 files changed, 4 insertions(+)
create mode 100644 readme.txt
[master 50eec66] new_branch readme.txt変更1
Date: San Jun 25 12:30:00 2017 +0900
1 file changed, 1 insertion(+)
[master 72958d6] new_branch index.html変更3, readme.txt変更2
Date: San Jun 25 12:35:00 2017 +0900
2 files changed, 2 insertions(+)l


一連のチェリーピックは、複数のコミットを圧縮するわけではないので、そのまま複数のコミットとして取り込まれる。しかし、コミットコメントは1回だけの入力になるので、複数チェリーピックであることを、コミットコメントとして残すようにする。
$ git log --graph
* commit 72958d63e1b0767fe9558b788613c95aa83e52f2
| Author: hoge hogehoge
| Date: San Jun 25 12:35:00 2017 +0900
|
| new_branch index.html変更3, readme.txt変更2
|
* commit 50eec6691da28becce129856846bd35cd7efb282
| Author: hoge hogehoge
| Date: San Jun 25 12:30:00 2017 +0900
|
| new_branch readme.txt変更1
|
* commit cc35b51f100c13ca134e874032567fb6fd7f9440
| Author: hoge hogehoge
| Date: San Jun 25 12:35:00 +0900
|
| new_branch index.html変更2, readme.txt追加
|
| cherry-pick new_branch ID:d72b493, 9d118d8, 510ada9 -> master
|
* commit 76eb1f66a10cea1ac9e3b3b1f59b5fe946a29467
| Author: hoge2
| Date: San Jun 25 10:10:00 2017 +0900
|
| Update another user.
|
* commit b396e3f7adef3c4cedbda55df20ceac837750e0b
Author: hoge hogehoge
Date: Sat Jun 24 16:00:00 2017 +0900

Add new file.






まとめ


開発作業の中でマージ作業が必須。作業してきたコミット内容をどのようにすれば次の作業に送れるのかを考え、どのようなマージを使うか決める。特にチェリーピックはいろいろな場面で役立つが、マージ操作と違い、コミットログに出てこないため、しっかりとコメントを残し、作業履歴を失わないように注意したい。






gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを付け替える
git コミット履歴を変更する
git コミットを取り消す
git 変更をリセットする
git リモートでの操作
git ファイルの追跡
git リリース準備
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 23:00| Comment(0) | Linux | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください