彷徨えるフジワラ

年がら年中さまよってます

作業途中成果の push 防止

TokyoMercurial #3 の懇親会において、@cointoss1973 氏から以下の様な相談が:

MQ パッチを "qpop -a" して、再度 "qpush -a" する際に、適用先リビジョンが変わってしまうのは、なんとかならないだろうか?

最初に相談された時には、何の事やらワケワカで「???? 」な状態だったのだが、お互いの前提条件に関して、色々刷り合わせを行った結果、以下の様なユースケースであることが判明:

  1. ローカルリポジトリの最新版を A-base とする
  2. MQ を使って、A-base を元に機能 A に関して作業を実施
  3. 優先度の高い機能 B の作業が、割り込んで来る
  4. 機能 A の MQ パッチを一旦 "qpop -a"
  5. 共有リポジトリから最新成果を pull + 作業領域の update
  6. この時点の最新版を B-base とする
  7. B-base を元に機能 B の作業を実施 ⇒ 完了(commit) ⇒ push
  8. 機能 A の作業を再開しようとしても、メモしてないと A-base が不明
  9. B-base に対して "qpush -a" すると衝突発生の可能性が…
  10. でも "qpop -a" でパッチを外しておかないと、(7) で中途成果が push されてしまう
  11. さて、どうしたものか?

@cointoss1973 氏の当初の要望としては:

MQ が適用先リビジョンに関する情報を覚えておいて欲しい or それに類する機能は無いか?

というものだったのだが、それをやってしまうと、折角の『任意のリビジョンに移動して適用可能』という MQ の特性が失われてしまうので、ここは以下のように発想を変える必要がある。

やりかけの途中成果を push 対象から外す方法は無いか?

以下は『仕組み』と『運用』のそれぞれによる実現方法。

ちなみに、(9) で衝突の可能性がある場合は、再開した機能 A の作業を統合する際に、rebase するにしろ、merge するにしろ、衝突が発生するのは変わらないので、まぁ、その辺の手間は覚悟が必要。

『仕組み』による防止

『仕組み』による push/pull の防止は、まさに 2.1 で導入された phase 機構の出番と言える。

MQ を使用するなら以下の設定は必須と言えよう。

[mq]
secret = True

この設定により、"hg qpush" で適用された MQ パッチによるリビジョンは、phase が secret 化されるため、push/pull の対象から除外される。

但し、この設定は『設定後に適用された MQ パッチ』に対して有効なので、既にパッチを適用済みの場合は、設定後に一旦パッチ適用を解除 ⇒ 再適用の必要がある。

また、MQ を使わない場合でも、"hg phase [-p|-d|-s] REV...." コマンド実行により、手動での phase 変更が可能なので、『実験的な作業』の際にあらかじめ phase を secret にしておけば、そのリビジョンを起点とした以後の作業成果は push/pull 対象から除外される。

なお、『secret の子リビジョンは secret』の原則があるので、phase が secert な枝と public/draft な枝のマージでは、マージリビジョンは自動的に secret になるので注意。マージリビジョンを public/draft 化しようとすると、その祖先である secret にしておきたいリビジョンまで public/draft 化されてしまう。

ちなみに、自分が作業する場合は、以下の理由から、コードの特性(バグ修正/新規機能/実験的変更 etc)に関わらず、普通にコミットする方が多かった気がする。

名前無しブランチとして枝分かれ (= マージされていない) していて、共有リポジトリに push する前なら、事後に何とでもなる (= MQ 取り込み/graft/rebase 等) ので、事前に『パッチとして利用』な事が確定している場合でもなければ、通常リビジョンとして普通にコミット

枝分かれしていれば、『ヘッドを増やす push は許さんのじゃぁ、われぇ!?』(意訳)ガードが働くので、うっかり push してしまう事も無いしね。

『運用』による防止

先のユースケースでは、push したい作業成果は『B-base を元に機能 B の作業を実施』したリビジョンだけの筈。

『機能 B の作業を実施』した場合、何らかの commit リビジョンが新規作成されているわけだから:

  • 『機能 B の作業を実施』したリビジョン群と、MQ 管理下の『機能 A の作業を実施』しているリビジョン群は、名前無しブランチとして枝分かれしている
  • 作業領域の親リビジョンは『機能 B の作業を実施』した側のブランチ

この場合、push 対象リビジョンとして『作業領域の親リビジョンに連なるリビジョンだけ』を以下のように指定すれば良い:

$ hg push -r .

id:troter 氏が度々 alias 指定をお勧めしている "nudge" コマンドは、まさにこのリビジョン指定での push のこと。