09

SVNKit(1)

今時SVNのAPI触ってるなんて人に聞かれたら袋叩きにされるのだろうか。 とにかくRepositoryにある内容を取得したい。

ドキュメントを見るとAPIが2段階あるらしいことがわかる。

  • high-level api (working copy api)
    • org.tmatesoft.svn.core.wc および org.tmatesoft.svn.core.wc2 パッケージ
    • たぶん、実際SVNを使ったツールを開発するにあたっては当然必要になるワーキングコピーをベースにしたユースケースをカバーするAPI
  • low-level api
    • org.tmatesoft.svn.core.io パッケージ
    • リポジトリとプロトコルを直接的にインタフェースするとある
    • 上記を構成するために使用される低レイヤーのAPIなんだろうか?
    • 個人的にはこのレベルでもだいぶユースケースよりな設計の印象を受ける
SVNRepository

APIドキュメントを眺めているとどうやらリポジトリオブジェクトを作ってそこから操作をするらしい。

SVNRepositoryオブジェクトを作るにはSVNRespositoryFactoryというファクトリクラスを使うらしいが、その前に接続先のプロトコル(http: か、svn:またはsvn+hoge:か、file:か)によってセットアップをしてやる必要があるらしい。

        if (url.toLowerCase().startsWith("http")) {
            DAVRepositoryFactory.setup();
        } else if (url.toLowerCase().startsWith("svn")) {
            SVNRepositoryFactoryImpl.setup();
        } else if (url.toLowerCase().startsWith("file")) {
            FSRepositoryFactory.setup();
        } else {
            fail();
        }

こんな感じか。urlは普通にsubversionの接続先として使用するURLの文字列。

なんでsvnの場合だけImplがついているのか。ってSVNRepositoryFactoryにそれをさせるわけにいかなかったってことか。しかしファクトリのセットアップを別のクラスに切り出して、そっちを呼ばせるというのも、なんだか妙な設計に思えるが・・・まあ大した実害はないかもしれない。

続いてリポジトリオブジェクトを作成

        final SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url));
        final ISVNAuthenticationManager authenticationManager = SVNWCUtil.createDefaultAuthenticationManager(user, password.toCharArray());
        repository.setAuthenticationManager(authenticationManager);

ISVNAuthenticationManagerはその名のとおり接続時の認証情報を管理してくれるものらしい。この場合は単純にhttpのベーシック認証に渡すユーザ名とパスワードをSVNWUtilというユーティリティに渡してインスタンスを作ってもらっている。なんでパスワードがchar配列なのかよくわからないが、これは単にパスワードの平文文字列。たぶんSSH経由のSVNとかだとこの辺のやり方は認証方式に応じて変わるんだろう。

ISVNEditor

でとりあえずチェックアウトはこんな感じになるが

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

最後の引数に要求されているExpEditorISVNEditorを実装したオブジェクトで、どうもチェックアウト実行中にはリポジトリ中の変更情報が順に流れてくるのでそれを処理するハンドラというか、Visitorのようなものを実装する必要があるようだ。ExpEditor自体は今回動きを確認するために適当に作った実装。

とりあえずどんな動きをするのか、ということでこんな構造のリポジトリを作った

[carrotsword trunk]$ ls -la
total 20
drwxrwxr-x 2 carrotsword carrotsword 4096 Mar  6 01:55 .
drwxrwxr-x 6 carrotsword carrotsword 4096 Mar  6 01:55 ..
-rw-rw-r-- 1 carrotsword carrotsword  208 Mar  6 01:55 .project
-rw-rw-r-- 1 carrotsword carrotsword   16 Mar  6 01:55 file1.txt
-rw-rw-r-- 1 carrotsword carrotsword    7 Mar  6 01:55 file2.txt

一応コミットは何度かに分けてある

[carrotsword trunk]$ svn log --diff

  ... 略 ...

------------------------------------------------------------------------
r3 | test | 2016-03-04 03:15:19 +0900 (Fri, 04 Mar 2016) | 1 line

commit2

Index: file2.txt
===================================================================
--- file2.txt   (revision 0)
+++ file2.txt   (revision 3)
@@ -0,0 +1 @@
+commit2
\ No newline at end of file

Property changes on: file2.txt
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: file1.txt
===================================================================
--- file1.txt   (revision 2)
+++ file1.txt   (revision 3)
@@ -1 +1,2 @@
-commit1
\ No newline at end of file
+commit1
+commit2
\ No newline at end of file

------------------------------------------------------------------------
r2 | test | 2016-03-04 03:14:34 +0900 (Fri, 04 Mar 2016) | 1 line

commit1

Index: .project
===================================================================
--- .project    (revision 0)
+++ .project    (revision 2)
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>test</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+       </buildSpec>
+       <natures>
+       </natures>
+</projectDescription>

Property changes on: .project
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: file1.txt
===================================================================
--- file1.txt   (revision 0)
+++ file1.txt   (revision 2)
@@ -0,0 +1 @@
+commit1
\ No newline at end of file

Property changes on: file1.txt
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property

------------------------------------------------------------------------
r1 | test | 2016-03-04 03:12:54 +0900 (Fri, 04 Mar 2016) | 1 line




------------------------------------------------------------------------

これに対して、ISVNEditorがどんな呼ばれ方をするのか。 各メソッドに呼ばれたメソッド名とパラメータを簡単に出力するだけの実装をして実行してみたところ、以下のような出力が得られた。

#targetRevision start > [rev.3]
#openRoot start > [rev.3]
#openDir start > trunk[rev.3]
#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 > 

これは面倒そう!

ざっと見る限り、コミットされているファイルの内容が見えない。 これはtextDeltaChunkの引数に渡されるSVNDiffWindowオブジェクトがその情報を持っている。 適当に吐き出してみたところSVNヘッダ+ファイルの内容のバイナリストリームを持っているっぽい。 ということは、その内容を指定のパスに吐き出してやれば(checkoutだけど)export相当のことはできるようになるのかも。

今回はこの辺で。