Git(ギット)勉強会メモに触発されてGitの利用を再検討。
あたりの機能が便利そうなので乗り換えることにした。 以前評価したときはCogitoでラップする必要があったりと使いづらいものだったけれど、 最近のバージョンはインターフェースもこなれていて悪くない。 ただ、Mercurialのリビジョンナンバー(ローカルでのみ有効な連番ID)相当の機能がないのが残念といえば残念か。
以下、試していて気になったポイントについて。
Gitのアーカイブに含まれるcontrib/hg-to-git/hg-to-git.pyで変換が可能。
「.gitconfigでaliasを設定すればよい」という解決策がすぐに見つかるものの、 それだとzsh上で補完が効かなくなるという問題がある。
これはグローバルエイリアスを設定して解決。
$ alias -g ann=annotate $ alias -g ci=commit $ alias -g co=checkout $ alias -g di=diff $ alias -g st=status
CVS HEADでは実装されているので、 そこから_gitを持ってきてfpathに配置すればOK。
revertの機能がMercurialのそれと異なるので注意。
$ hg revert --no-backup
に対応するコマンドは
$ git reset --hard (HEAD)
になる。
車輪の再発明のような気もするけれど。
次のようなWSFファイルを作成する。
<job id="sample">
<script language="JScript" src="pre_prototype.js"/>
<script language="JScript" src="prototype.js"/>
<script language="JScript" src="post_prototype.js"/>
<script language="JScript">
(3).times(function(i) {WScript.Echo(i)});
</script>
</job>
prototype.jsはブラウザで動かすことを前提とした作りになっているために、そのままではWSHでロードすることができない。 適当にダミーオブジェクトを定義する必要あり(pre_prototype.js)。
// For prototype.js version 1.6.0
var document = {
createElement: function() {return {appendChild: function(){}}},
createTextNode: function() {return {}},
createEvent: function() {return {__proto__: {}}},
write: function() {return {}},
getElementById: function() {return {}}
};
var window = {};
var navigator = {appVersion: ""};
var Element = {};
また、オートメーションオブジェクトにtoStringプロパティが定義されておらず Object.inspectの呼び出しに失敗するので、該当関数を再定義してみる(post_prototype.js)。
Object.extend(Object, {
inspect: function(object) {
try {
if (object === undefined) return 'undefined';
if (object === null) return 'null';
return object.inspect ? object.inspect() :
(object.toString ? object.toString() : "#<UnknownObject:" + object + ">");
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
}
}
});
ruby-breakpointはコード中に"breakpoint"と書いておくと実行時にその環境でreplに突入してくれるというもの。 オリジナルは複数行にわたる入力やdRubyを用いたリモートアクセスもサポートするが、そこまで考えなければ簡単に似たようなものが用意できる。
(function(i) {eval(breakpoint)})(0);
実行結果。
> i + 1 1
var breakpoint =
'for(;;) {' +
' WScript.StdOut.Write("> ");' +
' if (WScript.StdIn.AtEndOfStream) break;' +
' var expr = WScript.StdIn.ReadLine();' +
' try {' +
' WScript.StdOut.WriteLine(Object.inspect(eval(expr)));' +
' }' +
' catch (e) {' +
' WScript.StdOut.WriteLine("Error Code: " + e.number);' +
' WScript.StdOut.WriteLine(e.description);' +
' }' +
'}';
例外をキャッチしてスタックトレースを付与する関数でラップする。
function func1(i, j) {
(function () { func2("foo") })([]);
}
function func2(i, j) {
throw new Error("ERROR");
}
func2 = throwsInformationalError(func2);
var Test = Class.create({
func3: throwsInformationalError(function(i, j) {
this.func4("bar");
}, "func3"),
func4: throwsInformationalError(function(i, j) {
throw new Error("ERROR");
}, "func4")
});
それぞれ呼び出したときの実行結果。
func1();
test.wsf(149, 10) Microsoft JScript 実行時エラー: ERROR
--- Stack Trace ---
func2('foo')
[anonymous]([])
func1()
(new Test()).func3()
test.wsf(149, 10) Microsoft JScript 実行時エラー: ERROR
--- Stack Trace ---
func4('bar')
func3()
Object.extend(Function.prototype, {
getName: function () {
if (this.name) return this.name;
var regmatch = this.toString().match(/^function\s*(\w+)/);
return regmatch ? regmatch[1] : '[anonymous]';
}
});
function throwsInformationalError(fn, fn_name) {
function _throwsInformationalError() {
try {
return fn.apply(this, arguments);
}
catch (e) {
if (! e.informational) {
var stack = [];
for (var i = arguments.callee; i; i = i.caller) {
var args = [];
for (var j = 0; j < i.arguments.length; j++) {
args.push(Object.inspect(i.arguments[j]));
}
var _fn = (i == arguments.callee) ? fn : i;
if (_fn.getName() != "_throwsInformationalError") {
stack.push(_fn.getName() +"(" + args.join(", ") + ")");
}
}
e.description += "\n--- Stack Trace ---\n" + stack.join("\n");
e.informational = true;
}
throw e;
}
};
if (fn_name) {
fn.name = fn_name;
}
return _throwsInformationalError;
}
JScriptのfor...in文とVBScriptのFor Each...Next文は言語仕様的には似たようなものだけど、 その実装には決定的な違いがある。その差を埋めるためにEnumeratorが用意されてはいるけれど、 それでもなおカバーしきれないオブジェクトが存在する。ADSIによる属性アクセスなどがそれ。
> var o = GetObject("LDAP://DC=example,DC=com")
#<UnknownObject:>
> o.objectClass
[object Object]
> for (var i in o.objectClass) {WScript.Echo(i)}
undefined
> new Enumerator(o.objectClass)
オブジェクトがコレクションではありません。
ここで各要素にアクセスする方法を調べてみるも、MicrosoftのサンプルはVBScriptでFor Each...Nextしているものばかり。 しょうがないのでVBScript側でFor Each...Next文相当のプロシージャを用意しておいてWSFファイルで読み込んで利用する。 *1
Sub foreach(obj, fn)
Dim i
For Each i In obj
Call fn(i)
Next
End Sub
JScript側からはこのように。
> foreach(o.objectClass, function(i) {WScript.Echo(i)})
top
domain
domainDNS
undefined
*1 ただ、この例ならばo.objectClass.toArray()とやればアクセス可能。
Mercurial 0.9.4 released!より。 あまり取り上げられていないようだけど、地味に使いづらいところだったのでありがたい。
クイックハックだけどなかなか便利に。
--- mobileimap.in.orig 2005-05-22 16:04:40.000000000 +0900
+++ mobileimap.in 2007-05-19 21:55:10.000000000 +0900
@@ -862,7 +862,7 @@
:seq => seq)
begin
generate_from_line(g, seq, nmessages) +
- g.a(:href => url) {
+ g.a(:href => url, :accesskey => nmessages - nth(seq) + 1) {
emphasize(g, compact_subject(get_subject(seq)).escapeHTML)
} + g.hr
rescue Exception => e
@@ -1436,6 +1436,7 @@
" " + g.a(:href => "#bottom") { "↓" } +
g.br + generate_seen_mode(g) + g.hr +
generate_search_form(g) +
+ g.a(:name => 0, :href => "#0", :accesskey => 0) { "→" } + g.br +
generate_list(g, nmessages, messages) +
navi + " " +
g.a(:href => generate_url(:command => "folders",
@@ -1725,6 +1726,7 @@
g.a(:href => "#bottom") { "↓" } + " " +
generate_seen_mode(g) + " " + g.hr +
generate_fsearch_form(g) +
+ g.a(:name => 0, :href => "#0", :accesskey => 0) { "→" } + g.br +
folders.map {|folder|
line = g.font(:color => @seq_color) { sprintf("%03d", i) } +
if get_unseen_nmessages(folder) > 0 then
@@ -1733,7 +1735,8 @@
" "
end +
g.a(:href => generate_url(:folder => folder,
- :unseen_only => @query.unseen_only)){
+ :unseen_only => @query.unseen_only),
+ :accesskey => i){
simplify_folder_name(folder)
} + g.br
i += 1
メモ。
[HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice] "Progid"="Sleipnir.API"
ちょくちょく利用されているようなので、Gauche 0.8.9リリースにあわせて更新。
ついでに任意のバージョンのドキュメントを用意したい人向けにこの生成方法について簡単に書いておきます。 キモはisearch.html中の<table id="all">の中身なんですが、この部分はHTML化されたGaucheユーザリファレンスを適当なディレクトリに展開したあとその中で
grep 'class="summary-letter"' gauche-refj*|\ grep --only-matching 'gauche-refj_[0-9]*\.html'|sort|uniq|\ xargs grep --no-filename '^<tr><td></td><td valign="top"><a href="gauche-refj'|\ sed 's/a href/a target="ref" href/g'
としたものを埋め込んでいます。あとは必要なファイルを落として同じディレクトリに置けば完成。
CVSによるオープンソース開発にも話題が出てるけど、 実際にやってるって話をとんと聞かない/etcのバージョン管理。これをMercurialを使って試してみた。
この用途にCVSやSubversionではなくMercurialを使うメリットは少なくとも2つ考えられる。
試してないけど、サーバ間で変更の同期を取るのにも使えるかも。
で、初めて本格的にMercurialを使い始めたのだけど、いろいろカスタマイズしたいところが出てきた。
標準のhg logの出力は次のような形で、コミットメッセージはそのsummary(=1行目)が出力される。 つまり、コミットメッセージが複数行に渡る時はそのすべてを確認することが出来ない。
changeset: 3:b9d48fc01c60 user: User <user@example.com> date: Thu Jan 05 21:00:27 2007 +0900 summary: commit message line1.
-vオプションを使うというのも一つの手だけど、もう少し柔軟に何とかならないものかと調べてみると--styleオプションが使えそうだということが分かった。
hg log [OPTION]... [FILE]
options:
--style display using template map file
オリジナルのテンプレートはDebianの場合/usr/share/mercurial/templates/map-cmdline.defaultにあるのでそいつをカスタマイズして使えばいいみたい。
# mkdir ~/.hg
# cp /usr/share/mercurial/templates/map-cmdline.default ~/.hg/map-cmdline.custom
# vi ~/.hg/map-cmdline.custom
-changeset = 'changeset: {rev}:{node|short}\n ... summary: {desc|firstline}\n\n'
+changeset = 'changeset: {rev}:{node|short}\n ... description:\n{desc|strip}\n\n'
# cd /etc
# hg log --style ~/.hg/map-cmdline.custom
changeset: 3:b9d48fc01c60
user: User <user@example.com>
date: Thu Jan 05 21:00:27 2007 +0900
description:
commit message line1.
commit message line2.
うん、出来た。標準でこのフォーマットを使いたいなら次のようにする。
# vi ~/.hgrc [defaults] log = --style /root/.hg/map-cmdline.custom
先月末に公開されたので少し触ってみた。 結論から言えば、いろいろと改善点は見られるもののどうにも微妙といった印象。
純粋に機能の向上を評価できた数少ない機能の1つ。 XP Professionalにおける状況を以前書いたけど(その1、その2)、 その辺の問題がきちんと解決されている。
コマンドプロンプトの組み込みコマンドmklinkを使ってシンボリックリンクが作れるように。 これまでサポートされていたジャンクションとの違いはリンク/ジャンクション作成ツールにまとめられている。 ただ、一般ユーザで作成するにはあらかじめ「ローカルセキュリティポリシー」-「ローカルポリシー」-「ユーザー権利の割り当て」-「シンボリック リンクの作成」でユーザを登録しておかなければならない。
一見便利そうなんだけど、ファイルに対するシンボリックリンクは、リンク元の方を開いてから保存しようとすると新しいファイルとして保存されてしまうことがある(xyzzyで確認)。Vista付属のメモ帳ではきちんとリンク先が更新されたけど、アプリケーションによって扱いが変わってくるようではどう考えても実用的とは言えない。
これのおかげでOSインストール直後のさまざまな初期セットアップも標準ユーザーとしてログオンしつつ作業することが出来た。 事実上管理者として作業するしかなかったこれまでの状況と比べれば大きな進歩だけど、その仕様に「?」となるところも多々ある。
例えばWindows Vista: UAC の抜け道を考える4で挙げられている ファイル名の問題。setup.exeという実行ファイルについて、「管理者として実行」「実行しない」の2つの選択肢しかないので、 Cygwinを自身のホームディレクトリにインストールしたいなんて時にはインストーラをリネームするしかない。
いろいろ書いたけど、では今Vistaを使うかと聞かれたら間違いなくNoだ。 というのも、ウィンドウの最大/最小化やメニューの表示などのアニメーションを無効にすることが出来なくなっているから*1。 正確には「無効にすることは出来る。再起動*2すると勝手に有効になる」。……どうしようもないね。
他にも、クラシックスタートメニューの「ログオフ(L)...」をクリックするとダイアログボックスが出ることなくログオフしたり、システムにグローバルな設定がユーザによって変わっているように見えたり、IE7が起動しなくなったりと完成度が高いとは言えない。 今後修正されることを期待。
ウィンドウの最大/最小化やメニューの表示などのアニメーションを無効にすることが出来なくなっているから。 正確には「無効にすることは出来る。再起動すると勝手に有効になる」。
これ、やり方勘違いしていたからだったようだ。 これまではXPの時と同じように「システムのプロパティ」 - 「システムの詳細設定」から「パフォーマンス オプション」を開いていたのだが、 コンパネの「パフォーマンスと情報」から「視覚効果の調整」と進めないとだめらしい。
せっかく256色使えるんだから、ということで 開発環境 Screen x PuTTY x Emacs x 256color : Hasta Pronto.orgを 参考に作業した。OSはDebian sarge。
リンク先の流れに沿って進めればいいんだけど、最後に/etc/screenrcにあるxterm用の設定をxterm-256color用に移植しないとscreenが きちんと動いてくれなかった。