MapReduce向けの小さなクラス(Hash+Array版、SBBTree版、TokyoCabinet版)のデモのために、小さな検索エンジンのデモを書こうと思ったんだけど、普通のファイルの全文検索じゃなんだよなー、ということでCVSリポジトリの全リビジョンについてインデックスを作ってみようかな、というネタのためにRCSファイルを調べてそれぞれのリビジョンで増えた行、減った行を求めるスクリプトを書いたので、そのサンプルはgrepぽいのにしてみた。akrさんのRuby/CVSがすげーかっこいい。
require 'rcs' class RCSCrawler class Emitter def delta=(delta) @delta = delta @trunk = @delta.rev.on_trunk? end def add(line, &blk) @trunk ? add_trunk(line, &blk) : add_branch(line, &blk) line end def del(line, &blk) @trunk ? del_trunk(line, &blk) : del_branch(line, &blk) line end def add_trunk(line) yield(@delta.prevrev, :del, line) end def add_branch(line) yield(@delta.rev, :add, line) end def del_trunk(line) yield(@delta.prevrev, :add, line) end def del_branch(line) yield(@delta.rev, :del, line) end def initial_text(rev, text) text.lines.each do |line| yield(rev, :add, line) end end end def crawl(rcs, &blk) out = Emitter.new text = Hash.new text[rcs.head] = RCS::Text.new(rcs[rcs.head].text) min = rcs.head rcs.each_delta do |d| next unless d.prevrev out.delta = d text[d.rev] = text[d.prevrev].patch(d.text, lambda {|line| out.add(line, &blk)}, lambda {|line| out.del(line, &blk)}) min = d.rev if d.rev < min text.delete(d.prevrev) if rcs[d.prevrev].branches.size == 0 end out.initial_text(min, text[min], &blk) end end if __FILE__ == $0 reg = Regexp.new(ARGV.shift) rcs = RCS.parse(ARGV.shift) RCSCrawler.new.crawl(rcs) do |rev, mode, line| next unless reg === line p [rev.to_s, mode, line] end end
具は15行くらいのcrawlメソッドで、あとは化粧箱。
branches.sizeが0のときにdeleteしてるあたりが、富豪家になりきれなくて貧乏臭いなあ。
ところで最小のrevisionが1.1以外のことってあるんだろうか。