本文是參考官方文檔的實現,官方wiki:https://wiki.svnkit.com/Committing_To_A_Repository
本文核心使用的是ISVNEditor這個接口直接對Repository進行各種AM操作~
以下兩張示例圖分別代表我們操作前、操作後倉庫的結構:
具體實現:
package com.demo; import com.google.gson.Gson; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; import org.tmatesoft.svn.core.io.ISVNEditor; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.io.SVNRepositoryFactory; import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator; import org.tmatesoft.svn.core.wc.SVNWCUtil; import java.io.ByteArrayInputStream; import java.io.UnsupportedEncodingException; /** * 提交到倉庫 * 這塊看官方demno的意思如果不用權限認證,會使用a session user name作為提交的author,但是我試了會報錯401,author required~ * 本例是基於初始倉庫圖A轉換為目標倉庫圖B的過程,我們需要執行的操作有: * 1.刪除nodeB/itemB1 * 2.編輯nodeC/itemC1 * 3.新增nodeC/itemC2,並設置itemC2的文件屬性 * 4.新增nodeB子節點nodeD */ public class CommitToRepository { public static void main(String[] args) throws Exception{ //1.根據訪問協議初始化工廠 DAVRepositoryFactory.setup();; //2.初始化倉庫,由於我們所有的操作都是基於nodeB節點以下的,所以我們將nodeB作為本次操作的root節點 String url = "https://wlyfree-PC:8443/svn/svnkitRepository2/trunk/nodeB"; SVNRepository svnRepository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url)); //3.初始化權限 String username = "wly"; String password = "wly"; char[] pwd = password.toCharArray(); ISVNAuthenticationManager isvnAuthenticationManager = SVNWCUtil.createDefaultAuthenticationManager(username,pwd); svnRepository.setAuthenticationManager(isvnAuthenticationManager); //====================================DEMO START========================================= ISVNEditor editor = null; long revisionNo = -1; //指定版本號為最新版本 //4.1.刪除nodeB/itemB1 try{ //獲取編輯器 editor = svnRepository.getCommitEditor("delete file",null,true,null); String itemB1Path = "itemB1";//要刪除的文件路徑 SVNCommitInfo svnCommitInfo = deleteFile(editor,revisionNo);//執行刪除並返回執行結果 System.out.println("執行刪除操作的返回結果:" + svnCommitInfo); }catch (SVNException e){ //發生異常需要終止操作 editor.abortEdit(); e.printStackTrace();; } //4.2.編輯nodeC/itemC1 try{ //獲取編輯器 editor = svnRepository.getCommitEditor("modify file",null,true,null); SVNCommitInfo svnCommitInfo = modifyFile(editor,revisionNo); System.out.println("執行編輯操作的返回結果:" + svnCommitInfo); }catch(SVNException e){ //發生異常需要終止操作 editor.abortEdit(); e.printStackTrace();; } //4.3.新增nodeC/itemC2,並設置itemC2的文件屬性 try{ editor = svnRepository.getCommitEditor("add file",null,true,null); SVNCommitInfo svnCommitInfo = addFile(editor,revisionNo); System.out.println("執行新增文件操作的返回結果:" + svnCommitInfo); //校驗nodeC/itemC2的屬性是否成功設置進去 SVNProperties s = new SVNProperties(); svnRepository.getFile("nodeC/itemC2",-1,s,null); Gson gson = new Gson(); System.err.println(gson.toJson(s)); }catch (SVNException e){ editor.abortEdit(); e.printStackTrace(); } //4.4.新增nodeB子節點nodeD try{ editor = svnRepository.getCommitEditor("add dir",null,true,null); SVNCommitInfo svnCommitInfo = addDir(editor,revisionNo); System.out.println("執行新增目錄操作的返回結果:" + svnCommitInfo); }catch (SVNException e){ editor.abortEdit(); e.printStackTrace(); } } /** * 刪除文件 * @param editor 編輯器 * @param revisionNo 修訂版版本號 * @return SVNCommitInfo 提交結果信息 * @throws SVNException */ private static SVNCommitInfo deleteFile(ISVNEditor editor,long revisionNo) throws SVNException{ // 進入Root節點,即nodeB editor.openRoot(revisionNo); //4.3.刪除文件 editor.deleteEntry("itemB1",revisionNo); //操作完成要關閉編輯器,並返回操作結果 return editor.closeEdit(); } /** * 編輯文件 * @param editor 編輯器 * @param revisionNo 修訂版版本號 * @return SVNCommitInfo 提交結果信息 * @throws SVNException */ private static SVNCommitInfo modifyFile(ISVNEditor editor,long revisionNo) throws SVNException{ // 進入Root節點,即nodeB editor.openRoot(revisionNo); //.進入nodeC節點 editor.openDir("nodeC",revisionNo); // 編輯nodeC/itemC1的內容 String itemC1Path = "nodeC/itemC1";//路徑都是相對於root的 editor.openFile(itemC1Path,revisionNo); //確保客戶端這個文件的內容和服務端的是一樣的,如果不一致的話是不允許提交的。底層實現使用MD5 String baseChecksum = null; editor.applyTextDelta(itemC1Path,baseChecksum); //提交文件變更的數據,windows默認是100kb大小 byte[] oldData = new byte[]{}; byte[] newData = null; try { newData = "我來測試一下編輯2".getBytes("utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ByteArrayInputStream baseData = new ByteArrayInputStream(oldData); ByteArrayInputStream workingData = new ByteArrayInputStream(newData); SVNDeltaGenerator svnDeltaGenerator = new SVNDeltaGenerator();//100KB-windows generator String checksum = svnDeltaGenerator.sendDelta(itemC1Path,baseData,0,workingData,editor,true); // 關閉文件 editor.closeFile(itemC1Path,checksum); // 關閉目錄nodeC editor.closeDir(); // 關閉根目錄nodeB editor.closeDir(); // 關閉編輯器,並返回執行結果 return editor.closeEdit(); } /** * 新增文件 * @param editor * @param revisionNo * @return * @throws SVNException */ private static SVNCommitInfo addFile(ISVNEditor editor,long revisionNo) throws SVNException{ // 進入Root節點,即nodeB editor.openRoot(revisionNo); //.進入nodeC節點 editor.openDir("nodeC",revisionNo); // 新增itemC2文件 editor.addFile("nodeC/itemC2",null,revisionNo); //確保客戶端這個文件的內容和服務端的是一樣的,如果不一致的話是不允許提交的。底層實現使用MD5 String itemC2Path = "nodeC/itemC2"; String baseChecksum = null; editor.applyTextDelta(itemC2Path,baseChecksum); //提交文件變更的數據,windows默認是100kb大小 byte[] oldData = new byte[]{};//舊數據 byte[] newData = null;//新數據 try { newData = "我來測試一下 - addFile".getBytes("utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ByteArrayInputStream baseData = new ByteArrayInputStream(oldData); ByteArrayInputStream workingData = new ByteArrayInputStream(newData); SVNDeltaGenerator svnDeltaGenerator = new SVNDeltaGenerator();//100KB-windows generator String checksum = svnDeltaGenerator.sendDelta(itemC2Path,baseData,0,workingData,editor,true); //設置文件的屬性,key是字符串,值被包裝成SVNProperyValue了 editor.changeFileProperty("nodeC/itemC2","properName1",SVNPropertyValue.create("properValue1")); editor.changeFileProperty("nodeC/itemC2","properName2",SVNPropertyValue.create("properValue2")); System.out.println("checksum:" + checksum ); //關閉文件 editor.closeFile("nodeC/itemC2",checksum); //關閉目錄nodeC editor.closeDir(); //關閉root editor.closeDir(); return editor.closeEdit(); } /** * 新增目錄 * @param editor 編輯器 * @param revisionNo 修訂版本號 * @return SVNCommitInfo 提交結果信息 * @throws SVNException */ private static SVNCommitInfo addDir(ISVNEditor editor,long revisionNo) throws SVNException{ // 進入Root節點,即nodeB editor.openRoot(revisionNo); //新增目錄 editor.addDir("nodeD",null,revisionNo); editor.closeDir();//nodeD editor.closeDir();//nodeB return editor.closeEdit(); } }
運行效果:
執行刪除操作的返回結果:r51 by 'wly' at Wed Dec 07 13:48:15 CST 2016 執行編輯操作的返回結果:r52 by 'wly' at Wed Dec 07 13:48:15 CST 2016 checksum:a107fb58070bfbaf11513c8750f87466 執行新增文件操作的返回結果:r53 by 'wly' at Wed Dec 07 13:48:15 CST 2016 {"myProperties":{"svn:entry:uuid":{"myValue":"e5dd1e38-0390-574a-b68d-e269ce50c382"},"svn:entry:revision":{"myValue":"53"},"properName1":{"myData":[112,114,111,112,101,114,86,97,108,117,101,49]},"svn:entry:committed-date":{"myValue":"2016-12-07T05:48:15.464756Z"},"properName2":{"myData":[112,114,111,112,101,114,86,97,108,117,101,50]},"svn:wc:ra_dav:version-url":{"myValue":"/svn/svnkitRepository2/!svn/ver/53/trunk/nodeB/nodeC/itemC2"},"svn:entry:checksum":{"myValue":"a107fb58070bfbaf11513c8750f87466"},"svn:entry:committed-rev":{"myValue":"53"},"svn:entry:last-author":{"myValue":"wly"}}} 執行新增目錄操作的返回結果:r54 by 'wly' at Wed Dec 07 13:48:15 CST 2016
總結:
其實走讀一遍代碼就知道,無論進行什麼操作都是有一定規律性的。
無論是操作目錄還是文件,大的框架可以大體總結為以下幾步:
//1.根據訪問協議初始化工廠 //2.初始化倉庫,由於我們所有的操作都是基於nodeB節點以下的,所以我們將nodeB作為本次操作的root節點 //3.初始化權限 //4.獲取編輯器對象 //5.進入目錄/文件 //6.執行操作 //7.關閉目錄/文件 //8.關閉編輯器
實際工作中,感覺這種方式不是特別靈活,不一定適用於普通的應用場景,相對來講,High-Level API更傾向於用戶和SVN的交互。