dtlを各バージョン管理システムから使う

ふと思い立ってSubversionMercurial、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
$