git: リモートブランチに直接コミットしては駄目

2008-02-26T01:57:00 / git / comment

本格的にgitを使い始めたのだけど、どうもgitではリモートブランチに直接コミットしては駄目らしい(commitが駄目なのだから、mergeもrebaseもpullも駄目、なはず)。一応、コミット自体はできるし、その結果もちゃんとリポジトリに保存されるのだけど、一旦他のブランチに移ってしまうと簡単には元の位置に戻れなくなる。具体的には次のような感じ:

# 現在作業中のブランチがmasterだったとする。
$ git branch -a
* master
  svn/trunk

# リモートブランチsvn/trunkに移動する。
$ git checkout svn/trunk
Note: moving to "svn/trunk" which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b <new_branch_name>
HEAD is now at 1FFFFFF... Fix the bug #1.

# 「どのブランチにもいない」という扱いになる。
$ git branch -a
* (no branch)
  master
  svn/trunk

# このまま変更を行って、
$ $EDITOR hello.c

# コミットしてみる。
$ git commit -m 'Fix the bug #2.' -i hello.c
Created commit 2FFFFFF: Fix the bug #2.
 1 files changed, 1 insertions(+), 1 deletions(-)

# 一旦masterに戻って、
$ git checkout master
Previous HEAD position was 2FFFFFF... Fix the bug #2.
Switched to branch "master"

$ git branch -a
* master
  svn/trunk

# 再びsvn/trunkに移動すると、
# svn/trunkはそのままで、先程の変更内容は含まれていない。
$ git checkout svn/trunk
Note: moving to "svn/trunk" which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b <new_branch_name>
HEAD is now at 1FFFFFF... Fix the bug #1.

と言う訳で、リモートブランチはあくまで参照用らしい。

もしリモートブランチの内容をベースに作業をしたいのであれば; (1)リモートブランチをベースにローカルブランチを作成し(git checkout -b remote-branch local-branch); (2)ローカルブランチで作業し; (3)作業結果を反映させたければpushする; とするべき。

上記の例では消えたように見えるコミットはリポジトリに残っているので、git checkout 2FFFFFFと適切なオブジェクトを指定すれば元の位置に戻ることができる。しかし簡単に参照できないことと、リポジトリにゴミを残したような感じになってしまうので、できる限り避けるべきだろう。

GNU screen使用時に端末のタイトルが設定できない

2008-02-25T03:00:00 / nix, screen, putty / comment

GNU screenの外でなら問題なくできるのですが、GNU screenの中でだと端末のタイトルを設定することができません。自分で調べた範囲では、タイトル設定のエスケープシーケンスはGNU screen側で傍受され、内容自体はhardstatus等で%hを使えば参照できるが、端末には何も送られないのでタイトルは変化しないということは分かりました。

それならhardstatusの出力を利用して設定できないだろうかと思って、hardstatus string "\033]2;%h\007foobar"のような設定をしたらGNU screenがフリーズしてしまった。外部からdetachもできない。何故だ。

Vim: gitのブランチ情報を'tabline'に表示

2008-02-25T01:53:00 / vim, git / comment

gitだと気軽にブランチが作れて簡単にマージできるので、稀にどのブランチで作業していたのか混乱する。と言う訳で、現在どのブランチで作業しているかをVimの'tabline'に表示させてみた。平たく言うとtypesterさんがzshのプロンプトでやってたことの真似。

git branch name showed in Vim's 'tabline'

最初は'statusline'に表示させようと思ったけれど、他の情報でスペースに余裕がない場合が多く、頻繁に再描画されることから'tabline'にしてみた。この表示が有意義かどうかは微妙なところ。

一部リモートブランチの名前が取得できていなかった点を修正しました。

'tabline'の表示も微妙に変えてみました。あと、gitではリモートブランチに直接コミットすべきではないので、その辺も考慮して配色を変えたらいいかなぁ。

Vim: 'tabline'/'statusline'が再描画されるタイミング

2008-02-24T23:42:00 / vim / comment

ちょっと気になったのでVimの'tabline'が再描画されるタイミングを調べてみた。大雑把に言って、次のどれかが発生する度に再描画が行われると考えていい:

また、'statusline'の再描画は上記のタイミング+カーソル移動時に行われるものだと思われる(=細かいところまでは調べていない。途中まではソースを調べてみたけど、そこまでする程のことではないので止めた)。

Vim: Normal modeでマルチバイト文字を入力したら自動的にInsert modeに移行(できたらいいな編)

2008-02-23T03:51:00 / vim / comment

Vimで、稀にInsert modeに移行するのを忘れてNormal modeのままでマルチバイト文字を入力してしまうことがあるので、自動的にInsert modeに移行してはどうだろう、と思った。

でもどうやって実現しよう? 単純な方法としては「nnoremap あ iあ」のようなkey mappingsを定義するということが考えられるけれど、考えられる入力の種類を考えると現実的ではない。しかしVim scriptのレベルではkey mappings以外の方法でキー入力は取れないはず(getchar()はあるけど今回の用途には使えない)。となると本体に手を入れるしかないかなぁ。UndefinedNormalCommandみたいなeventがあればいいんだけど。

gitに移行中

2008-02-23T03:23:00 / nix / comment

常用しているバージョン管理システムを、CVS、Subversion、arch (途中で挫折)、SVKと流れてきて、現在gitに移行中。コンセプト的に馴染みのない点が多少あるので習熟するまでには時間がかかりそうですけど、今まで使ってきたものと比較して、いや、比較するまでもなく高速なので、それだけでも十分価値があると思っています。

使い始めで戸惑ったところ
CVS等でいう中央集権的なリポジトリはない。「リポジトリ作成→チェックアウトして作業ディレクトリ作成」という流れに慣れ過ぎていたので、git-initだけで準備完了と言われてもピンとこなかった。
indexの概念。これは今も納得し切れていない。
コマンド数の多さ。よく使うものに絞ればほとんど無視できるのだけど。あと、類似したニュアンスの単語のコマンドは差異が何なのかで迷う。
git {subcommand}とgit-{subcommand}の両方が用意されていること。
まだ把握し切れていないこと
SVKのように、複数のSubversionリポジトリに追従したり、それらの間で相互にマージすること。
そもそも他のリポジトリとの相互運用方法を把握し切れていない。

ドジ: インスタントコーヒーだばぁ

2008-02-20T02:35:00 / diary / comment
  1. インスタントコーヒー(瓶入り)が切れた。
  2. 詰め替え用パックから中身を補充した。
  3. 一杯目を淹れて、飲んだ。
  4. 一時間後、二杯目を入れようとして、切れかけ状態と同じ加減で瓶を傾けてしまい、コップの二分目ぐらいまで粉がだばぁ。
  5. 仕方なく大きめのコップに移してお湯を投下。
  6. さすがににがい。(いまここ)

Vim: エンコーディング関係の設定の仕方

2008-02-20T00:05:00 / vim / comment

Vimでのエンコーディング関係のオプションは次の通り:

'encoding'
グローバルなオプション。Vimが内部で用いるエンコーディングを指定する。バッファやレジスタやVimスクリプトでの文字列の中身はこのオプションで指定されたエンコーディングが用いられる。また、他のエンコーディング関係のオプションが未設定ならばデフォルトの値としても用いられる。
基本的にvimrc以外で変更すべきではない。また、このオプションを変更しても既存のバッファやレジスタやVimスクリプトでの文字列の中身のエンコーディングは変化しない。
(なお、ヘルプでは明言されていないがsystem()の結果は常に'encoding'として扱われ、'fileencoding'への変換は行われない。変換を期待する場合は:read!を使う必要がある。このためにvcscommandは一部で不具合が出る。)
'fileencoding'
バッファローカルなオプション。バッファの保存時に用いるエンコーディングを指定する。このオプションと'encoding'の値が異なる場合、後者から前者へのエンコーディングの変換が行われる。特に設定されていない場合、'encoding'の値が用いられる。
現在のバッファの内容を異なるエンコーディングで保存したい場合は:setlocal fileencoding={encoding}として値を変更してからバッファを保存する(:setは副作用があるので基本的に:setlocalを使うべき)。
'fileencodings'
グローバルなオプション。Vimが既存のファイルを開く際、適切なエンコーディングを自動的に判定するために用いられる。値はエンコーディング名をコンマ区切りで並べたものである。判定方法は単純で、Vimが既存のファイルを開いた場合、このオプションで指定されている各エンコーディングから'encoding'への変換を試し、最初に変換に成功したエンコーディングが'fileencoding'として用いられる。
指定されている全てのエンコーディングで変換が失敗した場合、'encoding'の値が'fileencoding'として用いられる。また、'encoding'で指定された値は変換の成否にかかわらず無条件に'fileencoding'として用いられる。よって'encoding'で指定された値は'fileencodings'に含めるべきではない。いつのまにか「'encoding'と同じ値は変換の成否にかかわらず無条件に用いられる」はなくなっていた。ただ誤解釈を避けるためできるだけ後にしておいた方が良いことは確か(例えばiso-2022-jpは最初の方におかないと誤解釈され易い)。
基本的にvimrc以外で変更すべきではない。
エンコーディングが誤認された場合は:edit ++enc={encoding}としてファイルを開きなおす(入力が面倒なのでエンコーディング指定済みのコマンドを作成しておくと便利)。
'termencoding'
グローバルなオプション。端末の出力に用いられるエンコーディングを指定する。特に指定されていなければ'encoding'の値が用いられる。
例えばCygwinなマシンからLinuxなマシンにSSHで接続してVimを起動した場合、Linux側のVimで:set termencoding=cp932等とすればよい(GNU screen使用時はそちら側で対処するという方法もある)。

設定例: 基本的な値の設定(ベースは香り屋版Vimのもの) / ファイルを開いた際にその内容にマルチバイト文字が含まれていない場合の対処

なお、$ENV_WORKINGだの$ENV_ACCESSだので切り分けているのは複数環境で作業するケースを考えての対策です(これらの環境変数の値はシェル側で設定しています)。必要に応じて削除すれば流用可能なはずです。また、後者の設定の:autocmdについてはMyAutoCmdを削除すれば流用可能なはずです(これは自分で定義したautocmdをリセットし易くするための処置です。そうしないとvimrcのリロードがし難くなるのです)。

Vimのダメなところ: Vim Scriptがアレ

2008-02-14T00:26:00 / prog, vim / comment

書けば書くほどVim Scriptのアレな点が気になってしょうがない。Vim scriptは基本的にCommand-line modeで入力するEx commandsをそのまま書き下しただけのもので、インタラクティブにちょちょっと使っているだけなら特に気にならないのですが、プログラミング言語として見るとかなりアレです。

Ex commandsをベースにしていることに由来する制限事項
基本的に1行につき1コマンドしか記述できない。
  • 一応、各コマンドを|で区切ることで1行に複数のコマンドを記述することができるが、コマンドによっては|を引数として解釈するために後続に複数のコマンドを記述できない場合がある。その一貫性のなさ。
  • 1コマンドが複数行に渡る場合、必ず行継続の記号\を書かなければならない。しかも\は後続の行頭に書かなければならず、他のほとんどの言語のスタイル(行末に特定の記号を記述するか、文脈に応じて適宜判定されるか、そもそも改行に深い意味がないもの)と比較すると違和感がある(これは各コマンドの引数の解釈方法が統一されてないことが原因で、行頭以外の場所の文字を使うことが不可能に近いからだ)。
各コマンドごとに引数の解釈がバラバラ
  • 適切なsyntax highlightの設定を書くことが非常に困難である。どのぐらい困難かというと、Vim自身に添付されているvim scriptのsyntax highlightの設定ですら不完全である($MYVIMRCでごく一部については修正するようにしているけれど、微妙なレベルの修正が多く神経を使うため、九分九厘放置気味)。
Vimの実装に由来しているであろうダサいインターフェース
tabpagesの各種情報を得るには各tabpageに対応する「番号」を使わなければならない(例: tabpagebuflist(3))。
  • 最悪なのが、この「番号」に対応するtabpageが固定でないこと。「番号」はtabpageの配置によって決まり、最も左側のtabpageが1、その右隣が2、以下1づつ増加していく。例えば3に対応するtabpageは左から3番目のtabpageである(これをAとしよう)。別のtabpage Bとtabpage Aの位置を入れ替えたとすると、その時点から3に対応するtabpageはAからBに変化するということだ。
  • (せっかくVim7でDictionary-function等のそれなりの機能が追加されたのだから、「番号」の代わりにtabpageを表すDictionaryを使えばいいのに)
windowsの各種情報を得るには各windowに対応する「番号」を使わなければならない。細かいことはtabpageの「番号」と同様。ただ、tabpageと違ってwindowの配置は立て分割と横分割が入り混じった複雑な状態になり得るため、「3番は上から3番目のwindow」のような単純な解釈はできない。
buffersの各種情報を得るには各bufferに対応する「番号」を使わなければならない。ただ、tabpageやwindowと異なり、「番号」はbuffer毎に一意で固定であるため、比較的軽傷ではある(ただbufferが作成される度に1づつ増加していくため、長期間起動しているとアレな気もするが、まあ実用上は問題ないか)。
ユーザーインターフェースが多過ぎる。あるいは統一されていない。
一つの機能を提供するにしても、key mappings、Ex commands、functionsと3つものインターフェースがある。プラグインを書く場合はこれらを一々書くのが結構面倒臭い。
Key mappingsもEx commandsもfunctionsも各々にしかない機能がある。つまり、全ての機能がどれか一つのインターフェースに統一されていない。
  • 例えばカーソル移動のhjklは、それに対応するEx commandsやfunctionsは存在しない(同等の機能を実現することは不可能ではないが、Emacsのように対応する関数がある訳ではない)。
  • このため、key mappingsでしか提供されていない機能を使うには:normal! hjklのような間接的な方法を取るしかない(全部functionsで統一すればいいのに)。
Key mappingsの諸問題
Key mappingsでしか提供されていない機能は、名前でその機能を参照できないため、key mappingsの記述の解読は困難になりがち。
名前のあるEx commandsやfunctionsに比べると覚えにくかったり検索しづらかったりする。
ポータブルなkey mappingsを記述することが難しい、というか面倒臭い。
  • Key mappingsの定義時、{rhs}のresursive/nonrecursiveの指定はどちらか一方でなければならず、一部だけに限定することはできない。
  • 安易にrecursiveなkey mappingsを使うと、他のkey mappingsの定義のために期待通りの動作を得られなくなる可能性が高い。
  • かといって全てnonrecursiveにすると記述が面倒になりがちか、不可能に近くなる(例: 2-stroke keysのprefixを入れ替える)。
一部の状況下ではkey mappingsが無効になる。常にデフォルトのものを使わなければならない(例: ins-completion-menuが表示されているときの一部キー)。
:command:functionの名前の制限
ユーザー定義の:command:functionの名前は大文字で始めなければならない。
  • 組み込みのものとの区別のためらしく、実際、実装の方もそれを当てにしたコードが書かれている。
  • :commandはまだ一貫性があるのでマシ。
  • :functionの方は次の場合ならこの名前の制限がないため、一貫性に欠ける: (1) スコープの接頭辞を明示する(s:foo()); (2) numbered-functionによる定義を行う(:function foo.bar()); (3) autoloadされる関数を使う(foo#bar())

他にも色々とあった気がしますけど、ぱっと思いついたのはこれぐらい。後で思い出したら追記しておきます。

:command:functionの名前の制限」を追加。

Metaobject Protocol on Vim Script

2008-02-12T23:47:00 / prog, vim / comment
  1. VimスクリプトでMOPを実装するというネタを思いつく(一年位前)。
  2. しかしやる気が出なくて放置。
  3. 今日になって急にやる気が出てきた。
  4. でも体調が思わしくなくて端末の電源を落とした(いまここ)。

MOP自体は実装できるのでしょうけど、問題はそれをどういうインターフェースで提供するかということ。一番の問題はメソッドの定義。簡単な構文なら:commandで対処できますが、:if-:endifのような入れ子構造になったものは:commandでは対処できません(まあ不可能ではないですが、:DefMethod-:endfunctionのようなペアを書かなければならず、凄くアレ。:EndMethod的なものを用意しても、Vimスクリプトの仕様上、それは読み飛ばされるので、対応関係が崩れてエラーになるのがオチ)。

どうしたものかな。まあこれを頑張るよりはGauche interface辺りを書いた方が有意義だと思うし。

Growing shell, or usefulness of interactive_comments

2008-02-11T00:51:00 / nix / comment

I usually use bash as my shell. It has the interactive_comments option which allows user to use comments in an interactive shell. I was recently noticed that interactive_comments is useful to name each command line. For example:

$ svk sync -s HEAD //mirror/coderepos  #crs

$ svk smerge --log //mirror/svn-repos/config/trunk/vim //mirror/coderepos/dotfiles/vim/kana  #crv

... some days later ...

$ !?#crs

$ !?#crv

Here I use long history of command lines (HISTSIZE=50000) and history expansion to execute each command line. !?string means to refer to the most recent command line containing string. The comments (#crs and #crv in this example) are enough to identify each command line.

Of cource, they should be rewritten in aliases, functions or scripts if they are frequently used (e.g. alias ll='ls -l') or they are considered as single and generic commands (e.g. function mount-x which I use to mount a Windows' drive from coLinux). But the above example aren't enough to satisfy these conditions, and there are many command lines like them. So interactive_comments is useful.

After a long time, my history contains many "named" commands. Sometimes it's necessary to fix "named" commands for some reasons. New definitions are automatically used as the defaults, but it's possible to refer older definitions by reverse-search-history. I feel that the shell is "growing", in a sence.

「逆襲8BIT」購入

2008-02-02T13:36:00 / mus, dojin / comment

DeviceHighの新作「逆襲8BIT」を購入しました。久々のアルバムですよ! 鼻血出そう! 合間合間にシングルがリリースされていたので気付きませんでしたが、前回のアルバムから約6年(と少し)もの間があったのですね。思えば遠くへ来たもんだ。

取り敢えずリッピングの合間にジャケットや歌詞カードを眺めていたのですが、既にその時点で笑いが。まずは歌詞カード表表紙イラストのれむにゃんが持っているもの。今までずっと鞄だと思っていたのですが、よく見ると大昔のノートPCかワープロ機器でした(この辺りは疎いので名前が出てこない)。歌詞カード裏表紙はREMさんの写真だったのですが、表表紙と一緒の構図で、かつ大昔のノートPCだかワープロ機器だかの実物を持っていて大笑いでした。「実物あったのか!」と。後は歌詞から漂うDeviceHigh臭にくらくらしたり、どういうメロディが付くのか想像したり。

で、曲は一通り聴き終えましたが、いつもどおりのDeviceHigh風味でした。でも以前より所々が尖っていて、感心したり笑ったりで大忙しです。駄目だ、今も聞きながら書いていたら腹筋が痛んで集中できない。