ファイルの管理を指示しない限り、リポジトリ中のファイルに対して Mercurial は何も行いません。“hg status” コマンドは、 Mercurial の管理下に無いファイルを “?” を表示することで知らせてくれます
Mercurial による構成管理を指示するには、“hg add” コマンドを使用します。ファイルの構成管理を指示したファイルの“hg status” による表示は、 “?” から “A” へと変化します。
“hg commit” を実行した直後は、コミット前に追加したファイルが“hg status” により表示されることはありません。これは、 “興味深い” ファイル — 変更したり、 Mercurial に何らかの操作を要求したファイル — について表示するのが“hg status”の役割だからです。数千のファイルから成るリポジトリがある場合、構成管理されてはいても特に変更されていない ファイルの一覧(後述するように、そのようなファイル一覧の情報を得ることもできます)を欲しいと思うことは稀です。
一旦ファイルを追加したとしても、そのファイルに対して Mercurial はすぐには何も行いません。その代わり、次にコミットを行った際にファイル状態のスナップショットを作成します。 Mercurial はそれ以降、構成管理下から除外するまで、コミットの際には常に当該ファイルの変更状況を確認します。
Mercurial の有用な振る舞いとして、 Mercurial のコマンドにディレクトリ名を指定した場合、その指定を “当該ディレクトリ配下の全ての ファイル1 に対する操作の実施 ” が要求されたものとみなします。
先の例で aファイルを構成管理対象に追加した際には、 Mercurial は追加されたファイルのファイル名を表示していませんが、この例では構成管理対象に追加されたファイルを表示している点に注意してください。
先の例では、追加するファイル名をコマンドラインで明示的に指定しましたので、そのような場合は利用者自身が自分の振る舞いを 理解しているものとみなし、 Mercurial は何も表示しません。
しかし、ディレクトリ名を指定することでファイル名を暗示した場合、 Mercurial は特別に操作対象となった個々のファイル名を表示します。こうすることで何が実施されたのかが明確になるため、ひっそりとやっかいな問題が発生する可能性を低減します。この振る 舞いは殆どの Mercurial コマンドに共通しています。
ディレクトリは Mercurial による構成管理の対象にはなりません。その代わり、 Mercurial はファイルのパスを構成管理します。ファ イルの生成の際には、それに先立ってパスに含まれる存在しないディレクトリを全て作成します。ファイルの削除の際には、削除 されたファイルへのパスに含まれる空ディレクトリを全て削除します。たわいも無いことに聞こえるかもしれません が、 Mercurial が完全に空っぽのディレクトリを取り扱えない、という小さいながらも実用上重大な性質を示していま す。
空のディレクトリが有用なことは滅多に無いですし、妥当な効果を得るための控えめな回避方法があります。 Empty directories are rarely useful, and there are unintrusive workarounds that you can use to achieve an appropriate effect. それ故に、空のディレ クトリを扱うことによる限定的な有益性が、それに必要とされる複雑さに見合うものではない、と Mercurial の開発陣は判断しまし た。
空のディレクトリをリポジトリで管理したい場合、複数の実現方法があります。1つは当該ディレクトリ直下の “隠し” ファイル を“hg add” することです。 UNIX ライクなシステムでは、ピリオド(“.”)で始まる名前のファイルは、殆どのコマンドや GUI ツー ルから隠しファイルとして扱われます。この手法を図 5.1に示します。
空ディレクトリを必要とする場合のもう一つの解決方法は、自動化されたビルドスクリプトで必要になる都度作成する、というものです。
リポジトリにとって不要になった2 ファ イルがある場合は、“hg remove” コマンドを使用ます。このコマンドはファイルを削除しつつ、 Mercurial に構成管理対象 からファイルを除外する旨を通知します。削除されたファイルは、“hg status” の出力では “R” 付きで表示されま す。
“hg remove” によるファイルの削除を行うと、作業領域ディレクトリに同名のファイルを再度作成したとしても、 Mercurial はそ のファイルを構成管理対象から除外します。同名ファイルを再生成し Mercurial による構成管理を行う場合には、単純にそのファイル を “hg add” してください。 Mercurial は新規に管理対象に加えられたファイルが、以前管理していた同名のファイルとは無関係であ るとみなします。
重要な事ですので、“hg remove” コマンドによる操作が持つ影響は2つだけである、と理解してください。
“hg remove” コマンドによる操作は、ファイルの変更履歴には一切変更を加えません。
作業領域ディレクトリを“hg remove” で削除したファイルがまだ構成管理されていた時点のチェンジセットで更新した場合、その チェンジセットがコミットされた時点の内容で、作業領域ディレクトリに当該ファイルが再生成されます。その後で、当該ファイルが “hg remove” で削除された時点のチェンジセットで更新すると、 Mercurial は再び当該ファイルを作業領域から削除しま す。
“hg remove” コマンドを使用せずに作業領域ディレクトリから削除したファイルを、 Mercurial は行方不明とみなします。行方不明の ファイルは、“hg status” の出力では “!” 付きで表示されます。 Mercurial のコマンド群全般は、行方不明のファイルに関しては何も 行いません。
“hg status” が行方不明として表示するファイルがリポジトリ中にある場 合3 、ファ イル削除後の任意の時点で“hg remove --after” を実行することで当該ファイルを構成管理対象から除外する意思があることを Mercurial に通知することができます。
その一方で、行方不明とされているファイルが意図せずに削除してしまったものなら、“hg revert” に当該ファイル名を指定する ことで、変更されていない状態にファイルを復旧することができます。
ファイル削除の意思表示を一々 Mercurial に示す必要性について、疑問に思われるかもしれません。 Mercurial の開発初期における削 除方法は、そのように思う人にとっては望ましいものかもしれません。 Mercurial は“hg commit” コマンド実行時にファイルの不在 を自動的に検知し、当該ファイルを構成管理対象から除外していたのです。実際問題、この削除方法では、不慮の事態で通知も無くファ イルが削除される事態が容易に起こり得ます。
Mercurial は、構成管理対象へのファイルの追加と除外を行う、組み合わせコマンドである“hg addremove” を提供していま す。
“hg commit” コマンドも、コミット実施の直前に“hg addremove” と同じ方針で構成管理対象への追加/除外を行う-A オプション を提供しています。
Mercurial はファイルの複製を行う“hg copy” コマンドを提供しています。このコマンドでファイルを複製した場合、 Mercurial はそ のファイルが元ファイルの複製であることを記録します。チェンジセットのマージの際には、 Mercurial はこの複製ファイルを特別扱 いします。
複製ファイルのマージの際には、変更内容が複製ファイルまで “追従” してきます。このことが持つ意味を上手く説明するた めに、簡単な例を作成しましょう。これまでの例と同様に、1つだけファイルを持つ簡易的なリポジトリを作成しま す。
マージを行うためには、別々の作業を平行して行う必要がありますので、リポジトリを複製しましょう。
最初のリポジトリに戻り、“hg copy” コマンドで最初に作成したファイルを複製します。
複製後の“hg status” コマンドの出力では、複製されたファイルは単に追加された普通のファイルと同じように見えま す。
しかし-C オプション付きで“hg status” を実行することで、別な行が表示されます。この行は、新たに追加されたファイルの複製 元であることを意味します。
複製したリポジトリに戻り、平行して変更作業を行います。複製元になったファイルに対して行を追加します。
このリポジトリでは複製元の file が変更されました。最初のリポジトリから変更内容を“hg pull” して2つの head を マージする際に Mercurial は、file に対してだけ行った変更内容を、その複製である new-file にまで伝播させま す。
ファイルの複製に対してる変更が伝播される挙動は、難解に思えるかもしれませんが、多くの場合は非常に好ましい振る舞いとなりま す。
まずは、この伝播がマージの時だけに行われる、ということに注意してください。ファイルを“hg copy” で複製し、それに引き続 き複製元ファイルを変更する、という通所の作業においては何も特別なことは行われません。
もう一点、変更を取り込んだリポジトリが、ファイルを複製したことを知らなかった場合に限り、変更内容が複製先ファイルに伝播 する、ということにも注意してください。
Mercurial がこのように振舞うのは以下のような理由のためです。例えば筆者が、ソースファイルに対して重要な バグ修正を行い、変更内容をコミットしたとします。その変更作業が行われている間に、バグの顕在化やその修正を 待つ事無く、当該ファイルを“hg copy” で複製し、その複製先ファイルの変更を読者が始めてしまうかもしれませ ん。
読者が筆者の変更を取り込んでマージした際に、 Mercurial が複製への変更の反映を行わない場合、読者の複製先ファイルはバグを 含んでいるため、手動でバグ修正を反映させる必要性を思い出さない限り、バグは複製先ファイルに残り続けるでしょ う。
バグ修正に関する変更内容の、複製元から複製先への自動反映により、 Mercurial はこの手の問題を回避してい ます。筆者の知る限り Mercurial は、複製ファイルに対するこのような変更伝播を行う唯一の構成管理システムで す。
ファイルの複製とそれに続くマージの実施が一旦変更履歴に記録されたなら、複製元ファイルから複製先ファイルへのそれ以上 の変更反映は通常は不要なので、 Mercurial はマージ時点までは複製へ変更を伝播させますが、それ以上は行いませ ん。
仮に、何らかの理由により、複製ファイルへの自動的な変更反映が必要ないと判断したなら、システムの通常の方法(Unix 的な システムの場合ならcp)でファイルを複製し、“hg add” により手動で複製ファイルを構成管理対象に追加してく ださい。ですが、その前に5.3.2節を読み直して、 Mercurial による自動変更反映の適切性を十分に検討してくださ い。
“hg copy” コマンドを使用した場合、 Mercurial は即座に作業領域ディレクトリに個々のファイルの複製を作成します。そのため、 ファイルに修正を加えた後で、その変更をチェンジセットとしてコミットすることなく“hg copy” を行った場合、複製先ファイルはそ の時点までの変更内容も含んでいることになります(この振る舞いについてここで述べたのは、少々直感に反するように感じられたから です)。
“hg copy” は Unix のcp コマンドと同様に振舞います(“hg cp” という別名方が好みであれば、こちらも使用できます)。末尾 の引数は複製先を、それ以外の先行する引数は複製元を意味します。複製元に単一のファイルを、複製先に存在しないパスを指定した場 合、 Mercurial は複製先に指定した名前で新たなファイルを生成します。
複製先がディレクトリの場合、 Mercurial は複製元ファイルを当該ディレクトリに複製します。
ディレクトリの複製の場合は、再帰的且つディレクトリ構成を保持しつつ複製されます。
複製元と複製先の両方がディレクトリの場合4 、 複製元のディレクトリ構造は、複製先ディレクトリ配下で再構築されます。
手動でファイルを複製した後で、当該ファイルが複製であることを Mercurial に通知するには、“hg remove” の場合と同様 に、--after 付きで“hg copy” コマンドを使用します。
ファイルを複製するよりも、むしろ改名の方が必要とされるのではないでしょうか。ファイルの改名よりも“hg copy” コマンドの方を 先に説明したのは、 Mercurial が複製と改名を本質的には同等に扱っているためです。そのため、ファイルの複製における Mercurial の挙動を知ることで、ファイルの改名で期待される振る舞いを知ることができます。
“hg rename” コマンドを使用した場合、 Mercurial は個々の改名元ファイルの複製を作成し、その上で改名元ファイルを削除し、 それらを構成管理対象から除外します。
“hg status” コマンドの出力から、新たに複製されたファイルが構成管理対象に追加され、改名元ファイルが除外されていること が読み取れます。
“hg copy” 実行の場合と同様に、-C オプション付きで“hg status” コマンドを実行することで、構成管理対象に追加されたファ イルが実際には、今は削除されてしまったファイルの複製ファイル、と Mercurial にみなされていることがわかりま す。
“hg remove” および“hg copy” と同様に、--after オプションを指定することで、実際に改名した後で Mercurial にその旨を通 知することができます。それ以外の殆どの点で、“hg rename” コマンドの振る舞い並びに指定可能なオプションは、“hg copy” コマン ドと同じです。
Mercurial の改名が「複製と削除」として実装されているため、複製の後でのマージの場合と同様に、改名の後でマージをした場合には 変更が伝播されます。
あるユーザがファイルを修正し、別のユーザがそのファイルを別なファイルに改名した場合、両者がお互いの変更をマージすると、 一方が行った改名元ファイルへの修正は改名先ファイルへと伝播します(この振る舞いは “普通の作業” で期待するであろう類のもので すが、全ての構成管理システムがこのように振舞うわけではありません)。
複製先に対する変更の伝播が、利用者にとっておそらく有用と思われる機能ですから、ファイルの改名においても変更の伝播が重要 であろうことは、明らかといえるでしょう。変更伝播機能が無い場合、ファイルの改名によって変更は簡単に行く先を失ってしまうこと でしょう。
名前の広がり(diverging names)は、二人の開発者がとあるファイル — これを foo と呼びます — を各自のリポジトリで扱うことで 発生します。
Anne がファイルを bar に改名します。
その一方で、 Bob がファイルを quux に改名します。
個々の開発者がファイルの命名に関する異なる意向を表明したわけですから、筆者はこの事態を衝突と捉えるのが良いと思いま す。
この場合のマージはどのように振舞うべきだと思いますか?改名による枝分かれが生じるチェンジセットのマージの場合、 Merging は常に両方の改名先ファイルを維持します。
筆者個人にとってこの振る舞いは大変意外であり、それがここでこの振る舞いを説明している理由でもあります。筆者は Mercurial に、bar を残すか、quux を残すか、あるいは両方を残すか、という選択肢による確認を行うことを期待していたのです。
実際には、ファイルの改名を行った場合、改名元ファイルを使用したビルドを行う他のファイル(例えば makefile)の修正が行われ るであろうことを意味します。そのため、 Anne がファイルを改名し、改名後のファイルでビルドが実施されるよう にMakefile を修正した場合、一方で Bob が同様の修正を別な名前で行っていますから、マージの際には作業領域ディレク トリに異なる名前のファイルのコピーが存在し、且つ Anne と Bob のMakefile への修正箇所が衝突している筈で す。
他の利用者もこの振る舞いに意外性を感じているようです。詳細はMercurial バグ番号 455 を参照してくださ い。
異なる複製元ファイルが同じファイルを複製先とした際に、改名による別な種類の衝突が発生します。この場合、 Mercurial は通常の マージ機構を使用し、適切な解決への誘導を要求してきます。
Mercurial は、一方がファイルに使用した名前を他方がディレクトリに使用した場合に、マージが失敗するバグが長い間残っています。 この問題はMercurial バグ番号 29 に詳細があります。
幾つかのありがちな間違いから復旧するために、 Mercurial は有用なコマンドを幾つか提供しています。
“hg revert” コマンドは、作業領域ディレクトリに対する変更を取り消します。例えば、うっかりファイルを“hg add” してし まった場合に、追加してしまったファイル名を指定して“hg revert” を実行することで、ファイルには一切変更を加える事無く Mercurial による構成管理対象から除外することができます。ファイルへの間違った変更を取り消すのにも“hg revert” が利用できま す。
“hg revert” コマンドは未コミットな変更に対して有効である、ということは憶えておきましょう。但し、一旦変更をコミットした後で変更内容が間違いであることに気が付いた場合でも、選択肢は限られてはいますが対処することはできま す。
“hg revert” コマンドに関する詳細と、コミット済みの変更に関する対処の詳細に関しては、9 章を参照してくださ い。