水野です。 先ほどのアナウンス文に引き続き、status report を報告します。 長文です。ご容赦下さい。 CVSTree project は、発足(ML発足 4/27)から一ヶ月以上が過ぎました。 この間、たくさんの問題点も浮上し、私や、大和さんも勉強すること が多かったです。 CVSTree project は、lilo のサブプロジェクトであり、定期的に lilo-ML にて報告をすることをお約束していました。ここに、レポートをまとめました ので、報告いたします。 なお、このレポートは空くまで「中間報告」です。これが そのまま CVSTree project の結論ではありません。また、これを書きはじめた頃(5月下旬) からでも、状況は変わっていることをおさえて お読みください。 --------------------------------------------------------- CVSTree project status report この文章では,liloによってネットワークリソースを提供していただいて, 開発中のemacs上で動作するcvsコマンドのフロントエンド,CVSTreeの開発状況と, そのプロジェクト状況について述べる. 1.では, なぜCVSTreeというツールが有用であるかについて述べる. 2. では,現在公開しているcvstree.el version 0.1.3の利用方法について 述べる. 3. では今後cvstree.elをどのように改良して行くか述べる. 4. ではCVSTreeプロジェクトの現状,特に問題点について述べる. 付録A.にCVSTreeプロジェクトのアナウンス文を掲載する. 付録B.にcvstree.el version 0.1.7を掲載する. 1. CVSサーバを用いたフリーソフトウェアの開発の問題点 -- なぜcvstreeが必要か. フリーソフトウェアの開発活動においてcvsをサーバとして利用することが注 目されている.その理由は,いくつか考えられるが,そのうち以下の2つのCVS サーバの利点がCVSTreeと関係がある. A. 複数人でソフトウェアを開発している場合,ソースの変更が容易になる. CVSサーバがない場合,ソースコード管理者以外(共同開発者)がソースコー ドを改良した場合,改良箇所をパッチとしてソースコード管理者に送る必要が あった.このことは共同開発者にとってもソースコード管理者にとっても負担 である. 共同開発者は,一度パッチを送ったら,次のソースコードの改良を,パッチ が反映された,新しいバージョンがリリースされるまで待つ必要がある. パッチを送った共同開発者の変更だけが新しいバージョンの変更点ではない かもしれない.ソースコード管理者本人の変更や,他の共同開発者のパッチが 含まれている可能性がある.もし,パッチを送った後,新しいリリースを待た ずに,改良を続けると,その改良の元になるソースコードと,あたらしいリリー スとの間に整合性がとれず,新しい改良をパッチにするための作業が必要にな る. ソースコード管理者は,共同開発者のパッチを自分の管理するソースコードに適用 する必要がある.しかしソースコードにはすでに,多くの送られてきたパッチ が適用されている可能性がある.そのため,ソースコード管理者は,patchコ マンドで正しくパッチを適用することができないかもしれず,苦労してハンド パッチする必要があるかもしれない. CVSサーバがあれば,共同開発者は常にコードを最新のバージョンに保つこ とができるため,次々にパッチを作り,それをソースコードに反映させること ができる.ソースコード管理者は,自分でパッチを管理する必要がなくなる. ソースコード管理者がやらなければならないのは,リリース時期の決定だけに なり,作業負荷が軽減する. B. 複数のバージョンの開発をオープンに行なうことができる. (CVSサーバでなく)CVSの機能により,複数のバージョンの開発を同時に進行 させることができる.たとえば,実験的な機能を追加したバージョンが,実行 速度のチューニング等を施したバージョンを,平行して開発することができる. フリーソフトウェアの性格上,フリーソフトウェアの特別なバージョンの開 発は,常に行われてきたと考えられる.ただし,そのような特別なバージョン の開発は,あまりオープンに行なわれなかったと予想される.なぜなら,その ような特別なバージョンは,プロジェクトの分裂を予感させるからである[1]. プロジェクトの分裂を予感させるのは,一つのプロジェクトは,一本のソー スツリー管理するというのが常識だったためではないか? 実際CVSサーバなし で,複数のバージョンを管理するのは,ソースコード管理者にとってかなりの 負担になるだろう. CVSサーバはこの常識を打破し,一つのプロジェクトが複数のバージョンを管 理できるようにした.そのため,プロジェクトの分裂を予感させることなしに, 特別なバージョンの開発をオープンに行うことができる. CVSサーバを用いてオープンに開発しているプロジェクトは,多くあ る.しかし それらのプロジェクトの多くは,A., B. 二つの利点のうち, A. の利点しか生かしていないようである.すなわち,複数のバージョンを 同時に開発しているプロジェクトは少ない. その理由は2つあると我々は考えている.一つは,cvsのコマンドが難解なた め多くのフリーソフトウェア開発者が,複数のプロジェクトを扱うコマンドを まだ理解していないからだと考えている.もう一つは,CVSの持つ複数のバー ジョンを扱う機能,特に情報を表示する機能が貧弱だからだと考えている. CVSTreeは,cvsのコマンドを完全に覚えなくても容易に複数のバージョンを 扱うことができるemacs上で動作するCVSサーバのフロントエンドである.我々 は特に複数のバージョンの情報の表示方法を工夫した. cvsサーバのフロントエンドにはいくつかある. vc.elはemacs上で動作する フロントエンドである.vc.elは現在編集しているファイルにcvsのコマンドを 適用することを支援する.ファイル単位のバージョン管理には有用である. pcl-cvs.elも,emacs上で動作するフロントエンドであり,vc.elよりも扱う対 象が大きい.pcl-cvs.elでは,複数のファイルやディレクトリをプロジェクト として,まとめて扱う.そしてプロジェクト中の複数のファイルへまとめて, cvsのコマンドを適用することを支援する.しかしながら,これらのフロント エンドには,プロジェクトが複数のバージョンを持つ場合について考慮してい ない.CVSTreeは,プロジェクトが複数のバージョンを持つという立場から, バージョン単位の操作やバージョン間の関係の理解することを支援する. vc.el, pcl-cvs.el, CVSTreeは支援の対象が異なるので,対象に合せて併用す ることができる. なお,CVSTreeがcvsサーバへアクセスするために,cvsのコマンドを使うため, CVSサーバを介さず,ローカルのリポジトリも扱うことができる. [1] ノウアスフィアの開墾 http://www.post1.com/home/hiyori13/freeware/noosphere.html Eric S. Raymond 著 山形浩生 YAMAGATA Hiroo 訳 2. cvstree.elの使い方 現在,複数のバージョンの関係を表示するところまでの実装した. それを表示させる方法までを説明する. cvstreeの最新版であるバージョン0.1.7をこの文章の最後に掲載した. それをcvstree.elの名前でload-pathで指定されたディレクトリに 保存して, .emacsに (require 'cvstree) あるいは, (autoload 'cvstree "cvstree" "Print relation of tags in CVS repository" t nil) と書く. load-pathの意味が,全然わからない人は,.emacsにそのまま貼って下さい. そして, A. CVSの保管庫から何かcheckoutする. B. checkoutしたファイル群(?)の中から, CVSの保管庫にもっとも古くから 存在していそうなファイルを選ぶ.以降では,このファイルを と表記する. C. emacs上でcvstreeコマンドを呼ぶ. M-x cvstree D. エコーバッファからを入力する. Keyfile: E. *cvstree*というバッファができ,そこに複数のバージョンの関係を表示する ツリーが表示されます. GIMPの例(keyfile: ChangeLog): *gimp ┣━┳━━━ (1 1 1 ) : GNU ┃ ┗━━━ (1 1 1 1 ): SNAP_19971121 ┣━━━━━ (1 26 ) : GIMP_0_99_16 ┣━━━━━ (1 33 2 ): HOLLYWOOD ┣━━━━━ (1 38 ): GIMP_0_99_17 ┣━━━━━ (1 46 ): BASE_ZERO ┣━━━━━ (1 59 ): GIMP_0_99_18 ┣━━━━━ (1 59 ): ROSALIA_BEFORE_COMMITTING_DL_AND_GNOME_HELLO ┣━━━━━ (1 100 ): GIMP_0_99_19 ┣━━━━━ (1 114 ): GIMP_0_99_20 ┣━━━━━ (1 135 ): GIMP_0_99_21 ┣━━━━━ (1 164 ): GIMP_0_99_22 ┣━━━━━ (1 194 ): GIMP_0_99_23 ┣━━━━━ (1 204 ): BEFORE_MATTS_CRAZY_TOOL_PATCH ┣━━━━━ (1 212 2 ): GTKOB_PROJECT ┣━━━━━ (1 226 ): GIMP_0_99_24 ┣━━━━━ (1 237 ): GIMP_0_99_25 ┣━━━━━ (1 269 ): GIMP_0_99_27 ┣━━━━━ (1 287 ): GIMP_0_99_28 ┣━━━━━ (1 320 ): GIMP_0_99_29 ┣┳━━━━ (1 371 ): GIMP_1_0_0 ┃┗┳━━━ (1 371 2 ): gimp-1-0 ┃ ┣━━━ (1 371 2 44 ): GIMP_1_0_1 ┃ ┣━━━ (1 371 2 50 ): GIMP_1_0_2 ┃ ┗━┳━ (1 371 2 54 2 ): gimp-1-0-gtk-1-2 ┃ ┣━ (1 371 2 54 2 14 ): GIMP_1_0_3 ┃ ┗━ (1 371 2 54 2 17 ): GIMP_1_0_4 ┣━━━━━ (1 476 ): BEFORE_TILE_MADNESS ┣━━━━━ (1 767 ): GIMP_1_1_0 ┣━━━━━ (1 812 ): GIMP_1_1_1 ┣━━━━━ (1 838 ): GIMP_1_1_2 ┣━━━━━ (1 902 ): GIMP_1_1_3 ┣━━━━━ (1 957 ): GIMP_1_1_4 ┣━━━━━ (1 970 ): FOR_PANEL ┗━━━━━ (1 1068 ): GIMP_1_1_5 3. 予定と課題 3.1. 予定 あと2か月でプロジェクトを完了,停止したい. まず次の1か月でliloローカルでやれることは全部やる. その次の1か月で,対外的に(liloの外)でアピールする. 今,liloの外で宣伝するとcvstreeの*プロジェクト*の主旨を知らない 有能なプログラマがcvstree.elを完成させてしまうので,あと一箇月は 外にアピールしなで,プロジェクトの主旨に従って活動する. 3.2. 課題 * cvs ** ブランチのマージってcvsコマンドでわかるの? ** treeの情報をcvsコマンドから得ることができるかどうか調べる. 大和と野首さんの調査の結果: logとstatusが,使えそう. 問題点: *** logだと大量の情報を得ることができるが,大量すぎる. *** statusだとファイルが一つ必要になる. 解決策: *** logをcacheできないか? => すばらしい. *** logの大量の出力からツリーを生成するコード *** cvsのメイリングリストで質問する. * 宣伝する. 1か月以内にやりたい: ** cvs,elisp関係のMLで宣伝する. ** 野首さん経由でnlug. ** ウェブページを大和のところからliloに移す. 以降: comp.os.liunx.announce by 水野さん! fj.sources by 水野さん!! gnu.emacs.sources by 水野さん!!! * emacs lisp ** cvs コマンドの出力のキャッシュ, 絶対必要 ** 異なるemasc/mule/xemacsでも動作するコードを書く方法について調べる. 特にtextの属性の問題とイベント ** 全角文字を使っているけど世界で使ってもらうのに,それで良いのか調べる. ** ツリー生成コードの改良(0.2の目標) ** ツリーが表示できるようになったあと,どんな機能を付け加えたいか 考える.例,ブランチのマージ, 特定のタグのダウンロード *** cvswebのようなことができるようにして欲しいby おくじさん@GNU/HURD => cvswebについて調査 * pcl-cvs.elとの連体動作 4. CVSTreeプロジェクトの問題点 (cvstree.elのプログラムの問題点とは違います) cvstree プロジェクトには、若いフリーソフトウエア プログラマを 育てるため、活躍の場を提供し、実際のソフトウエア開発に参加 してもらうことで、日本人の若いプログラマを増やそうという目的が、 ある. そのため、プログラム初心者にも参加の呼びかけを行ったが、 1) 使用言語が、Emacs Lisp である。 2) cvs というあまり一般的に使われていないソフトウエアの インターフェースを作ろうとしている。 (ここでいう一般的とは、メーラーやエディタなどの エンドユーザ にも使用されているソフトウエアの意味で使っている。) という2点の大きな壁があり、そもそも 3) 開発を進めていくためのML に入ってくれない という問題がある。当初、cvstree のプロトタイプが出来あがってから、 呼びかけ/宣伝 をしようという予定であったが、ミスであったかもしれない。 また、入ってもらったとしても、開発に大きく貢献できると思われる、 4)比較的自由な時間の多い学生の参加が少なく、 ML のメール流量自体がほとんどないので(約80通/月)、うっかりすると そのようなプロジェクトの ML に入っていたことを忘れてしまいそうになる こともままあった。 そのような状態になってしまったのは、プロジェクトの主催者側の問題も かなり大きい。 ML のメンバーは Emacs Lisp や cvs の初心者がほとんどであるため、発足 当初、なかなか質問ができない、何を質問して良いかわからないという状況 であったと思われる。しかし、 5)主催者側で活発な意見のやり取りがほとんど無かった ので、MLメンバーが傍観してしまっている期間が 非常に長かった。主催者側 は、メンバーが 意見しやすい雰囲気を 早く作るべきであった。雰囲気の 悪さは 言語教育にも響いている。Emacs Lisp を習得するためのきっかけを メールしたり、Web Site を作ったものの、それに対するメンバーの応答が ゼロに果てしなく近く、cvstree ML のなかで、Lisp を使える人が一体 どれくらいいるのか、主催者側さへ把握していないという、情けない状況に なってしまっている。 ソフトウエアそのものを完成させることも 当然、目標であるが、今回の プロジェクトでは、冒頭で述べたような目的もあったため、多少 過保護 過ぎる感もあるが、今までこの様なプロジェクトに参加したことが無かっ たメンバーがメールしやすい雰囲気を作るべきであった。 ここ一ヶ月のプロジェクトの停滞は、ここに集約されるように思われる。 しかし、このプロジェクトには、 6) 開発言語であるLisp を 始めは使えなくても、気軽に参加できる 7) lilo ML のメンバーのみで構成させており、互いの顔ぶれを知っている ので、質問がしやすい。 という非常に良い面がある。特に 7) は、まだフリーソフトウエアの開発 には加わった事がない初心者には かなり助かるはずである。この利点を 活かして、プロジェクトを進める必要がある。 それを受けて 今後の方針を考え直さないと、初心者ばかりでは ソフト ウエアは作れませんでした。という、悲しい報告書を出さないといけない 事になりかねないであろう。 付録A.プロジェクトのアナウンス文 lilo(Linux Install Learning Osaka)のサブプロジェクトとして、 cvstree プロジェクトというものが立ち上がっています。 cvstree は、一言でいうと、cvsというバージョン管理ソフトへの emacsでのインターフェースです。現在開発しているバージョン関係を treeにして視覚的に表示することが目標です。 cvs http://www.cyclic.com さて、今回はCVSTreeのウエブサイトが出来ましたので、案内させて 頂きました。興味の有る方はご覧下さい。 ------------------------------------------------------------ CVSTreeのウェブページ: http://www.aist-nara.ac.jp/~masata-y/cvstree/index.html サブプロジェクト制度の提案: http://www.aist-nara.ac.jp/~masata-y/cvstree/meta.html ------------------------------------------------------------ このプロジェクトの目的は、フリーソフトウエアを作り上げること だけではなく、 1. lilo のなかでサブプロジェクト制度が実現可能かどうかを調査する。 2. フリーソフトウエアを作ってみたいけど、どうやっていいかわからない 人に活躍の場を与える。 3. 若いプログラマーの発掘と育成。 という精神論も混じっています。ただし、この辺りは、プロジェクトが立ち 上がったばかりですので、まだ、方針等は煮詰めていく必要がありますが。 また、若い学生も募集していますので、興味を持たれた方は是非、MLに 参加してください。 参加方法は、メールの本文に、 subscribe [Your Name] を書いて、 cvstree-ctl@lilo.linux.or.jp までメールしてください。 なお、このML参加に際しては、cvs や Emacs-Lisp を熟知してないと 入れないとか、スキル的な制限はありません。これを気に、cvs を覚えて みよう、その上で、自分も cvstree.el 開発に参加しようと思う方は、 是非御参加下さい。 以上 付録B.cvstree.el version 0.1.7 --- ;; cvstree.el --- Print relation of tags in CVS repository. ;; ;; Copyright (C) 1999 Masatake YAMATO ;; Copyright (C) 1999 Linux Install Learning Osaka(LILO) cvstree project ;; ;; Primary author: Masatake YAMATO ;; Maintainer: mmizuno@ma.neweb.ne.jp ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; This program is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the Free ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ;; 02111-1307, USA. ;; ;; History: ;; ;; This is version: (defconst cvstree-version "0.1.7") ;; Mon Jun 7 19:36:43 1999 Masatake YAMATO ;; ;; * Version 0.1.7. ;; * Added new tree generator stubs. ;; * Added code to highlight text for mule-19.34. ;; The problem is reported by Hiroyuki Kataoka. ;; ;; Tue, 1 Jun 99 13:54:57 NOKUBI Takatsugu ;; ;; * Check current-language-environment to detect ;; 2byte or 1byte displaying. ;; ;; Sat May 29 12:34:58 1999 Masatake YAMATO ;; ;; * cmd, New name space. ;; * cvstree-buffer-* are cleaned up. ;; ;; Sat May 29 11:07:57 1999 Masatake YAMATO ;; ;; * Version 0.1.6. ;; * cvstree-mode-pick-tag returns a complete structure. ;; * Implementated cache. ;; * Versoin number is now list const. ;; ;; Sat May 29 03:48:11 1999 Masatake YAMATO ;; ;; * Version 0.1.5. ;; * Require string. ;; * Check mule feature. ;; * Added stubs for cache. ;; * cvstree-show-tag-name, new command for user. ;; ;; Tue May 25 01:07:05 1999 Masatake YAMATO ;; ;; * Version 0.1.4. ;; * Put text property on tags. ;; * Put tag type. ;; ;; Sun May 23 01:15:38 1999 Masatake YAMATO ;; ;; * Version 0.1.3. Code freeze for Status report. ;; * cvstree-vlist-compare: Check type, branch or revision. ;; ;; Fri May 21 01:41:52 1999 Masatake YAMATO ;; ;; * Version 0.1.2. ;; * cvstree-keyfile-get-directory: renamed from cvstree-keyfile-check. ;; ;; Wed May 19 23:55:43 1999 Toshihisa Tanaka ;; ;; * Require cl. cadr is defined in cl. ;; ;; Wed May 19 20:34:16 1999 Masatake YAMATO ;; ;; * Version 0.1.1. ;; * Put Repository and Root at the head of the cvstree buffer. ;; * Change the style of printing version number. ;; ;; Wed May 19 06:42:06 1999 Masatake YAMATO ;; ;; * Jump up to version 0.1. ;; * wanderlust based tree renderer. ;; * Rewrite tree constructor code. More complex! ;; * Merge tree constructor and renderer. ;; * New name spaces. ;; ;; Wed Apr 21 04:44:45 1999 Masatake YAMATO ;; ;; * cvstree.el: Added definition of split-string. ;; Code stolen from elib-1.0/string.el. ;; ;; Commentary: ;; ;; ;; Usage: ;; ;; まずは, CVSの保管庫から何かcheckoutして下さい. ;; checkoutしたファイル群(?)の中で, CVSの保管庫にもっとも古くから ;; 存在していそうなファイルをと呼ぶこととします. ;; (cvstree.elはこののcvs status -vの情報からツリーを作ろうと ;; します. ) ;; そして ;; M-x cvstree ;; KeyFile: ;; とすると, treeのそこそこ良いのが*cvstree*というバッファに印字されます. ;; 今のところは, それだけ. ;; にはCOPYINGとかChangeLogとかが良いでしょう. ;; ;; Acknowledgements: ;; ;; The tree idea comes from wanderlust. ;; ;; cvstree emacs lisp conding standard ;; ;; * Name space rule ;; ;; This is the default symbol naming rule. ;; cvstree-x0-y0-x1-y1-... ;; x0, x1 is noun. ;; y0, y1 is verb. ;; ;; TODO ;; ;; * Backward tree generation code ;; * Collaborate with pcl-cvs, vc. ;; * Use header... ;; (require 'cl) (require 'assoc) ;; for string-split (condition-case condition (require 'string) (file-error nil)) ; Ignore ;; ;; User variable. ;; (defvar cvstree-dstr-2byte-ready (if (featurep 'mule) (if (boundp 'current-language-environment) (if (string= current-language-environment "Japanese") t nil) ; emacs20 but Japanese t) ; mule/emacs-19 nil) ; No mule "*") ;; ;; Stolen from elib-1.0/string.el ;; (if (not (fboundp 'string-split)) (defun string-split (pattern string &optional limit) "Splitting on regexp PATTERN, turn string STRING into a list of substrings. Optional third arg LIMIT (>= 1) is a limit to the length of the resulting list." (let ((data (match-data))) (unwind-protect (let* ((start (string-match pattern string)) (result (list (substring string 0 start))) (count 1) (end (if start (match-end 0)))) (if end ; else nothing left (while (and (or (not (integerp limit)) (< count limit)) (string-match pattern string end)) (setq start (match-beginning 0) count (1+ count) result (cons (substring string end start) result) end (match-end 0) start end))) (if (and (or (not (integerp limit)) (< count limit)) end) ; else nothing left (setq result (cons (substring string end) result))) (nreverse result)) (store-match-data data))))) (if (not (fboundp 'buffer-substring-no-properties)) (defun buffer-substring-no-properties (b e) (buffer-substring b e))) ;; ;; Internal variable ;; (defvar cvstree-buffer-keyfile nil) ;buffer local (defvar cvstree-buffer-name "*cvstree*") (defvar cvstree-cache-file-name (expand-file-name "~/.cvstree")) ;; ;; Entry point for user ;; (defun cvstree (keyfile &optional force-update) (interactive "fKeyFile: \n") (let* ((fullpath (expand-file-name keyfile)) (cvsdir (cvstree-keyfile-get-directory fullpath))) (if cvsdir (let ((cache (cvstree-cache-load fullpath)) (reload force-update)) (if (and cache (not reload)) (setq reload (not (y-or-n-p (format "Use cache[%s]? " (cvstree-cache-date cache)))))) (cvstree-main fullpath cvsdir cache reload))))) ;; ;; ;; (defun cvstree-keyfile-get-directory (keyfile) (let ((cvsdir (concat (file-name-directory keyfile) "CVS"))) (cond ((file-directory-p keyfile) (message "No such a file: %s" keyfile) nil) ((not (file-directory-p cvsdir)) (message "Cannot find CVS directory: %s" cvsdir) nil) (t cvsdir)))) (defun cvstree-main (fullpath cvsdir cache reload) (let (status taglist (buf (get-buffer-create cvstree-buffer-name)) (dir (file-name-directory fullpath)) depth keyfile-simple) (setq keyfile-simple (file-name-nondirectory fullpath)) ;; Set up buffer to use (set-buffer buf) (make-variable-buffer-local 'cvstree-buffer-keyfile) (setq cvstree-buffer-keyfile fullpath) (setq buffer-read-only nil) (erase-buffer) ;; Call cvs status (or use cache) (cd dir) (setq status (if (and cache (not reload)) (cvstree-cache-map cache) (cvstree-call-status keyfile-simple buf))) (pop-to-buffer buf) ;; Draw tree (if status (progn (if (or (not cache) reload) (cvstree-cache-create fullpath)) ;; TODO use header! (cvstree-buffer-eliminate-header) (setq taglist (cvstree-buffer-parse)) (erase-buffer) (cvstree-buffer-insert-root cvsdir) (end-of-line) (newline) (cvstree-buffer-insert-repositry cvsdir) (end-of-line) (newline) (cvstree-taglist-sort taglist) (setq depth (cvstree-taglist-max-vlist-length taglist)) (cvstree-taglist-alloc-dstr taglist depth) (cvstree-taglist-calc-dstr taglist) (cvstree-taglist-draw taglist) (cvstree-mode) t) (message "Fail in call process\n") nil))) (defun cvstree-call-status (keyfile buf) ;; TODO use cvs-mode-status in pcl-cvs. (message "Invoking: cvs status %s..." keyfile) (prog1 (= 0 (call-process "cvs" nil buf nil "status" "-v" keyfile)) (message "Invoking: cvs status %s...done" keyfile))) ;; ;; Cache ;; (defun cvstree-cache-load (keyfile) (let ((flag nil) (cvstree-cache nil)) (condition-case condition (load cvstree-cache-file-name) (error (setq flag t))) (if flag nil (assoc keyfile cvstree-cache) ))) (defun cvstree-cache-date (cache) (cadr cache)) (defun cvstree-cache-map (cache) (insert (caddr cache)) t) ; nil->debug (defun cvstree-cache-create (keyfile) (let ((cache (buffer-string)) (cvstree-cache nil) (flag nil)) (condition-case condition (load cvstree-cache-file-name) (error (setq flag t))) (if flag (setq cvstree-cache (list (list keyfile (current-time-string) cache))) (if (assoc keyfile cvstree-cache) (progn (adelete 'cvstree-cache keyfile) (setq cvstree-cache (cons (list keyfile (current-time-string) cache) cvstree-cache))) (setq cvstree-cache (cons (list keyfile (current-time-string) cache) cvstree-cache)))) (save-excursion (find-file cvstree-cache-file-name) (erase-buffer) (insert (format ";; Cache file for cvstree.el version %s\n" cvstree-version)) (insert "(setq cvstree-cache '") (prin1 cvstree-cache (current-buffer)) (insert ")") (save-buffer) (kill-buffer (buffer-name)) ))) ;; ;; Buffer ;; (defun cvstree-buffer-insert-repositry (cvsdir) (let ((fname (format "%s/%s" cvsdir "Repository"))) (if (file-readable-p fname) (prog1 t (insert (cvstree-dstr-char-root)) (insert-file-contents fname)) nil))) (defun cvstree-buffer-insert-root (cvsdir) (let ((fname (format "%s/%s" cvsdir "Root"))) (if (file-readable-p fname) (prog1 t (insert-file-contents fname)) nil))) (defvar cvstree-buffer-header-end " Existing Tags:") (defun cvstree-buffer-eliminate-header () (goto-char (point-min)) (re-search-forward cvstree-buffer-header-end nil t) (delete-region (point-min) (match-end 0))) (defvar cvstree-buffer-tag-regexp "^[\t ]+\\([^ ]+\\)[\t ]+(\\(.+\\): \\(.+\\))$") (defun cvstree-buffer-parse () (let (result revision tag type elt) (goto-char (point-min)) (while (re-search-forward cvstree-buffer-tag-regexp nil t) (setq revision (cvstree-vlist-from-string (buffer-substring (match-beginning 3) (match-end 3))) tag (buffer-substring (match-beginning 1) (match-end 1)) type (if (string= (buffer-substring (match-beginning 2) (match-end 2)) "branch") 'branch 'revision)) (setq elt (cvstree-tagelt-alloc)) (cvstree-tagelt-set-vlist elt revision) (cvstree-tagelt-set-tname elt tag) (cvstree-tagelt-set-ttype elt type) (setq result (cons elt result))) result)) ;; ;; tag element structure ;; ;; cvstree-buffer-parse parses the output of `cvs status'. ;; cvstree-buffer-parse returns a list. We call this tag list. ;; And we use taglist as its name space. ;; ;; Hereafter we call the element of the list `cvstree tag element' ;; or `tag element'. We use `tagelt' as name space. ;; ;; The structure of tag element is a vector like: ;; [version-list tags-name branch-or-revision draw-string] ;; version-list-> list of number. We use `vlist' as name space ;; tags-name-> a string. We use `tname' as name space. ;; branch-or-revision-> a symbol. `branch' or `revision' is used. ;; We use 'ttype' as name space. ;; draw-string-> ... ;; ;; This is an example of tag element. ;; [(1 2 2) "dgs-0_1_1" branch ["??" "??"]] ;; vlist-> (1 2 2) ;; tname-> "dgs-0_1_1" ;; ttype-> branch ;; dstr-> ["??" "??"] ;; ;; Taglist ;; (defun cvstree-taglist-alloc-dstr (taglist size) (while (car taglist) (cvstree-tagelt-set-dstr (car taglist) (make-vector size (cvstree-dstr-char-space))) (setq taglist (cdr taglist)))) (defun cvstree-taglist-max-vlist-length (taglist) (let ((max 0) x) (while (car taglist) (setq x (length (cvstree-tagelt-get-vlist (car taglist)))) (if (> x max) (setq max x)) (setq taglist (cdr taglist))) max)) (defun cvstree-taglist-sort (taglist) (sort taglist 'cvstree-tagelt-compare)) (defun cvstree-taglist-draw (taglist) (while (car taglist) (cvstree-tagelt-insert (car taglist)) (newline) (setq taglist (cdr taglist)))) (defun cvstree-taglist-calc-dstr (taglist) (let ((max (cvstree-taglist-max-vlist-length taglist)) (length (length taglist)) (level 0) (height 0) rtaglist) (setq rtaglist (reverse taglist)) (if nil ; TODO (New tree generator) (while (car rtaglist) (let ((tagelt (car rtaglist)) (remain rtaglist) (legacy nil) (distance 0)) (while (car remain) (let ((prev (car remain)) (remain-remain (cdr remain))) (setq legacy (cvstree-tagelt-calc-dstr2 tagelt prev legacy max distance length height)) (setq distance (1+ distance)) (setq remain (cdr remain)))) (setq height (1+ height)) (setq rtaglist (cdr rtaglist))))) (if t ; (Old tree generator) (while (< level max) (cvstree-taglist-calc-dstr-with-level taglist level) (setq level (1+ level)))) )) ;; New tree implementation, still underwork (defun cvstree-tagelt-calc-dstr2 (tagelt prev legacy depth distance length height) (let ((dstr (cvstree-tagelt-get-dstr tagelt)) (pdstr (cvstree-tagelt-get-dstr prev)) (relation (cvstree-vlist-relation depth (cvstree-tagelt-get-vlist tagelt) (cvstree-tagelt-get-vlist prev))) (branch (cvstree-dstr-char-branch)) (bob (cvstree-dstr-char-bob)) (hbar (cvstree-dstr-char-hbar)) (eob (cvstree-dstr-char-eob)) (vbar (cvstree-dstr-char-vbar)) (i 0)) (setq legacy nil) (if legacy (cond ((eq (car relation) 'unrelated) ) ((eq (car relation) 'equal) ) ((eq (car relation) 'brother) ) ((eq (car relation) 'cousin) ) ((eq (car relation) 'child) ) ) (cond ((eq (car relation) 'unrelated) (if (= height 0) (cvstree-dstr-set-char dstr 0 eob) (cvstree-dstr-set-char dstr 0 branch)) (setq i 1) (while (< i depth) (cvstree-dstr-set-char dstr i hbar) (setq i (1+ i))) (cvstree-dstr-set-char pdstr 0 vbar) ) ((eq (car relation) 'equal) (if (= (1- length) height) (cvstree-dstr-set-char dstr 0 branch)) ) ((eq (car relation) 'brother) ) ((eq (car relation) 'cousin) (if (= 1 distance) (progn (cvstree-dstr-set-char dstr (1- (cadr relation)) eob) (cvstree-dstr-set-char pdstr (1- (cadr relation)) bob))) (setq i (cadr relation)) (while (< i depth) (cvstree-dstr-set-char dstr i hbar) (cvstree-dstr-set-char pdstr i hbar) (setq i (1+ i))) ) ((eq (car relation) 'child) ) ) ))) (defun cvstree-taglist-calc-dstr-with-level (taglist level) (let ((original taglist) (prev nil) (tagelt nil) (next nil)) (while (car taglist) (setq tagelt (car taglist)) (setq next (car (cdr taglist))) (setq taglist (cdr taglist)) (cvstree-tagelt-calc-dstr prev tagelt next level) (setq prev tagelt)) ;; TOOD (if (eq level 0) (cvstree-dstr-set-char (cvstree-tagelt-get-dstr (car original)) 0 (cvstree-dstr-char-bob))))) ;; ;; Tagelt ;; ;; Too long... ;; Don't read this function, this makes you crazy. (defun cvstree-tagelt-calc-dstr (prev tagelt next level) (let ((dstr (cvstree-tagelt-get-dstr tagelt)) (pdstr (cvstree-tagelt-get-dstr prev)) (prelation (cvstree-vlist-relation level (cvstree-tagelt-get-vlist prev) (cvstree-tagelt-get-vlist tagelt))) (nrelation (cvstree-vlist-relation level (cvstree-tagelt-get-vlist tagelt) (cvstree-tagelt-get-vlist next))) (branch (cvstree-dstr-char-branch)) (bob (cvstree-dstr-char-bob)) (hbar (cvstree-dstr-char-hbar)) (eob (cvstree-dstr-char-eob)) (vbar (cvstree-dstr-char-vbar))) (cond;; prelation ;; ((eq (car prelation) 'unrelated) (if next (cvstree-dstr-set-char dstr 0 branch) (cvstree-dstr-set-char dstr 0 (cvstree-dstr-char-eob))) (let ((i 1)) (while (<= i level) (cvstree-dstr-set-char dstr i hbar) (setq i (1+ i)) )) ) ;; ((eq (car prelation) 'equal) (cvstree-tagelt-set-dstr tagelt (copy-sequence pdstr)) (setq dstr (cvstree-tagelt-get-dstr tagelt)) (if (eq (car nrelation) 'equal) t)) ;; ((eq (car prelation) 'brother) (let ((pindx (cadr prelation))) (if (< 1 pindx) (progn (cvstree-dstr-set-char dstr 0 vbar))) (cond ((or (eq (car nrelation) 'brother) (eq (car nrelation) 'cousin) (eq (car nrelation) 'equal)) (cvstree-dstr-set-char dstr (1- pindx) branch)) ((eq (car nrelation) 'child) (cvstree-dstr-set-char dstr (1- pindx) "CC")) (t (cvstree-dstr-set-char dstr (1- pindx) eob))))) ;; ((eq (car prelation) 'cousin) (let* ((pindx (cadr prelation)) (i pindx)) (if (< 1 pindx) (progn (cvstree-dstr-set-char dstr 0 vbar))) (cvstree-dstr-set-char dstr (1- pindx) eob) (while (<= i level) (cvstree-dstr-set-char dstr i hbar) (setq i (1+ i)) ))) ;; ((eq (car prelation) 'child) (let ((pindx (cadr prelation))) (if (< 1 pindx) (progn (cvstree-dstr-set-char dstr 0 vbar))) (cond ((eq (car nrelation) 'brother) (cvstree-dstr-set-char dstr (1- pindx) branch)) ((eq (car nrelation) 'child) (cvstree-dstr-set-char dstr (1- pindx) eob) (let ((i pindx)) (while (<= i level) (cvstree-dstr-set-char dstr i hbar) (setq i (1+ i))))) (t (cvstree-dstr-set-char dstr (1- pindx) eob)) )))) ;; nrelation (cond ((eq 'child (car nrelation)) (cvstree-dstr-set-char dstr (1- (cadr nrelation)) bob) (let ((i (cadr nrelation))) (while (<= i level) (cvstree-dstr-set-char dstr i hbar) (setq i (1+ i)) )) ) ;; ((eq 'equal (car nrelation)) (cond ((eq (car prelation) 'equal) (cvstree-dstr-set-char dstr (1- (cadr nrelation)) hbar)) ((eq (car prelation) 'unrelated) (cvstree-dstr-set-char dstr (1- (cadr nrelation)) hbar) ) (t (cvstree-dstr-set-char dstr (1- (cadr nrelation)) "ff"))) (let ((i (cadr nrelation))) (while (<= i level) (cvstree-dstr-set-char dstr i hbar) (setq i (1+ i)) ))) ;; ((eq 'brother (car nrelation)) (cvstree-dstr-set-char dstr (cadr nrelation) branch) (let ((i (cadr nrelation))) (while (<= i level) (cvstree-dstr-set-char dstr i hbar) (setq i (1+ i)) )) ) ;; ((eq 'cousin (car nrelation)) (cvstree-dstr-set-char dstr (cadr nrelation) branch) (let ((i (cadr nrelation))) (while (<= i level) (cvstree-dstr-set-char dstr i hbar) (setq i (1+ i)) ))) ;; ((eq 'unrelated (car nrelation)) (cond ((eq (car prelation) 'brother) (let ((i (cadr prelation))) (while (<= i level) (cvstree-dstr-set-char dstr i hbar) (setq i (1+ i)) ))) ((eq (car prelation) 'child) (let ((i (cadr prelation))) (while (<= i level) (cvstree-dstr-set-char dstr i hbar) (setq i (1+ i)))))))))) (defun cvstree-tagelt-insert (tagelt) (cvstree-dstr-insert (cvstree-tagelt-get-dstr tagelt)) (insert " ") (cvstree-vlist-insert (cvstree-tagelt-get-vlist tagelt)) (insert ": ") (let ((b (point)) (e)) (insert (cvstree-tagelt-get-tname tagelt)) (setq e (point)) (put-text-property b e 'mouse-face 'highlight) ; For mule-19.34 (put-text-property b e 'highlight 'mouse)) (insert (format " [%s]" (symbol-name (cvstree-tagelt-get-ttype tagelt))))) (defun cvstree-tagelt-compare (a b) (cvstree-vlist-compare (cvstree-tagelt-get-vlist a) (cvstree-tagelt-get-ttype a) (cvstree-tagelt-get-vlist b) (cvstree-tagelt-get-ttype b))) (defun cvstree-tagelt-alloc () (vector nil nil nil nil)) (defun cvstree-tagelt-set-vlist (tagelt value) (aset tagelt 0 value)) (defun cvstree-tagelt-get-vlist (tagelt) (if tagelt (aref tagelt 0) nil)) (defun cvstree-tagelt-set-tname (tagelt value) (aset tagelt 1 value)) (defun cvstree-tagelt-get-tname (tagelt) (aref tagelt 1)) (defun cvstree-tagelt-set-ttype (tagelt value) (aset tagelt 2 value)) (defun cvstree-tagelt-get-ttype (tagelt) (aref tagelt 2)) (defun cvstree-tagelt-set-dstr (tagelt value) (aset tagelt 3 value)) (defun cvstree-tagelt-get-dstr (tagelt) (if tagelt (aref tagelt 3) nil)) ;; ;; Vlist, version list ;; (defun cvstree-vlist-from-string (str) (mapcar (function (lambda (x) (string-to-number x))) (save-match-data (if (fboundp 'split-string) (split-string str "\\.") (string-split "\\." str) )))) ;; comparison functions (defun cvstree-vlist-relation (level a b) ;; (let* ((ra (reverse a)) (rb (reverse b)) (ta (reverse (nthcdr level ra))) (tb (reverse (nthcdr level rb))) (la (length a)) (lb (length b)) (depth (cvstree-vlist-eq-depth a b))) (cond ((= depth 0) (list 'unrelated depth)) ((= depth 1) (list 'unrelated depth)) ;; OK? ((and (= depth la) (= depth lb)) (list 'equal depth)) ((eq la lb) (list 'brother depth)) ((not (= depth la)) (list 'cousin depth)) (t (list 'child depth))))) (defun cvstree-vlist-eq-depth (a b) (let* ((i 0) (la (length a)) (lb (length b)) (max (if (< la lb) lb la)) A B (break nil)) (while (and (not break) (< i max)) (setq A (nth i a)) (setq B (nth i b)) (cond ((or (eq A nil) (eq B nil)) (setq break t)) ((not (= A B)) (setq break t)) (t (setq i (1+ i))))) i)) (defun cvstree-vlist-compare (a atype b btype) (if (cvstree-vlist< a b) t (if (and (cvstree-vlist= a b) (eq atype 'revision) (eq btype 'branch)) t nil) nil)) (defun cvstree-vlist> (a b) (cond ((and (eq a nil) (eq b nil)) nil) ((eq a nil) nil) ((eq b nil) t) (t (cvstree-vlist>core a b)))) (defun cvstree-vlist>core (a b) (let ((a0 (car a)) (b0 (car b)) (an (cdr a)) (bn (cdr b))) (cond ((> a0 b0) t) ((< a0 b0) nil) (t (cvstree-vlist> an bn))))) (defun cvstree-vlist= (a b) (equal a b)) (defun cvstree-vlist< (a b) (cond ((and (eq a nil) (eq b nil)) nil) ((eq b nil) nil) ((eq a nil) t) (t (cvstree-vlist a0 b0) nil) (t (cvstree-vlist< an bn))))) ;; draw (defun cvstree-vlist-insert (vlist) (insert "") (while (cdr vlist) (insert (format "%d." (car vlist))) (setq vlist (cdr vlist))) (insert (format "%d" (car vlist))) (insert "")) ;; ;; dstr, draw string ;; ;; chars sets (defun cvstree-dstr-char-root () (if cvstree-dstr-2byte-ready "*" "*")) (defun cvstree-dstr-char-space () (if cvstree-dstr-2byte-ready " " " ")) (defun cvstree-dstr-char-hbar () (if cvstree-dstr-2byte-ready "━" "-")) (defun cvstree-dstr-char-vbar () (if cvstree-dstr-2byte-ready "┃" "|")) (defun cvstree-dstr-char-branch () (if cvstree-dstr-2byte-ready "┣" "+")) (defun cvstree-dstr-char-eob () " ;end of branch" (if cvstree-dstr-2byte-ready "┗" "+")) (defun cvstree-dstr-char-bob () ;beginning of branch (if cvstree-dstr-2byte-ready "┳" "+")) (defun cvstree-dstr-char-nil () ;unknown (if cvstree-dstr-2byte-ready "??" "?")) ;; draw (defun cvstree-dstr-insert (dstr) (let ((length (length dstr)) (i 0) c) (while (< i length) (setq c (aref dstr i)) (insert c) (setq i (1+ i))))) (defun cvstree-dstr-set-char (dstr index char) (aset dstr index char)) ;; ;; Mode ;; (defvar cvstree-mode-map nil) (if cvstree-mode-map nil (setq cvstree-mode-map (make-keymap)) (define-key cvstree-mode-map "n" 'next-line) (define-key cvstree-mode-map "p" 'previous-line) (define-key cvstree-mode-map "g" 'cvstree-cmd-force-update) (define-key cvstree-mode-map "." 'cvstree-cmd-show-tag-name) ;; (define-key cvstree-mode-map ">" 'cvstree-checkout) ) (put 'cvstree-mode 'mode-class 'special) ;??? what (defun cvstree-mode () (setq mode-name "CVSTree") (setq major-mode 'cvstree-mode) (setq mode-line-process nil) (shrink-window-if-larger-than-buffer) (setq buffer-read-only t) (use-local-map cvstree-mode-map) (run-hooks 'cvstree-mode-hook)) (defun cvstree-mode-pick-tag (p) (save-match-data (save-excursion (goto-char p) (beginning-of-line) (if (re-search-forward " \\([.0-9]*\\): \\(.*\\) \\[\\(.*\\)\\]$" nil t) (let ((elt (cvstree-tagelt-alloc))) (cvstree-tagelt-set-vlist elt (cvstree-vlist-from-string (buffer-substring-no-properties (match-beginning 1) (match-end 1)))) (cvstree-tagelt-set-tname elt (buffer-substring-no-properties (match-beginning 2) (match-end 2))) (cvstree-tagelt-set-ttype elt (make-symbol (buffer-substring-no-properties (match-beginning 3) (match-end 3)))) elt) nil)))) ;; ;; Cmd, command ;; (defun cvstree-cmd-force-update () (interactive) (if (y-or-n-p "Update tree? ") (cvstree cvstree-buffer-keyfile t))) (defun cvstree-cmd-show-tag-name () (interactive) (let ((tagelt (cvstree-mode-pick-tag (point)))) (if tagelt (message "Tag: %s" (cvstree-tagelt-get-tname tagelt)) (message "No tag entry here")))) (provide 'cvstree)