前言
实际开发中,因为开发人员往往在版本库中独享一个分支,或者几个负责同一模块的组员共享一个分支,一个模块开发完成后,代码以增量的方式部署测试服务器或者生产服务器,而非全量部署,因此,开发人员在完成代码编写后,需要整理自己更改过的文件都有哪些,然后在自己开发机器上找到文件对应的编译后的 .class 文件,最后才是部署到目标服务器,这一过程手动整理的话往往容易出错,落下一个文件都是致命的,于是乎我自己写了一个工具类,所有工作一键解决,再也不担心出错了,现在特分享出来,希望对大家有帮助。
功能
* 1.查询指定 svn 账户指定时间段的所有提交记录;
* 2.根据提交记录整理出指定用户开发机上所有变更文件的绝对路径;
* 3.清洗重复的数据、文件夹、删除文件、复制改名文件等数据,得到有效路径;
* 4.将有效路径数据写入文档,提供开发人员核对,包括两个文档,一个是可以直接正常部署的路径文档,一个是删除或者修改 了某文件名提醒文档,提醒是否对应删除或修改目标服务器对应文件;
* 5.调用cmd窗口执行windows命令,生成开发机上所有类型的变更文件压缩jar文件,所有文件归入各自绝对项目路径;
* 6.调用cmd窗口执行windows命令,解压jar文件,获得可参照包名直接部署到目标服务器的项目文件;
PS:可以配多个svn账号,解决多员工协同操作同一个文件问题,只要保证所有参与人和svn同步,无需考虑操作先后
代码
导包lib:
工具类代码如下:
package com.svnkit;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
/**
* SvnLogConversionUtil svn组件工具
* 1.查询指定 svn 账户指定时间段的所有提交记录;
* 2.根据提交记录整理出指定用户开发机上所有变更文件的绝对路径;
* 3.清洗重复的数据、文件夹、删除文件、复制改名文件等数据,得到有效路径;
* 4.将有效路径数据写入文档,提供开发人员核对;
* 5.生成开发机上所有类型的变更文件压缩jar文件,所有文件归入各自绝对项目路径;
* 6.解压jar文件,获得可参照包名直接部署到目标服务器的项目文件;
*
* PS:可以配多个svn账号,解决多员工协同操作同一个文件问题,只要保证所有参与人和svn同步,无需考虑操作先后
*
* @author weny.yang create date: 2020-05-20 09:00:00
*/
public class SvnLogConversionUtil {
private static Log log = LogFactory.getLog(SvnLogConversionUtil.class);
private String userName;
private String password;
private String svnUrl;
private String staDate;
private String endDate;
// File export path
private String writeUrl;
// native project path
private String nativeProjectUrl;
// content directory name: WebContent or WebRoot
private String contentDirectory;
private List<String> otherUserList = new ArrayList<String>();
private Collection<SVNLogEntry> logEntries;
private Map<String, Set<String>> cateMap = new HashMap<String, Set<String>>();
private List<String> cateList = new ArrayList<String>();
private Map<String, Set<String>> delOrCopyMap = new HashMap<String, Set<String>>();
private List<String> delOrCopyList = new ArrayList<String>();
private String tempDir = System.getProperty("java.io.tmpdir");
private DefaultSVNOptions options = SVNWCUtil.createDefaultOptions(true);
private SVNRepository repos;
private ISVNAuthenticationManager authManager;
/**
* 构造方法
*/
public SvnLogConversionUtil(String userName, String password, String svnUrl, String staDate, String endDate,
String writeUrl, String nativeProjectUrl, String contentDirectory, List<String> otherUserList) {
try {
this.userName = userName;
this.password = password;
this.svnUrl = svnUrl;
this.staDate = staDate;
this.endDate = endDate;
this.writeUrl = writeUrl;
this.nativeProjectUrl = nativeProjectUrl;
this.contentDirectory = contentDirectory;
this.otherUserList = otherUserList;
init();
} catch (SVNException e) {
e.printStackTrace();
}
}
/**
* 初始化
*/
private void init() throws SVNException{
log.info("init start...");
authManager = SVNWCUtil.createDefaultAuthenticationManager(new File(tempDir+"/auth"), userName, password.toCharArray());
options.setDiffCommand("-x -w");
repos = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(svnUrl));
repos.setAuthenticationManager(authManager);
log.info("init completed");
}
/**
* 获取指定版本号区间所有提交记录
*/
public void pathConversion() throws SVNException, IOException, ParseException {
//先清空相关文件夹
delAllFile(new File(writeUrl+userName));
//初始化工具类
init();
//获取提交记录
queryLogByRevision();
//清洗数据
washData();
//写数据
writeWellFile();
//执行cmd命令
exeCommand();
}
/**
* 获取指定版本号区间所有提交记录
*/
@SuppressWarnings("unchecked")
private void queryLogByRevision() throws SVNException, IOException, ParseException {
long startRevision = repos.getDatedRevision(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(staDate));
long endRevision = repos.getDatedRevision(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(endDate));
logEntries = repos.log(new String[] {""}, null, startRevision, endRevision, true, true);
}
/**
* 数据清洗
*/
private String strReplace(String path) {
path = path.replace("/", java.io.File.separator);
if (path.contains("src")) {
path = contentDirectory + java.io.File.separator + "WEB-INF" + java.io.File.separator
+ "classes" + path.substring(path.indexOf("src") + 3, path.length());
}
if (path.contains(contentDirectory)) {
path = nativeProjectUrl + java.io.File.separator
+ path.substring(path.indexOf(contentDirectory), path.length());
}
if (path.contains(".java")) {
path = path.replace(".java", ".class");
}
return path;
}
/**
* 数据清洗
*/
private void washData() throws IOException {
for (Iterator<SVNLogEntry> entries = logEntries.iterator(); entries.hasNext();) {
SVNLogEntry logEntry = (SVNLogEntry) entries.next();
if (logEntry.getAuthor().equals(userName) || otherUserList.contains(logEntry.getAuthor())) {
if (logEntry.getChangedPaths().size() > 0) {
Set<String> changedPathsSet = logEntry.getChangedPaths().keySet();
for (Iterator<String> changedPaths = changedPathsSet.iterator(); changedPaths.hasNext();) {
SVNLogEntryPath entryPath = (SVNLogEntryPath) logEntry.getChangedPaths().get(changedPaths.next());
String path = entryPath.getPath();
path = this.strReplace(path);
//新增、修改类变更文件
if (new File(path).exists() && path.contains(".")) {
String key = path.substring(path.lastIndexOf(".") + 1);
if (cateMap.containsKey(key)) {
Set<String> cateSet = cateMap.get(key);
cateSet.add(path);
} else {
Set<String> cateSet = new HashSet<String>();
cateSet.add(path);
cateMap.put(key, cateSet);
cateList.add(key);
}
}
//删除类变更文件
if (!new File(path).exists() && path.contains(".")) {
String key = path.substring(path.lastIndexOf(".") + 1);
if (delOrCopyMap.containsKey(key)) {
Set<String> cateSet = delOrCopyMap.get(key);
cateSet.add(path);
} else {
Set<String> cateSet = new HashSet<String>();
cateSet.add(path);
delOrCopyMap.put(key, cateSet);
delOrCopyList.add(key);
}
}
//复制类变更文件
String copyPath = entryPath.getCopyPath();
if(copyPath !=null) {
copyPath = this.strReplace(copyPath);
String key = copyPath.substring(copyPath.lastIndexOf(".") + 1);
if (delOrCopyMap.containsKey(key)) {
Set<String> cateSet = delOrCopyMap.get(key);
cateSet.add(copyPath);
} else {
Set<String> cateSet = new HashSet<String>();
cateSet.add(copyPath);
delOrCopyMap.put(key, cateSet);
delOrCopyList.add(key);
}
}
}
}
}
}
}
/**
* 写入文档
*/
private void writeWellFile() throws IOException {
File file = new File(writeUrl + java.io.File.separator + userName);
File writeName = new File(writeUrl + java.io.File.separator + userName + java.io.File.separator + userName + ".txt");
if (!file.exists()) {
file.mkdirs();
}
writeName.createNewFile();
FileWriter writer = new FileWriter(writeName);
@SuppressWarnings("resource")
BufferedWriter out = new BufferedWriter(writer);
for (String cate : cateList) {
Iterator<Entry<String, Set<String>>> iteratorMap = cateMap.entrySet().iterator();
while (iteratorMap.hasNext()) {
Entry<String, Set<String>> entry = iteratorMap.next();
if (entry.getKey().equals(cate)) {
Set<String> cateSet = entry.getValue();
Iterator<String> iteratorSet = cateSet.iterator();
while (iteratorSet.hasNext()) {
String str = iteratorSet.next();
//System.lineSeparator()本质是"\r\n", jdk1.8方法,如果报错,自行替换即可
str += System.lineSeparator();
out.write(str);
}
}
}
out.write("\n");
}
out.flush();
if(delOrCopyList.size()>0) {
writeWarnFile();
}
log.info("write catalog file completed, please check here >>> " + writeUrl + userName + java.io.File.separator + userName);
}
/**
* 写入警告文档
*/
private void writeWarnFile() throws IOException {
File file = new File(writeUrl + java.io.File.separator + userName);
File writeName = new File(writeUrl + java.io.File.separator + userName + java.io.File.separator + "警告.txt");
if (!file.exists()) {
file.mkdirs();
}
writeName.createNewFile();
FileWriter writer = new FileWriter(writeName);
@SuppressWarnings("resource")
BufferedWriter out = new BufferedWriter(writer);
out.write("注意 >>> 以下内容为复制改名项或本地已删除项,请确认目标环境是否需要变更!\r\n");
out.write("\r\n");
for (String cate : delOrCopyList) {
Iterator<Entry<String, Set<String>>> iteratorMap = delOrCopyMap.entrySet().iterator();
while (iteratorMap.hasNext()) {
Entry<String, Set<String>> entry = iteratorMap.next();
if (entry.getKey().equals(cate)) {
Set<String> cateSet = entry.getValue();
Iterator<String> iteratorSet = cateSet.iterator();
while (iteratorSet.hasNext()) {
String str = iteratorSet.next();
str += System.lineSeparator();
out.write(str);
}
}
}
out.write("\n");
}
out.flush();
}
/**
* 执行cmd命令
*/
private void exeCommand() throws IOException {
Runtime.getRuntime().exec("cmd /c " + writeUrl.substring(0, 2) + " && cd " + writeUrl + userName + " && start jar -cvf " + userName + ".jar @" + userName + ".txt");
boolean flg1 = true;
while (flg1) {
if (new File(writeUrl + userName + java.io.File.separator + userName + ".jar").exists()) {
flg1 = false;
log.info("create jar file completed, please check here >>> " + writeUrl + userName + java.io.File.separator + userName + ".jar");
Runtime.getRuntime().exec("cmd /c " + writeUrl.substring(0, 2) + " && cd " + writeUrl + userName + " && jar xvf " + userName + ".jar");
boolean flg2 = true;
while (flg2) {
if (new File(writeUrl + userName + java.io.File.separator + "META-INF").exists()) {
flg2 = false;
log.info("decompression file completed, please check here >>> " + writeUrl + userName);
}
}
}
}
}
/**
* 删除文件或文件夹
*/
private void delAllFile(File directory){
if (!directory.isDirectory()){
directory.delete();
} else{
File [] files = directory.listFiles();
// 空文件夹
if (files.length == 0){
directory.delete();
//System.out.println("删除" + directory.getAbsolutePath());
return;
}
// 删除子文件夹和子文件
for (File file : files){
if (file.isDirectory()){
delAllFile(file);
} else {
file.delete();
//System.out.println("删除" + file.getAbsolutePath());
}
}
// 删除文件夹本身
directory.delete();
//System.out.println("删除" + directory.getAbsolutePath());
}
}
}
测试类代码如下:
package com.svnkit;
import java.util.ArrayList;
import java.util.List;
public class TestSvnKit{
public static void main(String[] arg) {
try {
// svn账户、密码、地址
String userName = "ITL520";
String password = "xxx";
String svnUrl = "svn://xxxxxx/Image&PCM/ImageCode/imaging";
// 查询提交记录的时间区间
String staDate = "2020-05-20 00:00:00";
String endDate = "2020-05-21 23:59:59";
// 文件输出路径
String writeUrl = "C:\\Users\\86158\\Desktop";
// 本地开发机上项目路径,截止到项目名
String nativeProjectUrl = "D:\\Software\\Java\\WorkSpace\\imaging";
// 项目的content directory名: WebContent 或者 WebRoot
String contentDirectory = "WebRoot";
// 如果多人协同操作同一模块,将参与用户的svn账户配到此处
List<String> otherUserList = new ArrayList<String>();
// otherUserList.add("ITL521");
SvnLogConversionUtil SvnUtil = new SvnLogConversionUtil(userName, password, svnUrl, staDate, endDate, writeUrl, nativeProjectUrl, contentDirectory, otherUserList);
SvnUtil.pathConversion();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行演示
第一步:运行 TestSvnKit 中的 main 方法,桌面生成文件夹如下图:
第二步:打开文件夹,内部结构如下:
txt文档里面写入的是变更了那些类文件的本地绝对路径信息,会根据文件类型自动分类,打开如下:
如果有删除或者给某个文件改名了,会额外生成一个 警告.txt 文档,记录变更信息,以便开发人员核对。
Software 文件夹里面便是所有编译后的变更文件,按照项目分级目录存放,可以直接上传目标服务器进行部署,如下: