Rebase エクステンション
現在、このエクステンションは Mercurial とともに配布されています。
作者: Stefano Tortarolo
設定
設定ファイル(例えば .hg/hgrc)でエクステンションを有効にしてください:
[extensions] rebase =
はじめに
プロジェクトに貢献するときに、いくつかのパッチは非公開にしたまま、リポジトリ全体を最新の状態に保ちたい場合があります。
そういう場合には、一度手元の変更を"デタッチ"して、リポジトリをメインストリームと同期したあと、手元の変更をリモートから取得した最新の変更の上に追加するのが有効です。この手順を rebase(リベース) と呼びます。
一般に、このエクステンションを使って、リビジョンをある地点から別の地点に移動することができます。良くあるパターンを、"シナリオ" セクションで説明しています。
機能
- 単純なリベース、複雑なリベース
- 中断したリベースの中止
- 中断したリベースの再開
- 中断時の変更の検出
使い方
概要
hg rebase [--source REV | --base REV] [--dest REV] [--collapse] [--detach] [--keep] [--keepbranches] | [--continue] | [--abort]
説明
--source rev
- 指定したリビジョンとその子孫リビジョンがリベース先に移動されます。
--base rev
- 指定したリビジョンとリベース先リビジョンの共通の祖先から、指定したリビジョンおよびその子孫リビジョンが、リベースの対象になります。(共通の祖先自身は除きます)
--sourceと同時に指定することはできないことに注意してください
- 指定したリビジョンとリベース先リビジョンの共通の祖先から、指定したリビジョンおよびその子孫リビジョンが、リベースの対象になります。(共通の祖先自身は除きます)
--dest rev
- 指定されたリビジョンの上に、リベース対象のリビジョンが移動されます。
--continue
- 中断したリベースを再開します。
--abort
- 中断したリベースを中止します。
--collapse
- リベースされるリビジョンをまとめます。
--keep
- 元のリビジョンをリベース後も残します。
--keepbranches
- 元のブランチ名を引き継ぎます。
--detach (Mercurial 1.6以降)
- リベース対象のリビジョンを強制的に元のブランチから切り離します。
訳注:このオプションは、現在非推奨です。
- リベース対象のリビジョンを強制的に元のブランチから切り離します。
pullコマンドとの統合
このエクステンションによって、pullコマンドにオプションが追加されます。
hg pull --rebase
これを指定すると、もしリベースするべきリビジョンが手元にある場合はプル後にリベースされ、なければhg pull --updateと同様の動作をします。
一般的なケース
このエクステンションを引数なしで実行することができるというのは重要なポイントです。
意味的には、引数なしのリベースは、 自分が今作業しているブランチを取って、正しい状態にする ことであると言えます。言い換えると、手元で行った変更を、チェックアウト中の名前つきブランチの最新HEAD上に移動することを意味します。
次のようなシチュエーションを考えてみましょう。:
L* は、最後にプルしてから手元で行った変更を表しています。
hg pull
メインストリームから、新しいリビジョンを2つプルしました。
たいてい、ここでやりたいのは L* を R2 の上に移動することです。それは、以下のように簡単にできます。:
hg rebase
結果:
Note: 前述の通り, これは hg pull --rebase で1アクションで実行できます。
衝突するマージの扱い
L* における変更が R* における変更と衝突することがあります。その場合、エクステンションは停止し、現在の状態を保存して、ユーザ自身が衝突を解消できるようにします。
リベースが中断したときには、2つの選択肢があります。:
- 中止
- 続行
中止
以下のコマンドで、中断されている処理は中止され、リポジトリは元の状態に戻ります。:
$ hg rebase --abort
続行
しかし、中断されている処理を再開する方が一般的です。それは以下のコマンドで出来ます:
$ hg rebase --continue
リベースが出来ない場合
リベースができないシチュエーションがいくつかあります。:
- リベースポイント(リベース元)がリベース先の祖先の場合
- リベースポイント(リベース元)がリベース先の子孫の場合
この場合は、 transplant や strip コマンドを使う必要があります。例えば:
hg up -r <targetrev> # Needed because the transplant -m option doesn't work hg transplant <source>:tip hg strip <source>
- リベース元(source)がマージリビジョンで、その親がどちらも外部にある場合
MQパッチに関する注意事項
現在の実装では、MQパッチはqfinishされ、リベース後にqimportされます。これによって、exportのようなヘッダがリベースされたパッチに追加されます。例えば、
- 元のパッチ:
Description P0 diff --git a/f b/f etc...
- リベース後のパッチ:
# HG changeset patch # User Stefano Tortarolo <stefano.tortarolo@gmail.com> # Date 1217929313 -7200 # Node ID 92bd85e9196feac01fdf2eb2ce7275e9a575a730 # Parent 6e55161e68b2062d629c05b89b0ea3424eec9a2f Description P0 diff --git a/f b/f etc...
シナリオ
ここからは、もっと興味深いシナリオを分析していきましょう。
シナリオ A
最初は、もっとも単純なパターンの、シンプルブランチです。
このシナリオでは、2つの興味深い相互作用があります。
トップへのリベース
$ hg up C $ hg rebase -d E
以下のやり方でも同じ結果になります。:
$ hg up E $ hg rebase -s C
中間リビジョンへのリベース
$ hg up C $ hg rebase -d D
シナリオ B
2番目のシナリオには、もっと複雑なパターンが含まれます。このシナリオでは、アップストリームからクローンし、何度かマージを行っています。
Iの上にDをリベース
D はマージリビジョンですが、 H と違って スキップ されません。
Iの上にBをリベース
- この場合は、2つのリビジョン(D と H)はスキップされます。
Bの上にCをリベース
Iの上にGをリベース
Note: 親がリベース先の祖先の場合に 限り 、リベースによって親との関係が削除されます。 --detach オプションを指定すると、強制的にこの関係を削除します。
シナリオ C
このケースは、リポジトリがひとつだけの(マージされた)HEADを持つという、非常に一般的なシチュエーションを表しています。
Cの上にDをリベース
- 明らかに、リビジョン F はスキップされています。
集約
複数のチェンジセットを、ひとつだけのリビジョンとして別のブランチにリベースできると便利なこともあります。
これは --collapse を指定すればできます。
Bの上にCを集約してリベース
詳細
親リビジョンとの関係付け
与えられたノード(N) をリベースする際に、その親の状態によってことなる結果になることがあります。
ここからは、 P1N は N の一つ目の親、 P2N は二つ目の親を指すものとします。
例えば、 P1'N は、リベースされた N の一つ目の親です。
それらのシチュエーションは、以下の表にまとめられます。:
|
P2N = A |
P2N = S |
P2N = E |
P2N = N |
||||
P1N = A |
|
p1 = P2'N |
p1 = target, p2 = P2N |
p1 = target |
||||
P1N = S |
p1 = P1'N |
p1 = P1'N, p2 = P2'N |
p1 = P1'N, p2 = P2N |
p1 = P1'N |
||||
P1N = E |
p1 = target, p2 = P1N |
p1 = P2'N, p2 = P1N |
|
p1 = target, p2 = P1N |
A: リベース先の祖先 S: リベース対象内 E: 外部 N: なし
空のセルは、以下の場合にあたります。:
P1N = P2N = A は、 N もリベース先の祖先であることを意味しており、これはリベースできないパターンです。
P1N = P2N = E は、 N がマージされたリビジョンで、かつその親が両方ともリベース先の祖先でないことを意味しており、これもリベースできないパターンです。(アイデア: もっといいリビジョン位置を仮定することは可能?) このケースは N がリベースポイントの場合にだけ発生することに注意してください。
以下のことも注意してください:
P1N = None の場合、必然的に P2N = None になります。
P1N = P2N = None になるのは、 N がルートの場合だけです。(この場合は、ノードを自分の子孫の上にリベースできないというルールにより禁止されます)
コマンドのヘルプ
Mercurial 2.0では、以下がrebaseコマンドの公式なドキュメントです。
Rebase uses repeated merging to graft changesets from one part of history (the source) onto another (the destination). This can be useful for linearizing *local* changes relative to a master development tree. You should not rebase changesets that have already been shared with others. Doing so will force everybody else to perform the same rebase or they will end up with duplicated changesets after pulling in your rebased changesets. If you don't specify a destination changeset ("-d/--dest"), rebase uses the tipmost head of the current named branch as the destination. (The destination changeset is not modified by rebasing, but new changesets are added as its descendants.) You can specify which changesets to rebase in two ways: as a "source" changeset or as a "base" changeset. Both are shorthand for a topologically related set of changesets (the "source branch"). If you specify source ("-s/--source"), rebase will rebase that changeset and all of its descendants onto dest. If you specify base ("-b/--base"), rebase will select ancestors of base back to but not including the common ancestor with dest. Thus, "-b" is less precise but more convenient than "-s": you can specify any changeset in the source branch, and rebase will select the whole branch. If you specify neither "-s" nor "-b", rebase uses the parent of the working directory as the base. By default, rebase recreates the changesets in the source branch as descendants of dest and then destroys the originals. Use "--keep" to preserve the original source changesets. Some changesets in the source branch (e.g. merges from the destination branch) may be dropped if they no longer contribute any change. One result of the rules for selecting the destination changeset and source branch is that, unlike "merge", rebase will do nothing if you are at the latest (tipmost) head of a named branch with two heads. You need to explicitly specify source and/or destination (or "update" to the other head, if it's the head of the intended source branch). If a rebase is interrupted to manually resolve a merge, it can be continued with --continue/-c or aborted with --abort/-a.
関連リンク
RebaseIfExtension - (Mercurialに同梱はされていない)別のエクステンション。衝突がない場合だけリベースを行い、衝突するときはマージを行う。