09

SVNKit(2)

前回の続き。

前回はとりあえずISVNRepositoryISVNEditorISVNDiffWindowを使ってリポジトリの内容にアクセスできそう、というところまでやった。

一応注記しておくと、前回取得できた情報を何とかすればSVNクライアントとして動けるのかもしれないが、前回の内容ではいわゆる .svnフォルダ配下の情報を作ることについては何も気にしていないので、まっとうなSVNクライアントとして動作させるにはいろいろと足りないかもしれない。

ここの記事では今後もまっとうなSVNクライアントとして動かすことは考えていないことはここでお断りしておく。たぶん、SVNクライアントを実装したいのなら、high-level APIを使うべきだろう。

で、今回は定期または不定期的にリポジトリから最新の情報を取得することを考える。その場合、前回取得した内容から現時点での最新までの差分のみを取得したい。前回はcheckoutを呼んでみたので、差分となるとそこからのupdateということになるだろう。diffというメソッドもあるようなのでリビジョンを指定してDiffを取るのでもいいのかもしれないが、とりあえず今回はupdateを調べてみる。

ISVNRepository#update

セットアップと接続までは前回と同じ。updateメソッドには(にも)いくつかオーバライドがあるが、以下のように呼びだしてみる。

        repository.update(-1, "trunk", true, new ExpReporter(), new ExpEditor());

前回は端折ったが、引数について順に。

  • -1
    • アップデートする先のリビジョン。APIドキュメントによるとデフォルトはHEADだ、と書いてあるが、型がlongなのでデフォルトって何のことだと思ったが、どうも-1(というかたぶん0より小さい値だろう)っぽい。
  • "trunk"
    • APIドキュメントではこれはリポジトリのインスタンスを生成する際に指定するURLの次の1レベルのパス、見たいな説明がされている。今回、trunkの上までを指定してリポジトリのインスタンスを生成しているので、こんな感じになる。optionalと書かれているので、リポジトリオブジェクトを生成する際に trunk を含めている場合、なくてもいいのかもしれない。
    • ブランチを指定したい場合にどうなるのかよくわからないが branches/branch-name的な書き方でいいんだろうか
  • true
    • 再帰的に処理するかどうかフラグ。SVNクライアントの画面なんかでもチェックボックスを見ることがある
  • new ExpReporter()
    • ISVNReporterBatonをimplementsした適当実装。後述。
  • new ExpEditor()
    • 前回若干説明したやつ
    • 今回も呼ばれたメソッド名と引数を適当に結合して出力する適当実装
ISVNReporterBaton

前回のISVNEditorに加えてISVNReporterBatonというinterfaceの実装を要求されている。 APIドキュメントに加えて若干の説明がここにあるが、要はワーキングコピーに存在するディレクトリやファイルについて個別にどんな状態か(チェックアウトされたものか、どのリビジョンか、削除するつもりかどうかなど)個別のパス情報としてリポジトリに伝えてやる必要があるらしい。

  • 上記のインタフェースを実装すると引数にISVNReporterインスタンスを引数に受け取れるので、それに対してsetPathというメソッドを呼ぶことで個別のディレクトリ、ファイルについての状態を伝えていけということのようだ。
  • ユーザの操作によってチェックアウト時とurlが変わってしまう場合(ファイルの移動などだろうか)は代わりにlinkPathを呼べ、とか同様にしてファイルをリポジトリから削除するつもりなら、deletePathを呼べとも書かれている。
  • そして一通りの報告処理が終わったらfinishReportを呼ぶ必要があるようだ。なんでそんな必要があるのかよくわからないが、個別にパスを調べる処理はそれなりに時間がかかることが想像されるのでマルチスレッド化されて処理されることを見越してのことかな?

ということで、ワーキングコピーに何もないことにして以下のような報告をするコードを書いてみる。

public class ExpReporter implements ISVNReporterBaton {

    @Override
    public void report(final ISVNReporter reporter) throws SVNException {
        reporter.setPath("", null, 0, SVNDepth.INFINITY, false);
        reporter.finishReport();
        return;
    }

}

reporter.setPathの引数について

  • ワーキングコピーには""しか存在してない
  • 特にロックは使用していないので ロックトークンは null
  • ""のリビジョンは0(最初)
  • 対象にするサブエントリ(配下のファイルとかフォルダ)の階層は無限(SVNDepth.INFINITY)
  • このパス(この場合"")がディレクトリでかつ前回のupdateがエラーで失敗した場合はtrueである必要があるが、そうでないので false

setPathの第一引数は"/"ではないかと思ったのだが、それだとよくわからないエラーが返ってきた。というかapacheのSVNモジュールがassertで死んでた。ドキュメントでは絶対パスで書かれているように見えるが…。

で以下のような出力を得た

#targetRevision start > [rev.3]
#openRoot start > [rev.0]
#addDir start > trunk
#changeDirProperty start > svn:wc:ra_dav:version-url /svn/test/!svn/ver/3/trunk
#changeDirProperty start > svn:entry:committed-rev 3
#changeDirProperty start > svn:entry:committed-date 2016-03-03T18:15:19.061137Z
#changeDirProperty start > svn:entry:last-author test
#changeDirProperty start > svn:entry:uuid 5025b694-87bc-4570-9e45-8a2bcb52a122
#addFile start > trunk/file2.txt
#changeFileProperty start > trunk/file2.txt svnkit:entry:sha1-checksum 54563f95fefa691baa82a522156322c21f7d6df3
#changeFileProperty start > trunk/file2.txt svn:wc:ra_dav:version-url /svn/test/!svn/ver/3/trunk/file2.txt
#changeFileProperty start > trunk/file2.txt svn:entry:committed-rev 3
#changeFileProperty start > trunk/file2.txt svn:entry:committed-date 2016-03-03T18:15:19.061137Z
#changeFileProperty start > trunk/file2.txt svn:entry:last-author test
#changeFileProperty start > trunk/file2.txt svn:entry:uuid 5025b694-87bc-4570-9e45-8a2bcb52a122
#changeFileProperty start > trunk/file2.txt svn:mime-type text/plain
#applyTextDelta start > trunk/file2.txt
#textDeltaChunk start > trunk/file2.txt 0:0:7:1:7:8:0
#textDeltaEnd start > trunk/file2.txt
#closeFile start > trunk/file2.txt
#addFile start > trunk/.project
#changeFileProperty start > trunk/.project svnkit:entry:sha1-checksum 4d0a7da34b1a6f2e1416d746bf268c1137453e14
#changeFileProperty start > trunk/.project svn:wc:ra_dav:version-url /svn/test/!svn/ver/2/trunk/.project
#changeFileProperty start > trunk/.project svn:entry:committed-rev 2
#changeFileProperty start > trunk/.project svn:entry:committed-date 2016-03-03T18:14:34.687289Z
#changeFileProperty start > trunk/.project svn:entry:last-author test
#changeFileProperty start > trunk/.project svn:entry:uuid 5025b694-87bc-4570-9e45-8a2bcb52a122
#changeFileProperty start > trunk/.project svn:mime-type text/plain
#applyTextDelta start > trunk/.project
#textDeltaChunk start > trunk/.project 0:0:208:3:208:211:0
#textDeltaEnd start > trunk/.project
#closeFile start > trunk/.project
#addFile start > trunk/file1.txt
#changeFileProperty start > trunk/file1.txt svnkit:entry:sha1-checksum 994c650e0954fd76b6859dae6d87caf2fad19e35
#changeFileProperty start > trunk/file1.txt svn:wc:ra_dav:version-url /svn/test/!svn/ver/3/trunk/file1.txt
#changeFileProperty start > trunk/file1.txt svn:entry:committed-rev 3
#changeFileProperty start > trunk/file1.txt svn:entry:committed-date 2016-03-03T18:15:19.061137Z
#changeFileProperty start > trunk/file1.txt svn:entry:last-author test
#changeFileProperty start > trunk/file1.txt svn:entry:uuid 5025b694-87bc-4570-9e45-8a2bcb52a122
#changeFileProperty start > trunk/file1.txt svn:mime-type text/plain
#applyTextDelta start > trunk/file1.txt
#textDeltaChunk start > trunk/file1.txt 0:0:16:1:16:17:0
#textDeltaEnd start > trunk/file1.txt
#closeFile start > trunk/file1.txt
#closeDir start > 
#closeDir start > 
#closeEdit start > 

ファイルを順に追加しているのがわかる。

リポジトリの詳細な内容は前回 svn log --diff した結果を貼っているが、ざっくりおさらいすると

  • rev.0 : このリポジトリの最初
  • rev.1 : プロジェクト構造(trunkとかbranchesとか)つくった
  • rev.2 : /.projectと /file1.txtを追加
  • rev.3 : /file1.txtを更新、/file2.txtを追加

といった履歴になっている。

この状態でワーキングコピー""のリビジョンが2とだけ報告したらどうなるのだろうか。 つまり本来ワーキングコピー全体がリビジョン2であるならば/.projectと /file1.txtが存在するはずなのでそれらについても報告する必要があるが、意図的にそれをしない。

public class ExpReporter implements ISVNReporterBaton {

    @Override
    public void report(final ISVNReporter reporter) throws SVNException {
        reporter.setPath("", null, 2, SVNDepth.INFINITY, false);
        reporter.finishReport();
        return;
    }

}

得られた出力は以下

#targetRevision start > [rev.3]
#openRoot start > [rev.2]
#openDir start > trunk[rev.2]
#changeDirProperty start > svn:wc:ra_dav:version-url /svn/test/!svn/ver/3/trunk
#changeDirProperty start > svn:entry:committed-rev 3
#changeDirProperty start > svn:entry:committed-date 2016-03-03T18:15:19.061137Z
#changeDirProperty start > svn:entry:last-author test
#changeDirProperty start > svn:entry:uuid 5025b694-87bc-4570-9e45-8a2bcb52a122
#addFile start > trunk/file2.txt
#changeFileProperty start > trunk/file2.txt svnkit:entry:sha1-checksum 54563f95fefa691baa82a522156322c21f7d6df3
#changeFileProperty start > trunk/file2.txt svn:wc:ra_dav:version-url /svn/test/!svn/ver/3/trunk/file2.txt
#changeFileProperty start > trunk/file2.txt svn:entry:committed-rev 3
#changeFileProperty start > trunk/file2.txt svn:entry:committed-date 2016-03-03T18:15:19.061137Z
#changeFileProperty start > trunk/file2.txt svn:entry:last-author test
#changeFileProperty start > trunk/file2.txt svn:entry:uuid 5025b694-87bc-4570-9e45-8a2bcb52a122
#changeFileProperty start > trunk/file2.txt svn:mime-type text/plain
#applyTextDelta start > trunk/file2.txt
#textDeltaChunk start > trunk/file2.txt 0:0:7:1:7:8:0
#textDeltaEnd start > trunk/file2.txt
#closeFile start > trunk/file2.txt
#openFile start > trunk/file1.txt[rev.2]
#changeFileProperty start > trunk/file1.txt svn:wc:ra_dav:version-url /svn/test/!svn/ver/3/trunk/file1.txt
#changeFileProperty start > trunk/file1.txt svn:entry:committed-rev 3
#changeFileProperty start > trunk/file1.txt svn:entry:committed-date 2016-03-03T18:15:19.061137Z
#changeFileProperty start > trunk/file1.txt svn:entry:last-author test
#changeFileProperty start > trunk/file1.txt svn:entry:uuid 5025b694-87bc-4570-9e45-8a2bcb52a122
#applyTextDelta start > trunk/file1.txt
#textDeltaChunk start > trunk/file1.txt 0:7:16:3:9:12:0
#textDeltaEnd start > trunk/file1.txt
#closeFile start > trunk/file1.txt
#closeDir start > 
#closeDir start > 
#closeEdit start > 

/file2.txt がaddFileで /file1.txt はopenFileから始まっている。単純に差分になるようだ。

別途ISVNEditor#textDeltaChunkで受け取れるSVNDiffWindowオブジェクトのインスタンスから、ファイルの内容としてどういったものが取得できるのか見てみると、rev2->HEADでファイルの追加になる /file2.txt はファイルの内容すべて、内容の変更のみだった /file1.txt は変更差分のみ取得できた。

となると、ワーキングコピーとこれらの内容からどうにかして本来(というか、最新の全体)の内容を得るためには、ワーキングコピーに対してpatchしなければならない?

続きはまた次回。