ファイル更新に耐性のあるテキストタグ手法を考える

コンパイラの警告やシナリオのコメント、クラッシュレポートの処理等、"行の特定位置にタグを振りファイルが更新されても追跡したい"という需要がそれなりにある。...需要自体は有るんだけど、実装がバラバラになってしまっているので、ちゃんとモデル化し、フレームワークとして統一を図りたい。

行コンテンツとblame履歴によるアドレッシング

今回考えたタグのモデルは、ファイルを行に分割し、各行のコンテンツおよび初出現リビジョンのハッシュをアドレスとする。

このアドレッシングにより、ファイルが更新されたとしてもタグを頻繁に書き換える必要は無い。もちろん、行を書き換えた場合はハッシュ値が変わるためタグも"打ち直し"が必要になるが、それでも行番号を使用した場合よりも高い生存率が期待される。

これはblameにタグを付けるのと同じと言える。つまり、 VCS上の履歴を伴ったファイルにしかタグ付けできないし、タグを打つためには必ずblame情報を生成しなければならない 。(blameは一般にVCS上の履歴を集積したビューを指す。例: https://github.com/torvalds/linux/blame/master/Makefile LinuxMakefileのSUBLEVELバージョンはLinux3.0以降変わっていない)

blameを常に生成することを考えると、ソースコードを行ごとにバラした状態でDBに格納するのが良いと考えられる。Gitを始め多くのVCSではファイル 全体 のハッシュを使用してファイルを管理することになるが、1段階間接参照を増やしてファイルを行ハッシュのリストで表現することで簡単なデルタ圧縮と行に対するID付けの両方を得られる。

タグをwhitespace文字に付与することは無いため、コンテンツのハッシュをwhitespaceを無視したものに対して取ることでwhitespaceの変更について"耐性"を獲得することもできる。この場合行データは本来の行とwhitespaceを除いたデータの両方を持たせる必要がある。

耐性モデル

ここでの"耐性"は、ファイルに付けたタグが "迷子" になりづらい、または正確に迷子と見做されることを指す。例えば、ソースコードをビルドした際に出たコンパイラの警告をタグとした場合、 ファイルを更新したとしても同一の警告が出つづけたならば同じタグと見做す 必要がある。この特性は、例えば警告が導入されてから解消するまでの期間を計測したり、ある特定の警告を無視対象として追加した場合の効力に効いてくることになる。

GitやSubversionのようなVCS上で管理されるファイルの更新にはいくつかパターンが有り、耐性もそれぞれのパターンに対して考えることができる:

  1. 行挿入耐性 : 特に、タグが付与された行よりも前に行が追加された場合、行番号はズレることになる。例えば、タグの位置情報として"ファイル名:行番号"の2アドレスを使用する方式は行挿入耐性が無いと言える。
  2. 行コピー/移動耐性 : ある行がファイル中の別の行にコピーされた場合に、タグがどちらの側に付いていたかを判断する必要がある。
  3. 行改変耐性 : 行の中に文字列が挿入された場合に、当該行に付与されていたタグが生き残る必要がある。また、逆に、タグが付与されていた位置が削除された場合はタグを(積極的に)迷子にする必要がある。Whitespaceの無視をすることで、whitespace変更に対して耐性を獲得することもできる。
  4. ファイルrevert耐性 : ファイルがrevertされた場合に、元のタグを復活させる必要がある。...これ自体は一般的な期待だが、真面目に実装するべきかは何とも言えない。

これらの耐性モデルを考えた場合、一般的な(例えばCTAGSに見られるような) "ファイル名:行番号:桁番号" のような3アドレス方式ではいかなる耐性も提供できないことはすぐにわかる。例えば、タグ位置よりも上位に行が挿入された場合は、それ以下のタグは全て無効化する必要がある。

単純なblame履歴によるアドレッシングも行挿入以外の編集には耐性が無い。このため、何らかのヒューリステイックや人力のアノテーションを使って異なるタグ同士を纏める手法が別途必要になる。逆に、これらに耐性のあるblameをデザインすることでアサインされるタグにも同様の耐性を付けることができる。