ケーススタディで学ぶMercurialのRebase拡張(Mercurial Advent Calendar 2011 1日目)

今年から始まったMercurial Advent Calendar 2011 - [PARTAKE]の1日目です。(あんまりフライングといわれるので記事コピーしました。)最初は小ネタという事で、MercurialRebase拡張の使い方についてケーススタディで説明したいと思います。いろいろパターンを上げていったらかなり長い記事になってしまいました。読むのに10分くらいかかります。

説明に出てくるリポジトリhttps://bitbucket.org/troter/mercurial-advent-calendar-2011-1/に置いてあるシェルスクリプトで作成出来ます。unixcygwinの環境が有る方は次のコマンドで作業環境が整います。(コマンドを列挙しているだけなのでwindows環境でも簡単に再現できると思います。)

$ hg clone https://bitbucket.org/troter/mercurial-advent-calendar-2011-1
$ cd mercurial-advent-calendar-2011-1
$ make

注意点

まずはじめに履歴改変全般に関する注意点です。

  • 中央リポジトリに反映されてた公の履歴の改変は絶対厳禁です。

次に、rebaseに関する注意点です。

  • 現在のリビジョンの場所に気をつけて下さい。適切な場所(移動させたい場所)に居るとrebaseがスムーズに実行できます。
  • rebaseになれないうちは--keepオプション(後で出てきます)を利用しましょう。

rebase

指定したリビジョンの親リビジョンを差し替える事をrebaseと呼びます。rebaseを利用する主な目的には

  • 逆方向マージ(マージの前にマージ先の変更をあらかじめ取り込む事)の代替手段
  • マルチプルヘッドのマージ(≓無駄なマージ)を減らしリビジョングラフの可読性を高めるため

などが有ります。

RebaseExtensionを利用する為の準備

Mercurialでは履歴改変を行う(危険な?)機能はデフォルトで無効化されているため、rebaseを利用する為に設定が必要です。
$HOME/.hgrcや%USERPROFILE%\mercurial.iniに次の記述を追加しましょう。

[extensions]
rebase =

基本(ex1-*)

rebaseの基本的な使い方について説明します。利用するリポジトリのリビジョングラフです。

o  4:13c7303d173d | ex1-1-basic: OTHER2
|
o  3:2c915c1fe5c2 | ex1-1-basic: OTHER1
|
| @  2:70ad46ebeff7 | ex1-1-basic: LOCAL2
| |
| o  1:3ac8dc259e05 | ex1-1-basic: LOCAL1
|/
o  0:3bcd5ed25595 | ex1-1-basic: BASE

このリポジトリは次の設定で作成されています。

  • リビジョン1、リビジョン2は自分の変更(かつ、中央リポジトリに反映されていない変更)
  • リビジョン3、リビジョン4は他人の変更(中央リポジトリの変更)

この分岐をいかにrebaseするかが最初のテーマです。

ex1-1-basic(hg rebase)

ex1-1-basicのリポジトリに移動します。

$ cd ex1-1-basic
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o  4:13c7303d173d | ex1-1-basic: OTHER2
|
o  3:2c915c1fe5c2 | ex1-1-basic: OTHER1
|
| @  2:70ad46ebeff7 | ex1-1-basic: LOCAL2
| |
| o  1:3ac8dc259e05 | ex1-1-basic: LOCAL1
|/
o  0:3bcd5ed25595 | ex1-1-basic: BASE

この状態でhg rebaseを行います。hg rebaseコマンドを実行してみましょう。

$ hg rebase
saved backup bundle to /home/takumi/sandbox/mercurial-advent-calendar-2011-1/ex1-1-basic/.hg/strip-backup/3ac8dc259e05-backup.hg
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  4:b77036af9152 | ex1-1-basic: LOCAL2
|
o  3:5478e1dd53c5 | ex1-1-basic: LOCAL1
|
o  2:13c7303d173d | ex1-1-basic: OTHER2
|
o  1:2c915c1fe5c2 | ex1-1-basic: OTHER1
|
o  0:3bcd5ed25595 | ex1-1-basic: BASE

LOCALのコミットの親リビジョンからBASEからOTHERに移動しました。(現在のリビジョンの親リビジョンが差し替えられました。)

ex1-2-keep(hg rebase --keep)

rebaseの中で'saved backup bundle to XXXX'とうメッセージが有りました。これは実はrebaseはリビジョンを削除している為です。削除させないためには--keepオプションを利用します。ex1-2-keepのリポジトリに移動して確認して見ましょう。

$ cd ex1-2-keep
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o  4:d20c4afec103 | ex1-2-keep: OTHER2
|
o  3:437d2ae25159 | ex1-2-keep: OTHER1
|
| @  2:0625d4dfeda8 | ex1-2-keep: LOCAL2
| |
| o  1:0ba178973202 | ex1-2-keep: LOCAL1
|/
o  0:5163b8586f33 | ex1-2-keep: BASE

ex1-2-keepリポジトリはex1-1-basicリポジトリと同じ内容です。この状態でhg rebase --keepを行います。

$ hg rebase --keep
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  6:38aa44518d21 | ex1-2-keep: LOCAL2
|
o  5:aa158df0fa14 | ex1-2-keep: LOCAL1
|
o  4:d20c4afec103 | ex1-2-keep: OTHER2
|
o  3:437d2ae25159 | ex1-2-keep: OTHER1
|
| o  2:0625d4dfeda8 | ex1-2-keep: LOCAL2
| |
| o  1:0ba178973202 | ex1-2-keep: LOCAL1
|/
o  0:5163b8586f33 | ex1-2-keep: BASE

リビジョン5、リビジョン6という新しいリビジョンが作成されています。このためLOCAL1とLOCAL2のコミットが重複しています。
リビジョン1、リビジョン2を削除(MQ拡張のstripコマンド)するとex1-1-basicと同じリビジョングラフに変化します。

$ hg strip 1
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  4:38aa44518d21 | ex1-2-keep: LOCAL2
|
o  3:aa158df0fa14 | ex1-2-keep: LOCAL1
|
o  2:d20c4afec103 | ex1-2-keep: OTHER2
|
o  1:437d2ae25159 | ex1-2-keep: OTHER1
|
o  0:5163b8586f33 | ex1-2-keep: BASE

このようにrebaseは移動させたいリビジョンを複製させた後、古いリビジョンを削除します。

rebase初心者の場合や、複雑なrebaseを行う場合は--keepオプションを利用しましょう。

ex1-3-dest-base-or-source(hg rebase --base 2 --dest 4)

ex1-1-basic、ex1-2-keepの場合、リビジョン2上でrebaseを実施しました。リビジョン4上でrebaseを実施したい場合はどうすれば良いのでしょうか?ex1-3-dest-base-or-sourceのリポジトリに移動して確認して見ましょう。

$ cd ex1-3-dest-base-or-source
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  4:a188eaeb8f92 | ex1-3-dest-base-or-source: OTHER2
|
o  3:9cab071d1eff | ex1-3-dest-base-or-source: OTHER1
|
| o  2:41dff93dc631 | ex1-3-dest-base-or-source: LOCAL2
| |
| o  1:6d7692c7d470 | ex1-3-dest-base-or-source: LOCAL1
|/
o  0:d3029e217ceb | ex1-3-dest-base-or-source: BASE

rebaseを実行してみましょう。

$ hg rebase
nothing to rebase

rebaseする必要が無いと言われてしまいました。これはrebaseがリビジョン4とリビジョン3をリビジョン2に移動させようとしているからです。これは意図ではありませんし、公な履歴を改変しています。リビジョン2、リビジョン1を移動させる場合には次のコマンドを使います。

$ hg rebase --base 2 --dest 4
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  4:d4b4838eef58 | ex1-3-dest-base-or-source: LOCAL2
|
o  3:661066c37cfa | ex1-3-dest-base-or-source: LOCAL1
|
o  2:a188eaeb8f92 | ex1-3-dest-base-or-source: OTHER2
|
o  1:9cab071d1eff | ex1-3-dest-base-or-source: OTHER1
|
o  0:d3029e217ceb | ex1-3-dest-base-or-source: BASE

オプションはそれぞれ次の意味を持ちます。

  • --base そのリビジョンの祖先を移動させる。
    • (デフォルトでは現在のリビジョンが指定されていると考えるとわかりやすい)
  • --dest そのリビジョンの子孫として移動させる(移動先)

また、次のコマンドでも同じ意味になります。

$ hg rebase --source 1 --dest 4

オプションは次の意味を持ちます。

  • --source そのリビジョンの子孫を移動させる。

この例の通り、通常--baseと--sourceオプションはどちらか一方を指定します。

ex1-4-collapse(hg rebase --collapse)

rebaseのおまけ機能でrebaseするリビジョンを1つに圧縮します。ex1-4-collapseのリポジトリに移動して確認して見ましょう。

$ cd ex1-4-collapse
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o  4:b68c71c8948b | ex1-4-collapse: OTHER2
|
o  3:ef230d1fd291 | ex1-4-collapse: OTHER1
|
| @  2:3e41b228e009 | ex1-4-collapse: LOCAL2
| |
| o  1:3bd1d79bcd55 | ex1-4-collapse: LOCAL1
|/
o  0:5d14ee4686ba | ex1-4-collapse: BASE

この状態で--collapseオプションを付けてrebaseします。

$ hg rebase --collapse
(コミットログを入力)
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  3:1319e76ad21b | ex1-4-collapse: Collapsed revision
|
o  2:b68c71c8948b | ex1-4-collapse: OTHER2
|
o  1:ef230d1fd291 | ex1-4-collapse: OTHER1
|
o  0:5d14ee4686ba | ex1-4-collapse: BASE

リベースしたリビジョンは2つではなく1つになりました。

--collapseオプションの注意点
    • collapseオプションはある程度便利な機能ですが、rebaseの中でリビジョンを1つにするとコンフリクト時に混乱するので、rebaseとの併用はオススメしません。Collapse拡張などを利用してrebase後に実施するのをオススメします。

コンフリクトが発生したら(ex2-*)

rebase時にコンフリクトが発生したらどうするのでしょうか。ex2-conflictに移動して確認してみましょう。

$ cd ex2-conflict
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o  2:47a5a163dc57 | 2011-11-27 | ex2-conflict: EDIT2
|
| @  1:7f3aef9b8418 | 2011-11-27 | ex2-conflict: EDIT1
|/
o  0:2461f06b0aec | 2011-11-27 | ex2-conflict: BASE

この状態でrebaseします。

$ hg rebase
merging BASE
warning: conflicts during merge.
merging BASE failed!
abort: unresolved conflicts (see hg resolve, then hg rebase --continue)

失敗しました。無理矢理rebaseを再開してみましょう。

$ hg rebase --continue
abort: unresolved merge conflicts (see hg help resolve)

コンフリクトを解決しろ!と怒られてしまいましたね。諦めて解決しましょう。

$ emacs BASE
$ hg resolve -m BASE

コンフリクトを解決したので、改めてrebaseを再開します。

$ hg rebase --continue
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  2:c0d32db039b4 | ex2-conflict: EDIT1
|
o  1:47a5a163dc57 | ex2-conflict: EDIT2
|
o  0:2461f06b0aec | ex2-conflict: BASE

rebase出来ました。

マージもrebase(ex3-*)

ここまでの例でマージしていない履歴のrebaseについて説明してきました。ここでは「すでにマージしてしまったが、なんとかそこからrebaseしたい」場合にどうすればいいか説明します。

ex3-1-merge-only

一度マージしてしまった場合はどうなるのでしょうか?ex3-1-merge-onlyに移動して確認してみます。

$ cd ex3-1-merge-only
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o    5:6195cc3e01bf | ex3-1-merge-only: merge
|\
| o  4:5540efa50a68 | ex3-1-merge-only: OTHER2
| |
| o  3:d0bd3b8e7236 | ex3-1-merge-only: OTHER1
| |
@ |  2:16af0e39fbe7 | ex3-1-merge-only: LOCAL2
| |
o |  1:1fd3d89114bc | ex3-1-merge-only: LOCAL1
|/
o  0:25e0047eb7c3 | ex3-1-merge-only: BASE

この状態で単なるrebaseは行えません。Mercurialからすると「マージしているのだからrebaseは不要だろう」という話のようです。

$ hg rebase
nothing to rebase

こういう場合は--destで移動先を指定してrebaseしましょう。

$ hg rebase --dest 4
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  4:01f228c5f3a5 | ex3-1-merge-only: LOCAL2
|
o  3:248a46f623ba | ex3-1-merge-only: LOCAL1
|
o  2:5540efa50a68 | ex3-1-merge-only: OTHER2
|
o  1:d0bd3b8e7236 | ex3-1-merge-only: OTHER1
|
o  0:25e0047eb7c3 | ex3-1-merge-only: BASE

履歴が一直線になりました。

ex3-2-merge-and-commit-other

マージの後にコミットがある場合はどうなるでしょうか。ex3-2-merge-and-commit-otherはマージの後に他人がコミットをした場合の例です。

$ cd ex3-2-merge-and-commit-other
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o  7:f44c5f5c9660 | ex3-2-merge-and-commit-other: OTHER4
|
o  6:7ae5a6fd120a | ex3-2-merge-and-commit-other: OHTER3
|
| @  5:8e385de49ab5 | ex3-2-merge-and-commit-other: merge
|/|
o |  4:b1db10b3baff | ex3-2-merge-and-commit-other: OTHER2
| |
o |  3:fe08b9d4d925 | ex3-2-merge-and-commit-other: OTHER1
| |
| o  2:2ad0084b41ca | ex3-2-merge-and-commit-other: LOCAL2
| |
| o  1:c43a85c443bb | ex3-2-merge-and-commit-other: LOCAL1
|/
o  0:eba4416764f3 | ex3-2-merge-and-commit-other: BASE

実はこの例では特にオプションを指定せずにrebaseできます。

$ hg rebase
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  6:07d4081863b2 | ex3-2-merge-and-commit-other: LOCAL2
|
o  5:bb48a9b630a2 | ex3-2-merge-and-commit-other: LOCAL1
|
o  4:f44c5f5c9660 | ex3-2-merge-and-commit-other: OTHER4
|
o  3:7ae5a6fd120a | ex3-2-merge-and-commit-other: OHTER3
|
o  2:b1db10b3baff | ex3-2-merge-and-commit-other: OTHER2
|
o  1:fe08b9d4d925 | ex3-2-merge-and-commit-other: OTHER1
|
o  0:eba4416764f3 | ex3-2-merge-and-commit-other: BASE

さっぱりしました。

ex3-3-merge-and-commit-local

先ほどの例では、他人の変更がありましたが、自分の変更が有った場合はどうなるのでしょうか。ex3-3-merge-and-commit-localで確認します。

$ cd ex3-3-merge-and-commit-local
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  7:09def2675f8b | ex3-3-merge-and-commit-local: LOCAL4
|
o  6:a92ddb85a40d | ex3-3-merge-and-commit-local: LOCAL3
|
o    5:ffe8d1f24bed | ex3-3-merge-and-commit-local: merge
|\
| o  4:1c187ad5e5c1 | ex3-3-merge-and-commit-local: OTHER2
| |
| o  3:442140de6bfe | ex3-3-merge-and-commit-local: OTHER1
| |
o |  2:82bb3ae2f235 | ex3-3-merge-and-commit-local: LOCAL2
| |
o |  1:9ad889977f9e | ex3-3-merge-and-commit-local: LOCAL1
|/
o  0:158e95d07f09 | ex3-3-merge-and-commit-local: BASE

この場合、現在のリビジョンには全ての履歴が含まれているため単にrebase出来ません。
rebaseするためには--sourceと--destを指定します。

$ hg rebase --source 1 --dest 4
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  6:c7d3d06c9d5b | ex3-3-merge-and-commit-local: LOCAL4
|
o  5:0fee4e43d5fc | ex3-3-merge-and-commit-local: LOCAL3
|
o  4:2c9cd5e17396 | ex3-3-merge-and-commit-local: LOCAL2
|
o  3:74cba3f9f1ad | ex3-3-merge-and-commit-local: LOCAL1
|
o  2:1c187ad5e5c1 | ex3-3-merge-and-commit-local: OTHER2
|
o  1:442140de6bfe | ex3-3-merge-and-commit-local: OTHER1
|
o  0:158e95d07f09 | ex3-3-merge-and-commit-local: BASE

LOCALのリビジョンが全て移動しました。

ブランチ名の消失とその対策(ex4-*)

今までの例は全て同じブランチ名での話でした。ブランチ名が設定されている場合のrebaseの挙動についても確認しましょう。利用するリポジトリのリビジョングラフです。

o  7:e434c3378e3c | ex4-1-disappear-branch: OTHER4
|
o  6:770f9163e9c0 | ex4-1-disappear-branch: OTHER3
|
| @  5:6c621e72a1de | ex4-1-disappear-branch: merge with issue1
|/|
o |  4:a630160bf182 | ex4-1-disappear-branch: OTHER2
| |
o |  3:3ce08a627ab3 | ex4-1-disappear-branch: OTHER1
| |
| o  2:18c0f1d3c1f4 | ex4-1-disappear-branch: ISSUE2
| |  issue1
| o  1:d35b90f482d7 | ex4-1-disappear-branch: ISSUE1
|/   issue1
o  0:c9570bab2649 | ex4-1-disappear-branch: BASE

issue1という名前のブランチが存在します。

ex4-1-disappear-branch

では早速移動してrebaseしてみましょう。

$ cd ex4-1-disappear-branch
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o  7:e434c3378e3c | ex4-1-disappear-branch: OTHER4
|
o  6:770f9163e9c0 | ex4-1-disappear-branch: OTHER3
|
| @  5:6c621e72a1de | ex4-1-disappear-branch: merge with issue1
|/|
o |  4:a630160bf182 | ex4-1-disappear-branch: OTHER2
| |
o |  3:3ce08a627ab3 | ex4-1-disappear-branch: OTHER1
| |
| o  2:18c0f1d3c1f4 | ex4-1-disappear-branch: ISSUE2
| |  issue1
| o  1:d35b90f482d7 | ex4-1-disappear-branch: ISSUE1
|/   issue1
o  0:c9570bab2649 | ex4-1-disappear-branch: BASE

このリビジョングラフは ex3-2-merge-and-commit-other の場合と同じ形をしたリビジョングラフです。さくっとrebaseします。

$ hg rebase
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  6:4f6ab31cd614 | ex4-1-disappear-branch: ISSUE2
|
o  5:0d589fa6bed0 | ex4-1-disappear-branch: ISSUE1
|
o  4:e434c3378e3c | ex4-1-disappear-branch: OTHER4
|
o  3:770f9163e9c0 | ex4-1-disappear-branch: OTHER3
|
o  2:a630160bf182 | ex4-1-disappear-branch: OTHER2
|
o  1:3ce08a627ab3 | ex4-1-disappear-branch: OTHER1
|
o  0:c9570bab2649 | ex4-1-disappear-branch: BASE

履歴は一直線になりましたが、よく見るとissue1ブランチは消失しています。。。

ex4-2-keep-branch(hg rebase --keepbranches)

ex4-1-disappear-branchではrebase時にブランチ名が消え去ってしまいました。rebaseにはこういうときの為のオプションが存在します。ex4-2-keep-branchで試してみましょう。

$ cd ex4-2-keep-branch
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o  7:5e2c83b16d0a | ex4-2-keep-branch: OTHER4
|
o  6:e0d40631b9ed | ex4-2-keep-branch: OTHER3
|
| @  5:64d54c63c521 | ex4-2-keep-branch: merge with issue1
|/|
o |  4:8dca37a7bbe7 | ex4-2-keep-branch: OTHER2
| |
o |  3:6f0d95c11e29 | ex4-2-keep-branch: OTHER1
| |
| o  2:38e71c3d3bcb | ex4-2-keep-branch: ISSUE2
| |  issue1
| o  1:4ed2f9faf071 | ex4-2-keep-branch: ISSUE1
|/   issue1
o  0:11ee52fb0f36 | ex4-2-keep-branch: BASE

ここで--keepbranchesオプションを付けてrebaseしてみましょう。

$ hg rebase --keepbranches
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  6:2d926a11a853 | ex4-2-keep-branch: ISSUE2
|  issue1
o  5:8e1a12218fcc | ex4-2-keep-branch: ISSUE1
|  issue1
o  4:5e2c83b16d0a | ex4-2-keep-branch: OTHER4
|
o  3:e0d40631b9ed | ex4-2-keep-branch: OTHER3
|
o  2:8dca37a7bbe7 | ex4-2-keep-branch: OTHER2
|
o  1:6f0d95c11e29 | ex4-2-keep-branch: OTHER1
|
o  0:11ee52fb0f36 | ex4-2-keep-branch: BASE

ブランチ名が維持されました!

ブランチ名の問題はイシュー毎にブランチを切る運用の中でrebaseを利用すると発生しがちな問題です。何をrebaseしたいのか、ブランチ名を維持したいのかを確認して、適切なオプションを利用しましょう。

いろいろ複雑な例(ex5-*)

いろいろ複雑な例を挙げていくときりが無いので、RebaseExtension - Mercurialに有るScenario Bのrebaseを体験してみましょう。利用するリポジトリのリビジョングラフです。

o  8:ac54da325bfd | ex5-1-complex-first-commit: OTHER4
|
| @  7:14600c379a82 | ex5-1-complex-first-commit: merge
|/|
| o  6:8793ad44c285 | ex5-1-complex-first-commit: LOCAL2
| |
o |  5:960e7e1e9636 | ex5-1-complex-first-commit: OTHER3
| |
o |  4:ebcbc820a711 | ex5-1-complex-first-commit: OTHER2
| |
| o  3:0fae7a4e9b6a | ex5-1-complex-first-commit: merge
|/|
o |  2:ee28f1382c84 | ex5-1-complex-first-commit: OTHER1
| |
| o  1:f5fac891ec97 | ex5-1-complex-first-commit: LOCAL1
|/
o  0:f1be500ed8d5 | ex5-1-complex-first-commit: BASE
ex5-1-complex-first-commit

ex5-1-complex-first-commitはScenario Bでいくと、「rebase B on I」に当たる例です。

$ cd ex5-1-complex-first-commit
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o  8:ac54da325bfd | ex5-1-complex-first-commit: OTHER4
|
| @  7:14600c379a82 | ex5-1-complex-first-commit: merge
|/|
| o  6:8793ad44c285 | ex5-1-complex-first-commit: LOCAL2
| |
o |  5:960e7e1e9636 | ex5-1-complex-first-commit: OTHER3
| |
o |  4:ebcbc820a711 | ex5-1-complex-first-commit: OTHER2
| |
| o  3:0fae7a4e9b6a | ex5-1-complex-first-commit: merge
|/|
o |  2:ee28f1382c84 | ex5-1-complex-first-commit: OTHER1
| |
| o  1:f5fac891ec97 | ex5-1-complex-first-commit: LOCAL1
|/
o  0:f1be500ed8d5 | ex5-1-complex-first-commit: BASE

rebaseします。

$ hg rebase --source 1
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  6:799628cbc3a7 | ex5-1-complex-first-commit: LOCAL2
|
o  5:8d465f766047 | ex5-1-complex-first-commit: LOCAL1
|
o  4:ac54da325bfd | ex5-1-complex-first-commit: OTHER4
|
o  3:960e7e1e9636 | ex5-1-complex-first-commit: OTHER3
|
o  2:ebcbc820a711 | ex5-1-complex-first-commit: OTHER2
|
o  1:ee28f1382c84 | ex5-1-complex-first-commit: OTHER1
|
o  0:f1be500ed8d5 | ex5-1-complex-first-commit: BASE

履歴が一直線になる気持ちいいrebaseです。

ex5-2-complex-first-merge

ex5-2-complex-first-mergeはScenario Bでいくと、「rebase D on I」に当たる例です。

$ cd ex5-2-complex-first-merge
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o  8:1af1cf0d9dfb | ex5-2-complex-first-merge: OTHER4
|
| @  7:a452eed2b6ce | ex5-2-complex-first-merge: merge
|/|
| o  6:31f7eab4334c | ex5-2-complex-first-merge: LOCAL2
| |
o |  5:96a6abebd61e | ex5-2-complex-first-merge: OTHER3
| |
o |  4:c12d26b83797 | ex5-2-complex-first-merge: OTHER2
| |
| o  3:0399df38f6c4 | ex5-2-complex-first-merge: merge
|/|
o |  2:1ab0ff0e5c74 | ex5-2-complex-first-merge: OTHER1
| |
| o  1:35825d373efd | ex5-2-complex-first-merge: LOCAL1
|/
o  0:5ad3df58947e | ex5-2-complex-first-merge: BASE

rebaseします。

$ hg rebase --source 3
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  7:e7abf0748c53 | ex5-2-complex-first-merge: LOCAL2
|
o    6:d63291717231 | ex5-2-complex-first-merge: merge
|\
| o  5:1af1cf0d9dfb | ex5-2-complex-first-merge: OTHER4
| |
| o  4:96a6abebd61e | ex5-2-complex-first-merge: OTHER3
| |
| o  3:c12d26b83797 | ex5-2-complex-first-merge: OTHER2
| |
| o  2:1ab0ff0e5c74 | ex5-2-complex-first-merge: OTHER1
| |
o |  1:35825d373efd | ex5-2-complex-first-merge: LOCAL1
|/
o  0:5ad3df58947e | ex5-2-complex-first-merge: BASE

リビジョンが一つ減りました。LOCAL1が中央リポジトリに反映済みの場合は、この形になる(一見中途半端な)rebaseを利用する事があります。

ex5-3-complex-second-commit

ex5-2-complex-first-mergeはScenario Bでいくと、「rebase G on I」に当たる例です。

$ cd ex5-3-complex-second-commit
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o  8:420d1982a638 | ex5-3-complex-second-commit: OTHER4
|
| @  7:affb911b9482 | ex5-3-complex-second-commit: merge
|/|
| o  6:bb617e15abd0 | ex5-3-complex-second-commit: LOCAL2
| |
o |  5:846d860e63cf | ex5-3-complex-second-commit: OTHER3
| |
o |  4:377a3a13e147 | ex5-3-complex-second-commit: OTHER2
| |
| o  3:045472d4d43d | ex5-3-complex-second-commit: merge
|/|
o |  2:2630a7c2ffef | ex5-3-complex-second-commit: OTHER1
| |
| o  1:daaa64bde5f8 | ex5-3-complex-second-commit: LOCAL1
|/
o  0:cb03c8b6937a | ex5-3-complex-second-commit: BASE

rebaseします。

$ hg rebase --source 6
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@    7:1de16daccb66 | ex5-3-complex-second-commit: LOCAL2
|\
| o  6:420d1982a638 | ex5-3-complex-second-commit: OTHER4
| |
| o  5:846d860e63cf | ex5-3-complex-second-commit: OTHER3
| |
| o  4:377a3a13e147 | ex5-3-complex-second-commit: OTHER2
| |
o |  3:045472d4d43d | ex5-3-complex-second-commit: merge
|\|
| o  2:2630a7c2ffef | ex5-3-complex-second-commit: OTHER1
| |
o |  1:daaa64bde5f8 | ex5-3-complex-second-commit: LOCAL1
|/
o  0:cb03c8b6937a | ex5-3-complex-second-commit: BASE

あまり良くないリビジョンが出来てしまいました。というのも、リビジョン7は「LOCAL2の変更を含み、かつマージの変更を含む」リビジョンです。

ex5-4-complex-second-commit-detach

ex5-3-complex-second-commitではとても微妙なリビジョンが出来てしまいましたが、Mercurialのrebaseにはこの微妙なリビジョンの為のオプション --detachが存在します。
ex5-4-complex-second-commit-detachはScenario Bでいくと、「rebase G on I(detach)」に当たる例です。

$ cd ex5-4-complex-second-commit-detach
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
o  8:1510c11fdc49 | ex5-4-complex-second-commit-detach: OTHER4
|
| @  7:0bd62c642c81 | ex5-4-complex-second-commit-detach: merge
|/|
| o  6:c6e6de38d551 | ex5-4-complex-second-commit-detach: LOCAL2
| |
o |  5:4bbfa5821baf | ex5-4-complex-second-commit-detach: OTHER3
| |
o |  4:a70604d54f76 | ex5-4-complex-second-commit-detach: OTHER2
| |
| o  3:645d26bce2f1 | ex5-4-complex-second-commit-detach: merge
|/|
o |  2:fe23c998abd0 | ex5-4-complex-second-commit-detach: OTHER1
| |
| o  1:82599e0a5994 | ex5-4-complex-second-commit-detach: LOCAL1
|/
o  0:5e7a0fd9a07a | ex5-4-complex-second-commit-detach: BASE

--detachオプション付きrebaseします。

$ hg rebase --source 6 --detach
$ hg glog --template '{rev}:{node|short} | {author|user}: {desc|strip|firstline}\n{branches}\n'
@  7:f206ce0866f7 | ex5-4-complex-second-commit-detach: LOCAL2
|
o  6:1510c11fdc49 | ex5-4-complex-second-commit-detach: OTHER4
|
o  5:4bbfa5821baf | ex5-4-complex-second-commit-detach: OTHER3
|
o  4:a70604d54f76 | ex5-4-complex-second-commit-detach: OTHER2
|
| o  3:645d26bce2f1 | ex5-4-complex-second-commit-detach: merge
|/|
o |  2:fe23c998abd0 | ex5-4-complex-second-commit-detach: OTHER1
| |
| o  1:82599e0a5994 | ex5-4-complex-second-commit-detach: LOCAL1
|/
o  0:5e7a0fd9a07a | ex5-4-complex-second-commit-detach: BASE

今度はLOCAL2のみのマージを含まないリビジョンが作成されました。こちらの方がrebaseの意図通りです。

--detachの注意点

これも--collapseと同じでかなり危険な機能です。というのも、これがうまくいく為にはLOCAL2がLOCAL1の変更に影響されない、独立した変更であることが要求されます。切り離し行う場合はrebaseではなくMQ拡張やMQ拡張とTransplant拡張を組み合わせて利用した方が安全に履歴改変を行えます。

まとめ

rebaseの代表的な使い方を紹介しました。これ以外にも適応出来る場面はたくさんあると思いますが、用法用量を守って履歴改変をしていきましょう。またrebaseだけが履歴改変の手段ではありません。様々な拡張(=MQ、Transplant、Collapse、etc)も駆使して履歴改変をしていきましょう。