summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorshizhendong <[email protected]>2024-11-20 09:58:05 +0800
committershizhendong <[email protected]>2024-11-20 09:58:05 +0800
commit30b3f1c1015a46bdef2023419618d59104281597 (patch)
tree9556d82a46d7b217b5f7592f6940225cb71addf4
parentec4c58db4e888946b94c0f57a06f31c610930dba (diff)
refactor: application 模块代码重构,将部分 jgit 代码拆分开
-rw-r--r--src/main/java/net/geedge/asw/module/app/controller/ApplicationController.java95
-rw-r--r--src/main/java/net/geedge/asw/module/app/controller/BranchController.java111
-rw-r--r--src/main/java/net/geedge/asw/module/app/controller/GitController.java277
-rw-r--r--src/main/java/net/geedge/asw/module/app/controller/MergeRequestController.java109
-rw-r--r--src/main/java/net/geedge/asw/module/app/service/IApplicationMergeService.java9
-rw-r--r--src/main/java/net/geedge/asw/module/app/service/IApplicationService.java19
-rw-r--r--src/main/java/net/geedge/asw/module/app/service/IBranchService.java18
-rw-r--r--src/main/java/net/geedge/asw/module/app/service/IGitService.java71
-rw-r--r--src/main/java/net/geedge/asw/module/app/service/impl/ApplicationMergeServiceImpl.java397
-rw-r--r--src/main/java/net/geedge/asw/module/app/service/impl/ApplicationServiceImpl.java573
-rw-r--r--src/main/java/net/geedge/asw/module/app/service/impl/BranchServiceImpl.java156
-rw-r--r--src/main/java/net/geedge/asw/module/app/service/impl/GitServiceImpl.java1397
-rw-r--r--src/main/java/net/geedge/asw/module/app/util/JGitUtils.java820
-rw-r--r--src/main/java/net/geedge/asw/module/workspace/entity/WorkspaceEntity.java7
-rw-r--r--src/main/java/net/geedge/asw/module/workspace/service/IWorkspaceService.java4
-rw-r--r--src/main/java/net/geedge/asw/module/workspace/service/impl/WorkspaceServiceImpl.java32
16 files changed, 2105 insertions, 1990 deletions
diff --git a/src/main/java/net/geedge/asw/module/app/controller/ApplicationController.java b/src/main/java/net/geedge/asw/module/app/controller/ApplicationController.java
index c11b66e..55c488a 100644
--- a/src/main/java/net/geedge/asw/module/app/controller/ApplicationController.java
+++ b/src/main/java/net/geedge/asw/module/app/controller/ApplicationController.java
@@ -1,7 +1,6 @@
package net.geedge.asw.module.app.controller;
import cn.hutool.json.JSONObject;
-import cn.hutool.log.Log;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.R;
@@ -25,14 +24,100 @@ import java.util.stream.Collectors;
@RequestMapping("/api/v1/workspace")
public class ApplicationController {
- private static final Log log = Log.get();
-
@Autowired
private IWorkspaceService workspaceService;
@Autowired
private IApplicationService applicationService;
+ @GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}")
+ public R infoApplication(@PathVariable("workspaceId") String workspaceId,
+ @PathVariable("branchName") String branchName,
+ @PathVariable("applicationName") String applicationName) {
+ Map<Object, Object> record = applicationService.infoApplication(workspaceId, branchName, applicationName);
+ return R.ok().putData("record", record);
+ }
+
+ @GetMapping("/{workspaceId}/branch/{branchName}/application")
+ public R listApplication(@PathVariable("workspaceId") String workspaceId,
+ @PathVariable("branchName") String branchName,
+ @RequestParam(value = "q", required = false) String q) {
+ List<Map<Object, Object>> records = applicationService.listApplication(workspaceId, branchName, q);
+ return R.ok().putData("records", records);
+ }
+
+ @PostMapping("/{workspaceId}/branch/{branchName}/application")
+ public synchronized R newApplication(@PathVariable("workspaceId") String workspaceId,
+ @PathVariable("branchName") String branchName,
+ @RequestBody Map<String, String> body) {
+ String applicationName = T.MapUtil.getStr(body, "name");
+ T.VerifyUtil.is(applicationName).notEmpty(RCode.PARAM_CANNOT_EMPTY);
+
+ applicationService.newApplication(workspaceId, branchName, applicationName);
+ return R.ok();
+ }
+
+
+ @PostMapping("/{workspaceId}/branch/{branchName}/application/commit")
+ public synchronized R updateApplication(@PathVariable("workspaceId") String workspaceId,
+ @PathVariable("branchName") String branchName,
+ @RequestBody Map<String, Object> body) {
+ String lastCommitId = T.MapUtil.getStr(body, "lastCommitId");
+ String message = T.MapUtil.getStr(body, "message");
+
+ T.VerifyUtil.is(lastCommitId).notEmpty(RCode.PARAM_CANNOT_EMPTY)
+ .and(message).notEmpty(RCode.PARAM_CANNOT_EMPTY);
+
+ List<Map<String, String>> files = T.MapUtil.get(body, "files", List.class, T.ListUtil.list(true));
+ if (T.CollUtil.isEmpty(files)) {
+ return R.ok();
+ }
+
+ for (Map<String, String> file : files) {
+ String action = T.MapUtil.getStr(file, "action");
+ String path = T.MapUtil.getStr(file, "path");
+ if (T.StrUtil.hasEmpty(action, path)) {
+ return R.error(RCode.PARAM_CANNOT_EMPTY);
+ }
+ if (T.StrUtil.equalsAny(action, "create", "update")) {
+ String content = T.MapUtil.getStr(file, "content");
+ T.VerifyUtil.is(content).notEmpty(RCode.PARAM_CANNOT_EMPTY);
+ }
+ }
+ applicationService.updateApplication(workspaceId, branchName, lastCommitId, message, files);
+ return R.ok();
+ }
+
+ @DeleteMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}")
+ public synchronized R deleteApplication(@PathVariable("workspaceId") String workspaceId,
+ @PathVariable("branchName") String branchName,
+ @PathVariable("applicationName") String applicationName) {
+ applicationService.deleteApplication(workspaceId, branchName, applicationName);
+ return R.ok();
+ }
+
+
+ @GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}/commit")
+ public R listApplicationCommit(@PathVariable("workspaceId") String workspaceId,
+ @PathVariable("branchName") String branchName,
+ @PathVariable("applicationName") String applicationName,
+ @RequestParam(required = false) String file) {
+ List<Map<Object, Object>> records = applicationService.listApplicationCommit(workspaceId, branchName, applicationName, file);
+ return R.ok().putData("records", records);
+ }
+
+
+ @GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}/commit/{commitId}/content")
+ public R infoApplicationFileContent(@PathVariable("workspaceId") String workspaceId,
+ @PathVariable("branchName") String branchName,
+ @PathVariable("applicationName") String applicationName,
+ @PathVariable("commitId") String commitId,
+ @RequestParam(required = true) String file) {
+ Map<Object, Object> record = applicationService.infoApplicationFileContent(workspaceId, branchName, applicationName, commitId, file);
+ return R.ok().putData("record", record);
+ }
+
+
@PostMapping("/{workspaceId}/branch/{branchName}/application/import")
public synchronized R importApplication(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@@ -85,7 +170,7 @@ public class ApplicationController {
@RequestParam(defaultValue = "tsg2402") String format,
HttpServletResponse response) throws IOException {
// validate
- List<ApplicationEntity> appList = applicationService.queryList(workspaceId, branchName, T.StrUtil.splitToArray(names, ","));
+ List<ApplicationEntity> appList = applicationService.listApplication(workspaceId, branchName, T.StrUtil.splitToArray(names, ","));
T.VerifyUtil.is(appList).notEmpty(RCode.APP_NOT_EXIST);
// format
@@ -95,4 +180,4 @@ public class ApplicationController {
T.ResponseUtil.downloadFile(response, T.StrUtil.concat(true, "application_", System.currentTimeMillis() + ".json"), bytes);
}
-}
+} \ No newline at end of file
diff --git a/src/main/java/net/geedge/asw/module/app/controller/BranchController.java b/src/main/java/net/geedge/asw/module/app/controller/BranchController.java
new file mode 100644
index 0000000..bd8fd8e
--- /dev/null
+++ b/src/main/java/net/geedge/asw/module/app/controller/BranchController.java
@@ -0,0 +1,111 @@
+package net.geedge.asw.module.app.controller;
+
+import net.geedge.asw.common.util.ASWException;
+import net.geedge.asw.common.util.R;
+import net.geedge.asw.common.util.RCode;
+import net.geedge.asw.common.util.T;
+import net.geedge.asw.module.app.service.IBranchService;
+import net.geedge.asw.module.app.util.JGitUtils;
+import net.geedge.asw.module.workspace.service.IWorkspaceService;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/v1/workspace")
+public class BranchController {
+
+ @Autowired
+ private IBranchService branchService;
+
+ @Autowired
+ private IWorkspaceService workspaceService;
+
+ @GetMapping("/{workspaceId}/branch")
+ public R listBranch(@PathVariable("workspaceId") String workspaceId,
+ @RequestParam(value = "search", required = false) String search) {
+ List<Map<Object, Object>> list = branchService.listBranch(workspaceId, search);
+ return R.ok().putData("records", list);
+ }
+
+ @GetMapping("/{workspaceId}/branch/{branchName}")
+ public R infoBranch(@PathVariable("workspaceId") String workspaceId, @PathVariable("branchName") String branchName) {
+ Map<Object, Object> record = branchService.infoBranch(workspaceId, branchName);
+ return R.ok().putData("record", record);
+ }
+
+ @PostMapping("/{workspaceId}/branch")
+ public synchronized R newBranch(@PathVariable("workspaceId") String workspaceId, @RequestBody Map<String, String> requestBody) {
+ String branch = T.MapUtil.getStr(requestBody, "branch", "");
+ String ref = T.MapUtil.getStr(requestBody, "ref", "");
+ if (T.StrUtil.hasEmpty(branch, ref)) {
+ throw new ASWException(RCode.PARAM_CANNOT_EMPTY);
+ }
+
+ Map<Object, Object> record = branchService.newBranch(workspaceId, branch, ref);
+ return R.ok().putData("record", record);
+ }
+
+ @DeleteMapping("/{workspaceId}/branch/{branchName}")
+ public synchronized R deleteBranch(@PathVariable("workspaceId") String workspaceId, @PathVariable("branchName") String branchName) {
+ branchService.deleteBranch(workspaceId, branchName);
+ return R.ok();
+ }
+
+ @GetMapping("/{workspaceId}/branch/{branchName}/commits")
+ public R listBranchCommit(@PathVariable("workspaceId") String workspaceId,
+ @PathVariable("branchName") String branchName,
+ @RequestParam(required = false) String path,
+ @RequestParam(required = false) Integer current,
+ @RequestParam(required = false) Integer size) {
+ List<Map<Object, Object>> records = branchService.listBranchCommit(workspaceId, branchName, path);
+ long total = T.CollUtil.size(records);
+ if (null != current && null != size) {
+ int fromIndex = (current - 1) * size;
+ int toIndex = Math.min(fromIndex + size, records.size());
+ records = records.subList(fromIndex, toIndex);
+ }
+ return R.ok().putData("records", records).put("total", total);
+ }
+
+ @GetMapping({
+ "/{workspaceId}/branch/{branchName}/commit/{commitId}/diff",
+ "/{workspaceId}/branch/{branchName}/commit/{commitId}/diff/{oldCommitId}"
+ })
+ public R branchCommitDiff(@PathVariable("workspaceId") String workspaceId,
+ @PathVariable("branchName") String branchName,
+ @PathVariable("commitId") String newCommitId,
+ @PathVariable(value = "oldCommitId", required = false) String oldCommitId) throws IOException {
+
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir)) {
+ if (T.StrUtil.isEmpty(oldCommitId)) {
+ // oldCommitId 为空默认对比 parent commitId
+ RevCommit parentCommit = JGitUtils.getParentCommit(repository, branchName, newCommitId);
+ oldCommitId = parentCommit.getName();
+ }
+
+ RevCommit oldCommit = JGitUtils.infoCommit(repository, oldCommitId);
+ RevCommit newCommit = JGitUtils.infoCommit(repository, newCommitId);
+
+ List<Map<Object, Object>> diffList = JGitUtils.getDiffFileListInCommits(repository, newCommitId, oldCommitId);
+ Map<Object, Object> record = T.MapUtil.builder()
+ .put("oldBranch", branchName)
+ .put("newBranch", branchName)
+ .put("oldCommitId", oldCommitId)
+ .put("newCommitId", newCommitId)
+ .put("oldCommit", JGitUtils.buildAswCommitInfo(oldCommit))
+ .put("newCommit", JGitUtils.buildAswCommitInfo(newCommit))
+ .put("files", diffList)
+ .build();
+ return R.ok().putData("record", record);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/net/geedge/asw/module/app/controller/GitController.java b/src/main/java/net/geedge/asw/module/app/controller/GitController.java
deleted file mode 100644
index 58dfce8..0000000
--- a/src/main/java/net/geedge/asw/module/app/controller/GitController.java
+++ /dev/null
@@ -1,277 +0,0 @@
-package net.geedge.asw.module.app.controller;
-
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import net.geedge.asw.common.util.ASWException;
-import net.geedge.asw.common.util.R;
-import net.geedge.asw.common.util.RCode;
-import net.geedge.asw.common.util.T;
-import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
-import net.geedge.asw.module.app.service.IApplicationMergeService;
-import net.geedge.asw.module.app.service.IGitService;
-import net.geedge.asw.module.app.service.impl.ApplicationMergeServiceImpl.MergeRequestStatus;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@RestController
-@RequestMapping("/api/v1/workspace")
-public class GitController {
-
- @Autowired
- private IGitService gitService;
-
- @Autowired
- private IApplicationMergeService applicationMergeService;
-
- @GetMapping("/{workspaceId}/branch")
- public R listBranch(@PathVariable("workspaceId") String workspaceId,
- @RequestParam(value = "search", required = false) String search) {
- List<Map<Object, Object>> list = gitService.listBranch(workspaceId, search);
- return R.ok().putData("records", list);
- }
-
- @GetMapping("/{workspaceId}/branch/{branchName}")
- public R infoBranch(@PathVariable("workspaceId") String workspaceId, @PathVariable("branchName") String branchName) {
- Map<Object, Object> record = gitService.infoBranch(workspaceId, branchName);
- return R.ok().putData("record", record);
- }
-
- @PostMapping("/{workspaceId}/branch")
- public R newBranch(@PathVariable("workspaceId") String workspaceId, @RequestBody Map<String, String> requestBody) {
- String branch = T.MapUtil.getStr(requestBody, "branch", "");
- String ref = T.MapUtil.getStr(requestBody, "ref", "");
- if (T.StrUtil.hasEmpty(branch, ref)) {
- throw new ASWException(RCode.PARAM_CANNOT_EMPTY);
- }
-
- Map<Object, Object> record = gitService.newBranch(workspaceId, branch, ref);
- return R.ok().putData("record", record);
- }
-
- @DeleteMapping("/{workspaceId}/branch/{branchName}")
- public R deleteBranch(@PathVariable("workspaceId") String workspaceId, @PathVariable("branchName") String branchName) {
- gitService.deleteBranch(workspaceId, branchName);
- return R.ok();
- }
-
- @GetMapping("/{workspaceId}/branch/{branchName}/commits")
- public R listBranchCommit(@PathVariable("workspaceId") String workspaceId,
- @PathVariable("branchName") String branchName,
- @RequestParam(required = false) String path,
- @RequestParam(required = false) Integer current,
- @RequestParam(required = false) Integer size) {
- List<Map<Object, Object>> records = gitService.listBranchCommit(workspaceId, branchName, path);
- long total = T.CollUtil.size(records);
- if (null != current && null != size) {
- int fromIndex = (current - 1) * size;
- int toIndex = Math.min(fromIndex + size, records.size());
- records = records.subList(fromIndex, toIndex);
- }
- return R.ok().putData("records", records).put("total", total);
- }
-
- @GetMapping({
- "/{workspaceId}/branch/{branchName}/commit/{commitId}/diff",
- "/{workspaceId}/branch/{branchName}/commit/{commitId}/diff/{oldCommitId}"
- })
- public R branchCommitDiff(@PathVariable("workspaceId") String workspaceId,
- @PathVariable("branchName") String branchName,
- @PathVariable("commitId") String newCommitId,
- @PathVariable(value = "oldCommitId", required = false) String oldCommitId) {
-
- if (T.StrUtil.isEmpty(oldCommitId)) {
- // oldCommitId 为空默认对比 parent commitId
- oldCommitId = gitService.getParentCommitId(workspaceId, branchName, newCommitId);
- }
-
- Map<Object, Object> oldCommit = gitService.infoCommit(workspaceId, oldCommitId);
- Map<Object, Object> newCommit = gitService.infoCommit(workspaceId, newCommitId);
-
- List<Map<Object, Object>> diffList = gitService.getDiffFileListInCommits(workspaceId, newCommitId, oldCommitId);
- Map<Object, Object> record = T.MapUtil.builder()
- .put("oldBranch", branchName)
- .put("newBranch", branchName)
- .put("oldCommitId", oldCommitId)
- .put("newCommitId", newCommitId)
- .put("oldCommit", oldCommit)
- .put("newCommit", newCommit)
- .put("files", diffList)
- .build();
- return R.ok().putData("record", record);
- }
-
- @GetMapping("/{workspaceId}/branch/{branchName}/application")
- public R listApplication(@PathVariable("workspaceId") String workspaceId,
- @PathVariable("branchName") String branchName,
- @RequestParam(value = "q", required = false) String q) {
- List<Map<Object, Object>> records = gitService.listApplication(workspaceId, branchName, q);
- return R.ok().putData("records", records);
- }
-
- @GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}")
- public R infoApplication(@PathVariable("workspaceId") String workspaceId,
- @PathVariable("branchName") String branchName,
- @PathVariable("applicationName") String applicationName) {
- Map<Object, Object> record = gitService.infoApplication(workspaceId, branchName, applicationName);
- return R.ok().putData("record", record);
- }
-
- @PostMapping("/{workspaceId}/branch/{branchName}/application")
- public synchronized R newApplication(@PathVariable("workspaceId") String workspaceId,
- @PathVariable("branchName") String branchName,
- @RequestBody Map<String, String> body) {
- String applicationName = T.MapUtil.getStr(body, "name");
- T.VerifyUtil.is(applicationName).notEmpty(RCode.PARAM_CANNOT_EMPTY);
-
- gitService.newApplication(workspaceId, branchName, applicationName);
- return R.ok();
- }
-
-
- @PostMapping("/{workspaceId}/branch/{branchName}/application/commit")
- public synchronized R updateApplication(@PathVariable("workspaceId") String workspaceId,
- @PathVariable("branchName") String branchName,
- @RequestBody Map<String, Object> body) {
- String lastCommitId = T.MapUtil.getStr(body, "lastCommitId");
- String message = T.MapUtil.getStr(body, "message");
-
- T.VerifyUtil.is(lastCommitId).notEmpty(RCode.PARAM_CANNOT_EMPTY)
- .and(message).notEmpty(RCode.PARAM_CANNOT_EMPTY);
-
- List<Map<String, String>> files = T.MapUtil.get(body, "files", List.class, T.ListUtil.list(true));
- if (T.CollUtil.isEmpty(files)) {
- return R.ok();
- }
-
- for (Map<String, String> file : files) {
- String action = T.MapUtil.getStr(file, "action");
- String path = T.MapUtil.getStr(file, "path");
- if (T.StrUtil.hasEmpty(action, path)) {
- return R.error(RCode.PARAM_CANNOT_EMPTY);
- }
- if (T.StrUtil.equalsAny(action, "create", "update")) {
- String content = T.MapUtil.getStr(file, "content");
- T.VerifyUtil.is(content).notEmpty(RCode.PARAM_CANNOT_EMPTY);
- }
- }
- gitService.updateApplication(workspaceId, branchName, lastCommitId, message, files);
- return R.ok();
- }
-
- @DeleteMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}")
- public synchronized R deleteApplication(@PathVariable("workspaceId") String workspaceId,
- @PathVariable("branchName") String branchName,
- @PathVariable("applicationName") String applicationName) {
- gitService.deleteApplication(workspaceId, branchName, applicationName);
- return R.ok();
- }
-
-
- @GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}/commit")
- public R listApplicationCommit(@PathVariable("workspaceId") String workspaceId,
- @PathVariable("branchName") String branchName,
- @PathVariable("applicationName") String applicationName,
- @RequestParam(required = false) String file) {
- List<Map<Object, Object>> records = gitService.listApplicationCommit(workspaceId, branchName, applicationName, file);
- return R.ok().putData("records", records);
- }
-
-
- @GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}/commit/{commitId}/content")
- public R infoApplicationFileContent(@PathVariable("workspaceId") String workspaceId,
- @PathVariable("branchName") String branchName,
- @PathVariable("applicationName") String applicationName,
- @PathVariable("commitId") String commitId,
- @RequestParam(required = true) String file) {
- Map<Object, Object> record = gitService.infoApplicationFileContent(workspaceId, branchName, applicationName, commitId, file);
- return R.ok().putData("record", record);
- }
-
- @GetMapping("/{workspaceId}/mr")
- public R listMr(@PathVariable("workspaceId") String workspaceId, @RequestParam Map<String, Object> params) {
- // workspaceId
- params = T.MapUtil.defaultIfEmpty(params, new HashMap<>());
- params.put("workspaceId", workspaceId);
-
- Page page = applicationMergeService.queryList(params);
- return R.ok(page);
- }
-
- @GetMapping("/{workspaceId}/mr/{mrId}")
- public R infoMr(@PathVariable("mrId") String mrId) {
- ApplicationMergeEntity record = applicationMergeService.queryInfo(mrId);
- return R.ok().putData("record", record);
- }
-
- @GetMapping("/{workspaceId}/mr/{mrId}/commits")
- public R listMrCommit(@PathVariable("mrId") String mrId) {
- List<Map<Object, Object>> records = applicationMergeService.listMrCommit(mrId);
- return R.ok().putData("records", records);
- }
-
- @GetMapping("/{workspaceId}/mr/{mrId}/diffs")
- public R mrDiff(@PathVariable("mrId") String mrId) {
- Map<Object, Object> record = applicationMergeService.mrCommitDiff(mrId);
- return R.ok().putData("record", record);
- }
-
- @GetMapping("/{workspaceId}/mr/{mrId}/conflicts")
- public R getMrConflictList(@PathVariable("mrId") String mrId) {
- Map<Object, Object> record = applicationMergeService.getMrConflictList(mrId);
- return R.ok().putData("record", record);
- }
-
- @PostMapping("/{workspaceId}/mr/{mrId}/conflicts")
- public R resolveMrConflicts(@PathVariable("mrId") String mrId, @RequestBody Map<String, Object> body) {
- String commitId = T.MapUtil.getStr(body, "commitId");
- String commitMessage = T.MapUtil.getStr(body, "commitMessage");
- List<Map<String, String>> files = T.MapUtil.get(body, "files", List.class, new ArrayList());
- T.VerifyUtil.is(commitId).notEmpty(RCode.PARAM_CANNOT_EMPTY)
- .and(commitMessage).notEmpty(RCode.PARAM_CANNOT_EMPTY)
- .and(files).notEmpty(RCode.PARAM_CANNOT_EMPTY);
-
- for (Map<String, String> m : files) {
- String path = T.MapUtil.getStr(m, "path");
- String content = T.MapUtil.getStr(m, "content");
- T.VerifyUtil.is(path).notEmpty(RCode.PARAM_CANNOT_EMPTY)
- .and(content).notEmpty(RCode.PARAM_CANNOT_EMPTY);
- }
-
- applicationMergeService.resolveMrConflicts(mrId, body);
- return R.ok();
- }
-
- @PostMapping("/{workspaceId}/mr")
- public R newMr(@PathVariable("workspaceId") String workspaceId, @RequestBody ApplicationMergeEntity entity) {
- T.VerifyUtil.is(entity).notNull()
- .and(entity.getTitle()).notEmpty(RCode.PARAM_CANNOT_EMPTY)
- .and(entity.getTargetBranch()).notEmpty(RCode.PARAM_CANNOT_EMPTY)
- .and(entity.getTargetBranch()).notEmpty(RCode.PARAM_CANNOT_EMPTY);
-
- // workspaceId
- entity.setWorkspaceId(workspaceId);
-
- ApplicationMergeEntity record = applicationMergeService.newMr(entity);
- return R.ok().putData("record", record);
- }
-
- @PutMapping("/{workspaceId}/mr/{mrId}")
- public synchronized R mergeMr(@PathVariable("mrId") String mrId, @RequestBody(required = false) Map<String, Object> body) {
- String action = T.MapUtil.getStr(body, "action");
- T.VerifyUtil.is(action).notEmpty(RCode.PARAM_CANNOT_EMPTY);
-
- ApplicationMergeEntity record = applicationMergeService.mergeMr(mrId, action);
- if(T.BooleanUtil.and(
- T.ObjectUtil.equals("merge", action),
- T.ObjectUtil.notEqual(MergeRequestStatus.MERGED.toString(), record.getStatus())
- )){
- return R.error(RCode.GIT_MERGE_FAILED).putData("record", record);
- }
- return R.ok().putData("record", record);
- }
-
-} \ No newline at end of file
diff --git a/src/main/java/net/geedge/asw/module/app/controller/MergeRequestController.java b/src/main/java/net/geedge/asw/module/app/controller/MergeRequestController.java
new file mode 100644
index 0000000..9d4eb5e
--- /dev/null
+++ b/src/main/java/net/geedge/asw/module/app/controller/MergeRequestController.java
@@ -0,0 +1,109 @@
+package net.geedge.asw.module.app.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import net.geedge.asw.common.util.R;
+import net.geedge.asw.common.util.RCode;
+import net.geedge.asw.common.util.T;
+import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
+import net.geedge.asw.module.app.service.IApplicationMergeService;
+import net.geedge.asw.module.app.service.impl.ApplicationMergeServiceImpl.MergeRequestStatus;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/v1/workspace")
+public class MergeRequestController {
+
+ @Autowired
+ private IApplicationMergeService applicationMergeService;
+
+ @GetMapping("/{workspaceId}/mr")
+ public R listMr(@PathVariable("workspaceId") String workspaceId, @RequestParam Map<String, Object> params) {
+ // workspaceId
+ params = T.MapUtil.defaultIfEmpty(params, new HashMap<>());
+ params.put("workspaceId", workspaceId);
+
+ Page page = applicationMergeService.queryList(params);
+ return R.ok(page);
+ }
+
+ @GetMapping("/{workspaceId}/mr/{mrId}")
+ public R infoMr(@PathVariable("mrId") String mrId) {
+ ApplicationMergeEntity record = applicationMergeService.queryInfo(mrId);
+ return R.ok().putData("record", record);
+ }
+
+ @GetMapping("/{workspaceId}/mr/{mrId}/commits")
+ public R listMrCommit(@PathVariable("mrId") String mrId) throws IOException {
+ List<Map<Object, Object>> records = applicationMergeService.listMrCommit(mrId);
+ return R.ok().putData("records", records);
+ }
+
+ @GetMapping("/{workspaceId}/mr/{mrId}/diffs")
+ public R mrDiff(@PathVariable("mrId") String mrId) {
+ Map<Object, Object> record = applicationMergeService.mrCommitDiff(mrId);
+ return R.ok().putData("record", record);
+ }
+
+ @GetMapping("/{workspaceId}/mr/{mrId}/conflicts")
+ public R getMrConflictList(@PathVariable("mrId") String mrId) throws IOException {
+ Map<Object, Object> record = applicationMergeService.getMrConflictList(mrId);
+ return R.ok().putData("record", record);
+ }
+
+ @PostMapping("/{workspaceId}/mr/{mrId}/conflicts")
+ public R resolveMrConflicts(@PathVariable("mrId") String mrId, @RequestBody Map<String, Object> body) throws IOException{
+ String commitId = T.MapUtil.getStr(body, "commitId");
+ String commitMessage = T.MapUtil.getStr(body, "commitMessage");
+ List<Map<String, String>> files = T.MapUtil.get(body, "files", List.class, new ArrayList());
+ T.VerifyUtil.is(commitId).notEmpty(RCode.PARAM_CANNOT_EMPTY)
+ .and(commitMessage).notEmpty(RCode.PARAM_CANNOT_EMPTY)
+ .and(files).notEmpty(RCode.PARAM_CANNOT_EMPTY);
+
+ for (Map<String, String> m : files) {
+ String path = T.MapUtil.getStr(m, "path");
+ String content = T.MapUtil.getStr(m, "content");
+ T.VerifyUtil.is(path).notEmpty(RCode.PARAM_CANNOT_EMPTY)
+ .and(content).notEmpty(RCode.PARAM_CANNOT_EMPTY);
+ }
+
+ applicationMergeService.resolveMrConflicts(mrId, body);
+ return R.ok();
+ }
+
+ @PostMapping("/{workspaceId}/mr")
+ public R newMr(@PathVariable("workspaceId") String workspaceId, @RequestBody ApplicationMergeEntity entity) throws IOException {
+ T.VerifyUtil.is(entity).notNull()
+ .and(entity.getTitle()).notEmpty(RCode.PARAM_CANNOT_EMPTY)
+ .and(entity.getTargetBranch()).notEmpty(RCode.PARAM_CANNOT_EMPTY)
+ .and(entity.getTargetBranch()).notEmpty(RCode.PARAM_CANNOT_EMPTY);
+
+ // workspaceId
+ entity.setWorkspaceId(workspaceId);
+
+ ApplicationMergeEntity record = applicationMergeService.newMr(entity);
+ return R.ok().putData("record", record);
+ }
+
+ @PutMapping("/{workspaceId}/mr/{mrId}")
+ public synchronized R mergeMr(@PathVariable("mrId") String mrId, @RequestBody(required = false) Map<String, Object> body) {
+ String action = T.MapUtil.getStr(body, "action");
+ T.VerifyUtil.is(action).notEmpty(RCode.PARAM_CANNOT_EMPTY);
+
+ ApplicationMergeEntity record = applicationMergeService.mergeMr(mrId, action);
+ if(T.BooleanUtil.and(
+ T.ObjectUtil.equals("merge", action),
+ T.ObjectUtil.notEqual(MergeRequestStatus.MERGED.toString(), record.getStatus())
+ )){
+ return R.error(RCode.GIT_MERGE_FAILED).putData("record", record);
+ }
+ return R.ok().putData("record", record);
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/net/geedge/asw/module/app/service/IApplicationMergeService.java b/src/main/java/net/geedge/asw/module/app/service/IApplicationMergeService.java
index 13e515f..d3d88b7 100644
--- a/src/main/java/net/geedge/asw/module/app/service/IApplicationMergeService.java
+++ b/src/main/java/net/geedge/asw/module/app/service/IApplicationMergeService.java
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
+import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -13,15 +14,15 @@ public interface IApplicationMergeService extends IService<ApplicationMergeEntit
Page queryList(Map<String, Object> params);
- ApplicationMergeEntity newMr(ApplicationMergeEntity entity);
+ ApplicationMergeEntity newMr(ApplicationMergeEntity entity) throws IOException;
- List<Map<Object, Object>> listMrCommit(String mrId);
+ List<Map<Object, Object>> listMrCommit(String mrId) throws IOException;
Map<Object, Object> mrCommitDiff(String mrId);
- Map<Object, Object> getMrConflictList(String mrId);
+ Map<Object, Object> getMrConflictList(String mrId) throws IOException;
- void resolveMrConflicts(String mrId, Map<String, Object> body);
+ void resolveMrConflicts(String mrId, Map<String, Object> body) throws IOException;
ApplicationMergeEntity mergeMr(String mrId, String action);
diff --git a/src/main/java/net/geedge/asw/module/app/service/IApplicationService.java b/src/main/java/net/geedge/asw/module/app/service/IApplicationService.java
index e0bb4a3..ea1bbd9 100644
--- a/src/main/java/net/geedge/asw/module/app/service/IApplicationService.java
+++ b/src/main/java/net/geedge/asw/module/app/service/IApplicationService.java
@@ -4,13 +4,28 @@ import cn.hutool.json.JSONObject;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import java.util.List;
+import java.util.Map;
public interface IApplicationService {
- List<ApplicationEntity> queryList(String workspaceId, String branchName, String... names);
+ Map<Object, Object> infoApplication(String workspaceId, String branch, String name);
+
+ List<ApplicationEntity> listApplication(String workspaceId, String branch, String... names);
+
+ List<Map<Object, Object>> listApplication(String workspaceId, String branch, String q);
+
+ void newApplication(String workspaceId, String branch, String name);
+
+ void deleteApplication(String workspaceId, String branch, String name);
+
+ void updateApplication(String workspaceId, String branch, String lastCommitId, String message, List<Map<String, String>> updateContent);
+
+ List<Map<Object, Object>> listApplicationCommit(String workspaceId, String branch, String name, String file);
+
+ Map<Object, Object> infoApplicationFileContent(String workspaceId, String branch, String name, String commitId, String file);
byte[] exportAppByFormat(List<ApplicationEntity> appList, String format);
- List<ApplicationEntity> importAppByFormat(String workspaceId, String branchName, String format, List<JSONObject> dataList);
+ List<ApplicationEntity> importAppByFormat(String workspaceId, String branch, String format, List<JSONObject> dataList);
}
diff --git a/src/main/java/net/geedge/asw/module/app/service/IBranchService.java b/src/main/java/net/geedge/asw/module/app/service/IBranchService.java
new file mode 100644
index 0000000..862be9e
--- /dev/null
+++ b/src/main/java/net/geedge/asw/module/app/service/IBranchService.java
@@ -0,0 +1,18 @@
+package net.geedge.asw.module.app.service;
+
+import java.util.List;
+import java.util.Map;
+
+public interface IBranchService {
+
+ List<Map<Object, Object>> listBranch(String workspaceId, String search);
+
+ Map<Object, Object> infoBranch(String workspaceId, String branch);
+
+ Map<Object, Object> newBranch(String workspaceId, String name, String ref);
+
+ void deleteBranch(String workspaceId, String branch);
+
+ List<Map<Object, Object>> listBranchCommit(String workspaceId, String branch, String path);
+
+}
diff --git a/src/main/java/net/geedge/asw/module/app/service/IGitService.java b/src/main/java/net/geedge/asw/module/app/service/IGitService.java
deleted file mode 100644
index 66ce20e..0000000
--- a/src/main/java/net/geedge/asw/module/app/service/IGitService.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package net.geedge.asw.module.app.service;
-
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
-import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheEntry;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
-public interface IGitService {
-
- Git getGitInstance(String workspaceId);
-
- Repository initRepository(String workspaceId);
-
- Map<Object, Object> infoCommit(String workspaceId, String commitId);
-
- List<Map<Object, Object>> listBranch(String workspaceId, String search);
-
- Map<Object, Object> infoBranch(String workspaceId, String branch);
-
- Map<Object, Object> newBranch(String workspaceId, String branch, String ref);
-
- void deleteBranch(String workspaceId, String branch);
-
- boolean isBranchExists(String workspaceId, String branch);
-
- List<Map<Object, Object>> listBranchCommit(String workspaceId, String branch, String path);
-
- String getParentCommitId(String workspaceId, String branch, String commitId);
-
- String getLatestCommitId(String workspaceId, String branch);
-
- String getMergeBase(String workspaceId, String branchA, String branchB);
-
- ObjectId insertBlobFileToDatabase(Repository repository, byte[] data) throws IOException;
-
- DirCacheEntry buildDirCacheEntry(String path, FileMode mode, ObjectId objectId);
-
- List<Map<Object, Object>> getDiffFileListInCommits(String workspaceId, String newCommitId, String oldCommitId);
-
- String mergeBranch(String workspaceId, String sourceBranch, String targetBranch, String message, List<Map<String, String>> resolveConflictFileContent) throws RuntimeException;
-
- List<Map<Object, Object>> listApplication(String workspaceId, String branch, String q);
-
- Map<Object, Object> infoApplication(String workspaceId, String branch, String applicationName);
-
- void newApplication(String workspaceId, String branch, String applicationName);
-
- void deleteApplication(String workspaceId, String branch, String applicationName);
-
- void updateApplication(String workspaceId, String branch, String lastCommitId, String message, List<Map<String, String>> updateContent);
-
- List<Map<Object, Object>> listApplicationCommit(String workspaceId, String branch, String applicationName, String file);
-
- Map<Object, Object> infoApplicationFileContent(String workspaceId, String branch, String applicationName, String commitId, String file);
-
- List<Map<Object, Object>> listCommitRange(String workspaceId, String newCommitId, String oldCommitId);
-
- List<String> getConflictFilePathInBranches(String workspaceId, String srcBranch, String tgtBranch);
-
- List<Map<Object, Object>> getConflictFileInfoInBranches(String workspaceId, String srcBranch, String tgtBranch);
-
- boolean commitIndex(Repository repo, String branch, DirCache index, ObjectId parentId1, ObjectId parentId2, String message) throws IOException, ConcurrentRefUpdateException;
-
-}
diff --git a/src/main/java/net/geedge/asw/module/app/service/impl/ApplicationMergeServiceImpl.java b/src/main/java/net/geedge/asw/module/app/service/impl/ApplicationMergeServiceImpl.java
index 313bf65..da4cc51 100644
--- a/src/main/java/net/geedge/asw/module/app/service/impl/ApplicationMergeServiceImpl.java
+++ b/src/main/java/net/geedge/asw/module/app/service/impl/ApplicationMergeServiceImpl.java
@@ -13,12 +13,20 @@ import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationMergeDao;
import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
import net.geedge.asw.module.app.service.IApplicationMergeService;
-import net.geedge.asw.module.app.service.IGitService;
+import net.geedge.asw.module.app.service.IBranchService;
+import net.geedge.asw.module.app.util.JGitUtils;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysUserService;
+import net.geedge.asw.module.workspace.service.IWorkspaceService;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -29,10 +37,13 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
private final static Log log = Log.get();
@Autowired
- private IGitService gitService;
+ private ISysUserService userService;
@Autowired
- private ISysUserService userService;
+ private IBranchService branchService;
+
+ @Autowired
+ private IWorkspaceService workspaceService;
@Override
public ApplicationMergeEntity queryInfo(String mrId) {
@@ -53,7 +64,7 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
}
@Override
- public ApplicationMergeEntity newMr(ApplicationMergeEntity entity) {
+ public ApplicationMergeEntity newMr(ApplicationMergeEntity entity) throws IOException {
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
@@ -62,10 +73,14 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
// start_commit_id
String workspaceId = entity.getWorkspaceId();
- String sourceBranch = entity.getSourceBranch();
- String targetBranch = entity.getTargetBranch();
- String mergeBase = gitService.getMergeBase(workspaceId, sourceBranch, targetBranch);
- entity.setStartCommitId(mergeBase);
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir)) {
+ ObjectId branchAId = repository.resolve(entity.getSourceBranch());
+ ObjectId branchBId = repository.resolve(entity.getTargetBranch());
+
+ RevCommit mergeBaseCommit = JGitUtils.getMergeBaseCommit(repository, branchAId, branchBId);
+ entity.setStartCommitId(mergeBaseCommit.getName());
+ }
// save
this.save(entity);
@@ -76,29 +91,36 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
}
@Override
- public List<Map<Object, Object>> listMrCommit(String mrId) {
+ public List<Map<Object, Object>> listMrCommit(String mrId) throws IOException {
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
String workspaceId = entity.getWorkspaceId();
- String oldCommitId = entity.getStartCommitId();
- String newCommitId = null;
- MergeRequestStatus mergeRequestStatus = MergeRequestStatus.getInstance(entity.getStatus());
- switch (mergeRequestStatus) {
- case OPEN: {
- newCommitId = gitService.getLatestCommitId(workspaceId, entity.getSourceBranch());
- break;
- }
- case CLOSED:
- case MERGED: {
- newCommitId = entity.getEndCommitId();
- break;
+ File gitDir = workspaceService.getGitDir(workspaceId);
+
+ try (Repository repository = JGitUtils.openRepository(gitDir)) {
+ String oldCommitId = entity.getStartCommitId();
+ String newCommitId = null;
+ MergeRequestStatus mergeRequestStatus = MergeRequestStatus.getInstance(entity.getStatus());
+ switch (mergeRequestStatus) {
+ case OPEN: {
+ newCommitId = JGitUtils.getBranchLatestCommit(repository, entity.getSourceBranch()).getName();
+ break;
+ }
+ case CLOSED:
+ case MERGED: {
+ newCommitId = entity.getEndCommitId();
+ break;
+ }
}
- }
- // newCommitId-oldCommitId 期间的提交信息
- List<Map<Object, Object>> commitRange = gitService.listCommitRange(workspaceId, newCommitId, oldCommitId);
- return commitRange;
+ // newCommitId-oldCommitId 期间的提交信息
+ List<RevCommit> revCommits = JGitUtils.listCommitRange(repository, newCommitId, oldCommitId);
+
+ List<Map<Object, Object>> list = T.ListUtil.list(true);
+ revCommits.forEach(revCommit -> list.add(JGitUtils.buildAswCommitInfo(revCommit)));
+ return list;
+ }
}
@Override
@@ -107,103 +129,115 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
String workspaceId = entity.getWorkspaceId();
- String sourceBranch = entity.getSourceBranch();
- String targetBranch = entity.getTargetBranch();
-
- // 获取 sourceBranch,targetBranch 合并冲突文件
- List<String> conflictFileList = T.ListUtil.list(true);
-
- // src branch changed files
- String oldCommitId = entity.getStartCommitId();
- String newCommitId = null;
- MergeRequestStatus mergeRequestStatus = MergeRequestStatus.getInstance(entity.getStatus());
- switch (mergeRequestStatus) {
- case OPEN: {
- newCommitId = gitService.getLatestCommitId(workspaceId, sourceBranch);
- if (gitService.isBranchExists(workspaceId, targetBranch)) {
- // open 状态下,获取 sourceBranch,targetBranch 冲突文件
- conflictFileList = gitService.getConflictFilePathInBranches(workspaceId, sourceBranch, targetBranch);
+ File gitDir = workspaceService.getGitDir(workspaceId);
+
+ try (Repository repository = JGitUtils.openRepository(gitDir)) {
+ String sourceBranch = entity.getSourceBranch();
+ String targetBranch = entity.getTargetBranch();
+
+ // 获取 sourceBranch,targetBranch 合并冲突文件
+ List<String> conflictFileList = T.ListUtil.list(true);
+
+ // src branch changed files
+ String oldCommitId = entity.getStartCommitId();
+ String newCommitId = null;
+ MergeRequestStatus mergeRequestStatus = MergeRequestStatus.getInstance(entity.getStatus());
+ switch (mergeRequestStatus) {
+ case OPEN: {
+ RevCommit branchLatestCommit = JGitUtils.getBranchLatestCommit(repository, sourceBranch);
+ newCommitId = branchLatestCommit.getName();
+ if (JGitUtils.isBranchExists(repository, targetBranch)) {
+ // open 状态下,获取 sourceBranch,targetBranch 冲突文件
+ conflictFileList = JGitUtils.getConflictFilePathInBranches(repository, sourceBranch, targetBranch);
+ }
+ break;
+ }
+ case CLOSED:
+ case MERGED: {
+ newCommitId = entity.getEndCommitId();
+ break;
}
- break;
- }
- case CLOSED:
- case MERGED: {
- newCommitId = entity.getEndCommitId();
- break;
}
- }
- List<Map<Object, Object>> diffFileListInCommits = gitService.getDiffFileListInCommits(workspaceId, newCommitId, oldCommitId);
- List<String> finalConflictFileList = conflictFileList;
- diffFileListInCommits.parallelStream()
- .forEach(m -> {
- T.MapUtil.renameKey(m, "oldContent", "sourceContent");
- T.MapUtil.renameKey(m, "newContent", "targetContent");
-
- T.MapUtil.renameKey(m, "oldPath", "sourcePath");
- T.MapUtil.renameKey(m, "newPath", "targetPath");
-
- String sourcePath = T.MapUtil.getStr(m, "sourcePath");
- String targetPath = T.MapUtil.getStr(m, "targetPath");
- m.put("conflict", false);
- if (finalConflictFileList.contains(sourcePath) || finalConflictFileList.contains(targetPath)) {
- m.put("conflict", true);
- }
- });
-
- Map<Object, Object> sourceCommit = gitService.infoCommit(workspaceId, oldCommitId);
- Map<Object, Object> targetCommit = gitService.infoCommit(workspaceId, newCommitId);
-
- Map<Object, Object> m = T.MapUtil.builder()
- .put("sourceBranch", sourceBranch)
- .put("targetBranch", targetBranch)
- .put("sourceCommitId", oldCommitId)
- .put("targetCommitId", newCommitId)
- .put("sourceCommit", sourceCommit)
- .put("targetCommit", targetCommit)
- .put("files", diffFileListInCommits)
- .build();
- return m;
+ List<Map<Object, Object>> diffFileListInCommits = JGitUtils.getDiffFileListInCommits(repository, newCommitId, oldCommitId);
+ List<String> finalConflictFileList = conflictFileList;
+ diffFileListInCommits.parallelStream()
+ .forEach(m -> {
+ T.MapUtil.renameKey(m, "oldContent", "sourceContent");
+ T.MapUtil.renameKey(m, "newContent", "targetContent");
+
+ T.MapUtil.renameKey(m, "oldPath", "sourcePath");
+ T.MapUtil.renameKey(m, "newPath", "targetPath");
+
+ String sourcePath = T.MapUtil.getStr(m, "sourcePath");
+ String targetPath = T.MapUtil.getStr(m, "targetPath");
+ m.put("conflict", false);
+ if (finalConflictFileList.contains(sourcePath) || finalConflictFileList.contains(targetPath)) {
+ m.put("conflict", true);
+ }
+ });
+
+ RevCommit sourceCommit = JGitUtils.infoCommit(repository, oldCommitId);
+ RevCommit targetCommit = JGitUtils.infoCommit(repository, newCommitId);
+
+ Map<Object, Object> m = T.MapUtil.builder()
+ .put("sourceBranch", sourceBranch)
+ .put("targetBranch", targetBranch)
+ .put("sourceCommitId", oldCommitId)
+ .put("targetCommitId", newCommitId)
+ .put("sourceCommit", JGitUtils.buildAswCommitInfo(sourceCommit))
+ .put("targetCommit", JGitUtils.buildAswCommitInfo(targetCommit))
+ .put("files", diffFileListInCommits)
+ .build();
+ return m;
+ } catch (IOException e) {
+ log.error(e, "[mrCommitDiff] [error] [id: {}]", mrId);
+ throw new RuntimeException(e);
+ }
}
@Override
- public Map<Object, Object> getMrConflictList(String mrId) {
+ public Map<Object, Object> getMrConflictList(String mrId) throws IOException {
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
String workspaceId = entity.getWorkspaceId();
- String sourceBranch = entity.getSourceBranch();
- String targetBranch = entity.getTargetBranch();
-
- List<Map<Object, Object>> conflictFileInfoInBranches = gitService.getConflictFileInfoInBranches(workspaceId, sourceBranch, targetBranch);
- if (T.CollUtil.isNotEmpty(conflictFileInfoInBranches)) {
- // srcBranch->tgtBranch 有冲突,先从 tgtBranch merge 到 srcBranch,在 srcBranch 处理后再合并,参考 gitlab
- StringBuilder commitMessage = new StringBuilder();
- commitMessage.append("Merge branch '").append(targetBranch).append("' into '").append(sourceBranch).append("'\n\n");
- commitMessage.append("# Conflicts:\n");
- for (Map<Object, Object> map : conflictFileInfoInBranches) {
- String path = T.MapUtil.getStr(map, "path");
- commitMessage.append("# ").append(path).append("\n");
+ File gitDir = workspaceService.getGitDir(workspaceId);
+
+ try (Repository repository = JGitUtils.openRepository(gitDir)) {
+ String sourceBranch = entity.getSourceBranch();
+ String targetBranch = entity.getTargetBranch();
+
+ List<Map<Object, Object>> conflictFileInfoInBranches = JGitUtils.getConflictFileInfoInBranches(repository, sourceBranch, targetBranch);
+ if (T.CollUtil.isNotEmpty(conflictFileInfoInBranches)) {
+ // srcBranch->tgtBranch 有冲突,先从 tgtBranch merge 到 srcBranch,在 srcBranch 处理后再合并,参考 gitlab
+ StringBuilder commitMessage = new StringBuilder();
+ commitMessage.append("Merge branch '").append(targetBranch).append("' into '").append(sourceBranch).append("'\n\n");
+ commitMessage.append("# Conflicts:\n");
+ for (Map<Object, Object> map : conflictFileInfoInBranches) {
+ String path = T.MapUtil.getStr(map, "path");
+ commitMessage.append("# ").append(path).append("\n");
+ }
+
+ String parentId = JGitUtils.getBranchLatestCommit(repository, sourceBranch).getName();
+ Map<Object, Object> m = T.MapUtil.builder()
+ .put("sourceBranch", sourceBranch)
+ .put("targetBranch", targetBranch)
+ .put("commitId", parentId)
+ .put("commitMessage", commitMessage.toString())
+ .put("files", conflictFileInfoInBranches)
+ .build();
+ return m;
}
- String parentId = gitService.getLatestCommitId(workspaceId, sourceBranch);
- Map<Object, Object> m = T.MapUtil.builder()
+ return T.MapUtil.builder()
.put("sourceBranch", sourceBranch)
.put("targetBranch", targetBranch)
- .put("commitId", parentId)
- .put("commitMessage", commitMessage.toString())
+ .put("commitId", null)
+ .put("commitMessage", null)
.put("files", conflictFileInfoInBranches)
.build();
- return m;
}
-
- return T.MapUtil.builder()
- .put("sourceBranch", sourceBranch)
- .put("targetBranch", targetBranch)
- .put("commitId", null)
- .put("commitMessage", null)
- .put("files", conflictFileInfoInBranches)
- .build();
}
/**
@@ -213,20 +247,28 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
* @param body
*/
@Override
- public void resolveMrConflicts(String mrId, Map<String, Object> body) {
+ public void resolveMrConflicts(String mrId, Map<String, Object> body) throws IOException {
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
String workspaceId = entity.getWorkspaceId();
- String sourceBranch = entity.getSourceBranch();
- String targetBranch = entity.getTargetBranch();
-
- String commitId = T.MapUtil.getStr(body, "commitId");
- String commitMessage = T.MapUtil.getStr(body, "commitMessage");
- List<Map<String, String>> files = T.MapUtil.get(body, "files", List.class, new ArrayList());
-
- // 反过来合并
- gitService.mergeBranch(workspaceId, targetBranch, sourceBranch, commitMessage, files);
+ File gitDir = workspaceService.getGitDir(workspaceId);
+
+ try (Repository repository = JGitUtils.openRepository(gitDir)) {
+ SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
+ String author = loginUserEntity.getName();
+
+ String sourceBranch = entity.getSourceBranch();
+ String targetBranch = entity.getTargetBranch();
+ String commitMessage = T.MapUtil.getStr(body, "commitMessage");
+ List<Map<String, String>> files = T.MapUtil.get(body, "files", List.class, new ArrayList());
+
+ // 反过来合并
+ JGitUtils.mergeBranch(repository, targetBranch, sourceBranch, author, commitMessage, files);
+ } catch (GitAPIException e) {
+ log.error(e, "[resolveMrConflicts] [error] [id: {}]", mrId);
+ throw new RuntimeException(e);
+ }
}
/**
@@ -241,75 +283,84 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
- switch (action) {
- case "merge": {
- MergeRequestStatus mergeRequestStatus = MergeRequestStatus.getInstance(entity.getStatus());
- if (!mergeRequestStatus.canMerge()) {
- throw new ASWException(RCode.GIT_MERGE_NOT_SUPPORTED.setParam(mergeRequestStatus.toString()));
- }
-
- String srcBranch = entity.getSourceBranch();
- String tgtBranch = entity.getTargetBranch();
+ String workspaceId = entity.getWorkspaceId();
+ File gitDir = workspaceService.getGitDir(workspaceId);
+
+ try (Repository repository = JGitUtils.openRepository(gitDir)) {
+ switch (action) {
+ case "merge": {
+ MergeRequestStatus mergeRequestStatus = MergeRequestStatus.getInstance(entity.getStatus());
+ if (!mergeRequestStatus.canMerge()) {
+ throw new ASWException(RCode.GIT_MERGE_NOT_SUPPORTED.setParam(mergeRequestStatus.toString()));
+ }
- StringBuilder commitMessage = new StringBuilder();
- commitMessage.append("Merge branch '").append(srcBranch).append("' into '").append(tgtBranch).append("'\n\n");
- commitMessage.append(entity.getTitle());
- commitMessage.append("\n");
+ String srcBranch = entity.getSourceBranch();
+ String tgtBranch = entity.getTargetBranch();
- String workspaceId = entity.getWorkspaceId();
+ StringBuilder commitMessage = new StringBuilder();
+ commitMessage.append("Merge branch '").append(srcBranch).append("' into '").append(tgtBranch).append("'\n\n");
+ commitMessage.append(entity.getTitle());
+ commitMessage.append("\n");
- // 检查 tgtBranch 是否存在
- if (T.BooleanUtil.negate(gitService.isBranchExists(workspaceId, tgtBranch))) {
- throw new ASWException(RCode.GIT_MERGE_TARGET_BRANCH_NOT_EXIST.setParam(tgtBranch));
- }
- // merge
- try {
- gitService.mergeBranch(workspaceId, srcBranch, tgtBranch, commitMessage.toString(), null);
- String latestCommitId = gitService.getLatestCommitId(workspaceId, srcBranch);
- entity.setEndCommitId(latestCommitId);
- entity.setStatus(MergeRequestStatus.MERGED.toString());
- } catch (Exception e) {
- log.error(e, "[mergeMr] [merge error] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}] [msg: {}]", workspaceId, srcBranch, tgtBranch, e.getMessage());
- throw new ASWException(RCode.GIT_MERGE_FAILED.setParam(e.getMessage()));
- }
+ // 检查 tgtBranch 是否存在
+ if (T.BooleanUtil.negate(JGitUtils.isBranchExists(repository, tgtBranch))) {
+ throw new ASWException(RCode.GIT_MERGE_TARGET_BRANCH_NOT_EXIST.setParam(tgtBranch));
+ }
- // remove source branch
- if (1 == entity.getRemoveSourceBranch()) {
+ // merge
try {
- log.info("[mergeMr] [remove source branch] [workspaceId: {}] [branch: {}]", workspaceId, srcBranch);
- gitService.deleteBranch(workspaceId, srcBranch);
+ SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
+ JGitUtils.mergeBranch(repository, srcBranch, tgtBranch, loginUserEntity.getName(), commitMessage.toString(), null);
+
+ String latestCommitId = JGitUtils.getBranchLatestCommit(repository, srcBranch).getName();
+ entity.setEndCommitId(latestCommitId);
+ entity.setStatus(MergeRequestStatus.MERGED.toString());
} catch (Exception e) {
- log.error(e, "[mergeMr] [remove source branch error] [workspaceId: {}] [branch: {}] [msg: {}]", workspaceId, srcBranch, e.getMessage());
+ log.error(e, "[mergeMr] [merge error] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}] [msg: {}]", workspaceId, srcBranch, tgtBranch, e.getMessage());
+ throw new ASWException(RCode.GIT_MERGE_FAILED.setParam(e.getMessage()));
}
- }
- // update
- this.update(new LambdaUpdateWrapper<ApplicationMergeEntity>()
- .eq(ApplicationMergeEntity::getId, mrId)
- .set(ApplicationMergeEntity::getEndCommitId, entity.getEndCommitId())
- .set(ApplicationMergeEntity::getStatus, entity.getStatus())
- .set(ApplicationMergeEntity::getMessage, entity.getMessage())
- );
- return entity;
- }
- case "close": {
- String updateStatus = StrUtil.equals(MergeRequestStatus.OPEN.toString(), entity.getStatus()) ? MergeRequestStatus.CLOSED.toString() : entity.getStatus();
- entity.setStatus(updateStatus);
-
- // 关闭请求,保留当前 src branch last commit = end_commit_id
- String latestCommitId = gitService.getLatestCommitId(entity.getWorkspaceId(), entity.getSourceBranch());
- this.update(new LambdaUpdateWrapper<ApplicationMergeEntity>()
- .eq(ApplicationMergeEntity::getId, mrId)
- .set(ApplicationMergeEntity::getStatus, updateStatus)
- .set(ApplicationMergeEntity::getEndCommitId, latestCommitId)
- );
- return entity;
+ // remove source branch
+ if (1 == entity.getRemoveSourceBranch()) {
+ try {
+ log.info("[mergeMr] [remove source branch] [workspaceId: {}] [branch: {}]", workspaceId, srcBranch);
+ branchService.deleteBranch(workspaceId, srcBranch);
+ } catch (Exception e) {
+ log.error(e, "[mergeMr] [remove source branch error] [workspaceId: {}] [branch: {}] [msg: {}]", workspaceId, srcBranch, e.getMessage());
+ }
+ }
+
+ // update
+ this.update(new LambdaUpdateWrapper<ApplicationMergeEntity>()
+ .eq(ApplicationMergeEntity::getId, mrId)
+ .set(ApplicationMergeEntity::getEndCommitId, entity.getEndCommitId())
+ .set(ApplicationMergeEntity::getStatus, entity.getStatus())
+ .set(ApplicationMergeEntity::getMessage, entity.getMessage())
+ );
+ return entity;
+ }
+ case "close": {
+ String updateStatus = StrUtil.equals(MergeRequestStatus.OPEN.toString(), entity.getStatus()) ? MergeRequestStatus.CLOSED.toString() : entity.getStatus();
+ entity.setStatus(updateStatus);
+
+ // 关闭请求,保留当前 src branch last commit = end_commit_id
+ String latestCommitId = JGitUtils.getBranchLatestCommit(repository, entity.getSourceBranch()).getName();
+ this.update(new LambdaUpdateWrapper<ApplicationMergeEntity>()
+ .eq(ApplicationMergeEntity::getId, mrId)
+ .set(ApplicationMergeEntity::getStatus, updateStatus)
+ .set(ApplicationMergeEntity::getEndCommitId, latestCommitId)
+ );
+ return entity;
+ }
+ default:
+ break;
}
- default:
- break;
+ return entity;
+ } catch (IOException e) {
+ log.error(e, "[mergeMr] [error] [id: {}] [action: {}]", mrId, action);
+ throw new RuntimeException(e);
}
- return entity;
}
diff --git a/src/main/java/net/geedge/asw/module/app/service/impl/ApplicationServiceImpl.java b/src/main/java/net/geedge/asw/module/app/service/impl/ApplicationServiceImpl.java
index eb7b1a4..082425b 100644
--- a/src/main/java/net/geedge/asw/module/app/service/impl/ApplicationServiceImpl.java
+++ b/src/main/java/net/geedge/asw/module/app/service/impl/ApplicationServiceImpl.java
@@ -1,5 +1,6 @@
package net.geedge.asw.module.app.service.impl;
+import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONObject;
@@ -9,108 +10,568 @@ import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.service.IApplicationService;
-import net.geedge.asw.module.app.service.IGitService;
import net.geedge.asw.module.app.service.ITSGApplicationService;
+import net.geedge.asw.module.app.util.JGitUtils;
+import net.geedge.asw.module.sys.entity.SysUserEntity;
+import net.geedge.asw.module.sys.service.ISysUserService;
+import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.eclipse.jgit.util.io.DisabledOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
+import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
+import java.util.stream.Collectors;
@Service
public class ApplicationServiceImpl implements IApplicationService {
private static final Log log = Log.get();
+ @Value("${asw.application.template.meta.json}")
+ private String metaJsonTemplate;
+
+ @Value("${asw.application.template.signature.json}")
+ private String signatureJsonTemplate;
+
+ @Autowired
+ private ISysUserService userService;
+
@Autowired
- private IGitService gitService;
+ private IWorkspaceService workspaceService;
@Autowired
@Qualifier("tsg2402ApplicationService")
private ITSGApplicationService tsg2402ApplicationService;
@Override
- public List<ApplicationEntity> queryList(String workspaceId, String branch, String... names) {
- List<ApplicationEntity> resultList = T.ListUtil.list(true);
+ public Map<Object, Object> infoApplication(String workspaceId, String branch, String name) {
+ Map<Object, Object> result = T.MapUtil.builder()
+ .put("branch", branch)
+ .build();
- try (Git git = gitService.getGitInstance(workspaceId)) {
- Repository repository = git.getRepository();
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir)) {
+ ObjectId branchRef = repository.resolve(branch);
+ result.put("commitId", branchRef.getName());
+ List<Map<Object, Object>> files = T.ListUtil.list(true);
try (TreeWalk treeWalk = new TreeWalk(repository);
RevWalk revWalk = new RevWalk(repository)) {
- ObjectId branchRef = repository.resolve(branch);
treeWalk.addTree(revWalk.parseTree(branchRef));
- treeWalk.setFilter(PathFilter.create("applications/"));
+ treeWalk.setFilter(PathFilter.create("applications/" + name + "/"));
treeWalk.setRecursive(true);
- Map<String, String> signatureMapping = T.MapUtil.newHashMap();
-
+ Map<String, String> appFilePathMapping = T.MapUtil.newHashMap(true);
while (treeWalk.next()) {
- String filePath = treeWalk.getPathString();
+ String pathString = treeWalk.getPathString();
+ Map<Object, Object> m = T.MapUtil.builder()
+ .put("path", pathString)
+ .build();
+
+ Map<Object, Object> fileContent = JGitUtils.getFileContent(repository, pathString, treeWalk.getObjectId(0));
+ m.putAll(fileContent);
+ files.add(m);
+
+ appFilePathMapping.put(pathString, pathString);
+ }
+
+ Map<String, RevCommit> lastCommitMapping = this.getLastCommitIdDataByPath(repository, branch, appFilePathMapping);
+ for (Map<Object, Object> m : files) {
+ String path = T.MapUtil.getStr(m, "path");
+ RevCommit revCommit = lastCommitMapping.get(path);
+ m.put("lastCommitId", revCommit != null ? revCommit.getName() : null);
+ }
+ }
+ result.put("files", files);
+ } catch (IOException e) {
+ log.error(e, "[infoApplication] [error] [workspaceId: {}] [branch: {}] [name: {}]", workspaceId, branch, name);
+ throw new RuntimeException(e);
+ }
+ return result;
+ }
+
+ @Override
+ public List<Map<Object, Object>> listApplication(String workspaceId, String branch, String q) {
+ List<Map<Object, Object>> resultList = T.ListUtil.list(true);
+
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir);
+ TreeWalk treeWalk = new TreeWalk(repository);
+ RevWalk revWalk = new RevWalk(repository);) {
+
+ ObjectId branchRef = repository.resolve(branch);
+ treeWalk.addTree(revWalk.parseTree(branchRef));
+ treeWalk.setFilter(PathFilter.create("applications/"));
+ treeWalk.setRecursive(true);
+
+ Map<String, String> appDirPathMapping = T.MapUtil.newHashMap(true);
+ while (treeWalk.next()) {
+ String filePath = treeWalk.getPathString();
+ String fileName = treeWalk.getNameString();
+ if (T.StrUtil.equals("meta.json", fileName)) {
+ // application_name 从目录中获取
String applicationName = T.PathUtil.getPathEle(Path.of(filePath), 1).toString();
- if (T.BooleanUtil.and(
- T.ObjectUtil.isNotEmpty(names),
- !Arrays.asList(names).contains(applicationName))) {
+ // filter by name
+ if (T.StrUtil.isNotEmpty(q) && !T.StrUtil.containsIgnoreCase(applicationName, q)) {
continue;
}
- switch (treeWalk.getNameString()) {
- case "meta.json": {
- ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
- String metaJsonStr = T.StrUtil.utf8Str(loader.getBytes());
- metaJsonStr = T.StrUtil.emptyToDefault(metaJsonStr, T.StrUtil.EMPTY_JSON);
+ ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
+ String metaJsonStr = T.StrUtil.utf8Str(loader.getBytes());
+ metaJsonStr = T.StrUtil.emptyToDefault(metaJsonStr, T.StrUtil.EMPTY_JSON);
+ Map metaJsonMap = T.MapUtil.empty();
+ try {
+ metaJsonMap = T.JSONUtil.toBean(metaJsonStr, Map.class);
+ } catch (Exception e) {
+ log.error(e, "[listApplication] [meat.json format error] [application: {}]", applicationName);
+ }
+
+ Map<Object, Object> m = T.MapUtil.newHashMap(true);
+ m.putAll(metaJsonMap);
+ m.put("name", applicationName);
- ApplicationEntity application = T.JSONUtil.toBean(metaJsonStr, ApplicationEntity.class);
- application.setName(applicationName);
- resultList.add(application);
- break;
- }
- case "signature.json": {
- ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
- signatureMapping.put(applicationName, T.StrUtil.utf8Str(loader.getBytes()));
- break;
- }
- default:
- break;
+ String appDirPath = treeWalk.getPathString().replaceAll(fileName, "");
+ appDirPathMapping.put(applicationName, appDirPath);
+
+ resultList.add(m);
+ }
+ }
+
+ Map<String, RevCommit> lastCommitMapping = this.getLastCommitIdDataByPath(repository, branch, appDirPathMapping);
+
+ for (Map<Object, Object> map : resultList) {
+ String applicationName = T.MapUtil.getStr(map, "name");
+ RevCommit lastCommit = T.MapUtil.get(lastCommitMapping, applicationName, RevCommit.class);
+ map.put("commit", JGitUtils.buildAswCommitInfo(lastCommit));
+ }
+
+ // 按照提交日期倒叙
+ resultList = resultList.stream().sorted(Comparator.comparing(map -> {
+ Map commit = T.MapUtil.get((Map) map, "commit", Map.class, new HashMap(2));
+ return (Long) T.MapUtil.getLong(commit, "createdAt", 0l);
+ }).reversed()).collect(Collectors.toList());
+ } catch (IOException e) {
+ log.error(e, "[listApplication] [error] [workspaceId: {}] [branch: {}]", workspaceId, branch);
+ throw new RuntimeException(e);
+ }
+ return resultList;
+ }
+
+ @Override
+ public List<ApplicationEntity> listApplication(String workspaceId, String branch, String... names) {
+ List<ApplicationEntity> resultList = T.ListUtil.list(true);
+
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir);
+ TreeWalk treeWalk = new TreeWalk(repository);
+ RevWalk revWalk = new RevWalk(repository)) {
+
+ ObjectId branchRef = repository.resolve(branch);
+ treeWalk.addTree(revWalk.parseTree(branchRef));
+ treeWalk.setFilter(PathFilter.create("applications/"));
+ treeWalk.setRecursive(true);
+
+ Map<String, String> signatureMapping = T.MapUtil.newHashMap();
+
+ while (treeWalk.next()) {
+ String filePath = treeWalk.getPathString();
+ String applicationName = T.PathUtil.getPathEle(Path.of(filePath), 1).toString();
+ if (T.BooleanUtil.and(
+ T.ObjectUtil.isNotEmpty(names),
+ !Arrays.asList(names).contains(applicationName))) {
+ continue;
+ }
+
+ switch (treeWalk.getNameString()) {
+ case "meta.json": {
+ ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
+ String metaJsonStr = T.StrUtil.utf8Str(loader.getBytes());
+ metaJsonStr = T.StrUtil.emptyToDefault(metaJsonStr, T.StrUtil.EMPTY_JSON);
+
+ ApplicationEntity application = T.JSONUtil.toBean(metaJsonStr, ApplicationEntity.class);
+ application.setName(applicationName);
+ resultList.add(application);
+ break;
+ }
+ case "signature.json": {
+ ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
+ signatureMapping.put(applicationName, T.StrUtil.utf8Str(loader.getBytes()));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ for (ApplicationEntity entity : resultList) {
+ String name = entity.getName();
+ String signature = signatureMapping.getOrDefault(name, "");
+ if (T.StrUtil.isNotEmpty(signature)) {
+ JSONObject jsonObject = T.JSONUtil.parseObj(signature);
+ entity.setSignature(jsonObject);
+ }
+ }
+ } catch (IOException e) {
+ log.error(e, "[listApplication] [error] [workspaceId: {}] [branch: {}]", workspaceId, branch);
+ throw new RuntimeException(e);
+ }
+ return resultList;
+ }
+
+ @Override
+ public void newApplication(String workspaceId, String branch, String name) {
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir)) {
+ Map<String, ObjectId> filePathAndBlobIdMap = T.MapUtil.newHashMap(true);
+ for (String str : T.ListUtil.of("README.md", "meta.json", "signature.json")) {
+ String savePath = T.StrUtil.concat(true, "applications/", name, "/", str);
+
+ String fileContent = T.StrUtil.EMPTY;
+ if ("meta.json".equals(str)) {
+ JSONObject jsonObject = T.JSONUtil.parseObj(this.metaJsonTemplate);
+ jsonObject.set("id", T.StrUtil.uuid());
+ jsonObject.set("name", name);
+ jsonObject.set("longName", name);
+ fileContent = T.JSONUtil.parse(jsonObject).toJSONString(2);
+ }
+ if ("signature.json".equals(str)) {
+ fileContent = this.signatureJsonTemplate;
+ }
+
+ ObjectId objectId = JGitUtils.insertBlobFileToDatabase(repository, fileContent.getBytes());
+ filePathAndBlobIdMap.put(savePath, objectId);
+ }
+
+ try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
+ TreeWalk treeWalk = new TreeWalk(repository);
+ RevWalk revWalk = new RevWalk(repository)) {
+
+ // branch tree
+ ObjectId resolveId = repository.resolve(branch);
+ if (null != resolveId) {
+ treeWalk.addTree(revWalk.parseTree(resolveId));
+ } else {
+ treeWalk.addTree(new CanonicalTreeParser());
+ }
+ treeWalk.setRecursive(true);
+
+ DirCache newTree = DirCache.newInCore();
+ DirCacheBuilder newTreeBuilder = newTree.builder();
+ while (treeWalk.next()) {
+ DirCacheEntry entry = JGitUtils.buildDirCacheEntry(treeWalk.getPathString(), treeWalk.getFileMode(0), treeWalk.getObjectId(0));
+ newTreeBuilder.add(entry);
+ }
+
+ // update new tree
+ for (Map.Entry<String, ObjectId> entry : filePathAndBlobIdMap.entrySet()) {
+ String filePath = entry.getKey();
+ ObjectId blobId = entry.getValue();
+
+ // add file ref
+ DirCacheEntry dirCacheEntry = JGitUtils.buildDirCacheEntry(filePath, FileMode.REGULAR_FILE, blobId);
+ newTreeBuilder.add(dirCacheEntry);
+ }
+
+ newTreeBuilder.finish();
+ ObjectId newTreeId = newTree.writeTree(inserter);
+
+ String message = String.format("feat: add %s application", name);
+
+ SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
+ PersonIdent personIdent = JGitUtils.buildPersonIdent(loginUserEntity.getName());
+
+ // commit
+ JGitUtils.createCommit(repository, branch, newTreeId, message, personIdent);
+ }
+ } catch (IOException | ConcurrentRefUpdateException e) {
+ log.error(e, "[newApplication] [error] [workspaceId: {}] [branch: {}] [name: {}]", workspaceId, branch, name);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void deleteApplication(String workspaceId, String branch, String name) {
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir);
+ ObjectInserter inserter = repository.getObjectDatabase().newInserter();
+ TreeWalk treeWalk = new TreeWalk(repository);
+ RevWalk revWalk = new RevWalk(repository);) {
+
+ ObjectId branchRef = repository.resolve(branch);
+ treeWalk.addTree(revWalk.parseTree(branchRef));
+ treeWalk.setRecursive(true);
+
+ DirCache newTree = DirCache.newInCore();
+ DirCacheBuilder newTreeBuilder = newTree.builder();
+
+ String appFilePrefixStr = T.StrUtil.concat(true, "applications/", name, "/");
+ while (treeWalk.next()) {
+ String pathString = treeWalk.getPathString();
+ if (!pathString.startsWith(appFilePrefixStr)) {
+ DirCacheEntry entry = JGitUtils.buildDirCacheEntry(pathString, treeWalk.getFileMode(0), treeWalk.getObjectId(0));
+ newTreeBuilder.add(entry);
+ }
+ }
+
+ newTreeBuilder.finish();
+ ObjectId newTreeId = newTree.writeTree(inserter);
+
+ String message = String.format("refactor: remove %s application", name);
+
+ SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
+ PersonIdent personIdent = JGitUtils.buildPersonIdent(loginUserEntity.getName());
+
+ // commit
+ JGitUtils.createCommit(repository, branch, newTreeId, message, personIdent);
+ } catch (IOException | ConcurrentRefUpdateException e) {
+ log.error(e, "[deleteApplication] [error] [workspaceId: {}] [branch: {}] [name: {}]", workspaceId, branch, name);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void updateApplication(String workspaceId, String branch, String lastCommitId, String message, List<Map<String, String>> updateContent) {
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir);
+ Git git = Git.open(repository.getDirectory());) {
+ // 分支最新 commitId
+ ObjectId branchRef = repository.resolve(branch);
+ // 如果不相等,检查 param.lastCommitId --- branch.HEAD 期间是否更新了本次修改文件
+ if (!T.StrUtil.equals(lastCommitId, branchRef.getName())) {
+ List<String> updateFilePath = updateContent.stream()
+ .filter(map -> {
+ String action = T.MapUtil.getStr(map, "action");
+ return T.StrUtil.equalsAny(action, "create", "update");
+ })
+ .map(map -> T.MapUtil.getStr(map, "path"))
+ .collect(Collectors.toList());
+
+ ObjectId currentCommitId = repository.resolve(lastCommitId);
+ try (RevWalk walk = new RevWalk(repository)) {
+ CanonicalTreeParser c1 = new CanonicalTreeParser();
+ c1.reset(repository.newObjectReader(), walk.parseCommit(currentCommitId).getTree());
+
+ CanonicalTreeParser c2 = new CanonicalTreeParser();
+ c2.reset(repository.newObjectReader(), walk.parseCommit(branchRef).getTree());
+
+ List<DiffEntry> diffs = git.diff()
+ .setOldTree(c1)
+ .setNewTree(c2)
+ .call();
+ List<String> affectFilePathList = diffs.stream()
+ .filter(entry -> T.StrUtil.equalsAnyIgnoreCase(entry.getChangeType().name(), DiffEntry.ChangeType.MODIFY.name(), DiffEntry.ChangeType.ADD.name()))
+ .map(DiffEntry::getNewPath)
+ .collect(Collectors.toList());
+ List<String> intersection = T.CollUtil.intersection(updateFilePath, affectFilePathList).stream().toList();
+ if (T.CollUtil.isEmpty(intersection)) {
+ // 在 param.lastCommitId 不是当前分支最新提交时,本次提交文件没有冲突,更新 commit=branch.HEAD
+ lastCommitId = branchRef.getName();
+ } else {
+ throw new ASWException(RCode.GIT_COMMIT_CONFLICT_ERROR);
+ }
+ }
+ }
+ try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
+ TreeWalk treeWalk = new TreeWalk(repository);
+ RevWalk revWalk = new RevWalk(repository)) {
+
+ // branch tree
+ treeWalk.addTree(revWalk.parseTree(branchRef));
+ treeWalk.setRecursive(true);
+
+ DirCache newTree = DirCache.newInCore();
+ DirCacheBuilder newTreeBuilder = newTree.builder();
+
+ List<String> updateFilePath = updateContent.stream().map(map -> T.MapUtil.getStr(map, "path")).collect(Collectors.toList());
+ while (treeWalk.next()) {
+ String pathString = treeWalk.getPathString();
+ // 先删
+ if (!updateFilePath.contains(pathString)) {
+ DirCacheEntry entry = JGitUtils.buildDirCacheEntry(pathString, treeWalk.getFileMode(0), treeWalk.getObjectId(0));
+ newTreeBuilder.add(entry);
}
}
+ // 后增
+ for (Map<String, String> map : updateContent) {
+ String action = T.MapUtil.getStr(map, "action");
+ if (T.StrUtil.equalsAnyIgnoreCase(action, "create", "update")) {
+ String path = T.MapUtil.getStr(map, "path");
+ DirCacheEntry dirCacheEntry = new DirCacheEntry(path);
+ dirCacheEntry.setFileMode(FileMode.REGULAR_FILE);
- for (ApplicationEntity entity : resultList) {
- String name = entity.getName();
- String signature = signatureMapping.getOrDefault(name, "");
- if (T.StrUtil.isNotEmpty(signature)) {
- JSONObject jsonObject = T.JSONUtil.parseObj(signature);
- entity.setSignature(jsonObject);
+ String content = T.MapUtil.getStr(map, "content");
+ String encoding = T.MapUtil.getStr(map, "encoding");
+ if ("base64".equals(encoding)) {
+ // binary
+ byte[] data = Base64.getDecoder().decode(content);
+ ObjectId objectId = JGitUtils.insertBlobFileToDatabase(repository, data);
+ dirCacheEntry.setObjectId(objectId);
+ } else {
+ // text
+ ObjectId objectId = JGitUtils.insertBlobFileToDatabase(repository, content.getBytes());
+ dirCacheEntry.setObjectId(objectId);
+ }
+ newTreeBuilder.add(dirCacheEntry);
}
}
- } catch (IOException e) {
- log.error(e, "[queryList] [error] [workspaceId: {}] [branch: {}]", workspaceId, branch);
- throw new RuntimeException(e);
+
+ newTreeBuilder.finish();
+ ObjectId newTreeId = newTree.writeTree(inserter);
+
+ SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
+ PersonIdent personIdent = JGitUtils.buildPersonIdent(loginUserEntity.getName());
+
+ // commit
+ JGitUtils.createCommit(repository, branch, newTreeId, message, personIdent);
+ }
+ } catch (IOException | GitAPIException e) {
+ log.error(e, "[updateApplication] [error] [workspaceId: {}] [branch: {}] [lastCommitId: {}]", workspaceId, branch, lastCommitId);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public List<Map<Object, Object>> listApplicationCommit(String workspaceId, String branch, String name, String file) {
+ List<Map<Object, Object>> resultList = T.ListUtil.list(true);
+
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir);
+ Git git = Git.open(repository.getDirectory());) {
+
+ String filterPath = T.StrUtil.concat(true, "applications/", name);
+ if (T.StrUtil.isNotEmpty(file)) {
+ filterPath = T.StrUtil.concat(true, filterPath, "/", file);
+ }
+
+ ObjectId branchRef = git.getRepository().resolve(branch);
+ Iterable<RevCommit> iterable = git.log()
+ .add(branchRef)
+ .addPath(filterPath)
+ .call();
+
+ for (RevCommit commit : iterable) {
+ resultList.add(JGitUtils.buildAswCommitInfo(commit));
}
+ } catch (IOException | GitAPIException e) {
+ log.error(e, "[listApplicationCommit] [error] [workspaceId: {}] [branch: {}] [name: {}]", workspaceId, branch, name);
+ throw new RuntimeException(e);
}
return resultList;
}
+ @Override
+ public Map<Object, Object> infoApplicationFileContent(String workspaceId, String branch, String name, String commitId, String file) {
+ // applications/qq/meta.json
+ String path = T.StrUtil.concat(true, "applications/", name, "/", file);
+
+ Map<Object, Object> result = T.MapUtil.builder()
+ .put("path", path)
+ .build();
+
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir);
+ TreeWalk treeWalk = new TreeWalk(repository);
+ RevWalk revWalk = new RevWalk(repository);
+ ) {
+ RevCommit revCommit = revWalk.parseCommit(ObjectId.fromString(commitId));
+ ObjectId treeId = revCommit.getTree().getId();
+
+ treeWalk.addTree(treeId);
+ treeWalk.setRecursive(true);
+
+ while (treeWalk.next()) {
+ if (T.StrUtil.equals(path, treeWalk.getPathString())) {
+ Map<Object, Object> fileContent = JGitUtils.getFileContent(repository, treeWalk.getPathString(), treeWalk.getObjectId(0));
+ result.putAll(fileContent);
+ }
+ }
+ } catch (IOException e) {
+ log.error(e, "[infoApplicationFileContent] [error] [workspaceId: {}] [branch: {}] [name: {}]", workspaceId, branch, name);
+ throw new RuntimeException(e);
+ }
+ return result;
+ }
+
+ /**
+ * 通过 path 获取 lastCommit
+ *
+ * @param repository
+ * @param branch
+ * @param pathMapping
+ * @return
+ */
+ public Map<String, RevCommit> getLastCommitIdDataByPath(Repository repository, String branch, Map<String, String> pathMapping) {
+ Map<String, RevCommit> result = new HashMap<>();
+ try (Git git = new Git(repository);
+ DiffFormatter diffFormatter = new DiffFormatter(DisabledOutputStream.INSTANCE);
+ ) {
+ diffFormatter.setRepository(repository);
+
+ // 指定分支获取 log
+ Iterable<RevCommit> commits = git.log()
+ .add(git.getRepository().resolve(branch))
+ .call();
+ for (RevCommit commit : commits) {
+ // 获取上次提交,用以对比差异
+ RevCommit parent = commit.getParentCount() > 0 ? commit.getParent(0) : null;
+ if (parent == null) {
+ continue;
+ }
+
+ // 计算当前提交与上次提交之间的差异
+ List<DiffEntry> diffs = diffFormatter.scan(parent.getTree(), commit.getTree());
+ for (DiffEntry diff : diffs) {
+ String newPath = diff.getNewPath();
+ String oldPath = diff.getOldPath();
+
+ // 检查是否匹配目标路径
+ for (Map.Entry<String, String> entry : pathMapping.entrySet()) {
+ String key = entry.getKey();
+ String path = entry.getValue();
+ if (T.BooleanUtil.and(
+ (newPath.startsWith(path) || oldPath.startsWith(path)),
+ !result.containsKey(key)
+ )) {
+ result.put(key, commit);
+ }
+ }
+ }
+
+ // 如果所有路径都找到对应的最后提交,提前结束循环
+ if (result.size() == pathMapping.size()) {
+ break;
+ }
+ }
+ } catch (IOException | GitAPIException e) {
+ log.error(e, "[getLastCommitIdDataByPath] [workspace: {}] [branch: {}]", repository.getDirectory().getPath(), branch);
+ }
+ return result;
+ }
+
public void saveOrUpdateBatch(String workspaceId, String branch, String commitMessage, List<ApplicationEntity> list) {
- try (Git git = gitService.getGitInstance(workspaceId);
- Repository repository = git.getRepository();
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir);
RevWalk revWalk = new RevWalk(repository)) {
ObjectId branchRef = repository.resolve(branch);
@@ -186,7 +647,7 @@ public class ApplicationServiceImpl implements IApplicationService {
DirCacheEntry dirCacheEntry = new DirCacheEntry(filePath);
dirCacheEntry.setFileMode(FileMode.REGULAR_FILE);
- ObjectId blobId = gitService.insertBlobFileToDatabase(repository, fileContent.getBytes());
+ ObjectId blobId = JGitUtils.insertBlobFileToDatabase(repository, fileContent.getBytes());
dirCacheEntry.setObjectId(blobId);
newTreeBuilder.add(dirCacheEntry);
}
@@ -207,8 +668,8 @@ public class ApplicationServiceImpl implements IApplicationService {
}
String fileContent = T.JSONUtil.parse(jsonObject).toJSONString(2);
- ObjectId objectId = gitService.insertBlobFileToDatabase(repository, fileContent.getBytes());
- DirCacheEntry dirCacheEntry = gitService.buildDirCacheEntry(T.StrUtil.concat(true, "applications/", entity.getName(), "/meta.json"), FileMode.REGULAR_FILE, objectId);
+ ObjectId objectId = JGitUtils.insertBlobFileToDatabase(repository, fileContent.getBytes());
+ DirCacheEntry dirCacheEntry = JGitUtils.buildDirCacheEntry(T.StrUtil.concat(true, "applications/", entity.getName(), "/meta.json"), FileMode.REGULAR_FILE, objectId);
newTreeBuilder.add(dirCacheEntry);
// signature.json
@@ -216,14 +677,18 @@ public class ApplicationServiceImpl implements IApplicationService {
JSONObject jsonObject2 = T.JSONUtil.parseObj(jsonStr);
String fileContent2 = T.JSONUtil.parse(jsonObject2).toJSONString(2);
- ObjectId objectId2 = gitService.insertBlobFileToDatabase(repository, fileContent2.getBytes());
- DirCacheEntry dirCacheEntry2 = gitService.buildDirCacheEntry(T.StrUtil.concat(true, "applications/", entity.getName(), "/signature.json"), FileMode.REGULAR_FILE, objectId2);
+ ObjectId objectId2 = JGitUtils.insertBlobFileToDatabase(repository, fileContent2.getBytes());
+ DirCacheEntry dirCacheEntry2 = JGitUtils.buildDirCacheEntry(T.StrUtil.concat(true, "applications/", entity.getName(), "/signature.json"), FileMode.REGULAR_FILE, objectId2);
newTreeBuilder.add(dirCacheEntry2);
}
newTreeBuilder.finish();
RevCommit commitId = revWalk.parseCommit(branchRef);
- boolean success = gitService.commitIndex(repository, branch, newTree, commitId, null, commitMessage);
+
+ SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
+ PersonIdent personIdent = JGitUtils.buildPersonIdent(loginUserEntity.getName());
+
+ boolean success = JGitUtils.commitIndex(repository, branch, newTree, commitId, null, commitMessage, personIdent);
log.info("[saveOrUpdateBatch] [workspaceId: {}] [branch: {}] [commitId: {}] [success: {}]", workspaceId, branch, commitId, success);
}
} catch (IOException | ConcurrentRefUpdateException e) {
@@ -252,7 +717,7 @@ public class ApplicationServiceImpl implements IApplicationService {
}
@Override
- public List<ApplicationEntity> importAppByFormat(String workspaceId, String branchName, String format, List<JSONObject> dataList) {
+ public List<ApplicationEntity> importAppByFormat(String workspaceId, String branch, String format, List<JSONObject> dataList) {
try {
switch (format) {
case "tsg2402": {
@@ -260,7 +725,7 @@ public class ApplicationServiceImpl implements IApplicationService {
// commit
String commitMessage = "feat: import application data from TSG system (version 2402)";
- this.saveOrUpdateBatch(workspaceId, branchName, commitMessage, records);
+ this.saveOrUpdateBatch(workspaceId, branch, commitMessage, records);
return records;
}
default:
diff --git a/src/main/java/net/geedge/asw/module/app/service/impl/BranchServiceImpl.java b/src/main/java/net/geedge/asw/module/app/service/impl/BranchServiceImpl.java
new file mode 100644
index 0000000..42bebc9
--- /dev/null
+++ b/src/main/java/net/geedge/asw/module/app/service/impl/BranchServiceImpl.java
@@ -0,0 +1,156 @@
+package net.geedge.asw.module.app.service.impl;
+
+import cn.hutool.log.Log;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import net.geedge.asw.common.util.ASWException;
+import net.geedge.asw.common.util.RCode;
+import net.geedge.asw.common.util.T;
+import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
+import net.geedge.asw.module.app.service.IBranchService;
+import net.geedge.asw.module.app.service.IApplicationMergeService;
+import net.geedge.asw.module.app.service.impl.ApplicationMergeServiceImpl.MergeRequestStatus;
+import net.geedge.asw.module.app.util.JGitUtils;
+import net.geedge.asw.module.workspace.service.IWorkspaceService;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class BranchServiceImpl implements IBranchService {
+
+ private final static Log log = Log.get();
+
+ @Autowired
+ private IWorkspaceService workspaceService;
+
+ @Autowired
+ private IApplicationMergeService applicationMergeService;
+
+ @Override
+ public List<Map<Object, Object>> listBranch(String workspaceId, String search) {
+ List<Map<Object, Object>> resultList = T.ListUtil.list(true);
+
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir);
+ Git git = Git.open(repository.getDirectory())) {
+ String fullBranch = repository.getFullBranch();
+
+ String defaultBranch = "main";
+ if (fullBranch != null && fullBranch.startsWith(JGitUtils.LOCAL_BRANCH_PREFIX)) {
+ defaultBranch = fullBranch.substring(JGitUtils.LOCAL_BRANCH_PREFIX.length());
+ }
+
+ // 默认行为,进查询本地分支
+ List<Ref> call = git.branchList().call();
+ RevWalk revCommits = new RevWalk(repository);
+
+ for (Ref ref : call) {
+ String branchName = ref.getName();
+ // 返回时去掉前缀
+ branchName = branchName.replaceAll(JGitUtils.LOCAL_BRANCH_PREFIX, "");
+ if (T.StrUtil.isNotEmpty(search)) {
+ if (!T.StrUtil.contains(branchName, search)) {
+ continue;
+ }
+ }
+
+ Map<Object, Object> m = T.MapUtil.builder()
+ .put("name", branchName)
+ .put("default", T.StrUtil.equals(defaultBranch, branchName))
+ .build();
+
+ RevCommit commit = revCommits.parseCommit(ref.getObjectId());
+ m.put("commit", JGitUtils.buildAswCommitInfo(commit));
+
+ resultList.add(m);
+ }
+ revCommits.close();
+ revCommits.dispose();
+ } catch (GitAPIException | IOException e) {
+ log.error(e, "[listBranch] [error] [workspaceId: {}]", workspaceId);
+ throw new RuntimeException(e);
+ }
+ return resultList;
+ }
+
+ @Override
+ public Map<Object, Object> infoBranch(String workspaceId, String branchName) {
+ List<Map<Object, Object>> listBranch = this.listBranch(workspaceId, branchName);
+ // 分支不存在
+ if (T.CollUtil.isEmpty(listBranch)) {
+ throw new ASWException(RCode.SYS_RECORD_NOT_FOUND);
+ }
+ return T.CollUtil.getFirst(listBranch);
+ }
+
+ @Override
+ public Map<Object, Object> newBranch(String workspaceId, String name, String ref) {
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir);
+ Git git = Git.open(repository.getDirectory())) {
+
+ git.branchCreate()
+ .setName(name)
+ .setStartPoint(ref)
+ .call();
+
+ return this.infoBranch(workspaceId, name);
+ } catch (GitAPIException | IOException e) {
+ log.error(e, "[newBranch] [error] [workspaceId: {}] [branch: {}] [ref: {}]", workspaceId, name, ref);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void deleteBranch(String workspaceId, String branchName) {
+ log.info("[deleteBranch] [begin] [workspaceId: {}] [branch: {}]", workspaceId, branchName);
+
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir);
+ Git git = Git.open(repository.getDirectory())) {
+
+ git.branchDelete()
+ .setBranchNames(branchName)
+ .setForce(true)
+ .call();
+
+ // OPEN 状态,mr 源分支被删除,mr 记录直接删掉
+ applicationMergeService.remove(new LambdaQueryWrapper<ApplicationMergeEntity>()
+ .eq(ApplicationMergeEntity::getWorkspaceId, workspaceId)
+ .eq(ApplicationMergeEntity::getSourceBranch, branchName)
+ .eq(ApplicationMergeEntity::getStatus, MergeRequestStatus.OPEN.toString())
+ );
+ } catch (GitAPIException | IOException e) {
+ log.error(e, "[deleteBranch] [error] [workspaceId: {}] [branchName: {}]", workspaceId, branchName);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public List<Map<Object, Object>> listBranchCommit(String workspaceId, String branch, String path) {
+ List<Map<Object, Object>> resultList = T.ListUtil.list(true);
+
+ File gitDir = workspaceService.getGitDir(workspaceId);
+ try (Repository repository = JGitUtils.openRepository(gitDir)) {
+
+ List<RevCommit> branchCommitList = JGitUtils.getBranchCommitList(repository, branch, path);
+ branchCommitList.forEach(revCommit -> resultList.add(JGitUtils.buildAswCommitInfo(revCommit)));
+
+ } catch (GitAPIException | IOException e) {
+ log.error(e, "[listBranchCommit] [error] [workspaceId: {}] [branch: {}] [path: {}]", workspaceId, branch, path);
+ throw new RuntimeException(e);
+ }
+ return resultList;
+ }
+
+}
diff --git a/src/main/java/net/geedge/asw/module/app/service/impl/GitServiceImpl.java b/src/main/java/net/geedge/asw/module/app/service/impl/GitServiceImpl.java
deleted file mode 100644
index 5005f63..0000000
--- a/src/main/java/net/geedge/asw/module/app/service/impl/GitServiceImpl.java
+++ /dev/null
@@ -1,1397 +0,0 @@
-package net.geedge.asw.module.app.service.impl;
-
-import cn.dev33.satoken.stp.StpUtil;
-import cn.hutool.json.JSONObject;
-import cn.hutool.log.Log;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import net.geedge.asw.common.util.ASWException;
-import net.geedge.asw.common.util.RCode;
-import net.geedge.asw.common.util.T;
-import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
-import net.geedge.asw.module.app.service.IApplicationMergeService;
-import net.geedge.asw.module.app.service.IGitService;
-import net.geedge.asw.module.app.service.impl.ApplicationMergeServiceImpl.MergeRequestStatus;
-import net.geedge.asw.module.sys.entity.SysUserEntity;
-import net.geedge.asw.module.sys.service.ISysUserService;
-import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
-import net.geedge.asw.module.workspace.service.IWorkspaceService;
-import org.apache.commons.io.FilenameUtils;
-import org.eclipse.jgit.api.*;
-import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.diff.*;
-import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheBuilder;
-import org.eclipse.jgit.dircache.DirCacheEntry;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.*;
-import org.eclipse.jgit.merge.MergeFormatter;
-import org.eclipse.jgit.merge.MergeStrategy;
-import org.eclipse.jgit.merge.RecursiveMerger;
-import org.eclipse.jgit.merge.ThreeWayMerger;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.revwalk.filter.RevFilter;
-import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
-import org.eclipse.jgit.transport.PushResult;
-import org.eclipse.jgit.transport.RemoteRefUpdate;
-import org.eclipse.jgit.treewalk.CanonicalTreeParser;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.treewalk.filter.PathFilter;
-import org.eclipse.jgit.util.io.DisabledOutputStream;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.text.MessageFormat;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-@Service
-public class GitServiceImpl implements IGitService {
-
- private final static Log log = Log.get();
-
- @Value("${file.extensions.text:txt,csv,md,html,xml,json,log,bat,py,sh,ini,conf,yaml,yml,properties,toml,java,c,cpp,js,php,ts,go,rb,rtf,tex,rss,xhtml,sql}")
- private String textExtensions;
-
- @Value("${asw.application.template.meta.json}")
- private String metaJsonTemplate;
-
- @Value("${asw.application.template.signature.json}")
- private String signatureJsonTemplate;
-
- /**
- * 本地分支引用前缀
- */
- public static final String LOCAL_BRANCH_PREFIX = "refs/heads/";
-
- @Autowired
- private ISysUserService userService;
-
- @Autowired
- private IWorkspaceService workspaceService;
-
- @Autowired
- private IApplicationMergeService applicationMergeService;
-
- /**
- * is binary file
- *
- * @param filename
- * @return
- */
- private boolean isBinary(String filename) {
- String extension = FilenameUtils.getExtension(filename);
- List<String> split = T.StrUtil.split(this.textExtensions, ",");
- return !split.contains(extension.toLowerCase());
- }
-
- /**
- * get repository path
- * path= {webRootPath}/workspace/{workspace.id}
- */
- private File getRepoDirPath(String workspaceId) {
- WorkspaceEntity workspace = workspaceService.getById(workspaceId);
- File repoDir = T.FileUtil.file(T.WebPathUtil.getRootPath(), "workspace", workspace.getId());
- return repoDir;
- }
-
- /**
- * get git instance
- */
- @Override
- public Git getGitInstance(String workspaceId) {
- File repoDir = this.getRepoDirPath(workspaceId);
- try {
- // 目录不存在,初始化裸仓库
- if (!repoDir.exists()) {
- log.info("[getGitInstance] [dir not exist] [init new repository] [path: {}]", repoDir);
- Repository repo = this.initRepository(workspaceId);
- return new Git(repo);
- }
-
- FileRepositoryBuilder builder = new FileRepositoryBuilder();
- Repository repository = builder.setGitDir(repoDir)
- .readEnvironment()
- .findGitDir()
- .build();
-
- // 是否为 Git 仓库
- if (repository.getObjectDatabase().exists()) {
- return new Git(repository);
- } else {
- log.info("[getGitInstance] [init new repository] [path: {}]", repoDir);
- Repository repo = this.initRepository(workspaceId);
- return new Git(repo);
- }
- } catch (Exception e) {
- log.error(e, "[getGitInstance] [error] [path: {}]", repoDir);
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public Repository initRepository(String workspaceId) {
- File repoDir = this.getRepoDirPath(workspaceId);
- try (
- Git git = Git.init()
- .setBare(true)
- .setDirectory(repoDir)
- .setInitialBranch("main")
- .call();
- Repository repository = git.getRepository();
- ObjectInserter inserter = repository.getObjectDatabase().newInserter();
- ) {
- DirCache dirCache = DirCache.newInCore();
- DirCacheBuilder builder = dirCache.builder();
-
- ObjectId objectId = this.insertBlobFileToDatabase(repository, "".getBytes());
- DirCacheEntry entry = this.buildDirCacheEntry("README.md", FileMode.REGULAR_FILE, objectId);
- builder.add(entry);
- builder.finish();
-
- // commit
- this.createCommit(repository, "main", dirCache.writeTree(inserter), "Initial commit");
-
- return git.getRepository();
- } catch (GitAPIException | IOException e) {
- log.error(e, "[initRepository] [git init error]");
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public Map<Object, Object> infoCommit(String workspaceId, String commitId) {
- try (Git git = this.getGitInstance(workspaceId);
- Repository repository = git.getRepository();
- RevWalk revCommits = new RevWalk(repository);
- ) {
- RevCommit commit = revCommits.parseCommit(ObjectId.fromString(commitId));
- return this.buildAswCommitInfo(commit);
- } catch (IOException e) {
- log.error(e, "[infoCommit] [error] [workspaceId: {}] [commitId: {}]", workspaceId, commitId);
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public List<Map<Object, Object>> listBranch(String workspaceId, String search) {
- List<Map<Object, Object>> resultList = T.ListUtil.list(true);
-
- try (Git git = this.getGitInstance(workspaceId)) {
- Repository repository = git.getRepository();
- String fullBranch = repository.getFullBranch();
-
- String defaultBranch = "main";
- if (fullBranch != null && fullBranch.startsWith(LOCAL_BRANCH_PREFIX)) {
- defaultBranch = fullBranch.substring(LOCAL_BRANCH_PREFIX.length());
- }
-
- // 默认行为,进查询本地分支
- List<Ref> call = git.branchList().call();
- RevWalk revCommits = new RevWalk(repository);
-
- for (Ref ref : call) {
- String branchName = ref.getName();
- // 返回时去掉前缀
- branchName = branchName.replaceAll(LOCAL_BRANCH_PREFIX, "");
- if (T.StrUtil.isNotEmpty(search)) {
- if (!T.StrUtil.contains(branchName, search)) {
- continue;
- }
- }
-
- Map<Object, Object> m = T.MapUtil.builder()
- .put("name", branchName)
- .put("default", T.StrUtil.equals(defaultBranch, branchName))
- .build();
-
- RevCommit commit = revCommits.parseCommit(ref.getObjectId());
- m.put("commit", this.buildAswCommitInfo(commit));
-
- resultList.add(m);
- }
- revCommits.close();
- revCommits.dispose();
- } catch (GitAPIException | IOException e) {
- log.error(e, "[listBranch] [error] [workspaceId: {}]", workspaceId);
- throw new ASWException(RCode.ERROR);
- }
- return resultList;
- }
-
- @Override
- public Map<Object, Object> infoBranch(String workspaceId, String branchName) {
- List<Map<Object, Object>> listBranch = this.listBranch(workspaceId, branchName);
-
- // 分支不存在
- if (T.CollUtil.isEmpty(listBranch)) {
- throw new ASWException(RCode.SYS_RECORD_NOT_FOUND);
- }
- return T.CollUtil.getFirst(listBranch);
- }
-
- @Override
- public Map<Object, Object> newBranch(String workspaceId, String branchName, String ref) {
- try (Git git = this.getGitInstance(workspaceId)) {
- git.branchCreate()
- .setName(branchName)
- .setStartPoint(ref)
- .call();
-
- return this.infoBranch(workspaceId, branchName);
- } catch (GitAPIException e) {
- log.error(e, "[newBranch] [error] [workspaceId: {}] [branchName: {}] [ref: {}]", workspaceId, branchName, ref);
- throw new ASWException(RCode.ERROR);
- }
- }
-
- @Override
- public void deleteBranch(String workspaceId, String branchName) {
- log.info("[deleteBranch] [begin] [workspaceId: {}] [branch: {}]", workspaceId, branchName);
- try (Git git = this.getGitInstance(workspaceId)) {
- git.branchDelete()
- .setBranchNames(branchName)
- .setForce(true)
- .call();
-
- // OPEN 状态,mr 源分支被删除,mr 记录直接删掉
- applicationMergeService.remove(new LambdaQueryWrapper<ApplicationMergeEntity>()
- .eq(ApplicationMergeEntity::getWorkspaceId, workspaceId)
- .eq(ApplicationMergeEntity::getSourceBranch, branchName)
- .eq(ApplicationMergeEntity::getStatus, MergeRequestStatus.OPEN.toString())
- );
- } catch (GitAPIException e) {
- log.error(e, "[deleteBranch] [error] [workspaceId: {}] [branchName: {}]", workspaceId, branchName);
- throw new ASWException(RCode.ERROR);
- }
- }
-
- @Override
- public boolean isBranchExists(String workspaceId, String branch) {
- try (Git git = this.getGitInstance(workspaceId)) {
- Ref ref = git.getRepository().findRef(T.StrUtil.concat(true, LOCAL_BRANCH_PREFIX, branch));
- return null != ref;
- } catch (IOException e) {
- log.error(e, "[isBranchExists] [workspaceId: {}] [branch: {}]", workspaceId, branch);
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public List<Map<Object, Object>> listBranchCommit(String workspaceId, String branch, String path) {
- List<Map<Object, Object>> resultList = T.ListUtil.list(true);
- try (Git git = this.getGitInstance(workspaceId)) {
- ObjectId branchRef = git.getRepository().resolve(branch);
- LogCommand command = git.log().add(branchRef);
- if (T.StrUtil.isNotEmpty(path)) {
- command.addPath(path);
- }
-
- Iterable<RevCommit> iterable = command.call();
- for (RevCommit commit : iterable) {
- resultList.add(this.buildAswCommitInfo(commit));
- }
- } catch (GitAPIException | IOException e) {
- log.error(e, "[listBranchCommit] [error] [workspaceId: {}] [branch: {}] [path: {}]", workspaceId, branch, path);
- throw new RuntimeException(e);
- }
- return resultList;
- }
-
- @Override
- public String getParentCommitId(String workspaceId, String branch, String commitId) {
- try (Git git = this.getGitInstance(workspaceId);
- Repository repository = git.getRepository();
- RevWalk revWalk = new RevWalk(repository);) {
-
- Ref branchRef = repository.findRef(branch);
- revWalk.markStart(revWalk.parseCommit(branchRef.getObjectId()));
-
- String parentCommitId = null;
- Iterator<RevCommit> iterator = revWalk.iterator();
- while (iterator.hasNext()) {
- RevCommit currentCommit = iterator.next();
- if (currentCommit.getId().getName().equals(commitId)) {
- if (currentCommit.getParentCount() > 0) {
- parentCommitId = currentCommit.getParent(0).getName();
- }
- break;
- }
- }
-
- if (T.StrUtil.isEmpty(parentCommitId)) {
- throw new ASWException(RCode.GIT_PARENT_COMMITID_NOT_FOUND);
- }
- return parentCommitId;
- } catch (IOException e) {
- log.error(e, "[getParentCommitId] [error] [workspaceId: {}] [branch: {}] [commitId: {}]", workspaceId, branch, commitId);
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public String getLatestCommitId(String workspaceId, String branch) {
- try (Git git = this.getGitInstance(workspaceId);
- Repository repository = git.getRepository();
- RevWalk revWalk = new RevWalk(repository);) {
- RevCommit commit = revWalk.parseCommit(repository.resolve(branch));
- return commit.getName();
- } catch (IOException e) {
- log.error(e, "[getLatestCommitId] [error] [workspaceId: {}] [branch: {}]", workspaceId, branch);
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public String getMergeBase(String workspaceId, String branchA, String branchB) {
- try (Git git = this.getGitInstance(workspaceId);
- Repository repository = git.getRepository();) {
- ObjectId branchAId = repository.resolve(branchA);
- ObjectId branchBId = repository.resolve(branchB);
-
- return this.getMergeBase(repository, branchAId, branchBId);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 获取 commitIdA -> commitIdB 文件差异
- *
- * @param workspaceId
- * @param newCommitId
- * @param oldCommitId
- * @return
- */
- @Override
- public List<Map<Object, Object>> getDiffFileListInCommits(String workspaceId, String newCommitId, String oldCommitId) {
- log.info("[getDiffFileListInCommits] [begin] [workspaceId: {}] [newCommitId: {}] [oldCommitId: {}]", workspaceId, newCommitId, oldCommitId);
- try (Git git = this.getGitInstance(workspaceId);
- Repository repository = git.getRepository();
- RevWalk revWalk = new RevWalk(repository);
- DiffFormatter diffFormatter = new DiffFormatter(null)) {
-
- RevCommit oldCommit = revWalk.parseCommit(repository.resolve(oldCommitId));
- RevCommit newCommit = revWalk.parseCommit(repository.resolve(newCommitId));
-
- // oldTree
- CanonicalTreeParser oldTree = new CanonicalTreeParser();
- oldTree.reset(repository.newObjectReader(), oldCommit.getTree());
- // newTree
- CanonicalTreeParser newTree = new CanonicalTreeParser();
- newTree.reset(repository.newObjectReader(), newCommit.getTree());
-
- // diff
- List<Map<Object, Object>> files = T.ListUtil.list(true);
-
- diffFormatter.setRepository(repository);
- diffFormatter.setDiffComparator(RawTextComparator.DEFAULT);
- diffFormatter.setDetectRenames(true);
-
- List<DiffEntry> diffs = diffFormatter.scan(oldTree, newTree);
- for (DiffEntry diff : diffs) {
- int addedLines = 0;
- int deletedLines = 0;
- EditList edits = diffFormatter.toFileHeader(diff).toEditList();
- for (Edit edit : edits) {
- switch (edit.getType()) {
- case INSERT:
- addedLines += edit.getLengthB();
- break;
- case DELETE:
- deletedLines += edit.getLengthA();
- break;
- case REPLACE:
- addedLines += edit.getLengthB();
- deletedLines += edit.getLengthA();
- break;
- default:
- break;
- }
- }
-
- String oldPath = diff.getOldPath(), newPath = diff.getNewPath(), encoding = null, oldContent = null, newContent = null;
- switch (diff.getChangeType()) {
- case COPY:
- case ADD: {
- Map<Object, Object> fileContent = this.getFileContent(repository, newPath, diff.getNewId().toObjectId());
- encoding = T.MapUtil.getStr(fileContent, "encoding", "");
- newContent = T.MapUtil.getStr(fileContent, "content", "");
- break;
- }
- case DELETE: {
- Map<Object, Object> fileContent = this.getFileContent(repository, oldPath, diff.getOldId().toObjectId());
- encoding = T.MapUtil.getStr(fileContent, "encoding", "");
- oldContent = T.MapUtil.getStr(fileContent, "content", "");
- break;
- }
- case MODIFY: {
- Map<Object, Object> fileContent = this.getFileContent(repository, oldPath, diff.getOldId().toObjectId());
- oldContent = T.MapUtil.getStr(fileContent, "content", "");
-
- Map<Object, Object> fileContent1 = this.getFileContent(repository, newPath, diff.getNewId().toObjectId());
- encoding = T.MapUtil.getStr(fileContent1, "encoding", "");
- newContent = T.MapUtil.getStr(fileContent1, "content", "");
- break;
- }
- case RENAME: {
- break;
- }
- default:
- break;
- }
- files.add(
- T.MapUtil.builder()
- .put("oldPath", oldPath)
- .put("newPath", newPath)
- .put("addedLines", addedLines)
- .put("removedLines", deletedLines)
- .put("encoding", encoding)
- .put("oldContent", oldContent)
- .put("newContent", newContent)
- .put("action", diff.getChangeType().name().toLowerCase())
- .build()
- );
- }
- return files;
- } catch (IOException e) {
- log.error(e, "[getDiffFileListInCommits] [error] [workspaceId: {}] [newCommitId: {}] [oldCommitId: {}]", workspaceId, newCommitId, oldCommitId);
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public String mergeBranch(String workspaceId, String srcBranch, String tgtBranch, String message, List<Map<String, String>> resolveConflictFileContent) throws RuntimeException {
- log.info("[mergeBranch] [begin] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}]", workspaceId, srcBranch, tgtBranch);
- // prepare a new folder for the cloned repository
- File localPath = T.FileUtil.file(net.geedge.asw.common.util.Constants.TEMP_PATH, T.StrUtil.uuid());
- T.FileUtil.del(localPath);
- T.FileUtil.mkdir(localPath);
-
- // bare repository
- File repoDir = this.getRepoDirPath(workspaceId);
-
- // clone
- try (Git git = Git.cloneRepository()
- .setBare(false)
- .setURI(repoDir.getAbsolutePath())
- .setDirectory(localPath)
- .setCredentialsProvider(null)
- .call();
- Repository repository = git.getRepository();) {
- StoredConfig config = repository.getConfig();
- SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
- config.setString("user", null, "name", loginUserEntity.getName());
- config.setString("user", null, "email", "[email protected]");
- config.save();
-
- // git fetch
- git.fetch().call();
-
- // checout
- git.checkout()
- .setCreateBranch(T.StrUtil.equals("main", tgtBranch) ? false : true)
- .setName(tgtBranch)
- .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
- .setStartPoint("origin/" + tgtBranch)
- .call();
-
- // merge
- MergeResult mergeResult = git.merge()
- .setCommit(true)
- .setMessage(message)
- .setStrategy(MergeStrategy.RECURSIVE)
- .setFastForward(MergeCommand.FastForwardMode.NO_FF)
- .include(repository.findRef("origin/" + srcBranch))
- .call();
-
- MergeResult.MergeStatus mergeStatus = mergeResult.getMergeStatus();
- log.info("[mergeBranch] [merge status: {}]", mergeStatus);
- if (!mergeStatus.isSuccessful()) {
- // 解决冲突
- if (mergeStatus == MergeResult.MergeStatus.CONFLICTING && T.CollUtil.isNotEmpty(resolveConflictFileContent)) {
- Map<String, int[][]> conflicts = mergeResult.getConflicts();
- for (Map.Entry<String, int[][]> entry : conflicts.entrySet()) {
- String conflictFilePath = entry.getKey();
- Map<String, String> map = resolveConflictFileContent.stream()
- .filter(m -> T.StrUtil.equals(conflictFilePath, T.MapUtil.getStr(m, "path")))
- .findFirst()
- .orElse(null);
- if (null != map) {
- Path filePath = Paths.get(localPath.getAbsolutePath(), conflictFilePath);
- Files.write(filePath, T.MapUtil.getStr(map, "content").getBytes(StandardCharsets.UTF_8));
- git.add().addFilepattern(conflictFilePath).call();
- }
- }
- git.commit().setMessage(message).call();
- } else {
- // 其他类型的合并错误,抛出异常
- String errorMessage = String.format("Merge failed: %s, Conflicts: %s", mergeStatus, mergeResult.getConflicts());
- throw new RuntimeException(errorMessage);
- }
- }
-
- // push
- Iterable<PushResult> pushResultIterable = git.push()
- .setRemote("origin")
- .add(tgtBranch)
- .call();
- for (PushResult pushResult : pushResultIterable) {
- for (RemoteRefUpdate update : pushResult.getRemoteUpdates()) {
- if (update.getStatus() != RemoteRefUpdate.Status.OK && update.getStatus() != RemoteRefUpdate.Status.UP_TO_DATE) {
- log.error("[mergeBranch] [push error] [remote: {}] [status: {}]", update.getRemoteName(), update.getStatus());
- String errorMessage = "Push failed: " + update.getStatus();
- throw new RuntimeException(errorMessage);
- }
- }
- }
-
- if (null != mergeResult.getNewHead()) {
- return mergeResult.getNewHead().getName();
- }
- // 解决冲突合并的情况下无法从 mergeResult 中获取提交信息,查询最新提交信息返回
- return this.getLatestCommitId(workspaceId, srcBranch);
- } catch (Exception e) {
- log.error(e, "[mergeBranch] [error] [workspaceId: {}]", workspaceId);
- throw new RuntimeException(e.getMessage());
- } finally {
- T.FileUtil.del(localPath);
- log.info("[mergeBranch] [finshed] [workspaceId: {}]", workspaceId);
- }
- }
-
- @Override
- public List<Map<Object, Object>> listApplication(String workspaceId, String branch, String q) {
- List<Map<Object, Object>> resultList = T.ListUtil.list(true);
-
- try (Git git = this.getGitInstance(workspaceId)) {
- Repository repository = git.getRepository();
-
- try (TreeWalk treeWalk = new TreeWalk(repository);
- RevWalk revWalk = new RevWalk(repository)) {
-
- ObjectId branchRef = repository.resolve(branch);
- treeWalk.addTree(revWalk.parseTree(branchRef));
- treeWalk.setFilter(PathFilter.create("applications/"));
- treeWalk.setRecursive(true);
-
- Map<String, String> appDirPathMapping = T.MapUtil.newHashMap(true);
- while (treeWalk.next()) {
- String filePath = treeWalk.getPathString();
- String fileName = treeWalk.getNameString();
- if (T.StrUtil.equals("meta.json", fileName)) {
- // application_name 从目录中获取
- String applicationName = T.PathUtil.getPathEle(Path.of(filePath), 1).toString();
- // filter by name
- if (T.StrUtil.isNotEmpty(q) && !T.StrUtil.containsIgnoreCase(applicationName, q)) {
- continue;
- }
-
- ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
- String metaJsonStr = T.StrUtil.utf8Str(loader.getBytes());
- metaJsonStr = T.StrUtil.emptyToDefault(metaJsonStr, T.StrUtil.EMPTY_JSON);
- Map metaJsonMap = T.MapUtil.empty();
- try {
- metaJsonMap = T.JSONUtil.toBean(metaJsonStr, Map.class);
- } catch (Exception e) {
- log.error(e, "[listApplication] [meat.json format error] [applicationName: {}]", applicationName);
- }
-
- Map<Object, Object> m = T.MapUtil.newHashMap(true);
- m.putAll(metaJsonMap);
- m.put("name", applicationName);
-
- String appDirPath = treeWalk.getPathString().replaceAll(fileName, "");
- appDirPathMapping.put(applicationName, appDirPath);
-
- resultList.add(m);
- }
- }
-
- Map<String, RevCommit> lastCommitMapping = this.getLastCommitIdDataByPath(repository, branch, appDirPathMapping);
-
- for (Map<Object, Object> map : resultList) {
- String applicationName = T.MapUtil.getStr(map, "name");
- RevCommit lastCommit = T.MapUtil.get(lastCommitMapping, applicationName, RevCommit.class);
- map.put("commit", this.buildAswCommitInfo(lastCommit));
- }
- }
-
- // 按照提交日期倒叙
- resultList = resultList.stream().sorted(Comparator.comparing(map -> {
- Map commit = T.MapUtil.get((Map) map, "commit", Map.class, new HashMap(2));
- return (Long) T.MapUtil.getLong(commit, "createdAt", 0l);
- }).reversed()).collect(Collectors.toList());
- } catch (IOException e) {
- log.error(e, "[listApplication] [error] [workspaceId: {}] [branch: {}]", workspaceId, branch);
- throw new ASWException(RCode.ERROR);
- }
- return resultList;
- }
-
- @Override
- public Map<Object, Object> infoApplication(String workspaceId, String branch, String applicationName) {
- Map<Object, Object> result = T.MapUtil.builder()
- .put("branch", branch)
- .build();
-
- try (Git git = this.getGitInstance(workspaceId)) {
- Repository repository = git.getRepository();
-
- // 获取指定版本的最新ID
- ObjectId branchRef = repository.resolve(branch);
- result.put("commitId", branchRef.getName());
-
- List<Map<Object, Object>> files = T.ListUtil.list(true);
- try (TreeWalk treeWalk = new TreeWalk(repository);
- RevWalk revWalk = new RevWalk(repository)) {
-
- treeWalk.addTree(revWalk.parseTree(branchRef));
- treeWalk.setFilter(PathFilter.create("applications/" + applicationName + "/"));
- treeWalk.setRecursive(true);
-
- Map<String, String> appFilePathMapping = T.MapUtil.newHashMap(true);
- while (treeWalk.next()) {
- String pathString = treeWalk.getPathString();
- Map<Object, Object> m = T.MapUtil.builder()
- .put("path", pathString)
- .build();
-
- Map<Object, Object> fileContent = this.getFileContent(repository, pathString, treeWalk.getObjectId(0));
- m.putAll(fileContent);
- files.add(m);
-
- appFilePathMapping.put(pathString, pathString);
- }
-
- Map<String, RevCommit> lastCommitMapping = this.getLastCommitIdDataByPath(repository, branch, appFilePathMapping);
- for (Map<Object, Object> m : files) {
- String path = T.MapUtil.getStr(m, "path");
- RevCommit revCommit = lastCommitMapping.get(path);
- m.put("lastCommitId", revCommit != null ? revCommit.getName() : null);
- }
- }
- result.put("files", files);
- } catch (IOException e) {
- log.error(e, "[infoApplication] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
- throw new ASWException(RCode.ERROR);
- }
- return result;
- }
-
- @Override
- public void newApplication(String workspaceId, String branch, String applicationName) {
- try (Git git = this.getGitInstance(workspaceId)) {
- Repository repository = git.getRepository();
-
- Map<String, ObjectId> filePathAndBlobIdMap = T.MapUtil.newHashMap(true);
- for (String str : T.ListUtil.of("README.md", "meta.json", "signature.json")) {
- String savePath = T.StrUtil.concat(true, "applications/", applicationName, "/", str);
-
- String fileContent = T.StrUtil.EMPTY;
- if ("meta.json".equals(str)) {
- JSONObject jsonObject = T.JSONUtil.parseObj(this.metaJsonTemplate);
- jsonObject.set("id", T.StrUtil.uuid());
- jsonObject.set("name", applicationName);
- jsonObject.set("longName", applicationName);
- fileContent = T.JSONUtil.parse(jsonObject).toJSONString(2);
- }
- if ("signature.json".equals(str)) {
- fileContent = this.signatureJsonTemplate;
- }
-
- ObjectId objectId = this.insertBlobFileToDatabase(repository, fileContent.getBytes());
- filePathAndBlobIdMap.put(savePath, objectId);
- }
-
- try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
- TreeWalk treeWalk = new TreeWalk(repository);
- RevWalk revWalk = new RevWalk(repository)) {
-
- // branch tree
- ObjectId resolveId = repository.resolve(branch);
- if (null != resolveId) {
- treeWalk.addTree(revWalk.parseTree(resolveId));
- } else {
- treeWalk.addTree(new CanonicalTreeParser());
- }
- treeWalk.setRecursive(true);
-
- DirCache newTree = DirCache.newInCore();
- DirCacheBuilder newTreeBuilder = newTree.builder();
- while (treeWalk.next()) {
- DirCacheEntry entry = this.buildDirCacheEntry(treeWalk.getPathString(), treeWalk.getFileMode(0), treeWalk.getObjectId(0));
- newTreeBuilder.add(entry);
- }
-
- // update new tree
- for (Map.Entry<String, ObjectId> entry : filePathAndBlobIdMap.entrySet()) {
- String filePath = entry.getKey();
- ObjectId blobId = entry.getValue();
-
- // add file ref
- DirCacheEntry dirCacheEntry = this.buildDirCacheEntry(filePath, FileMode.REGULAR_FILE, blobId);
- newTreeBuilder.add(dirCacheEntry);
- }
-
- newTreeBuilder.finish();
- ObjectId newTreeId = newTree.writeTree(inserter);
-
- String message = String.format("feat: add %s application", applicationName);
- this.createCommit(repository, branch, newTreeId, message);
- }
- } catch (IOException | ConcurrentRefUpdateException e) {
- log.error(e, "[newApplication] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
- throw new ASWException(RCode.ERROR);
- }
- }
-
- @Override
- public void deleteApplication(String workspaceId, String branch, String applicationName) {
- try (Git git = this.getGitInstance(workspaceId)) {
- Repository repository = git.getRepository();
-
- try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
- TreeWalk treeWalk = new TreeWalk(repository);
- RevWalk revWalk = new RevWalk(repository)) {
-
- ObjectId branchRef = repository.resolve(branch);
- treeWalk.addTree(revWalk.parseTree(branchRef));
- treeWalk.setRecursive(true);
-
- DirCache newTree = DirCache.newInCore();
- DirCacheBuilder newTreeBuilder = newTree.builder();
-
- String appFilePrefixStr = T.StrUtil.concat(true, "applications/", applicationName, "/");
- while (treeWalk.next()) {
- String pathString = treeWalk.getPathString();
- if (!pathString.startsWith(appFilePrefixStr)) {
- DirCacheEntry entry = this.buildDirCacheEntry(pathString, treeWalk.getFileMode(0), treeWalk.getObjectId(0));
- newTreeBuilder.add(entry);
- }
- }
-
- newTreeBuilder.finish();
- ObjectId newTreeId = newTree.writeTree(inserter);
-
- String message = String.format("refactor: remove %s application", applicationName);
- this.createCommit(repository, branch, newTreeId, message);
- }
- } catch (IOException | ConcurrentRefUpdateException e) {
- log.error(e, "[deleteApplication] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
- throw new ASWException(RCode.ERROR);
- }
- }
-
- @Override
- public void updateApplication(String workspaceId, String branch, String lastCommitId, String message, List<Map<String, String>> updateContent) {
- try (Git git = this.getGitInstance(workspaceId)) {
- Repository repository = git.getRepository();
-
- // 分支最新 commitId
- ObjectId branchRef = repository.resolve(branch);
- // 如果不相等,检查 param.lastCommitId --- branch.HEAD 期间是否更新了本次修改文件
- if (!T.StrUtil.equals(lastCommitId, branchRef.getName())) {
- List<String> updateFilePath = updateContent.stream()
- .filter(map -> {
- String action = T.MapUtil.getStr(map, "action");
- return T.StrUtil.equalsAny(action, "create", "update");
- })
- .map(map -> T.MapUtil.getStr(map, "path"))
- .collect(Collectors.toList());
-
- ObjectId currentCommitId = repository.resolve(lastCommitId);
- try (RevWalk walk = new RevWalk(repository)) {
- CanonicalTreeParser c1 = new CanonicalTreeParser();
- c1.reset(repository.newObjectReader(), walk.parseCommit(currentCommitId).getTree());
-
- CanonicalTreeParser c2 = new CanonicalTreeParser();
- c2.reset(repository.newObjectReader(), walk.parseCommit(branchRef).getTree());
-
- List<DiffEntry> diffs = git.diff()
- .setOldTree(c1)
- .setNewTree(c2)
- .call();
- List<String> affectFilePathList = diffs.stream()
- .filter(entry -> T.StrUtil.equalsAnyIgnoreCase(entry.getChangeType().name(), DiffEntry.ChangeType.MODIFY.name(), DiffEntry.ChangeType.ADD.name()))
- .map(DiffEntry::getNewPath)
- .collect(Collectors.toList());
- List<String> intersection = T.CollUtil.intersection(updateFilePath, affectFilePathList).stream().toList();
- if (T.CollUtil.isEmpty(intersection)) {
- // 在 param.lastCommitId 不是当前分支最新提交时,本次提交文件没有冲突,更新 commit=branch.HEAD
- lastCommitId = branchRef.getName();
- } else {
- throw new ASWException(RCode.GIT_COMMIT_CONFLICT_ERROR);
- }
- }
- }
- try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
- TreeWalk treeWalk = new TreeWalk(repository);
- RevWalk revWalk = new RevWalk(repository)) {
-
- // branch tree
- treeWalk.addTree(revWalk.parseTree(branchRef));
- treeWalk.setRecursive(true);
-
- DirCache newTree = DirCache.newInCore();
- DirCacheBuilder newTreeBuilder = newTree.builder();
-
- List<String> updateFilePath = updateContent.stream().map(map -> T.MapUtil.getStr(map, "path")).collect(Collectors.toList());
- while (treeWalk.next()) {
- String pathString = treeWalk.getPathString();
- // 先删
- if (!updateFilePath.contains(pathString)) {
- DirCacheEntry entry = this.buildDirCacheEntry(pathString, treeWalk.getFileMode(0), treeWalk.getObjectId(0));
- newTreeBuilder.add(entry);
- }
- }
- // 后增
- for (Map<String, String> map : updateContent) {
- String action = T.MapUtil.getStr(map, "action");
- if (T.StrUtil.equalsAnyIgnoreCase(action, "create", "update")) {
- String path = T.MapUtil.getStr(map, "path");
- DirCacheEntry dirCacheEntry = new DirCacheEntry(path);
- dirCacheEntry.setFileMode(FileMode.REGULAR_FILE);
-
- String content = T.MapUtil.getStr(map, "content");
- String encoding = T.MapUtil.getStr(map, "encoding");
- if ("base64".equals(encoding)) {
- // binary
- byte[] data = Base64.getDecoder().decode(content);
- ObjectId objectId = this.insertBlobFileToDatabase(repository, data);
- dirCacheEntry.setObjectId(objectId);
- } else {
- // text
- ObjectId objectId = this.insertBlobFileToDatabase(repository, content.getBytes());
- dirCacheEntry.setObjectId(objectId);
- }
- newTreeBuilder.add(dirCacheEntry);
- }
- }
-
- newTreeBuilder.finish();
- ObjectId newTreeId = newTree.writeTree(inserter);
- this.createCommit(repository, branch, newTreeId, message);
- }
- } catch (IOException | GitAPIException e) {
- log.error(e, "[updateApplication] [error] [workspaceId: {}] [branch: {}] [lastCommitId: {}]", workspaceId, branch, lastCommitId);
- throw new ASWException(RCode.ERROR);
- }
- }
-
- @Override
- public List<Map<Object, Object>> listApplicationCommit(String workspaceId, String branch, String applicationName, String file) {
- List<Map<Object, Object>> resultList = T.ListUtil.list(true);
-
- try (Git git = this.getGitInstance(workspaceId)) {
- String filterPath = T.StrUtil.concat(true, "applications/", applicationName);
- if (T.StrUtil.isNotEmpty(file)) {
- filterPath = T.StrUtil.concat(true, filterPath, "/", file);
- }
-
- ObjectId branchRef = git.getRepository().resolve(branch);
- Iterable<RevCommit> iterable = git.log()
- .add(branchRef)
- .addPath(filterPath)
- .call();
-
- for (RevCommit commit : iterable) {
- resultList.add(this.buildAswCommitInfo(commit));
- }
- } catch (IOException | GitAPIException e) {
- log.error(e, "[listApplicationCommit] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
- throw new ASWException(RCode.ERROR);
- }
- return resultList;
- }
-
- @Override
- public Map<Object, Object> infoApplicationFileContent(String workspaceId, String branch, String applicationName, String commitId, String file) {
- // applications/qq/meta.json
- String path = T.StrUtil.concat(true, "applications/", applicationName, "/", file);
-
- Map<Object, Object> result = T.MapUtil.builder()
- .put("path", path)
- .build();
-
- try (Git git = this.getGitInstance(workspaceId)) {
- Repository repository = git.getRepository();
-
- try (TreeWalk treeWalk = new TreeWalk(repository);
- RevWalk revWalk = new RevWalk(repository);
- ) {
- RevCommit revCommit = revWalk.parseCommit(ObjectId.fromString(commitId));
- ObjectId treeId = revCommit.getTree().getId();
-
- treeWalk.addTree(treeId);
- treeWalk.setRecursive(true);
-
- while (treeWalk.next()) {
- if (T.StrUtil.equals(path, treeWalk.getPathString())) {
- Map<Object, Object> fileContent = this.getFileContent(repository, treeWalk.getPathString(), treeWalk.getObjectId(0));
- result.putAll(fileContent);
- }
- }
- }
- } catch (IOException e) {
- log.error(e, "[infoApplicationFileContent] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
- throw new ASWException(RCode.ERROR);
- }
- return result;
- }
-
- /**
- * commit range
- * 沿着 parent(0) 的主路径提交列表,忽略所有分支路径的提交
- *
- * @param workspaceId
- * @param newCommitId
- * @param oldCommitId
- * @return
- */
- public List<Map<Object, Object>> listCommitRange(String workspaceId, String newCommitId, String oldCommitId) {
- log.info("[listCommitRange] [begin] [workspaceId: {}] [newCommitId: {}] [oldCommitId: {}]", workspaceId, newCommitId, oldCommitId);
- List<Map<Object, Object>> commitList = T.ListUtil.list(true);
- try (Git git = this.getGitInstance(workspaceId);
- Repository repository = git.getRepository();) {
-
- try (RevWalk revWalk = new RevWalk(repository)) {
- RevCommit revCommit = revWalk.parseCommit(repository.resolve(newCommitId));
-
- Set<ObjectId> visitedCommits = new HashSet<>();
- while (revCommit != null && !visitedCommits.contains(revCommit.getId())) {
- if (oldCommitId != null && revCommit.getId().getName().equals(oldCommitId)) {
- break;
- }
-
- commitList.add(this.buildAswCommitInfo(revCommit));
- visitedCommits.add(revCommit.getId());
-
- // 沿着 parent(0) 前进
- if (revCommit.getParentCount() > 0) {
- revCommit = revWalk.parseCommit(revCommit.getParent(0));
- } else {
- revCommit = null;
- }
- }
- } catch (IOException e) {
- log.error(e, "[listCommitRange] [error]");
- throw new RuntimeException(e);
- } finally {
- log.info("[listCommitRange] [finshed] [workspaceId: {}] [commits size: {}]", workspaceId, commitList.size());
- }
- }
- return commitList;
- }
-
- /**
- * 获取 srcBranch -> tgtBranch 冲突文件路径
- * srcBranch 合并到 tgtBranch
- *
- * @param workspaceId
- * @param srcBranch
- * @param tgtBranch
- */
- @Override
- public List<String> getConflictFilePathInBranches(String workspaceId, String srcBranch, String tgtBranch) {
- log.info("[getConflictFileListInBranches] [begin] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}]", workspaceId, srcBranch, tgtBranch);
- try (Git git = this.getGitInstance(workspaceId);
- Repository repository = git.getRepository();
- RevWalk revWalk = new RevWalk(repository);) {
-
- RevCommit commitA = revWalk.parseCommit(repository.resolve(srcBranch));
- RevCommit commitB = revWalk.parseCommit(repository.resolve(tgtBranch));
-
- // Find the merge base
- String mergeBaseId = this.getMergeBase(repository, commitA, commitB);
- log.info("[getConflictFileListInBranches] [mergeBase: {}]", mergeBaseId);
-
- RevCommit mergeBase = revWalk.parseCommit(repository.resolve(mergeBaseId));
-
- ThreeWayMerger threeWayMerger = (ThreeWayMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
- threeWayMerger.setBase(mergeBase);
-
- boolean isOk = threeWayMerger.merge(commitA, commitB);
- log.info("[getConflictFileListInBranches] [merge result] [isOk: {}]", isOk);
- if (!isOk) {
- List<String> unmergedPaths = ((RecursiveMerger) threeWayMerger).getUnmergedPaths();
- return unmergedPaths;
- }
- return T.ListUtil.empty();
- } catch (IOException e) {
- log.error(e, "[getConflictFileListInBranches] [error]");
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 三路对比 srcBranch,tgtBranch,返回冲突文件详情
- *
- * @param workspaceId
- * @param srcBranch
- * @param tgtBranch
- * @return
- */
- @Override
- public List<Map<Object, Object>> getConflictFileInfoInBranches(String workspaceId, String srcBranch, String tgtBranch) {
- log.info("[getConflictFileInfoInBranches] [begin] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}]", workspaceId, srcBranch, tgtBranch);
- try (Git git = this.getGitInstance(workspaceId);
- Repository repository = git.getRepository();
- RevWalk revWalk = new RevWalk(repository);) {
-
- RevCommit commitA = revWalk.parseCommit(repository.resolve(srcBranch));
- RevCommit commitB = revWalk.parseCommit(repository.resolve(tgtBranch));
-
- // Find the merge base
- String mergeBaseId = this.getMergeBase(repository, commitA, commitB);
- log.info("[getConflictFileInfoInBranches] [mergeBase: {}]", mergeBaseId);
-
- RevCommit mergeBase = revWalk.parseCommit(repository.resolve(mergeBaseId));
-
- ThreeWayMerger threeWayMerger = (ThreeWayMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
- threeWayMerger.setBase(mergeBase);
-
- boolean isOk = threeWayMerger.merge(commitA, commitB);
- log.info("[getConflictFileInfoInBranches] [merge result] [isOk: {}]", isOk);
-
- if (!isOk) {
- List<Map<Object, Object>> files = T.ListUtil.list(true);
-
- Map<String, org.eclipse.jgit.merge.MergeResult<? extends Sequence>> mergeResults = ((RecursiveMerger) threeWayMerger).getMergeResults();
- for (Map.Entry<String, org.eclipse.jgit.merge.MergeResult<? extends Sequence>> entry : mergeResults.entrySet()) {
- String unmergedPath = entry.getKey();
- // 暂不支持处理二进制文件冲突
- if (this.isBinary(T.FileNameUtil.getName(unmergedPath))) {
- throw new ASWException(RCode.GIT_BINARY_CONFLICT_ERROR);
- }
-
- org.eclipse.jgit.merge.MergeResult<? extends Sequence> mergeResult = entry.getValue();
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- MergeFormatter formatter = new MergeFormatter();
-
- String oursName = unmergedPath;
- String theirsName = unmergedPath;
- formatter.formatMerge(outputStream, mergeResult, mergeBaseId, oursName, theirsName, StandardCharsets.UTF_8);
- files.add(
- T.MapUtil.builder()
- .put("path", unmergedPath)
- .put("content", outputStream.toString(StandardCharsets.UTF_8.name()))
- .put("conflict", true)
- .build()
- );
- }
- }
- return files;
- }
- return T.ListUtil.empty();
- } catch (IOException e) {
- log.error(e, "[getConflictFileInfoInBranches] [error]");
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 将 file object 添加到 objectDatabases,也就是 ./objects 目录下
- *
- * @param repository
- * @param data
- * @return
- * @throws IOException
- */
- @Override
- public ObjectId insertBlobFileToDatabase(Repository repository, byte[] data) throws IOException {
- try (ObjectInserter inserter = repository.getObjectDatabase().newInserter()) {
- ObjectId blobId = inserter.insert(Constants.OBJ_BLOB, data);
- inserter.flush();
- return blobId;
- }
- }
-
- @Override
- public DirCacheEntry buildDirCacheEntry(String path, FileMode mode, ObjectId objectId) {
- DirCacheEntry dirCacheEntry = new DirCacheEntry(path);
- dirCacheEntry.setFileMode(mode);
- dirCacheEntry.setObjectId(objectId);
- return dirCacheEntry;
- }
-
- /**
- * 根据 path,objectId 读取文件内容
- * 响应 Map,key=encoding,content
- *
- * @param repository
- * @param path
- * @param objectId
- * @return
- * @throws IOException
- */
- private Map<Object, Object> getFileContent(Repository repository, String path, ObjectId objectId) throws IOException {
- String encoding = null, content = null;
-
- ObjectLoader loader = repository.open(objectId);
- if (this.isBinary(T.FileNameUtil.getName(path))) {
- encoding = "base64";
- content = Base64.getEncoder().encodeToString(loader.getBytes());
- } else {
- content = T.StrUtil.utf8Str(loader.getBytes());
- }
- return T.MapUtil.builder()
- .put("encoding", encoding)
- .put("content", content)
- .build();
- }
-
- /**
- * Returns the merge base of two commits or null if there is no common ancestry.
- * 返回两个提交的合并基础,如果没有共同祖先,则返回 null。
- *
- * @param repository
- * @param commitIdA
- * @param commitIdB
- * @return the commit id of the merge base or null if there is no common base
- */
- public String getMergeBase(Repository repository, ObjectId commitIdA, ObjectId commitIdB) {
- try (RevWalk rw = new RevWalk(repository)) {
- RevCommit a = rw.lookupCommit(commitIdA);
- RevCommit b = rw.lookupCommit(commitIdB);
-
- rw.setRevFilter(RevFilter.MERGE_BASE);
- rw.markStart(a);
- rw.markStart(b);
- RevCommit mergeBase = rw.next();
- if (mergeBase == null) {
- return null;
- }
- return mergeBase.getName();
- } catch (Exception e) {
- log.error(e, "Failed to determine merge base");
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 通过 path 获取 lastCommit
- *
- * @param repository
- * @param branch
- * @param pathMapping
- * @return
- */
- public Map<String, RevCommit> getLastCommitIdDataByPath(Repository repository, String branch, Map<String, String> pathMapping) {
- Map<String, RevCommit> result = new HashMap<>();
- try (Git git = new Git(repository);
- DiffFormatter diffFormatter = new DiffFormatter(DisabledOutputStream.INSTANCE);
- ) {
- diffFormatter.setRepository(repository);
-
- // 指定分支获取 log
- Iterable<RevCommit> commits = git.log()
- .add(git.getRepository().resolve(branch))
- .call();
- for (RevCommit commit : commits) {
- // 获取上次提交,用以对比差异
- RevCommit parent = commit.getParentCount() > 0 ? commit.getParent(0) : null;
- if (parent == null) {
- continue;
- }
-
- // 计算当前提交与上次提交之间的差异
- List<DiffEntry> diffs = diffFormatter.scan(parent.getTree(), commit.getTree());
- for (DiffEntry diff : diffs) {
- String newPath = diff.getNewPath();
- String oldPath = diff.getOldPath();
-
- // 检查是否匹配目标路径
- for (Map.Entry<String, String> entry : pathMapping.entrySet()) {
- String key = entry.getKey();
- String path = entry.getValue();
- if (T.BooleanUtil.and(
- (newPath.startsWith(path) || oldPath.startsWith(path)),
- !result.containsKey(key)
- )) {
- result.put(key, commit);
- }
- }
- }
-
- // 如果所有路径都找到对应的最后提交,提前结束循环
- if (result.size() == pathMapping.size()) {
- break;
- }
- }
- } catch (IOException | GitAPIException e) {
- log.error(e, "[getLastCommitIdDataByPath] [workspace: {}] [branch: {}]", repository.getDirectory().getPath(), branch);
- }
- return result;
- }
-
- /**
- * commit
- *
- * @param repo
- * @param branch
- * @param treeId
- * @param message
- * @throws IOException
- */
- public void createCommit(Repository repo, String branch, ObjectId treeId, String message) throws IOException, ConcurrentRefUpdateException {
- try (ObjectInserter inserter = repo.getObjectDatabase().newInserter();
- RevWalk revWalk = new RevWalk(repo);) {
-
- CommitBuilder builder = new CommitBuilder();
- builder.setTreeId(treeId);
- builder.setMessage(message);
-
- ObjectId branchRef = repo.resolve(branch);
- if (null != branchRef) {
- RevCommit parentId = revWalk.parseCommit(branchRef);
- builder.setParentId(parentId);
- }
-
- SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
- builder.setAuthor(new PersonIdent(loginUserEntity.getName(), "[email protected]"));
- builder.setCommitter(new PersonIdent(loginUserEntity.getName(), "[email protected]"));
-
- // 插入新的提交对象
- ObjectId commitId = inserter.insert(builder);
- inserter.flush();
-
- // 更新 branch 指向新的提交
- RefUpdate ru = null;
- RefUpdate.Result rc = null;
- if (null != branchRef) {
- ru = repo.updateRef(T.StrUtil.concat(true, LOCAL_BRANCH_PREFIX, branch));
- ru.setNewObjectId(commitId);
- rc = ru.update();
- } else {
- ru = repo.updateRef(Constants.HEAD);
- ru.setNewObjectId(commitId);
- rc = ru.update();
- }
- switch (rc) {
- case NEW:
- case FORCED:
- case FAST_FORWARD:
- break;
- case REJECTED:
- case LOCK_FAILURE:
- throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
- default:
- throw new JGitInternalException(MessageFormat.format(
- JGitText.get().updatingRefFailed, branch, commitId.toString(),
- rc));
- }
- }
- }
-
- /**
- * commit index
- *
- * @param repo
- * @param branch
- * @param index
- * @param parentId1
- * @param parentId2
- * @param message
- * @return
- * @throws IOException
- * @throws ConcurrentRefUpdateException
- */
- @Override
- public boolean commitIndex(Repository repo, String branch, DirCache index, ObjectId parentId1, ObjectId parentId2, String message) throws IOException, ConcurrentRefUpdateException {
- boolean success = false;
- try (ObjectInserter odi = repo.newObjectInserter()) {
-
- // new index
- ObjectId indexTreeId = index.writeTree(odi);
-
- // PersonIdent
- SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
- PersonIdent ident = new PersonIdent(loginUserEntity.getName(), "[email protected]");
-
- CommitBuilder commit = new CommitBuilder();
- commit.setAuthor(ident);
- commit.setCommitter(ident);
-
- if (null != parentId1 && null == parentId2) {
- commit.setParentId(parentId1);
- }
- if (null != parentId1 && null != parentId2) {
- commit.setParentIds(parentId1, parentId2);
- }
-
- commit.setTreeId(indexTreeId);
- commit.setMessage(message);
-
-
- // insert the commit into the repository
- ObjectId commitId = odi.insert(commit);
- odi.flush();
-
- RefUpdate ru = repo.updateRef(T.StrUtil.concat(true, LOCAL_BRANCH_PREFIX, branch));
- ru.setNewObjectId(commitId);
- RefUpdate.Result rc = ru.update();
- switch (rc) {
- case NEW:
- case FORCED:
- case FAST_FORWARD:
- success = true;
- break;
- case REJECTED:
- case LOCK_FAILURE:
- throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
- default:
- throw new JGitInternalException(MessageFormat.format(
- JGitText.get().updatingRefFailed, branch, commitId.toString(),
- rc));
- }
- }
- return success;
- }
-
- /**
- * build asw commit info
- *
- * @param commit
- * @return
- */
- private Map<Object, Object> buildAswCommitInfo(RevCommit commit) {
- if (null == commit) {
- return T.MapUtil.newHashMap();
- }
-
- Map<Object, Object> m = new LinkedHashMap<>();
- m.put("id", commit.getName());
- m.put("shortId", T.StrUtil.subPre(commit.getName(), 8));
- m.put("createdAt", TimeUnit.SECONDS.toMillis(commit.getCommitTime()));
-
- m.put("title", commit.getShortMessage());
- m.put("message", commit.getFullMessage());
-
- List<String> parentIds = Arrays.stream(commit.getParents()).map(RevCommit::getName).collect(Collectors.toList());
- m.put("parentIds", parentIds);
-
- PersonIdent authorIdent = commit.getAuthorIdent();
- m.put("authorName", authorIdent.getName());
- m.put("authorEmail", authorIdent.getEmailAddress());
- m.put("authoredDate", authorIdent.getWhen().getTime());
-
- PersonIdent committerIdent = commit.getCommitterIdent();
- m.put("committerName", committerIdent.getName());
- m.put("committerEmail", committerIdent.getEmailAddress());
- m.put("committedDate", committerIdent.getWhen().getTime());
- return m;
- }
-
-} \ No newline at end of file
diff --git a/src/main/java/net/geedge/asw/module/app/util/JGitUtils.java b/src/main/java/net/geedge/asw/module/app/util/JGitUtils.java
new file mode 100644
index 0000000..08bd526
--- /dev/null
+++ b/src/main/java/net/geedge/asw/module/app/util/JGitUtils.java
@@ -0,0 +1,820 @@
+package net.geedge.asw.module.app.util;
+
+import cn.hutool.log.Log;
+import net.geedge.asw.common.util.ASWException;
+import net.geedge.asw.common.util.RCode;
+import net.geedge.asw.common.util.T;
+import org.apache.commons.io.FilenameUtils;
+import org.eclipse.jgit.api.*;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.diff.*;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.*;
+import org.eclipse.jgit.merge.MergeFormatter;
+import org.eclipse.jgit.merge.MergeStrategy;
+import org.eclipse.jgit.merge.RecursiveMerger;
+import org.eclipse.jgit.merge.ThreeWayMerger;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.revwalk.filter.RevFilter;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Component;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Component
+public class JGitUtils {
+
+ private final static Log log = Log.get();
+ /**
+ * 本地分支引用前缀
+ */
+ public static final String LOCAL_BRANCH_PREFIX = "refs/heads/";
+
+ /**
+ * 默认分支
+ */
+ public static final String DEFAULT_BRANCH = "main";
+
+ private static String textExtensions;
+
+ @Autowired
+ public JGitUtils(Environment environment) {
+ textExtensions = environment.getProperty("file.extensions.text", "txt,csv,md,html,xml,json,log,bat,py,sh,ini,conf,yaml,yml,properties,toml,java,c,cpp,js,php,ts,go,rb,rtf,tex,rss,xhtml,sql");
+ }
+
+ /**
+ * 返回 git repository
+ *
+ * @param gitDir
+ * @return
+ * @throws IOException
+ */
+ public static Repository openRepository(File gitDir) throws IOException {
+ FileRepositoryBuilder builder = new FileRepositoryBuilder();
+ Repository repository = builder.setGitDir(gitDir)
+ .readEnvironment()
+ .findGitDir()
+ .build();
+ return repository;
+ }
+
+ /**
+ * 初始化仓库
+ *
+ * @param repoDir
+ * @param author
+ */
+ public static void initRepository(File repoDir, String author) {
+ try (
+ Git git = Git.init()
+ .setBare(true)
+ .setDirectory(repoDir)
+ .setInitialBranch(DEFAULT_BRANCH)
+ .call();
+ Repository repository = git.getRepository();
+ ObjectInserter inserter = repository.getObjectDatabase().newInserter();
+ ) {
+ DirCache dirCache = DirCache.newInCore();
+ DirCacheBuilder builder = dirCache.builder();
+
+ ObjectId objectId = JGitUtils.insertBlobFileToDatabase(repository, "".getBytes());
+ DirCacheEntry entry = JGitUtils.buildDirCacheEntry("README.md", FileMode.REGULAR_FILE, objectId);
+ builder.add(entry);
+ builder.finish();
+
+ // commit
+ String message = "Initial commit";
+ PersonIdent personIdent = new PersonIdent(author, "[email protected]");
+
+ JGitUtils.createCommit(repository, DEFAULT_BRANCH, dirCache.writeTree(inserter), message, personIdent);
+ } catch (GitAPIException | IOException e) {
+ log.error(e, "[initRepository] [git init error]");
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 返回分支是否存在
+ *
+ * @param repository
+ * @param branch
+ * @return
+ * @throws IOException
+ */
+ public static boolean isBranchExists(Repository repository, String branch) throws IOException {
+ Ref ref = repository.findRef(T.StrUtil.concat(true, LOCAL_BRANCH_PREFIX, branch));
+ return null != ref;
+ }
+
+ /**
+ * 获取提交对象
+ *
+ * @param repository
+ * @param commitId
+ * @return
+ * @throws IOException
+ */
+ public static RevCommit infoCommit(Repository repository, String commitId) throws IOException {
+ try (RevWalk revCommits = new RevWalk(repository);) {
+ RevCommit commit = revCommits.parseCommit(ObjectId.fromString(commitId));
+ return commit;
+ }
+ }
+
+
+ /**
+ * 返回分支最新提交
+ *
+ * @param repository
+ * @param branch
+ * @return
+ */
+ public static RevCommit getBranchLatestCommit(Repository repository, String branch) throws IOException {
+ RevWalk revWalk = new RevWalk(repository);
+ RevCommit commit = revWalk.parseCommit(repository.resolve(branch));
+ return commit;
+ }
+
+ /**
+ * 获取分支提交记录
+ *
+ * @param repository
+ * @param branch
+ * @param path
+ * @return
+ * @throws GitAPIException
+ * @throws IOException
+ */
+ public static List<RevCommit> getBranchCommitList(Repository repository, String branch, String path) throws GitAPIException, IOException {
+ List<RevCommit> resultList = T.ListUtil.list(true);
+ Git git = Git.open(repository.getDirectory());
+ ObjectId branchRef = git.getRepository().resolve(branch);
+ LogCommand command = git.log().add(branchRef);
+ if (T.StrUtil.isNotEmpty(path)) {
+ command.addPath(path);
+ }
+
+ Iterable<RevCommit> iterable = command.call();
+ for (RevCommit commit : iterable) {
+ resultList.add(commit);
+ }
+ return resultList;
+ }
+
+ /**
+ * 获取父提交
+ *
+ * @param repository
+ * @param branch
+ * @param commitId
+ * @return
+ * @throws IOException
+ */
+ public static RevCommit getParentCommit(Repository repository, String branch, String commitId) throws IOException {
+ try (RevWalk revWalk = new RevWalk(repository)) {
+ Ref branchRef = repository.findRef(branch);
+ revWalk.markStart(revWalk.parseCommit(branchRef.getObjectId()));
+
+ RevCommit parentCommit = null;
+ Iterator<RevCommit> iterator = revWalk.iterator();
+ while (iterator.hasNext()) {
+ RevCommit currentCommit = iterator.next();
+ if (currentCommit.getId().getName().equals(commitId)) {
+ if (currentCommit.getParentCount() > 0) {
+ parentCommit = currentCommit.getParent(0);
+ }
+ break;
+ }
+ }
+
+ if (null == parentCommit) {
+ throw new ASWException(RCode.GIT_PARENT_COMMITID_NOT_FOUND);
+ }
+ return parentCommit;
+ }
+ }
+
+ /**
+ * Returns the merge base of two commits or null if there is no common ancestry.
+ * 返回两个提交的合并基础,如果没有共同祖先,则返回 null。
+ *
+ * @param repository
+ * @param commitIdA
+ * @param commitIdB
+ * @return the commit id of the merge base or null if there is no common base
+ */
+ public static RevCommit getMergeBaseCommit(Repository repository, ObjectId commitIdA, ObjectId commitIdB) {
+ try (RevWalk rw = new RevWalk(repository)) {
+ RevCommit a = rw.lookupCommit(commitIdA);
+ RevCommit b = rw.lookupCommit(commitIdB);
+
+ rw.setRevFilter(RevFilter.MERGE_BASE);
+ rw.markStart(a);
+ rw.markStart(b);
+ RevCommit mergeBase = rw.next();
+ if (mergeBase == null) {
+ return null;
+ }
+ return mergeBase;
+ } catch (Exception e) {
+ log.error(e, "Failed to determine merge base");
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * commit range
+ * 沿着 parent(0) 的主路径提交列表,忽略所有分支路径的提交
+ *
+ * @param repository
+ * @param newCommitId
+ * @param oldCommitId
+ * @return
+ * @throws IOException
+ */
+ public static List<RevCommit> listCommitRange(Repository repository, String newCommitId, String oldCommitId) throws IOException {
+ log.info("[listCommitRange] [begin] [repository: {}] [newCommitId: {}] [oldCommitId: {}]", repository, newCommitId, oldCommitId);
+ List<RevCommit> commitList = T.ListUtil.list(true);
+
+ try (RevWalk revWalk = new RevWalk(repository)) {
+ RevCommit revCommit = revWalk.parseCommit(repository.resolve(newCommitId));
+
+ Set<ObjectId> visitedCommits = new HashSet<>();
+ while (revCommit != null && !visitedCommits.contains(revCommit.getId())) {
+ if (oldCommitId != null && revCommit.getId().getName().equals(oldCommitId)) {
+ break;
+ }
+
+ commitList.add(revCommit);
+ visitedCommits.add(revCommit.getId());
+
+ // 沿着 parent(0) 前进
+ if (revCommit.getParentCount() > 0) {
+ revCommit = revWalk.parseCommit(revCommit.getParent(0));
+ } else {
+ revCommit = null;
+ }
+ }
+ } finally {
+ log.info("[listCommitRange] [finshed] [repository: {}] [commits size: {}]", repository, commitList.size());
+ }
+ return commitList;
+ }
+
+ /**
+ * 构建 DirCacheEntry
+ *
+ * @param path
+ * @param mode
+ * @param objectId
+ * @return
+ */
+ public static DirCacheEntry buildDirCacheEntry(String path, FileMode mode, ObjectId objectId) {
+ DirCacheEntry dirCacheEntry = new DirCacheEntry(path);
+ dirCacheEntry.setFileMode(mode);
+ dirCacheEntry.setObjectId(objectId);
+ return dirCacheEntry;
+ }
+
+ public static PersonIdent buildPersonIdent(String name) {
+ return buildPersonIdent(name, "[email protected]");
+ }
+
+ public static PersonIdent buildPersonIdent(String name, String email) {
+ return new PersonIdent(name, email);
+ }
+
+ /**
+ * 将 file object 添加到 objectDatabases 并返回 ObjectId
+ *
+ * @param repository
+ * @param data
+ * @return
+ * @throws IOException
+ */
+ public static ObjectId insertBlobFileToDatabase(Repository repository, byte[] data) throws IOException {
+ try (ObjectInserter inserter = repository.getObjectDatabase().newInserter()) {
+ ObjectId blobId = inserter.insert(Constants.OBJ_BLOB, data);
+ inserter.flush();
+ return blobId;
+ }
+ }
+
+ /**
+ * commit
+ *
+ * @param repository
+ * @param branch
+ * @param treeId
+ * @param message
+ * @param personIdent
+ * @throws IOException
+ * @throws ConcurrentRefUpdateException
+ */
+ public static void createCommit(Repository repository, String branch, ObjectId treeId, String message, PersonIdent personIdent) throws IOException, ConcurrentRefUpdateException {
+ try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
+ RevWalk revWalk = new RevWalk(repository);) {
+
+ CommitBuilder builder = new CommitBuilder();
+ builder.setTreeId(treeId);
+ builder.setMessage(message);
+
+ ObjectId branchRef = repository.resolve(branch);
+ if (null != branchRef) {
+ RevCommit parentId = revWalk.parseCommit(branchRef);
+ builder.setParentId(parentId);
+ }
+
+ builder.setAuthor(personIdent);
+ builder.setCommitter(personIdent);
+
+ // 插入新的提交对象
+ ObjectId commitId = inserter.insert(builder);
+ inserter.flush();
+
+ // 更新 branch 指向新的提交
+ RefUpdate ru = null;
+ RefUpdate.Result rc = null;
+ if (null != branchRef) {
+ ru = repository.updateRef(T.StrUtil.concat(true, LOCAL_BRANCH_PREFIX, branch));
+ ru.setNewObjectId(commitId);
+ rc = ru.update();
+ } else {
+ ru = repository.updateRef(Constants.HEAD);
+ ru.setNewObjectId(commitId);
+ rc = ru.update();
+ }
+ switch (rc) {
+ case NEW:
+ case FORCED:
+ case FAST_FORWARD:
+ break;
+ case REJECTED:
+ case LOCK_FAILURE:
+ throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
+ default:
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().updatingRefFailed, branch, commitId.toString(),
+ rc));
+ }
+ }
+ }
+
+ /**
+ * commit index
+ *
+ * @param repository
+ * @param branch
+ * @param index
+ * @param parentId1
+ * @param parentId2
+ * @param message
+ * @param personIdent
+ * @return
+ * @throws IOException
+ * @throws ConcurrentRefUpdateException
+ */
+ public static boolean commitIndex(Repository repository, String branch, DirCache index, ObjectId parentId1, ObjectId parentId2, String message, PersonIdent personIdent) throws IOException, ConcurrentRefUpdateException {
+ boolean success = false;
+ try (ObjectInserter odi = repository.newObjectInserter()) {
+
+ // new index
+ ObjectId indexTreeId = index.writeTree(odi);
+
+ // PersonIdent
+ CommitBuilder commit = new CommitBuilder();
+ commit.setAuthor(personIdent);
+ commit.setCommitter(personIdent);
+
+ if (null != parentId1 && null == parentId2) {
+ commit.setParentId(parentId1);
+ }
+ if (null != parentId1 && null != parentId2) {
+ commit.setParentIds(parentId1, parentId2);
+ }
+
+ commit.setTreeId(indexTreeId);
+ commit.setMessage(message);
+
+
+ // insert the commit into the repository
+ ObjectId commitId = odi.insert(commit);
+ odi.flush();
+
+ RefUpdate ru = repository.updateRef(T.StrUtil.concat(true, JGitUtils.LOCAL_BRANCH_PREFIX, branch));
+ ru.setNewObjectId(commitId);
+ RefUpdate.Result rc = ru.update();
+ switch (rc) {
+ case NEW:
+ case FORCED:
+ case FAST_FORWARD:
+ success = true;
+ break;
+ case REJECTED:
+ case LOCK_FAILURE:
+ throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
+ default:
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().updatingRefFailed, branch, commitId.toString(),
+ rc));
+ }
+ }
+ return success;
+ }
+
+
+ /**
+ * 获取 commitIdA -> commitIdB 文件差异
+ *
+ * @param repository
+ * @param newCommitId
+ * @param oldCommitId
+ * @return
+ * @throws IOException
+ */
+ public static List<Map<Object, Object>> getDiffFileListInCommits(Repository repository, String newCommitId, String oldCommitId) throws IOException {
+ log.info("[getDiffFileListInCommits] [begin] [repository: {}] [newCommitId: {}] [oldCommitId: {}]", repository, newCommitId, oldCommitId);
+ try (RevWalk revWalk = new RevWalk(repository);
+ DiffFormatter diffFormatter = new DiffFormatter(null);
+ ) {
+
+ RevCommit oldCommit = revWalk.parseCommit(repository.resolve(oldCommitId));
+ RevCommit newCommit = revWalk.parseCommit(repository.resolve(newCommitId));
+
+ // oldTree
+ CanonicalTreeParser oldTree = new CanonicalTreeParser();
+ oldTree.reset(repository.newObjectReader(), oldCommit.getTree());
+ // newTree
+ CanonicalTreeParser newTree = new CanonicalTreeParser();
+ newTree.reset(repository.newObjectReader(), newCommit.getTree());
+
+ // diff
+ List<Map<Object, Object>> files = T.ListUtil.list(true);
+
+ diffFormatter.setRepository(repository);
+ diffFormatter.setDiffComparator(RawTextComparator.DEFAULT);
+ diffFormatter.setDetectRenames(true);
+
+ List<DiffEntry> diffs = diffFormatter.scan(oldTree, newTree);
+ for (DiffEntry diff : diffs) {
+ int addedLines = 0;
+ int deletedLines = 0;
+ EditList edits = diffFormatter.toFileHeader(diff).toEditList();
+ for (Edit edit : edits) {
+ switch (edit.getType()) {
+ case INSERT:
+ addedLines += edit.getLengthB();
+ break;
+ case DELETE:
+ deletedLines += edit.getLengthA();
+ break;
+ case REPLACE:
+ addedLines += edit.getLengthB();
+ deletedLines += edit.getLengthA();
+ break;
+ default:
+ break;
+ }
+ }
+
+ String oldPath = diff.getOldPath(), newPath = diff.getNewPath(), encoding = null, oldContent = null, newContent = null;
+ switch (diff.getChangeType()) {
+ case COPY:
+ case ADD: {
+ Map<Object, Object> fileContent = getFileContent(repository, newPath, diff.getNewId().toObjectId());
+ encoding = T.MapUtil.getStr(fileContent, "encoding", "");
+ newContent = T.MapUtil.getStr(fileContent, "content", "");
+ break;
+ }
+ case DELETE: {
+ Map<Object, Object> fileContent = getFileContent(repository, oldPath, diff.getOldId().toObjectId());
+ encoding = T.MapUtil.getStr(fileContent, "encoding", "");
+ oldContent = T.MapUtil.getStr(fileContent, "content", "");
+ break;
+ }
+ case MODIFY: {
+ Map<Object, Object> fileContent = getFileContent(repository, oldPath, diff.getOldId().toObjectId());
+ oldContent = T.MapUtil.getStr(fileContent, "content", "");
+
+ Map<Object, Object> fileContent1 = getFileContent(repository, newPath, diff.getNewId().toObjectId());
+ encoding = T.MapUtil.getStr(fileContent1, "encoding", "");
+ newContent = T.MapUtil.getStr(fileContent1, "content", "");
+ break;
+ }
+ case RENAME: {
+ break;
+ }
+ default:
+ break;
+ }
+ files.add(
+ T.MapUtil.builder()
+ .put("oldPath", oldPath)
+ .put("newPath", newPath)
+ .put("addedLines", addedLines)
+ .put("removedLines", deletedLines)
+ .put("encoding", encoding)
+ .put("oldContent", oldContent)
+ .put("newContent", newContent)
+ .put("action", diff.getChangeType().name().toLowerCase())
+ .build()
+ );
+ }
+ return files;
+ }
+ }
+
+
+ /**
+ * 获取 sourceBranch,targetBranch 冲突文件路径
+ *
+ * @param repository
+ * @param srcBranch
+ * @param tgtBranch
+ * @return
+ * @throws IOException
+ */
+ public static List<String> getConflictFilePathInBranches(Repository repository, String srcBranch, String tgtBranch) throws IOException {
+ log.info("[getConflictFileListInBranches] [begin] [repository: {}] [srcBranch: {}] [tgtBranch: {}]", repository, srcBranch, tgtBranch);
+ try (RevWalk revWalk = new RevWalk(repository)) {
+
+ RevCommit commitA = revWalk.parseCommit(repository.resolve(srcBranch));
+ RevCommit commitB = revWalk.parseCommit(repository.resolve(tgtBranch));
+
+ // Find the merge base
+ RevCommit mergeBaseCommit = JGitUtils.getMergeBaseCommit(repository, commitA, commitB);
+ String mergeBaseId = mergeBaseCommit.getName();
+ log.info("[getConflictFileListInBranches] [mergeBase: {}]", mergeBaseId);
+
+ RevCommit mergeBase = revWalk.parseCommit(repository.resolve(mergeBaseId));
+
+ ThreeWayMerger threeWayMerger = (ThreeWayMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
+ threeWayMerger.setBase(mergeBase);
+
+ boolean isOk = threeWayMerger.merge(commitA, commitB);
+ log.info("[getConflictFileListInBranches] [merge result] [isOk: {}]", isOk);
+ if (!isOk) {
+ List<String> unmergedPaths = ((RecursiveMerger) threeWayMerger).getUnmergedPaths();
+ return unmergedPaths;
+ }
+ return T.ListUtil.empty();
+ }
+ }
+
+ /**
+ * 获取 sourceBranch,targetBranch 冲突文件详情
+ *
+ * @param repository
+ * @param srcBranch
+ * @param tgtBranch
+ * @return
+ * @throws IOException
+ */
+ public static List<Map<Object, Object>> getConflictFileInfoInBranches(Repository repository, String srcBranch, String tgtBranch) throws IOException {
+ log.info("[getConflictFileInfoInBranches] [begin] [repository: {}] [srcBranch: {}] [tgtBranch: {}]", repository, srcBranch, tgtBranch);
+ try (RevWalk revWalk = new RevWalk(repository);) {
+
+ RevCommit commitA = revWalk.parseCommit(repository.resolve(srcBranch));
+ RevCommit commitB = revWalk.parseCommit(repository.resolve(tgtBranch));
+
+ // Find the merge base
+ String mergeBaseId = getMergeBaseCommit(repository, commitA, commitB).getName();
+ log.info("[getConflictFileInfoInBranches] [mergeBase: {}]", mergeBaseId);
+
+ RevCommit mergeBase = revWalk.parseCommit(repository.resolve(mergeBaseId));
+
+ ThreeWayMerger threeWayMerger = (ThreeWayMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
+ threeWayMerger.setBase(mergeBase);
+
+ boolean isOk = threeWayMerger.merge(commitA, commitB);
+ log.info("[getConflictFileInfoInBranches] [merge result] [isOk: {}]", isOk);
+
+ if (!isOk) {
+ List<Map<Object, Object>> files = T.ListUtil.list(true);
+
+ Map<String, org.eclipse.jgit.merge.MergeResult<? extends Sequence>> mergeResults = ((RecursiveMerger) threeWayMerger).getMergeResults();
+ for (Map.Entry<String, org.eclipse.jgit.merge.MergeResult<? extends Sequence>> entry : mergeResults.entrySet()) {
+ String unmergedPath = entry.getKey();
+ // 暂不支持处理二进制文件冲突
+ if (isBinary(T.FileNameUtil.getName(unmergedPath))) {
+ throw new ASWException(RCode.GIT_BINARY_CONFLICT_ERROR);
+ }
+
+ org.eclipse.jgit.merge.MergeResult<? extends Sequence> mergeResult = entry.getValue();
+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+ MergeFormatter formatter = new MergeFormatter();
+
+ String oursName = unmergedPath;
+ String theirsName = unmergedPath;
+ formatter.formatMerge(outputStream, mergeResult, mergeBaseId, oursName, theirsName, StandardCharsets.UTF_8);
+ files.add(
+ T.MapUtil.builder()
+ .put("path", unmergedPath)
+ .put("content", outputStream.toString(StandardCharsets.UTF_8.name()))
+ .put("conflict", true)
+ .build()
+ );
+ }
+ }
+ return files;
+ }
+ return T.ListUtil.empty();
+ }
+ }
+
+ /**
+ * 分支合并
+ *
+ * @param centralRepository
+ * @param srcBranch
+ * @param tgtBranch
+ * @param author
+ * @param message
+ * @param resolveConflictFileContent
+ * @throws RuntimeException
+ * @throws GitAPIException
+ * @throws IOException
+ */
+ public static void mergeBranch(Repository centralRepository, String srcBranch, String tgtBranch, String author, String message, List<Map<String, String>> resolveConflictFileContent) throws RuntimeException, GitAPIException, IOException {
+ log.info("[mergeBranch] [begin] [repository: {}] [srcBranch: {}] [tgtBranch: {}]", centralRepository, srcBranch, tgtBranch);
+ // prepare a new folder for the cloned repository
+ File localPath = T.FileUtil.file(net.geedge.asw.common.util.Constants.TEMP_PATH, T.StrUtil.uuid());
+ T.FileUtil.del(localPath);
+ T.FileUtil.mkdir(localPath);
+
+ // bare repository
+ File repoDir = centralRepository.getDirectory();
+
+ // clone
+ try (Git git = Git.cloneRepository()
+ .setBare(false)
+ .setURI(repoDir.getAbsolutePath())
+ .setDirectory(localPath)
+ .setCredentialsProvider(null)
+ .call();
+ Repository repository = git.getRepository();) {
+ StoredConfig config = repository.getConfig();
+ config.setString("user", null, "name", author);
+ config.setString("user", null, "email", "[email protected]");
+ config.save();
+
+ // git fetch
+ git.fetch().call();
+
+ // checout
+ git.checkout()
+ .setCreateBranch(T.StrUtil.equals(DEFAULT_BRANCH, tgtBranch) ? false : true)
+ .setName(tgtBranch)
+ .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
+ .setStartPoint("origin/" + tgtBranch)
+ .call();
+
+ // merge
+ MergeResult mergeResult = git.merge()
+ .setCommit(true)
+ .setMessage(message)
+ .setStrategy(MergeStrategy.RECURSIVE)
+ .setFastForward(MergeCommand.FastForwardMode.NO_FF)
+ .include(repository.findRef("origin/" + srcBranch))
+ .call();
+
+ MergeResult.MergeStatus mergeStatus = mergeResult.getMergeStatus();
+ log.info("[mergeBranch] [merge status: {}]", mergeStatus);
+ if (!mergeStatus.isSuccessful()) {
+ // 解决冲突
+ if (mergeStatus == MergeResult.MergeStatus.CONFLICTING && T.CollUtil.isNotEmpty(resolveConflictFileContent)) {
+ Map<String, int[][]> conflicts = mergeResult.getConflicts();
+ for (Map.Entry<String, int[][]> entry : conflicts.entrySet()) {
+ String conflictFilePath = entry.getKey();
+ Map<String, String> map = resolveConflictFileContent.stream()
+ .filter(m -> T.StrUtil.equals(conflictFilePath, T.MapUtil.getStr(m, "path")))
+ .findFirst()
+ .orElse(null);
+ if (null != map) {
+ Path filePath = Paths.get(localPath.getAbsolutePath(), conflictFilePath);
+ Files.write(filePath, T.MapUtil.getStr(map, "content").getBytes(StandardCharsets.UTF_8));
+ git.add().addFilepattern(conflictFilePath).call();
+ }
+ }
+ git.commit().setMessage(message).call();
+ } else {
+ // 其他类型的合并错误,抛出异常
+ String errorMessage = String.format("Merge failed: %s, Conflicts: %s", mergeStatus, mergeResult.getConflicts());
+ throw new RuntimeException(errorMessage);
+ }
+ }
+
+ // push
+ Iterable<PushResult> pushResultIterable = git.push()
+ .setRemote("origin")
+ .add(tgtBranch)
+ .call();
+ for (PushResult pushResult : pushResultIterable) {
+ for (RemoteRefUpdate update : pushResult.getRemoteUpdates()) {
+ if (update.getStatus() != RemoteRefUpdate.Status.OK && update.getStatus() != RemoteRefUpdate.Status.UP_TO_DATE) {
+ log.error("[mergeBranch] [push error] [remote: {}] [status: {}]", update.getRemoteName(), update.getStatus());
+ String errorMessage = "Push failed: " + update.getStatus();
+ throw new RuntimeException(errorMessage);
+ }
+ }
+ }
+ } finally {
+ T.FileUtil.del(localPath);
+ }
+ }
+
+ /**
+ * build asw commit info
+ *
+ * @param commit
+ * @return
+ */
+ public static Map<Object, Object> buildAswCommitInfo(RevCommit commit) {
+ if (null == commit) {
+ return T.MapUtil.newHashMap();
+ }
+
+ Map<Object, Object> m = new LinkedHashMap<>();
+ m.put("id", commit.getName());
+ m.put("shortId", T.StrUtil.subPre(commit.getName(), 8));
+ m.put("createdAt", TimeUnit.SECONDS.toMillis(commit.getCommitTime()));
+
+ m.put("title", commit.getShortMessage());
+ m.put("message", commit.getFullMessage());
+
+ List<String> parentIds = Arrays.stream(commit.getParents()).map(RevCommit::getName).collect(Collectors.toList());
+ m.put("parentIds", parentIds);
+
+ PersonIdent authorIdent = commit.getAuthorIdent();
+ m.put("authorName", authorIdent.getName());
+ m.put("authorEmail", authorIdent.getEmailAddress());
+ m.put("authoredDate", authorIdent.getWhen().getTime());
+
+ PersonIdent committerIdent = commit.getCommitterIdent();
+ m.put("committerName", committerIdent.getName());
+ m.put("committerEmail", committerIdent.getEmailAddress());
+ m.put("committedDate", committerIdent.getWhen().getTime());
+ return m;
+ }
+
+
+ /**
+ * is binary file
+ *
+ * @param filename
+ * @return
+ */
+ public static boolean isBinary(String filename) {
+ String extension = FilenameUtils.getExtension(filename);
+ List<String> split = T.StrUtil.split(textExtensions, ",");
+ return !split.contains(extension.toLowerCase());
+ }
+
+ /**
+ * 根据 path,objectId 读取文件内容
+ * 响应 Map,key=encoding,content
+ *
+ * @param repository
+ * @param path
+ * @param objectId
+ * @return
+ * @throws IOException
+ */
+ public static Map<Object, Object> getFileContent(Repository repository, String path, ObjectId objectId) throws IOException {
+ String encoding = null, content = null;
+
+ ObjectLoader loader = repository.open(objectId);
+ if (isBinary(T.FileNameUtil.getName(path))) {
+ encoding = "base64";
+ content = Base64.getEncoder().encodeToString(loader.getBytes());
+ } else {
+ content = T.StrUtil.utf8Str(loader.getBytes());
+ }
+ return T.MapUtil.builder()
+ .put("encoding", encoding)
+ .put("content", content)
+ .build();
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/net/geedge/asw/module/workspace/entity/WorkspaceEntity.java b/src/main/java/net/geedge/asw/module/workspace/entity/WorkspaceEntity.java
index 9d1024f..4bb1c43 100644
--- a/src/main/java/net/geedge/asw/module/workspace/entity/WorkspaceEntity.java
+++ b/src/main/java/net/geedge/asw/module/workspace/entity/WorkspaceEntity.java
@@ -5,9 +5,10 @@ import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
-import net.geedge.asw.module.sys.entity.SysRoleEntity;
+import net.geedge.asw.common.util.T;
import net.geedge.asw.module.sys.entity.SysUserEntity;
+import java.io.File;
import java.util.List;
@Data
@@ -36,4 +37,8 @@ public class WorkspaceEntity {
@TableField(exist = false)
private List<WorkspaceMemberEntity> members;
+ public File buildGitPath() {
+ return T.FileUtil.file(T.WebPathUtil.getRootPath(), "workspace", this.getId());
+ }
+
} \ No newline at end of file
diff --git a/src/main/java/net/geedge/asw/module/workspace/service/IWorkspaceService.java b/src/main/java/net/geedge/asw/module/workspace/service/IWorkspaceService.java
index b1a3ec0..c263560 100644
--- a/src/main/java/net/geedge/asw/module/workspace/service/IWorkspaceService.java
+++ b/src/main/java/net/geedge/asw/module/workspace/service/IWorkspaceService.java
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
+import java.io.File;
import java.util.Map;
public interface IWorkspaceService extends IService<WorkspaceEntity>{
@@ -15,4 +16,7 @@ public interface IWorkspaceService extends IService<WorkspaceEntity>{
WorkspaceEntity updateWorkspace(WorkspaceEntity workspace);
void deleteWorkspace(String ids);
+
+ File getGitDir(String workspaceId);
+
} \ No newline at end of file
diff --git a/src/main/java/net/geedge/asw/module/workspace/service/impl/WorkspaceServiceImpl.java b/src/main/java/net/geedge/asw/module/workspace/service/impl/WorkspaceServiceImpl.java
index ebef0ac..e1df771 100644
--- a/src/main/java/net/geedge/asw/module/workspace/service/impl/WorkspaceServiceImpl.java
+++ b/src/main/java/net/geedge/asw/module/workspace/service/impl/WorkspaceServiceImpl.java
@@ -14,10 +14,12 @@ import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
+import net.geedge.asw.module.app.util.JGitUtils;
import net.geedge.asw.module.feign.client.DashboardClient;
import net.geedge.asw.module.feign.client.KibanaClient;
+import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysConfigService;
-import net.geedge.asw.module.app.service.IGitService;
+import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.dao.WorkspaceDao;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.entity.WorkspaceMemberEntity;
@@ -38,9 +40,6 @@ public class WorkspaceServiceImpl extends ServiceImpl<WorkspaceDao, WorkspaceEnt
private final static Log log = Log.get();
@Autowired
- private IGitService gitService;
-
- @Autowired
private IWorkspaceService workspaceService;
@Autowired
@@ -55,6 +54,9 @@ public class WorkspaceServiceImpl extends ServiceImpl<WorkspaceDao, WorkspaceEnt
@Autowired
private ISysConfigService sysConfigService;
+ @Autowired
+ private ISysUserService userService;
+
@Override
public Page queryList(Map<String, Object> params) {
Page page = new Query(WorkspaceEntity.class).getPage(params);
@@ -96,8 +98,8 @@ public class WorkspaceServiceImpl extends ServiceImpl<WorkspaceDao, WorkspaceEnt
}
// init repository
- gitService.initRepository(workspace.getId());
-
+ SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
+ JGitUtils.initRepository(workspace.buildGitPath(), loginUserEntity.getName());
return workspace;
}
@@ -235,6 +237,24 @@ public class WorkspaceServiceImpl extends ServiceImpl<WorkspaceDao, WorkspaceEnt
}
}
+ /**
+ * get repository path
+ * path= {webRootPath}/workspace/{workspace.id}
+ */
+ @Override
+ public File getGitDir(String workspaceId) {
+ WorkspaceEntity workspace = workspaceService.getById(workspaceId);
+ File gitDir = workspace.buildGitPath();
+
+ // 目录不存在,初始化裸仓库
+ if (!gitDir.exists()) {
+ log.info("[getGitDir] [dir not exist] [init new repository] [path: {}]", gitDir);
+ SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
+ JGitUtils.initRepository(gitDir, loginUserEntity.getName());
+ }
+ return gitDir;
+ }
+
private void validateWorkspaceInfo(WorkspaceEntity workspace, boolean isUpdate) {
if (!T.StrUtil.equalsAnyIgnoreCase(workspace.getVisibility(), "private", "public")) {