dtlを各バージョン管理システムから使う
ふと思い立ってSubversion、Mercurial、Gitでdiffする際にdtlのサンプルプログラムであるunidiffを呼ぶようにしてみた。dtlを使ったunidiffはこんな感じ。dtl-1.00のexamplesにあるやつそのままなんだけど、改めて眺めてみるとちょっとテキトーだなあ。
#include "../dtl.hpp" #include "common.hpp" #include <iostream> #include <fstream> #include <sstream> #include <vector> #include <algorithm> #include <time.h> #include <sys/types.h> #include <sys/stat.h> using namespace std; static void showStats (string fp1, string fp2); static void unifiedDiff (string fp1, string fp2); static void showStats (string fp1, string fp2) { const int MAX_LENGTH = 255; time_t rawtime[2]; struct tm *timeinfo[2]; char time_format[] = "%Y-%m-%d %H:%M:%S %z"; struct stat st[2]; if (stat(fp1.c_str(), &st[0]) == -1) { cerr << "argv1 is invalid." << endl; exit(-1); } if (stat(fp2.c_str(), &st[1]) == -1) { cerr << "argv2 is invalid" << endl; exit(-1); } char buf[2][MAX_LENGTH + 1]; rawtime[0] = st[0].st_mtime; timeinfo[0] = localtime(&rawtime[0]); strftime(buf[0], MAX_LENGTH, time_format, timeinfo[0]); cout << "--- " << fp1 << '\t' << buf[0] << endl; rawtime[1] = st[1].st_mtime; timeinfo[1] = localtime(&rawtime[1]); strftime(buf[1], MAX_LENGTH, time_format, timeinfo[1]); cout << "+++ " << fp2 << '\t' << buf[1] << endl; } static void unifiedDiff (string fp1, string fp2) { typedef string elem; ifstream Aifs(fp1.c_str()); ifstream Bifs(fp2.c_str()); elem buf; vector<elem> ALines, BLines; ostringstream ossLine, ossInfo; while(getline(Aifs, buf)){ ALines.push_back(buf); } while(getline(Bifs, buf)){ BLines.push_back(buf); } dtl::Diff<elem, vector<elem> > diff(ALines, BLines); diff.onHuge(); diff.compose(); if (diff.getEditDistance() > 0) { showStats(fp1, fp2); } diff.composeUnifiedHunks(); diff.printUnifiedFormat(); } int main(int argc, char *argv[]) { if (isFewArgs(argc)) { cerr << "few argument." << endl; return -1; } string s1(argv[1]); string s2(argv[2]); bool fileExist = true; if (!isFileExist(s1)) { cerr << s1 << " is invalid." << endl; fileExist = false; } if (!isFileExist(s2)) { cerr << s2 << " is invalid." << endl; fileExist = false; } if (!fileExist) { return -1; } unifiedDiff(s1, s2); return 0; }
↑は簡単に言うと引数で受け取った2つのファイルの差分をUnified Formatで表示するプログラム。巷でよく使われているようなバージョン管理システムには、diffコマンドを実行する際にユーザがそのバージョン管理システムが最初から用意しているdiffプログラムとは別のdiffプログラムを呼び出すための機構が用意されている。今回はその機構を使って自製のdiffプログラムを使うようにしてみたという話。ちなみに↑のプログラムのままだとテキストファイルは正しく扱えるが、バイナリファイルは正しく扱えない。
実際にやってみたところ、Mercurialはそのままで動作したんだが、Subversionだとコマンドライン引数を処理する際に、
// string s1(argv[1]); // string s2(argv[2]); string s1(argv[6]); string s2(argv[7]);
と修正してやる必要があり、Gitだと、
// string s1(argv[1]); // string s2(argv[2]); string s1(argv[2]); string s2(argv[1]);
と引数の順番を逆にしてやる必要があった。
Subversion
元のdiff
$ svn diff Index: test =================================================================== --- test (リビジョン 1) +++ test (作業コピー) @@ -0,0 +1 @@ +contents $
dtlのunidiff
$ svn diff --diff-cmd=/home/bokko/programming/cpp/dtl/examples/unidiff Index: test =================================================================== --- .svn/text-base/test.svn-base 2009-07-23 00:13:27 +0900 +++ test 2009-07-23 00:13:36 +0900 @@ -1,0 +1,1 @@ +contents $
Mercurial
元のdiff
$ hg diff diff -r 46416510fdf2 test --- a/test Thu Jul 23 00:14:35 2009 +0900 +++ b/test Thu Jul 23 00:52:07 2009 +0900 @@ -0,0 +1,1 @@ +contents $
dtlのunidiff
$ hg extdiff -p /home/bokko/programming/cpp/dtl/examples/unidiff --- hg.46416510fdf2/test 2009-07-23 00:20:22 +0900 +++ /home/bokko/workspace/vcstest/hg/test 2009-07-23 00:14:39 +0900 @@ -1,0 +1,1 @@ +contents $
Git
$HOME/.gitconfigに以下を追加。(ほかと同じく、コマンドラインオプションでやろうとしたけどうまくいかなかった)
[diff] external = /home/bokko/programming/cpp/dtl/examples/unidiff
元のdiff
$ git diff diff --git a/test b/test index e69de29..12f00e9 100644 --- a/test +++ b/test @@ -0,0 +1 @@ +contents
dtlのunidiff
$ git diff --- /tmp/.diff_50kzN4 2009-07-23 00:30:46 +0900 +++ test 2009-07-23 00:15:46 +0900 @@ -1,0 +1,1 @@ +contents $