※ 左右のカーソルキーでもページ繰りができます(但しブラウザ依存)
情報発信系活動:
コミュニティ系活動
私の記憶では、 2008 〜 2009 年頃の日経SYSTEMS誌で実施した 『開発支援ツール』に関するアンケートでは、 『使ってみたい構成管理ツール』枠での CVS への投票が 50% 前後、 という結果でした。
つまりこれは、 『回答者の 50% 前後は CVS すら使ったことが無い 』 ということを意味します。
母集団である『日経SYSTEMの購読者層』には、 色々なバイアスが掛かっているかもしれませんが、 一概に見当違いの結果と一笑に付すわけにもいきません。
日経SYSTEMのアンケート以外にも、 以下のような状況を見るに、 構成管理ツールの普及は、 想像以上に遅れているのかもしれません。
そこで本資料は、 以下のような状況に持って行くための、 説得材料の元ネタとしてまとめてみました。
SCMBC に参加した皆さん自身が、 構成管理ツールの用法を習得した後には、 『組織への導入』における障壁が大なり小なりあると思いますが、 本資料がその一助になれば幸いです。
なお、 本資料では、 完全に『変更履歴を管理する』部分に特化した話に絞るため、 以後は『履歴管理ツール』という呼称で統一します。
一般的な履歴管理ツールは、以下の情報を記録します。
記録される情報は、大きく以下の2種類に大別できます。
第0世代 (手動管理)
+ 格納先 ..... +-- 2012-07-02 +-- .... 2012-07-02 時点の成果物 +-- 2012-07-03 +-- .... 2012-07-03 時点の成果物 .....
第1世代 (SCCS/RCS)
+ 作業領域ルート +-- foo.c ※ 管理対象ファイル +-- RCS ※ 履歴データ格納場所 | +-- foo.c,v ※ foo.c の履歴データ +-- subdir +-- bar.c +-- RCS +-- bar.c
第2世代 (CVS/SVN/VSS)
+ 『モジュールA』の作業領域ルート +-- foo.c ※ 管理対象ファイル +-- CVS | +-- Entries, Root .... ※ 最小限の管理情報 +-- subdir +-- bar.c +-- CVS +-- .... + 『リポジトリ』ルート ※ 履歴データ格納場所 +-- 『モジュールA』のルート +-- foo.c,v ※ foo.c の履歴データ +-- subdir +-- bar.c,v
第3世代 (Bazaar/Git/Mercurial)
+ 作業領域 (=広義の『リポジトリ』) ルート +-- foo.c ※ 管理対象ファイル +-- subdir | +-- bar.c | +-- .hg ※ 管理データ領域 (=狭義の『リポジトリ』) +-- store +-- .....
重要な事なので第3世代履歴管理ツールの特徴をもう一度:
『リポジトリ』+『作業領域』は各自で複製を保持
作業を実施する主体ごとにリポジトリが分散 ⇒ 分散リポジトリ型 履歴管理ツール
第2世代履歴管理ツールに関して、時々見かける表現:
『Client/Server モデル』、 つまり分散コンピューティングモデルであるから、 『分散履歴管理ツール』である
基本的には、 『分散リポジトリ型』以外の履歴管理ツールを 『分散履歴管理ツール』とは呼んでほしく無いのですが、 『分散履歴管理ツール』を上記のように定義している人も居ますので、 話をする際にはご注意を。
分散リポジトリ型履歴管理ツールでの履歴の記録
良くある誤解 (1):
分散リポジトリ型は、 常にリポジトリ同士が連携するに違いない!
ユーザが明示的に指示しない限り、 他のリポジトリとの連携は発生しません。
良くある誤解 (2):
各リポジトリは『完全な履歴情報を持っている』らしいので、 常に最新の情報を得られるんでしょ?
多くの英文で "complete history" などと記述されていますが、 perfect とか whole のような『全ての』的なニュアンスではなく、 『それ単独で機能し得る』というニュアンスの、 『完結した履歴情報を持っている』 と考えてください。
『最小限の管理情報』しか持たない第2世代履歴管理ツールの『作業領域』は、 『リポジトリ』と分離されてしまうと、 単独では殆ど機能しません。
第0世代での『変更情報』管理は、 主に『Excel シート』や『成果物への埋め込み』 といった方法での手動管理なので、 以下のような問題があります。
『変更情報』の『成果物への埋め込み』により、成果物の可読性が低下する例:
// 2012-07-05:fujiwara: 変数名のリファクタリング >>> retval = do_something(handle, // 2012-06-25:fujiwara: 引数指定の間違いを修正 >>> // 2012-07-02:fujiwara: 算出処理を関数化 >>> calc(value, addend), // 2012-07-02:fujiwara: 算出処理を関数化 <<< extra, // 2012-07-05:fujiwara: 変数名のリファクタリング <<< option, // 2012-06-25:fujiwara: 引数指定の間違いを修正 <<< flags);
第0世代では、 『作業成果』の格納先切り替えにより『変更内容』の(擬似的な)管理を行うので、 以下のような問題があります。
(第0世代の問題点の続き)
複数人による並行作業の調停が困難な例:
『元の内容のコメントアウトを必須』にした場合の、 成果物の可読性低下の例:
// 2012-07-05:fujiwara: 変数名のリファクタリング >>> // retval = do_something(hp, //// 2012-06-25:fujiwara: 引数指定の間違いを修正 >>> //// a * MUL + c, //// o, //// x, //// 2012-07-02:fujiwara: 算出処理を関数化 >>> //// a * MUL + b, // calc(a, b), //// 2012-07-02:fujiwara: 算出処理を関数化 <<< // x, // o, //// 2012-06-25:fujiwara: 引数指定の間違いを修正 <<< retval = do_something(nandle, calc(value, addend), extra, option, // 2012-07-05:fujiwara: 変数名のリファクタリング <<< flags);
変更履歴の管理における基本は:
第3世代とはいわなくても、 せめて第1世代ツールに移行しましょう。
第0世代に拘る人の主張(1):
現に管理できているから手動管理で十分
いや、多分出来てません。
あるいは、 裏で現場の誰かが苦労しているんだと思いますよ?
例えば『一週間前に A 氏が変更して以降のファイル F に対する全変更内容』 を容易に取り出せますか?
あるいは『ファイル F のある行が、誰が何時変更したものに由来するか』 を簡単に確認できますか?
第0世代に拘る人の主張(2):
難しいことはやらないから手動管理で十分
『数行のメール』だからと言って、 メール送信の際に telnet で SMTP を喋る人は居ませんよね? 『簡単にできる』というのは非常に重要です。
$ telnet mail.xxxx.jp 25 Trying xxxx .... HELO xxxx 250 mail.xxxx.jp Hello ..... MAIL FROM: foo@xxxx.jp 250 foo@xxxx.jp... Sender ok RCPT TO: bar@xxxx.jp 250 bar@xxxx.jp... Recipient ok DATA 354 Enter mail, end with "." on a line by itself From: foo@xxxx.jp Subjet: test ....
第0世代に拘る人の主張(3):
自分だけの作業だから手動管理で十分
『他人からの圧力』という強制力の働かない『一人作業』こそ、 簡単に変更履歴を記録できるツールの力を借りないと、 あっという間に管理を放棄することになります。
第0世代に拘る人の主張(4):
だからツールの使い方を憶えるのが面倒だって言ってるでしょ!
やっと本音を喋ってくれましたね。
ちゃんとした参考資料を元にすれば、 変更記録、履歴参照、変更内容確認、 特定時点の成果物の取り出しといった基本機能は、 半日もあれば習得できると思います。
後は必要に応じて徐々に応用方法を習得すれば良いでしょう。
簡単に元は取れます。
基本的に、 第1世代ツールは『複数人による並行作業』を想定していない (あるいは想定が不十分)ので、 以下のような問題があります。
ファイル共有ベースでの複数人作業における問題:
第1世代に拘る人の主張(1):
一ディレクトリあたり一人とか、 一ファイルあたり一人での作業に限定するから大丈夫
どの分野でも、 成果物の規模/複雑さが増加する一方で、 変更作業に許される時間は減少する一方です。
そんな状況での『並行作業が出来ない』というダイナミズムの欠如は、 他の組織に対して競争力を失う事に繋がりますよ?
第1世代に拘る人の主張(2):
ツールを使えば各自の作業領域を分離できるから大丈夫
そういえば、20世紀時分は 『(1) ディレクトリ構造をコピーした上で (2) 個々のファイルにリンクを貼ることで、 ソースとビルド作業先を分離する』ツールを良く目にしましたね。
しかし (1) 変更後の内容を一時ファイルに保存し (2) 元ファイルをバックアップファイルに改名してから、 (3) 一時ファイルを元ファイルに改名するエディタも多々あるため、 上記の手法は『ビルド』には良くても『編集作業』には向いていません。
『ファイルロック』ベースでの運用における問題点:
複数人で『並行作業』を行う場合、 各メンバーが以下のような点を意識している必要があります。
自動車の安全運転における先読みと同じで、 並行作業で失敗する人は、 以下のように希望的観測で考えがちです。
『並行作業』を意識できていないユーザによる問題事例:
衝突発生により自分の作業成果が即座にコミットできなかったので、 CVS リポジトリ中の管理ファイルを書き換えて (= 他の人の作業成果の破棄)、 無事自分の作業成果をコミットできました!!
20世紀末の話ですが誇張無しの実話です。
『管理ファイルの改変』ができたので、 RCS の知識はあったのでしょうが、 『並行作業』における『衝突』の意味が、 良く理解できていなかったのだと思われます。
第1世代に拘る人の主張(3):
作業メンバーが一人なので並行作業の必要性が無い
ちょっとした契機で並行作業が必要になるので、 あらかじめ備えておいたほうが得策です。
第2世代の履歴管理ツールの場合、 以下のようなケースで、 ネットワーク帯域 (あるいはサーバ性能) が原因となって、 履歴管理ツールの応答性能が低下します。
ネットワーク帯域に関する殆どの問題は、 第3世代の履歴管理ツールが、 通常の履歴参照/記録操作の際に、 外部へのネットワークアクセスを必要としないことで、 解決されてしまいます。
また、利用メンバー/拠点の多さが問題となるケースでも、 小規模チーム/拠点毎に中間リポジトリを用意することで、 帯域の狭いネットワークの利用頻度を低下させることが可能です。
第2世代に拘る人の主張 (1):
リポジトリの複製は、 同一内容のファイルが散在することになるので、 ディスク消費量が増えてしまう!
タイの洪水騒動もひと段落した現在、 2TB HDD は一万円を切っています。 さて、あなたの成果物は何GBあるのでしょうか?
Mercurial なら、同一ファイルシステム上での複製時に、 ハードリンク使用でディスク消費を抑止する仕組みがありますし、 『イマドキのファイルシステム/ストレージサーバ』であれば、 重複排除 (dedup) 機能も利用可能です。
以下のような状況下では、 リポジトリサーバにアクセスできないため、 第2世代の履歴管理ツールは、 ほぼ何もできないと言えます。
第3世代の履歴管理ツールは、 手元にリポジトリがありますから、 ネットワーク的に隔離されていても、 履歴参照や変更記録が可能です。
後は、 物理的にリポジトリを持ち帰るなり、 ネットワーク接続が可能になった時点で転送するなりして、 共有リポジトリに追加の履歴情報を反映すれば良いのです。
リポジトリに直接アクセスできない環境での作業を履歴記録する際の失敗例:
個人端末にチェックアウトした作業領域から (1) 対象環境にファイル一式を転送し、 (2) 動作確認/障害修正した後で、 (3) 修正後のファイルを個人端末に転送。 作業領域中のファイルを、転送した修正後ファイルで上書きしてから、 変更履歴を記録したら、 他の人の直前の作業成果を破棄してしまった。
記録前に差分を確認しないという点で、 『並行作業』の意識が不十分なのも問題ではありますが、 そもそも対象環境で直接変更履歴を記録できていれば、 問題は起きなかった筈です。
最低限『共有リポジトリ⇔個人端末』と『個人端末⇔対象環境』は、 ネットワークが通じていると仮定すれば、 以下のような解決が可能です。
『共有リポジトリ』と『対象環境』のどちらにもアクセス可能な 『個人端末』を、 履歴情報転送のピボットとして機能させるのがミソです。
第2世代に拘る人の主張 (2):
他のリポジトリとの相互連携を、 明示的に指定するのは面倒だ!
『明示的に指定』する事を、 『余計な一手間』と見るか、 『連携契機の柔軟性』と見るかの視点の差です。
Mercurial で言う、 リビジョン探索機能 (bisect) や、 ログ探索機能 (revset) が実用的な速度で利用できるのは、 履歴参照においてネットワーク接続が必要ないから、 と言っても過言ではないでしょ。
『並行作業』の際に、 複数の人による変更内容は、 最終的に1つの履歴へと統合する必要があります。
このような作業を『マージ』(merge) と呼びます。
『並行作業』の際に、 同一ファイルの近接した部分 (あるいは同一箇所) を、 複数の人が変更していた場合、 これを『衝突』(conflict) と呼びます。
例えば二人がそれぞれ以下のような変更をした場合、 『衝突』の要因となります。
10: result = do_something(handle, 11: calc(a, b), 12: extra,
10: result = do_something(handle, 11: value * MUL + addend, 12: extra,
『衝突』が発生した場合は、 どちらか一方の変更を採用するのか、 あるいは全く異なる第3の内容で変更するのかを、 利用者が判断しなければなりません。
これは、 変更内容/ファイルの用途に応じた判断が必要なため、 機械的に処理できないためです。
『並行作業』で『衝突』が発生する状況で、 第2世代の履歴管理ツールにおける、 変更作業開始から『マージ』を完了するまでの流れは、 以下のようになります。
ここでは、 (4) 〜 (8) を『履歴のマージ』(※)、 (4) 〜 (5) を『変更内容のマージ』(★)と呼びます。
第2世代の履歴管理ツールの場合、 『変更内容のマージ』は、 『commit していない変更内容』が含まれている作業領域中のファイルを、 直接書き換えることで実現されています。
その後、 『履歴のマージ』完了に向けて、 作業領域中のファイルを適宜変更するわけですが、 何かの拍子で『commit していない変更内容』が欠損ないし全損した場合、 その変更内容はどこにも記録されていないのですから、 やり直そうにも、 再度手動で変更をやり直すしかありません。
第2世代の履歴管理ツールの場合、 実は『変更内容のマージ』は非常に危うい操作なのです。
第3世代の履歴管理ツールの場合、 『変更内容のマージ』は、 基本的にcommit 済み成果を元に実施されます。
元になっているのが『commit 済み成果』(= 変更内容が記録済み) なので、 何度『変更内容のマージ』をやり直しても、 元となる変更の成果が失われることはありません。
第2世代に拘る人の主張 (3):
『履歴のマージ』に先立って、 自分の変更内容を必ず commit しなければならないのは面倒臭い!
ちょっとしたその手間で、 変更作業の成果を失う危険を防げるのであれば、 安い買い物だと思いませんか?
Mercurial では、 第2世代と同様の、 未コミット成果ベースでの『変更内容のマージ』も一応可能ではありますが、 私個人は全くお勧めしておりません。
第2世代に拘る人の主張 (4):
『履歴のマージ』のために余計なリビジョンが増えるので、 ログが汚れてしまう!
(rebase を使わない場合の) Mercurial 運用で良く聞かれますが、 個人的な経験を言えば、 変更履歴が 100 を超えたあたりから、 履歴のログは、 『全部見るもの』ではなく、 『条件指定で抽出してから見るもの』になりますから、 『汚れ』なんて気になりません。
こう主張する人に限って、 第2世代使用時には、 ろくにコミットメッセージを読んでない/書いていない場合も。
『パターンによる構成管理』における 『Active Development Line』や『Release Line』、 『Task Branch』といったパターンは、 今時の開発における履歴管理では、 使わずには済ませられない、 非常に基本的なパターンと言えます。
これらのパターンは、 履歴管理ツールで『ブランチ』(branch) と呼ばれる機能で実現するのが一般的です。
第2世代の履歴管理ツールでの『ブランチ』運用に関する情報は、 概ね怨嗟の声で溢れていると言って良いでしょう。
『入門Mercurial』出版の際に、 Mercurial の作者である Matt Mackall 氏から頂いたメッセージにも、 以下のように書かれています。
…… おまけにブランチのマージともなれば、 時には数日に及ぶ作業の間、 全てのメンバーが作業を止めて完了を待たねばならなかったのです。
第3世代の履歴管理ツールの場合、 『ブランチ』の運用は驚くほどに簡単です。
『履歴のマージ』が、 日常的な作業として簡便化されていることも、 『ブランチ』運用の簡便性に関係していると思われます。
第2世代に拘る人の主張 (5):
第2世代の履歴管理ツールでも、 『ブランチ』運用は簡単だよ?
その幸運がいつまでも続くと良いですね。
第2世代に拘る人の主張 (6):
『ブランチ』運用なんて必要無い!
以下のような複数の作業を並行実施する必要性は、 あなたのすぐ隣で息を潜めて、 今か今かと出番を待っていると思われます。
ちなみに、 Bazaar/Git/Mercurial 個々における専門用語としての『ブランチ』は、 全く同じ言葉を使っているのに、 全然話が通じないという、 『バベルの塔(改)』な状態と思っていた方が良いでしょう。