Mercurial 勉強中 (5) - conflict と multiple heads, merge
他レポジトリとの conflict 状態と multiple heads の関係を step by step で確認していきます。
Step 1: 既存レポジトリを clone
する
まずは,既存のレポジトリを clone
します。
[hg]% hg clone remote local 1 files updated, 0 files merged, 0 files removed, 0 files unresolved [hg]% cd remote/ [remote]% ls -F message.txt
message.txt があるだけの単純なレポジトリです。
remote と local の間に conflict を発生させる
あえて conflict を発生させます。
remote 側に「foo」という内容,local 側には「bar」という内容を追加して,それぞれ commit
してみます。
[remote]% cat >> message.txt foo ^D [remote]% cat message.txt === contents of message.txt === foo [remote]% hg ci -m "foo added"
[local]% cat >> message.txt bar ^D [local]% cat message.txt === contents of message.txt === bar [local]% hg ci -m "bar added"
それぞれのレポジトリでリビジョン番号 #2 が振られていますが,変更内容の異なる changeset です。
local に remote の内容を pull
する
まだお互いのレポジトリで編集作業を行っただけであり,conflict は発生していません。
なので,いざいざ pull
してみましょう。
[local]% hg pull pulling from /home/dayflower/hg/remote searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge)
hg pull
自体はうまくいきました。ですが,heads
が増えたこと(「+1 heads
」というメッセージ)が示されています。また,それを確かめるには hg heads
と入力すればいいこと,merge するためには hg merge
すればいいことも表示されています。
hg heads
で現状どのような heads が存在するのか見てみましょう。
[local]% hg heads changeset: 3:618e76e5a8d4 tag: tip parent: 1:9ed8a117e04c user: dayflower date: Mon Mar 10 16:57:30 2008 +0900 summary: foo added changeset: 2:fe56186971fd user: dayflower date: Mon Mar 10 16:57:49 2008 +0900 summary: bar added
ローカルレポジトリとしては remote で追加した foo に関する changeset が一番新しいものになっていること((tip
というタグがついています)),また 2 つの heads が存在することがわかります。
いままで編集していた内容は消えてしまったのでしょうか?心配ご無用。Working copy の parents
(直近の親 changeset)を確認してみましょう。
[local]% hg parents changeset: 2:fe56186971fd user: dayflower date: Mon Mar 10 16:57:49 2008 +0900 summary: bar added [local]% cat message.txt === contents of message.txt === bar
この通り,いままで編集していた内容のままになっていると考えてよさそうです。
merge
する
では merge
してみます。
[local]% hg merge merging message.txt conflicts detected in /home/dayflower/hg/local/message.txt (このあと,$EDITOR で指定されたエディタが開き, conflict を解消するべく編集することになります)
デフォルトでは hgmerge
というコマンドにより,diff3 形式のファイルを編集することになります。具体的にはこんな見た目です。
=== contents of message.txt === <<<<<<< /home/dayflower/hg/local/message.txt.orig.248014591 bar ||||||| /tmp/message.txt~base.GQR1A- ======= foo >>>>>>> /tmp/message.txt~other.ZwCxK9
んーわかりにくいっすね。
ともかく,編集を終了すると下記のメッセージが表示されます。
0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) [local]% cat message.txt === contents of message.txt === bar foo
単純に両方の成果をマージしたものにしました。
この時点で Working copy の parents
がどのようになったのか確認してみましょう。
[local]% hg parents changeset: 2:fe56186971fd user: dayflower date: Mon Mar 10 16:57:49 2008 +0900 summary: bar added changeset: 3:618e76e5a8d4 tag: tip parent: 1:9ed8a117e04c user: dayflower date: Mon Mar 10 16:57:30 2008 +0900 summary: foo added
remote の内容を反映した changeset:3 と local でいじっていた changeset:2 の両方が parents となっていることがわかります。
「don't forget to commit」といわれたので commit
します。
[local]% hg ci -m "merged from remote" [local] % hg heads changeset: 4:1a899fefa9db tag: tip parent: 2:fe56186971fd parent: 3:618e76e5a8d4 user: dayflower date: Mon Mar 10 17:00:28 2008 +0900 summary: merged from remote
無事 multiple heads が解消され,一本化されました。大事なのは multiple heads が解消された,ということより,merge をすることにより merge 元の heads が head ではなくなる,ということです。
local の変遷を hg log
コマンドで確認してみます。
[local]% hg log | egrep -e '(changeset:|summary:)' changeset: 4:1a899fefa9db summary: merged from remote changeset: 3:618e76e5a8d4 summary: foo added changeset: 2:fe56186971fd summary: bar added changeset: 1:9ed8a117e04c summary: message.txt created changeset: 0:d2966889cc12 summary: Initial import
図と対応していることがわかるかと思います。
ここで push
するとどうなるか
merge の成果を push
してみます。
[local]% hg push pushing to /home/dayflower/hg/remote searching for changes adding changesets adding manifests adding file changes added 2 changesets with 2 changes to 1 files
一見,同じ内容が clone されているように見えますが,local レポジトリ側では foo という内容が changeset revision number 2 になっているのに対して,remote レポジトリ側では changeset revision number 3 になっているところが異なります。
hg log
して,見てみましょう。
[local]% cd ../remote/ [remote]% hg log | egrep -e '(changeset:|summary:)' changeset: 4:1a899fefa9db summary: merged from remote changeset: 3:fe56186971fd summary: bar added changeset: 2:618e76e5a8d4 summary: foo added changeset: 1:9ed8a117e04c summary: message.txt created changeset: 0:d2966889cc12 summary: Initial import
先ほどの hg log
の結果と比較すればわかりますが,まさに上記の図のようになっていることがわかります。また,対応する changeset の hash 値が等しくなっていること*1がわかります。
すなわち,『レポジトリ全体の management としては』changeset hash id のほうが重要であることがわかります。ま,あくまでローカルで作業する分には changeset revision number で扱っていても支障はありません。
注意点
先ほどの例では hg pull
する前に hg commit
してますが,Mercurial は commit
する前に pull
してもうまく扱ってはくれます。ですが,heads の状態がわかりにくくなる可能性があること,working copy での作業内容が不明確になりうることを鑑みると,hg pull
する前に,ローカル側で hg commit
しておくほうがいいと思います。もちろん hg incoming
してあらかじめ状況を把握しておくとなおベターです。
補足(2008/03/11 追記)
もう一度,レポジトリ間で conflict が発生している状態に立ち返って考えてみます。
この状況で remote へ push するとどうなるでしょうか。remote で branch が分岐して multiple heads の状態になりそうなものですが……
[local]% hg push pushing to /home/dayflower/hg/remote searching for changes abort: push creates new remote branches! (did you forget to merge? use push -f to force)
「abort: push creates new remote branches!」と,怒られてしまいました。
これ,意図的に切った branch を commit しようとしているのか,あるいは単に Master Repository の成果・変更を無視して push しようとしているのか,Mercurial には判断がつかないためです(だから「merge 忘れてない?」と聞かれているんですね)。安全サイドに倒す意味で,remote side で branch が分岐する(heads が増える)状況では,-f
オプションをつけない限り abort するようになっているのです。
一般的には push する前に,pull して remote の変更を merge してから push することになります*2。ですが今回は,テストのため,force な push をしてみましょう。
[local]% hg push -f pushing to /home/dayflower/hg/remote searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads)
無事 push できました。heads が +1 されたよ,といわれています。
remote side での heads を見てみます。
[remote]% hg heads changeset: 3:fe56186971fd tag: tip parent: 1:9ed8a117e04c user: dayflower date: Mon Mar 10 16:57:49 2008 +0900 summary: bar added changeset: 2:618e76e5a8d4 user: dayflower date: Mon Mar 10 16:57:30 2008 +0900 summary: foo added
たしかに multiple heads になっています。