SVNKit(3)
さて、前回は雑にSVNRepository#update
してみたわけだけど、どうもコンテンツのDiffが流れてくるので、ワーキングコピーに対してpatchしなければならなさそう、というところまでだった。
このDiffのコンテンツはISVNEditor
のtextDaltaChunk
というメソッドの引数に渡される SVNDiffWindow 型のオブジェクトの内部に格納されている。
diffをいわゆるdiffテキスト的なものから自分でpatch処理をする必要があるのかというと、Javadocを見るにSVNDiffWindow
がよろしくやってくれるためのメソッドを持っているように見える。
apply(byte[] sourceBuffer, byte[] targetBuffer)
これはいかにもsourceBuffer
がワーキングコピーで、targetBuffer
が適用後のバッファとなるようだ。
もう一つこんなのがあるが
apply(SVNDiffWindowApplyBaton applyBaton)
SVNDiffWindowApplyBaton インスタンスを作るためにはDiff適用後のファイルのMD5をチェックサムとして渡す必要があるようだが、このMD5をどこから取得すればよいのかわからない。ISVNEditor
の一連の呼び出しの中でチェックサムが受け取れるが、それは closeFile
のタイミングなので、ここでは間に合わない。別途リポジトリに問い合わせるのだろうか。
仕方ないので、とりあえず前者でやってみることにする。
現在テストのために使用しているリポジトリの内容は前回、前々回に詳細を記載しているのでそちらを参照していただきたい。今回とりあえずrevision2からrevision3(最新)でfile1.txtが変更になっているので、その差分を適用することを考えていく。
とりあえずrevision2の時点でfile1.txtの内容は以下のようになっている。
commit1
末尾には改行がない。
これがrevision3(最新)では以下のように変更なっている。
commit1 commit2
なので差分は
(改行) commit2
が来ることになる。
この差分を適用するために、textDaltaChunk
に以下のような内容を書いた。
@Override public OutputStream textDeltaChunk(final String path, final SVNDiffWindow diffWindow) throws SVNException { logStart(path + " " + diffWindow.toString()); final Map<String, String> pathContentsMap = new HashMap<>(); pathContentsMap.put("trunk/file1.txt", "commit1"); final String content = pathContentsMap.get(path); byte[] souceBuffer = new byte[0]; if (content != null) { souceBuffer = content.getBytes(); } final byte[] targetBuffer = new byte[diffWindow.getTargetViewLength()]; diffWindow.apply(souceBuffer, targetBuffer); System.out.println(new String(targetBuffer)); logEnd(); return null; }
file1.txtに対して呼び出されたときのみ、"commit1"
という内容をワーキングコピーのソースとして与えて、それ以外の場合はソースは空のバッファにしている。
で、ターゲットの長さ分をtargetBuffer
として用意して、その内容をapply
実行後に標準出力へ出力してみている。
logStart
はメソッド名と適当なメッセージを出力している。
logEnd
は最初終了のメッセージを出力していたのだけど、対して意味なかったので現状何もしていない。
戻りがnull
になっているのはJavadocの説明にそうしろとあったため。戻りがOutputStream
になっているのは過去の互換性のためらしい。ここで出力ストリームを戻すと戻した先で閉じられてしまう。
さて、これを実行した結果は以下の通り。
#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 #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 commit1 commit2 #textDeltaEnd start > trunk/file1.txt #closeFile start > trunk/file1.txt #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 commit2 #textDeltaEnd start > trunk/file2.txt #closeFile start > trunk/file2.txt #closeDir start > #closeDir start > #closeEdit start >
今回のコードが出力している部分を抜粋すると
#textDeltaChunk start > trunk/file1.txt 0:7:16:3:9:12:0 commit1 commit2
ちゃんと最新版と一致しているようだ。
まあ目視の上ではこれでいいんだけど、一応データのチェックサムを確認するべきではある。
上記で少し触れているけど、ISVNEditor#closeFile
の引数にチェックサムが与えられている。
型はただのString
。Javadocによれば、これはMD5らしい。ExpEditor
のインスタンス変数にファイルパスとMD5の結果を持つMap
型の変数を定義して、textDeltaChunk
の処理後にMD5を記録し、closeFile
で渡される値を比較してみることにしてみる。
textDeltaChunk
を以下のように修正。上記までと同様の処理の後に、MD5を計算する処理を追加している。
@Override public OutputStream textDeltaChunk(final String path, final SVNDiffWindow diffWindow) throws SVNException { logStart(path + " " + diffWindow.toString()); try { final Map<String, String> pathContentsMap = new HashMap<>(); pathContentsMap.put("trunk/file1.txt", "commit1"); final String content = pathContentsMap.get(path); byte[] souceBuffer = new byte[0]; if (content != null) { souceBuffer = content.getBytes(); } final byte[] targetBuffer = new byte[diffWindow.getTargetViewLength()]; diffWindow.apply(souceBuffer, targetBuffer); System.out.println(new String(targetBuffer)); final MessageDigest instance = MessageDigest.getInstance("MD5"); final byte[] digest = instance.digest(targetBuffer); final String hexBinary = DatatypeConverter.printHexBinary(digest); pathMD5Map.put(path, hexBinary); System.out.println("[MD5] " + path + " -> " + hexBinary); } catch (final NoSuchAlgorithmException e) { throw new SVNException(SVNErrorMessage.UNKNOWN_ERROR_MESSAGE, e); } logEnd(); return null; }
加えて、closeFile
で以下のように検証してみる。
@Override public void closeFile(final String path, final String textChecksum) throws SVNException { logStart(path); // do nothing if (!textChecksum.equalsIgnoreCase(pathMD5Map.get(path))) { System.err.println("[MD5] UNmatch for " + path + " / " + textChecksum + " != " + String.valueOf(pathMD5Map.get(path))); } else { System.out.println("[MD5] match for " + path + " / " + textChecksum + " == " + pathMD5Map.get(path)); } logEnd(); }
すると以下のような出力が得られた。
#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 #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 commit1 commit2 [MD5] trunk/file1.txt -> CAE91EA5B6E3F99FE23E6C4F5089A91D #textDeltaEnd start > trunk/file1.txt #closeFile start > trunk/file1.txt [MD5] match for trunk/file1.txt / cae91ea5b6e3f99fe23e6c4f5089a91d == CAE91EA5B6E3F99FE23E6C4F5089A91D #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 commit2 [MD5] trunk/file2.txt -> 8AFDB541A011CC47D258C4962EC19D90 #textDeltaEnd start > trunk/file2.txt #closeFile start > trunk/file2.txt [MD5] match for trunk/file2.txt / 8afdb541a011cc47d258c4962ec19d90 == 8AFDB541A011CC47D258C4962EC19D90 #closeDir start > #closeDir start > #closeEdit start >
どうやら問題なさそうだ。