feat: support SVN auth and project credentials
- add username/password fields to project dialog and model - pass optional auth to SVN info/status/log/diff/update/commit services - centralize SVN CLI auth flags in SvnService and fix header text Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -203,7 +203,10 @@ public class MainController {
|
||||
protected Void call() throws Exception {
|
||||
try {
|
||||
// 获取SVN信息
|
||||
com.svnmanager.model.SvnInfo info = infoService.getInfo(currentProject.getPath());
|
||||
com.svnmanager.model.SvnInfo info = infoService.getInfo(
|
||||
currentProject.getPath(),
|
||||
currentProject.getUsername(),
|
||||
currentProject.getPassword());
|
||||
if (info != null && info.getRevision() != null) {
|
||||
Platform.runLater(() -> {
|
||||
currentVersionLabel.setText("r" + info.getRevision());
|
||||
@@ -212,7 +215,10 @@ public class MainController {
|
||||
}
|
||||
|
||||
// 获取状态
|
||||
SvnStatus status = statusService.getStatus(currentProject.getPath());
|
||||
SvnStatus status = statusService.getStatus(
|
||||
currentProject.getPath(),
|
||||
currentProject.getUsername(),
|
||||
currentProject.getPassword());
|
||||
Platform.runLater(() -> {
|
||||
fileStatusList.clear();
|
||||
fileStatusList.addAll(status.getFiles());
|
||||
@@ -263,8 +269,15 @@ public class MainController {
|
||||
if (result == ButtonType.OK) {
|
||||
Project project = controller.getProject();
|
||||
if (project != null) {
|
||||
ConfigUtil.addProject(project);
|
||||
boolean saved = ConfigUtil.addProject(project);
|
||||
if (saved) {
|
||||
loadProjects();
|
||||
} else {
|
||||
showError("保存失败", "无法保存项目配置,请检查当前用户目录下是否有写入权限:\n"
|
||||
+ ConfigUtil.getProjectsFilePath());
|
||||
}
|
||||
} else {
|
||||
showWarning("提示", "项目名称和项目路径不能为空。");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -306,7 +319,10 @@ public class MainController {
|
||||
Task<UpdateService.UpdateResult> task = new Task<UpdateService.UpdateResult>() {
|
||||
@Override
|
||||
protected UpdateService.UpdateResult call() throws Exception {
|
||||
return updateService.update(currentProject.getPath());
|
||||
return updateService.update(
|
||||
currentProject.getPath(),
|
||||
currentProject.getUsername(),
|
||||
currentProject.getPassword());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -348,7 +364,11 @@ public class MainController {
|
||||
Task<CommitService.CommitResult> task = new Task<CommitService.CommitResult>() {
|
||||
@Override
|
||||
protected CommitService.CommitResult call() throws Exception {
|
||||
return commitService.commit(currentProject.getPath(), message);
|
||||
return commitService.commit(
|
||||
currentProject.getPath(),
|
||||
message,
|
||||
currentProject.getUsername(),
|
||||
currentProject.getPassword());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -380,7 +400,10 @@ public class MainController {
|
||||
Task<SvnStatus> task = new Task<SvnStatus>() {
|
||||
@Override
|
||||
protected SvnStatus call() throws Exception {
|
||||
return statusService.getStatus(currentProject.getPath());
|
||||
return statusService.getStatus(
|
||||
currentProject.getPath(),
|
||||
currentProject.getUsername(),
|
||||
currentProject.getPassword());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -409,7 +432,11 @@ public class MainController {
|
||||
Task<List<com.svnmanager.model.SvnLog>> task = new Task<List<com.svnmanager.model.SvnLog>>() {
|
||||
@Override
|
||||
protected List<com.svnmanager.model.SvnLog> call() throws Exception {
|
||||
return logService.getLog(currentProject.getPath(), 20);
|
||||
return logService.getLog(
|
||||
currentProject.getPath(),
|
||||
20,
|
||||
currentProject.getUsername(),
|
||||
currentProject.getPassword());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -435,7 +462,11 @@ public class MainController {
|
||||
Task<String> task = new Task<String>() {
|
||||
@Override
|
||||
protected String call() throws Exception {
|
||||
return diffService.getDiff(currentProject.getPath());
|
||||
return diffService.getDiff(
|
||||
currentProject.getPath(),
|
||||
null,
|
||||
currentProject.getUsername(),
|
||||
currentProject.getPassword());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -461,7 +492,10 @@ public class MainController {
|
||||
Task<com.svnmanager.model.SvnInfo> task = new Task<com.svnmanager.model.SvnInfo>() {
|
||||
@Override
|
||||
protected com.svnmanager.model.SvnInfo call() throws Exception {
|
||||
return infoService.getInfo(currentProject.getPath());
|
||||
return infoService.getInfo(
|
||||
currentProject.getPath(),
|
||||
currentProject.getUsername(),
|
||||
currentProject.getPassword());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.svnmanager.controller;
|
||||
|
||||
import com.svnmanager.model.Project;
|
||||
import com.svnmanager.model.SvnInfo;
|
||||
import com.svnmanager.service.InfoService;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
@@ -16,6 +18,8 @@ public class ProjectDialogController {
|
||||
@FXML private TextField nameField;
|
||||
@FXML private TextField pathField;
|
||||
@FXML private TextField svnUrlField;
|
||||
@FXML private TextField usernameField;
|
||||
@FXML private TextField passwordField;
|
||||
|
||||
private Project project;
|
||||
|
||||
@@ -34,7 +38,31 @@ public class ProjectDialogController {
|
||||
|
||||
File selectedDirectory = directoryChooser.showDialog(pathField.getScene().getWindow());
|
||||
if (selectedDirectory != null) {
|
||||
pathField.setText(selectedDirectory.getAbsolutePath());
|
||||
String selectedPath = selectedDirectory.getAbsolutePath();
|
||||
pathField.setText(selectedPath);
|
||||
|
||||
// 如果是已存在的SVN工作副本,自动获取URL填充
|
||||
autoFillSvnUrl(selectedPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据工作副本自动填充 SVN URL
|
||||
*/
|
||||
private void autoFillSvnUrl(String workingDirectory) {
|
||||
// 仅当当前输入框为空时才自动填充,避免覆盖用户手动输入
|
||||
if (svnUrlField.getText() != null && !svnUrlField.getText().trim().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
InfoService infoService = new InfoService();
|
||||
SvnInfo info = infoService.getInfo(workingDirectory);
|
||||
if (info != null && info.getUrl() != null && !info.getUrl().isEmpty()) {
|
||||
svnUrlField.setText(info.getUrl());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 自动填充失败时静默忽略,不影响正常使用
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +75,8 @@ public class ProjectDialogController {
|
||||
String name = nameField.getText().trim();
|
||||
String path = pathField.getText().trim();
|
||||
String svnUrl = svnUrlField.getText().trim();
|
||||
String username = usernameField.getText().trim();
|
||||
String password = passwordField.getText();
|
||||
|
||||
if (name.isEmpty() || path.isEmpty()) {
|
||||
return null;
|
||||
@@ -59,6 +89,8 @@ public class ProjectDialogController {
|
||||
project.setName(name);
|
||||
project.setPath(path);
|
||||
project.setSvnUrl(svnUrl);
|
||||
project.setUsername(username.isEmpty() ? null : username);
|
||||
project.setPassword(password == null || password.isEmpty() ? null : password);
|
||||
|
||||
return project;
|
||||
}
|
||||
@@ -74,6 +106,14 @@ public class ProjectDialogController {
|
||||
nameField.setText(project.getName());
|
||||
pathField.setText(project.getPath());
|
||||
svnUrlField.setText(project.getSvnUrl());
|
||||
usernameField.setText(project.getUsername());
|
||||
passwordField.setText(project.getPassword());
|
||||
|
||||
// 如果已有路径但URL为空,尝试自动获取
|
||||
if (project.getPath() != null && !project.getPath().isEmpty()
|
||||
&& (project.getSvnUrl() == null || project.getSvnUrl().isEmpty())) {
|
||||
autoFillSvnUrl(project.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,12 @@ public class Project {
|
||||
@JsonProperty("svnUrl")
|
||||
private String svnUrl;
|
||||
|
||||
@JsonProperty("username")
|
||||
private String username;
|
||||
|
||||
@JsonProperty("password")
|
||||
private String password;
|
||||
|
||||
@JsonProperty("currentVersion")
|
||||
private String currentVersion;
|
||||
|
||||
@@ -71,6 +77,22 @@ public class Project {
|
||||
this.svnUrl = svnUrl;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getCurrentVersion() {
|
||||
return currentVersion;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ public class CommitService extends SvnService {
|
||||
* @throws InterruptedException 中断异常
|
||||
* @throws TimeoutException 超时异常
|
||||
*/
|
||||
public CommitResult commit(String workingDirectory, String message, List<String> files)
|
||||
public CommitResult commit(String workingDirectory, String message, List<String> files,
|
||||
String username, String password)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
logger.info("提交修改: {}", workingDirectory);
|
||||
|
||||
@@ -39,6 +40,7 @@ public class CommitService extends SvnService {
|
||||
}
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
addAuthArgs(args, username, password);
|
||||
args.add("-m");
|
||||
args.add(message);
|
||||
|
||||
@@ -85,9 +87,14 @@ public class CommitService extends SvnService {
|
||||
* @throws InterruptedException 中断异常
|
||||
* @throws TimeoutException 超时异常
|
||||
*/
|
||||
public CommitResult commit(String workingDirectory, String message, String username, String password)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return commit(workingDirectory, message, null, username, password);
|
||||
}
|
||||
|
||||
public CommitResult commit(String workingDirectory, String message)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return commit(workingDirectory, message, null);
|
||||
return commit(workingDirectory, message, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ public class DiffService extends SvnService {
|
||||
* @throws InterruptedException 中断异常
|
||||
* @throws TimeoutException 超时异常
|
||||
*/
|
||||
public String getDiff(String workingDirectory, String filePath)
|
||||
public String getDiff(String workingDirectory, String filePath, String username, String password)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
logger.debug("获取差异: {}", workingDirectory);
|
||||
|
||||
@@ -34,6 +34,7 @@ public class DiffService extends SvnService {
|
||||
}
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
addAuthArgs(args, username, password);
|
||||
if (filePath != null && !filePath.isEmpty()) {
|
||||
args.add(filePath);
|
||||
}
|
||||
@@ -59,6 +60,21 @@ public class DiffService extends SvnService {
|
||||
*/
|
||||
public String getDiff(String workingDirectory)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return getDiff(workingDirectory, null);
|
||||
return getDiff(workingDirectory, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文件的差异(不带认证信息)
|
||||
*
|
||||
* @param workingDirectory 工作目录
|
||||
* @param filePath 文件路径(null表示所有文件)
|
||||
* @return 差异内容
|
||||
* @throws IOException IO异常
|
||||
* @throws InterruptedException 中断异常
|
||||
* @throws TimeoutException 超时异常
|
||||
*/
|
||||
public String getDiff(String workingDirectory, String filePath)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return getDiff(workingDirectory, filePath, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class InfoService extends SvnService {
|
||||
* @throws InterruptedException 中断异常
|
||||
* @throws TimeoutException 超时异常
|
||||
*/
|
||||
public SvnInfo getInfo(String workingDirectory)
|
||||
public SvnInfo getInfo(String workingDirectory, String username, String password)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
logger.debug("获取信息: {}", workingDirectory);
|
||||
|
||||
@@ -45,6 +45,11 @@ public class InfoService extends SvnService {
|
||||
return info;
|
||||
}
|
||||
|
||||
public SvnInfo getInfo(String workingDirectory)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return getInfo(workingDirectory, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析svn info输出
|
||||
*
|
||||
|
||||
@@ -30,7 +30,7 @@ public class LogService extends SvnService {
|
||||
* @throws InterruptedException 中断异常
|
||||
* @throws TimeoutException 超时异常
|
||||
*/
|
||||
public List<SvnLog> getLog(String workingDirectory, Integer limit)
|
||||
public List<SvnLog> getLog(String workingDirectory, Integer limit, String username, String password)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
logger.debug("获取日志: {}", workingDirectory);
|
||||
|
||||
@@ -39,6 +39,7 @@ public class LogService extends SvnService {
|
||||
}
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
addAuthArgs(args, username, password);
|
||||
args.add("-v"); // 详细输出
|
||||
if (limit != null && limit > 0) {
|
||||
args.add("-l");
|
||||
@@ -68,9 +69,14 @@ public class LogService extends SvnService {
|
||||
* @throws InterruptedException 中断异常
|
||||
* @throws TimeoutException 超时异常
|
||||
*/
|
||||
public List<SvnLog> getLog(String workingDirectory, String username, String password)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return getLog(workingDirectory, 50, username, password);
|
||||
}
|
||||
|
||||
public List<SvnLog> getLog(String workingDirectory)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return getLog(workingDirectory, 50);
|
||||
return getLog(workingDirectory, 50, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,7 @@ public class StatusService extends SvnService {
|
||||
* @throws InterruptedException 中断异常
|
||||
* @throws TimeoutException 超时异常
|
||||
*/
|
||||
public SvnStatus getStatus(String workingDirectory)
|
||||
public SvnStatus getStatus(String workingDirectory, String username, String password)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
logger.debug("获取状态: {}", workingDirectory);
|
||||
|
||||
@@ -35,6 +35,7 @@ public class StatusService extends SvnService {
|
||||
}
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
addAuthArgs(args, username, password);
|
||||
args.add("-v"); // 详细输出
|
||||
|
||||
ProcessUtil.ProcessResult result = executeSvnCommand("status", args, workingDirectory);
|
||||
@@ -53,6 +54,11 @@ public class StatusService extends SvnService {
|
||||
return status;
|
||||
}
|
||||
|
||||
public SvnStatus getStatus(String workingDirectory)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return getStatus(workingDirectory, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析svn status输出
|
||||
*
|
||||
|
||||
@@ -15,6 +15,27 @@ import java.util.concurrent.TimeoutException;
|
||||
public abstract class SvnService {
|
||||
protected static final Logger logger = LoggerFactory.getLogger(SvnService.class);
|
||||
|
||||
/**
|
||||
* 将认证参数追加到命令参数列表
|
||||
*
|
||||
* @param args 命令参数列表
|
||||
* @param username 用户名(可为空)
|
||||
* @param password 密码(可为空)
|
||||
*/
|
||||
protected void addAuthArgs(List<String> args, String username, String password) {
|
||||
if (username != null && !username.isEmpty()) {
|
||||
args.add("--username");
|
||||
args.add(username);
|
||||
if (password != null && !password.isEmpty()) {
|
||||
args.add("--password");
|
||||
args.add(password);
|
||||
}
|
||||
// 避免弹出交互式输入框,统一走非交互模式
|
||||
args.add("--no-auth-cache");
|
||||
args.add("--non-interactive");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行SVN命令
|
||||
*
|
||||
|
||||
@@ -25,7 +25,7 @@ public class UpdateService extends SvnService {
|
||||
* @throws InterruptedException 中断异常
|
||||
* @throws TimeoutException 超时异常
|
||||
*/
|
||||
public UpdateResult update(String workingDirectory, String revision)
|
||||
public UpdateResult update(String workingDirectory, String revision, String username, String password)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
logger.info("更新工作副本: {}", workingDirectory);
|
||||
|
||||
@@ -34,6 +34,7 @@ public class UpdateService extends SvnService {
|
||||
}
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
addAuthArgs(args, username, password);
|
||||
if (revision != null && !revision.isEmpty()) {
|
||||
args.add("-r");
|
||||
args.add(revision);
|
||||
@@ -77,9 +78,14 @@ public class UpdateService extends SvnService {
|
||||
* @throws InterruptedException 中断异常
|
||||
* @throws TimeoutException 超时异常
|
||||
*/
|
||||
public UpdateResult update(String workingDirectory, String username, String password)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return update(workingDirectory, null, username, password);
|
||||
}
|
||||
|
||||
public UpdateResult update(String workingDirectory)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return update(workingDirectory, null);
|
||||
return update(workingDirectory, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
<URL value="@../css/styles.css" />
|
||||
</stylesheets>
|
||||
|
||||
<headerText>
|
||||
<Label text="添加/编辑项目" styleClass="dialog-title"/>
|
||||
</headerText>
|
||||
<!-- 对话框标题。
|
||||
注意:headerText 节点只能是纯文本,如果放 Label 会显示成 Label@xxx 文本 -->
|
||||
<headerText>添加/编辑项目</headerText>
|
||||
|
||||
<content>
|
||||
<VBox spacing="16" style="-fx-padding: 20;">
|
||||
@@ -36,6 +36,14 @@
|
||||
<!-- SVN URL -->
|
||||
<Label text="SVN URL:" GridPane.columnIndex="0" GridPane.rowIndex="2"/>
|
||||
<TextField fx:id="svnUrlField" promptText="请输入SVN仓库URL" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
|
||||
|
||||
<!-- SVN 用户名 -->
|
||||
<Label text="SVN 用户名:" GridPane.columnIndex="0" GridPane.rowIndex="3"/>
|
||||
<TextField fx:id="usernameField" promptText="可选,留空则使用默认认证" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
|
||||
|
||||
<!-- SVN 密码 -->
|
||||
<Label text="SVN 密码:" GridPane.columnIndex="0" GridPane.rowIndex="4"/>
|
||||
<PasswordField fx:id="passwordField" promptText="可选,留空则使用默认认证" GridPane.columnIndex="1" GridPane.rowIndex="4"/>
|
||||
</GridPane>
|
||||
</VBox>
|
||||
</content>
|
||||
|
||||
Reference in New Issue
Block a user