|
package com.prhythm.batch.source.control; |
|
|
|
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.internal.io.fs.FSRepositoryFactory; |
|
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl; |
|
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.*; |
|
import java.util.Arrays; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
|
|
/** |
|
* Created by nanashi07 on 2016/6/22. |
|
*/ |
|
@SuppressWarnings("Duplicates") |
|
public class SvnCommitter implements ICommitable { |
|
|
|
public static void main(String[] args) throws Throwable { |
|
String url1 = "https://svn.your_server.com"; |
|
String userName = "account"; |
|
String userPassword = "password"; |
|
|
|
DAVRepositoryFactory.setup(); |
|
SVNRepositoryFactoryImpl.setup(); |
|
FSRepositoryFactory.setup(); |
|
|
|
new SvnCommitter().commit( |
|
url1, |
|
userName, |
|
userPassword, |
|
"Commit updates", |
|
"trunk/other", |
|
new File("xom/src") |
|
); |
|
} |
|
|
|
/** |
|
* 最新版本 |
|
*/ |
|
private int revision = -1; |
|
|
|
@Override |
|
public void commit(String repositoryUrl, String account, String password, String commitMessage, String pathToCommit, File... fileOrDirectory) throws Throwable { |
|
// 解析網址 |
|
SVNURL url = SVNURL.parseURIEncoded(repositoryUrl); |
|
// 取得資源庫 |
|
SVNRepository repository = SVNRepositoryFactory.create(url); |
|
// 身份驗證 |
|
ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(account, password.toCharArray()); |
|
repository.setAuthenticationManager(authManager); |
|
|
|
// 檢查路徑是否存在 |
|
if (pathToCommit == null) pathToCommit = ""; |
|
SVNNodeKind nodeKind = repository.checkPath(pathToCommit, revision); |
|
|
|
if (nodeKind == SVNNodeKind.NONE) { |
|
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "No entry at URL ''{0}''", url); |
|
throw new SVNException(err); |
|
} else if (nodeKind == SVNNodeKind.FILE) { |
|
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "Entry at URL ''{0}'' is a file while directory was expected", url); |
|
throw new SVNException(err); |
|
} |
|
|
|
Map<String, SVNNodeKind> kinds = new HashMap<String, SVNNodeKind>(); |
|
for (File file : fileOrDirectory) { |
|
kinds.putAll(collect(repository, pathToCommit, file)); |
|
} |
|
|
|
ISVNEditor editor = repository.getCommitEditor(commitMessage, null); |
|
try { |
|
editor.openRoot(revision); |
|
SVNDeltaGenerator svnDeltaGenerator = new SVNDeltaGenerator(); |
|
|
|
for (String segment : (pathToCommit.charAt(0) == '/' ? pathToCommit.substring(1) : pathToCommit).split("[/]")) { |
|
System.out.printf("開啟資料夾 %s%n", segment); |
|
editor.openDir(segment, revision); |
|
} |
|
|
|
boolean result = false; |
|
for (File file : fileOrDirectory) { |
|
result = sendDelta(kinds, editor, svnDeltaGenerator, pathToCommit, file); |
|
} |
|
|
|
for (String segment : (pathToCommit.charAt(0) == '/' ? pathToCommit.substring(1) : pathToCommit).split("[/]")) { |
|
System.out.printf("關閉資料夾 %s%n", segment); |
|
editor.closeDir(); |
|
} |
|
|
|
if (!result) { |
|
editor.abortEdit(); |
|
System.out.println("無變更"); |
|
} else { |
|
SVNCommitInfo svnCommitInfo = editor.closeEdit(); |
|
System.out.println(svnCommitInfo); |
|
} |
|
} catch (Throwable e) { |
|
editor.abortEdit(); |
|
e.printStackTrace(); |
|
} |
|
|
|
} |
|
|
|
/** |
|
* 收集版本資訊 |
|
* |
|
* @param repository 版本庫 |
|
* @param path 路徑 |
|
* @param fileOrDir 檔案 |
|
* @return |
|
* @throws SVNException |
|
*/ |
|
private Map<String, SVNNodeKind> collect(final SVNRepository repository, String path, File fileOrDir) throws SVNException, FileNotFoundException { |
|
Map<String, SVNNodeKind> values = new HashMap<String, SVNNodeKind>(); |
|
|
|
final String targetPath = path.endsWith("/") ? path + fileOrDir.getName() : path + "/" + fileOrDir.getName(); |
|
final SVNNodeKind kind = repository.checkPath(targetPath, revision); |
|
System.out.println(String.format("%s: %s", targetPath, kind)); |
|
|
|
// 取得相關資料 |
|
boolean ignore = false; |
|
if (kind == SVNNodeKind.FILE) { |
|
SVNProperties p = new SVNProperties(); |
|
ByteArrayOutputStream output = new ByteArrayOutputStream(); |
|
repository.getFile(targetPath, revision, p, output); |
|
byte[] data = output.toByteArray(); |
|
// 檔案未變更 |
|
ignore = Arrays.equals(data, toByteArray(new FileInputStream(fileOrDir))); |
|
} |
|
|
|
// 比對版本,檔案相同時不處理 |
|
if (!ignore) values.put(targetPath, kind); |
|
|
|
if (fileOrDir.isDirectory()) { |
|
File[] files = fileOrDir.listFiles(); |
|
if (files != null) { |
|
for (File file : files) { |
|
values.putAll(collect(repository, targetPath, file)); |
|
} |
|
} |
|
} |
|
|
|
return values; |
|
} |
|
|
|
/** |
|
* 處理提交的內容。 |
|
* |
|
* @param kindMap 版本資訊 |
|
* @param editor |
|
* @param deltaGenerator |
|
* @param path 處理路徑 |
|
* @param fileOrDir 檔案 |
|
* @return |
|
* @throws SVNException |
|
* @throws FileNotFoundException |
|
*/ |
|
private boolean sendDelta(Map<String, SVNNodeKind> kindMap, ISVNEditor editor, SVNDeltaGenerator deltaGenerator, String path, File fileOrDir) throws SVNException, FileNotFoundException { |
|
boolean result = false; |
|
|
|
String targetPath = path.endsWith("/") ? path + fileOrDir.getName() : path + "/" + fileOrDir.getName(); |
|
SVNNodeKind info = kindMap.get(targetPath); |
|
if (info == SVNNodeKind.NONE) { |
|
// 新增 |
|
if (fileOrDir.isDirectory()) { |
|
System.out.printf("加入資料夾 %s%n", fileOrDir.getName()); |
|
editor.addDir(fileOrDir.getName(), null, revision); |
|
|
|
File[] subFiles = fileOrDir.listFiles(); |
|
if (subFiles != null) { |
|
for (File subFile : subFiles) { |
|
result = result || sendDelta(kindMap, editor, deltaGenerator, targetPath, subFile); |
|
} |
|
} |
|
editor.closeDir(); |
|
result = true; |
|
} else if (fileOrDir.isFile()) { |
|
System.out.printf("加入檔案 %s%n", fileOrDir.getName()); |
|
editor.addFile(fileOrDir.getName(), null, revision); |
|
editor.applyTextDelta(fileOrDir.getName(), null); |
|
|
|
String checksum = deltaGenerator.sendDelta(fileOrDir.getName(), new FileInputStream(fileOrDir), editor, true); |
|
|
|
editor.closeFile(fileOrDir.getName(), checksum); |
|
result = true; |
|
} |
|
} else if (info == SVNNodeKind.DIR || info == SVNNodeKind.FILE) { |
|
// 修改 |
|
if (fileOrDir.isDirectory()) { |
|
if (info != SVNNodeKind.DIR) |
|
throw new RuntimeException(String.format("無法提交資料夾 %s 至檔案格式", fileOrDir)); |
|
System.out.printf("開啟資料夾 %s%n", fileOrDir); |
|
editor.openDir(fileOrDir.getName(), revision); |
|
File[] subFiles = fileOrDir.listFiles(); |
|
if (subFiles != null) { |
|
for (File subFile : subFiles) { |
|
result = result || sendDelta(kindMap, editor, deltaGenerator, targetPath, subFile); |
|
} |
|
} |
|
editor.closeDir(); |
|
} else if (fileOrDir.isFile()) { |
|
if (info != SVNNodeKind.FILE) |
|
throw new RuntimeException(String.format("無法提交檔案 %s 至資料夾格式", fileOrDir)); |
|
|
|
System.out.printf("變更檔案 %s%n", fileOrDir.getName()); |
|
editor.openFile(fileOrDir.getName(), revision); |
|
editor.applyTextDelta(fileOrDir.getName(), null); |
|
|
|
String checksum = deltaGenerator.sendDelta(fileOrDir.getName(), new FileInputStream(fileOrDir), editor, true); |
|
|
|
editor.closeFile(fileOrDir.getName(), checksum); |
|
result = true; |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
private byte[] toByteArray(InputStream input) { |
|
if (input == null) throw new IllegalArgumentException("Input can't be null"); |
|
|
|
ByteArrayOutputStream output = null; |
|
|
|
try { |
|
output = new ByteArrayOutputStream(); |
|
byte[] buffer = new byte[128]; |
|
int len; |
|
while ((len = input.read(buffer)) > 0) { |
|
output.write(buffer, 0, len); |
|
} |
|
return output.toByteArray(); |
|
} catch (Throwable e) { |
|
throw new RuntimeException(e.getMessage(), e); |
|
} finally { |
|
if (output != null) try { |
|
output.close(); |
|
} catch (IOException e) { |
|
e.printStackTrace(); |
|
} |
|
try { |
|
input.close(); |
|
} catch (IOException e) { |
|
e.printStackTrace(); |
|
} |
|
} |
|
} |
|
|
|
} |