feat: ASW-123 新增 application MR diff,conflict,commit 接口

1. 参考 gitlab 实现 merge request 功能
This commit is contained in:
shizhendong
2024-11-01 18:26:05 +08:00
parent 7decd03eaa
commit 54bcd63e33
7 changed files with 932 additions and 102 deletions

View File

@@ -62,6 +62,8 @@ public enum RCode {
// GIT // GIT
GIT_COMMIT_CONFLICT_ERROR(203001, "Commit failed; fix conflicts and then commit the result"), GIT_COMMIT_CONFLICT_ERROR(203001, "Commit failed; fix conflicts and then commit the result"),
GIT_MERGE_FAILED(203002, "Merge failed"), GIT_MERGE_FAILED(203002, "Merge failed"),
GIT_PARENT_COMMITID_NOT_FOUND(203003, "Parent commitId not found"),
GIT_BINARY_CONFLICT_ERROR(203004, "Binary file conflict found; resolve conflicts in binary files manually"),
// Runner // Runner

View File

@@ -11,6 +11,7 @@ import net.geedge.asw.module.app.service.IGitService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -56,6 +57,46 @@ public class GitController {
return R.ok(); 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);
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);
}
@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 commitId,
@PathVariable(value = "oldCommitId", required = false) String oldCommitId) {
if (T.StrUtil.isEmpty(oldCommitId)) {
// oldCommitId 为空默认对比 parent commitId
oldCommitId = gitService.getParentCommitId(workspaceId, branchName, commitId);
}
List<Map<Object, Object>> diffList = gitService.getDiffFileListInCommits(workspaceId, commitId, oldCommitId);
Map<Object, Object> record = T.MapUtil.builder()
.put("oldBranch", branchName)
.put("newBranch", branchName)
.put("oldCommitId", oldCommitId)
.put("newCommitId", commitId)
.put("files", diffList)
.build();
return R.ok().putData("record", record);
}
@GetMapping("/{workspaceId}/branch/{branchName}/application") @GetMapping("/{workspaceId}/branch/{branchName}/application")
public R listApplication(@PathVariable("workspaceId") String workspaceId, public R listApplication(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName, @PathVariable("branchName") String branchName,
@@ -153,8 +194,52 @@ public class GitController {
return R.ok(page); 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") @PostMapping("/{workspaceId}/mr")
public synchronized R newMr(@PathVariable("workspaceId") String workspaceId, @RequestBody ApplicationMergeEntity entity) { public R newMr(@PathVariable("workspaceId") String workspaceId, @RequestBody ApplicationMergeEntity entity) {
T.VerifyUtil.is(entity).notNull() T.VerifyUtil.is(entity).notNull()
.and(entity.getTitle()).notEmpty(RCode.PARAM_CANNOT_EMPTY) .and(entity.getTitle()).notEmpty(RCode.PARAM_CANNOT_EMPTY)
.and(entity.getTargetBranch()).notEmpty(RCode.PARAM_CANNOT_EMPTY) .and(entity.getTargetBranch()).notEmpty(RCode.PARAM_CANNOT_EMPTY)
@@ -164,6 +249,12 @@ public class GitController {
entity.setWorkspaceId(workspaceId); entity.setWorkspaceId(workspaceId);
ApplicationMergeEntity record = applicationMergeService.newMr(entity); ApplicationMergeEntity record = applicationMergeService.newMr(entity);
return R.ok().putData("record", record);
}
@PutMapping("/{workspaceId}/mr/{mrId}")
public synchronized R mergeMr(@PathVariable("mrId") String mrId) {
ApplicationMergeEntity record = applicationMergeService.mergeMr(mrId);
if (T.StrUtil.equals("success", record.getStatus())) { if (T.StrUtil.equals("success", record.getStatus())) {
return R.ok().putData("record", record); return R.ok().putData("record", record);
} }

View File

@@ -4,12 +4,25 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationMergeEntity; import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
import java.util.List;
import java.util.Map; import java.util.Map;
public interface IApplicationMergeService extends IService<ApplicationMergeEntity> { public interface IApplicationMergeService extends IService<ApplicationMergeEntity> {
ApplicationMergeEntity queryInfo(String mrId);
Page queryList(Map<String, Object> params); Page queryList(Map<String, Object> params);
ApplicationMergeEntity newMr(ApplicationMergeEntity entity); ApplicationMergeEntity newMr(ApplicationMergeEntity entity);
List<Map<Object, Object>> listMrCommit(String mrId);
Map<Object, Object> mrCommitDiff(String mrId);
Map<Object, Object> getMrConflictList(String mrId);
void resolveMrConflicts(String mrId, Map<String, Object> body);
ApplicationMergeEntity mergeMr(String mrId);
} }

View File

@@ -1,7 +1,11 @@
package net.geedge.asw.module.app.service; package net.geedge.asw.module.app.service;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -13,11 +17,21 @@ public interface IGitService {
List<Map<Object, Object>> listBranch(String workspaceId, String search); List<Map<Object, Object>> listBranch(String workspaceId, String search);
Map<Object, Object> infoBranch(String workspaceId, String branchName); Map<Object, Object> infoBranch(String workspaceId, String branch);
Map<Object, Object> newBranch(String workspaceId, String branchName, String ref); Map<Object, Object> newBranch(String workspaceId, String branch, String ref);
void deleteBranch(String workspaceId, String branchName); void deleteBranch(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);
List<Map<Object, Object>> getDiffFileListInCommits(String workspaceId, String commitId, String oldCommitId);
List<Map<Object, Object>> getDiffFileListInBranches(String workspaceId, String srcBranch, String tgtBranch);
String mergeBranch(String workspaceId, String sourceBranch, String targetBranch, String message) throws RuntimeException; String mergeBranch(String workspaceId, String sourceBranch, String targetBranch, String message) throws RuntimeException;
@@ -35,4 +49,14 @@ public interface IGitService {
Map<Object, Object> infoApplicationFileContent(String workspaceId, String branch, String applicationName, String commitId, String file); Map<Object, Object> infoApplicationFileContent(String workspaceId, String branch, String applicationName, String commitId, String file);
List<Map<Object, Object>> listCommitAfterMergeBase(String workspaceId, String srcBranch, String tgtBranch);
List<String> getConflictFilePathInBranches(String workspaceId, String srcBranch, String tgtBranch);
List<Map<Object, Object>> getConflictFileInfoInBranches(String workspaceId, String srcBranch, String tgtBranch);
void resolveMrConflicts(String workspaceId, String srcBranch, String tgtBranch, String commitId, String commitMessage, List<Map<String, String>> files);
boolean commitIndex(Repository repo, String branch, DirCache index, ObjectId parentId1, ObjectId parentId2, String message) throws IOException, ConcurrentRefUpdateException;
} }

View File

@@ -2,9 +2,11 @@ package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.log.Log; import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.config.Query; import net.geedge.asw.common.config.Query;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T; import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationMergeDao; import net.geedge.asw.module.app.dao.ApplicationMergeDao;
import net.geedge.asw.module.app.entity.ApplicationMergeEntity; import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
@@ -15,6 +17,7 @@ import net.geedge.asw.module.sys.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -29,6 +32,25 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
@Autowired @Autowired
private ISysUserService userService; private ISysUserService userService;
@Override
public ApplicationMergeEntity queryInfo(String mrId) {
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
String commitId = entity.getCommitId();
if (T.StrUtil.isNotEmpty(commitId)) {
try {
Map<Object, Object> infoCommit = gitService.infoCommit(entity.getWorkspaceId(), commitId);
entity.setCommit(infoCommit);
} catch (Exception e) {
}
}
SysUserEntity user = userService.getById(entity.getCreateUserId());
entity.setCreateUser(user);
return entity;
}
@Override @Override
public Page queryList(Map<String, Object> params) { public Page queryList(Map<String, Object> params) {
Page page = new Query(ApplicationMergeEntity.class).getPage(params); Page page = new Query(ApplicationMergeEntity.class).getPage(params);
@@ -51,23 +73,155 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
@Override @Override
public ApplicationMergeEntity newMr(ApplicationMergeEntity entity) { public ApplicationMergeEntity newMr(ApplicationMergeEntity entity) {
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
// save
this.save(entity);
SysUserEntity user = userService.getById(entity.getCreateUserId());
entity.setCreateUser(user);
return entity;
}
@Override
public List<Map<Object, Object>> listMrCommit(String mrId) {
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();
// 获取 sourceBranch targetBranch 共同祖先之后, sourceBranch 的提交记录
List<Map<Object, Object>> listCommitAfterMergeBase = gitService.listCommitAfterMergeBase(workspaceId, sourceBranch, targetBranch);
return listCommitAfterMergeBase;
}
@Override
public Map<Object, Object> mrCommitDiff(String mrId) {
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();
// 获取 sourceBranch,targetBranch 合并冲突文件
List<String> conflictFileList = gitService.getConflictFilePathInBranches(workspaceId, sourceBranch, targetBranch);
// 获取 sourceBranch,targetBranch 文件差异
String commitA = gitService.getLatestCommitId(workspaceId, sourceBranch);
String commitB = gitService.getLatestCommitId(workspaceId, targetBranch);
List<Map<Object, Object>> diffFileListInCommits = gitService.getDiffFileListInCommits(workspaceId, commitA, commitB);
diffFileListInCommits.parallelStream()
.forEach(m -> {
T.MapUtil.renameKey(m, "oldContent", "sourceContent");
T.MapUtil.renameKey(m, "newContent", "targetContent");
String path = T.MapUtil.getStr(m, "path");
m.put("conflict", false);
if (conflictFileList.contains(path)) {
m.put("conflict", true);
}
});
Map<Object, Object> m = T.MapUtil.builder()
.put("sourceBranch", sourceBranch)
.put("targetBranch", targetBranch)
.put("sourceCommitId", commitA)
.put("targetCommitId", commitB)
.put("files", diffFileListInCommits)
.build();
return m;
}
@Override
public Map<Object, Object> getMrConflictList(String mrId) {
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");
}
String parentId = gitService.getLatestCommitId(workspaceId, sourceBranch);
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;
}
return T.MapUtil.builder()
.put("sourceBranch", sourceBranch)
.put("targetBranch", targetBranch)
.put("commitId", null)
.put("commitMessage", null)
.put("files", conflictFileInfoInBranches)
.build();
}
/**
* resolve mr conflicts
*
* @param mrId
* @param body
*/
@Override
public void resolveMrConflicts(String mrId, Map<String, Object> body) {
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.resolveMrConflicts(workspaceId, sourceBranch, targetBranch, commitId, commitMessage, files);
}
/**
* merge operation
*
* @param mrId
* @return
*/
@Override
public ApplicationMergeEntity mergeMr(String mrId) {
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
String srcBranch = entity.getSourceBranch(); String srcBranch = entity.getSourceBranch();
String tgtBranch = entity.getTargetBranch(); String tgtBranch = entity.getTargetBranch();
String message = entity.getTitle(); String message = entity.getTitle();
String workspaceId = entity.getWorkspaceId(); String workspaceId = entity.getWorkspaceId();
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
// merge // merge
try { try {
String commitId = gitService.mergeBranch(workspaceId, srcBranch, tgtBranch, message); String commitId = gitService.mergeBranch(workspaceId, srcBranch, tgtBranch, message);
entity.setCommitId(commitId); entity.setCommitId(commitId);
entity.setStatus("success"); entity.setStatus("success");
Map<Object, Object> infoCommit = gitService.infoCommit(workspaceId, commitId);
entity.setCommit(infoCommit);
} catch (Exception e) { } catch (Exception e) {
log.error(e, "[newMr] [merge error] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}] [msg: {}]", workspaceId, srcBranch, tgtBranch, e.getMessage()); log.error(e, "[newMr] [merge error] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}] [msg: {}]", workspaceId, srcBranch, tgtBranch, e.getMessage());
entity.setCommitId(""); entity.setCommitId("");
@@ -85,12 +239,13 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
} }
} }
// save // update
this.save(entity); this.update(new LambdaUpdateWrapper<ApplicationMergeEntity>()
.eq(ApplicationMergeEntity::getId, mrId)
SysUserEntity user = userService.getById(entity.getCreateUserId()); .set(ApplicationMergeEntity::getCommitId, entity.getCommitId())
entity.setCreateUser(user); .set(ApplicationMergeEntity::getStatus, entity.getStatus())
.set(ApplicationMergeEntity::getMessage, entity.getMessage())
);
return entity; return entity;
} }

View File

@@ -12,19 +12,24 @@ import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity; import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceService; import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.eclipse.jgit.api.CreateBranchCommand; import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.MergeCommand;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.diff.*;
import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.*; import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.merge.MergeFormatter;
import org.eclipse.jgit.merge.MergeStrategy; 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.RevCommit;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RemoteRefUpdate; import org.eclipse.jgit.transport.RemoteRefUpdate;
@@ -36,8 +41,11 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -213,6 +221,8 @@ public class GitServiceImpl implements IGitService {
resultList.add(m); resultList.add(m);
} }
revCommits.close();
revCommits.dispose();
} catch (GitAPIException | IOException e) { } catch (GitAPIException | IOException e) {
log.error(e, "[listBranch] [error] [workspaceId: {}]", workspaceId); log.error(e, "[listBranch] [error] [workspaceId: {}]", workspaceId);
throw new ASWException(RCode.ERROR); throw new ASWException(RCode.ERROR);
@@ -259,6 +269,198 @@ public class GitServiceImpl implements IGitService {
} }
} }
@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 (iterator.hasNext()) {
parentCommitId = iterator.next().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);
}
}
/**
* 获取 commitIdA -> commitIdB 文件差异
*
* @param workspaceId
* @param commitIdA
* @param commitIdB
* @return
*/
@Override
public List<Map<Object, Object>> getDiffFileListInCommits(String workspaceId, String commitIdA, String commitIdB) {
try (Git git = this.getGitInstance(workspaceId);
Repository repository = git.getRepository();
RevWalk revWalk = new RevWalk(repository);
DiffFormatter diffFormatter = new DiffFormatter(null)) {
CanonicalTreeParser oldTree = new CanonicalTreeParser();
oldTree.reset(repository.newObjectReader(), revWalk.parseCommit(repository.resolve(commitIdB)).getTree());
CanonicalTreeParser newTree = new CanonicalTreeParser();
newTree.reset(repository.newObjectReader(), revWalk.parseCommit(repository.resolve(commitIdA)).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 path = null, encoding = null, oldContent = null, newContent = null;
switch (diff.getChangeType()) {
case ADD: {
path = diff.getNewPath();
Map<Object, Object> fileContent = this.getFileContent(repository, path, diff.getNewId().toObjectId());
encoding = T.MapUtil.getStr(fileContent, "encoding", "");
newContent = T.MapUtil.getStr(fileContent, "content", "");
break;
}
case DELETE: {
path = diff.getOldPath();
Map<Object, Object> fileContent = this.getFileContent(repository, path, diff.getOldId().toObjectId());
encoding = T.MapUtil.getStr(fileContent, "encoding", "");
oldContent = T.MapUtil.getStr(fileContent, "content", "");
break;
}
case MODIFY: {
path = diff.getOldPath();
Map<Object, Object> fileContent = this.getFileContent(repository, path, diff.getOldId().toObjectId());
oldContent = T.MapUtil.getStr(fileContent, "content", "");
Map<Object, Object> fileContent1 = this.getFileContent(repository, path, diff.getNewId().toObjectId());
encoding = T.MapUtil.getStr(fileContent1, "encoding", "");
newContent = T.MapUtil.getStr(fileContent1, "content", "");
break;
}
default:
break;
}
files.add(
T.MapUtil.builder()
.put("path", path)
.put("addedLines", (addedLines == 0 ? 0 : ++addedLines))
.put("removedLines", (deletedLines == 0 ? 0 : ++deletedLines))
.put("encoding", encoding)
.put("oldContent", oldContent)
.put("newContent", newContent)
.build()
);
}
return files;
} catch (IOException e) {
log.error(e, "[getDiffFileListInCommits] [error] [workspaceId: {}] [commitId: {}] [oldCommitId: {}]", workspaceId, commitIdA, commitIdB);
throw new RuntimeException(e);
}
}
/**
* 获取 branchA -> branchB 文件差异
*
* @param workspaceId
* @param branchA
* @param branchB
* @return
*/
@Override
public List<Map<Object, Object>> getDiffFileListInBranches(String workspaceId, String branchA, String branchB) {
try (Git git = this.getGitInstance(workspaceId);
Repository repository = git.getRepository();
RevWalk revWalk = new RevWalk(repository);) {
RevCommit commitA = revWalk.parseCommit(repository.resolve(branchA));
RevCommit commitB = revWalk.parseCommit(repository.resolve(branchB));
List<Map<Object, Object>> diffFileListInCommits = this.getDiffFileListInCommits(workspaceId, commitA.getName(), commitB.getName());
diffFileListInCommits.parallelStream()
.forEach(m -> {
T.MapUtil.renameKey(m, "oldContent", "sourceContent");
T.MapUtil.renameKey(m, "newContent", "targetContent");
});
return diffFileListInCommits;
} catch (IOException e) {
log.error(e, "[getDiffFileListInBranches] [error]");
throw new RuntimeException(e);
}
}
@Override @Override
public String mergeBranch(String workspaceId, String srcBranch, String tgtBranch, String message) throws RuntimeException { public String mergeBranch(String workspaceId, String srcBranch, String tgtBranch, String message) throws RuntimeException {
log.info("[mergeBranch] [begin] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}]", workspaceId, srcBranch, tgtBranch); log.info("[mergeBranch] [begin] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}]", workspaceId, srcBranch, tgtBranch);
@@ -458,15 +660,8 @@ public class GitServiceImpl implements IGitService {
.put("path", treeWalk.getPathString()) .put("path", treeWalk.getPathString())
.build(); .build();
ObjectLoader loader = repository.open(treeWalk.getObjectId(0)); Map<Object, Object> fileContent = this.getFileContent(repository, treeWalk.getPathString(), treeWalk.getObjectId(0));
if (this.isBinary(treeWalk.getNameString())) { m.putAll(fileContent);
String content = Base64.getEncoder().encodeToString(loader.getBytes());
m.put("encoding", "base64");
m.put("content", content);
} else {
m.put("encoding", "");
m.put("content", T.StrUtil.utf8Str(loader.getBytes()));
}
// lastCommitId // lastCommitId
Iterable<RevCommit> iterable = git.log() Iterable<RevCommit> iterable = git.log()
@@ -551,7 +746,7 @@ public class GitServiceImpl implements IGitService {
String message = String.format("feat: add %s application", applicationName); String message = String.format("feat: add %s application", applicationName);
this.createCommit(repository, branch, newTreeId, message); this.createCommit(repository, branch, newTreeId, message);
} }
} catch (IOException e) { } catch (IOException | ConcurrentRefUpdateException e) {
log.error(e, "[newApplication] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName); log.error(e, "[newApplication] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
throw new ASWException(RCode.ERROR); throw new ASWException(RCode.ERROR);
} }
@@ -590,7 +785,7 @@ public class GitServiceImpl implements IGitService {
String message = String.format("refactor: remove %s application", applicationName); String message = String.format("refactor: remove %s application", applicationName);
this.createCommit(repository, branch, newTreeId, message); this.createCommit(repository, branch, newTreeId, message);
} }
} catch (IOException e) { } catch (IOException | ConcurrentRefUpdateException e) {
log.error(e, "[deleteApplication] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName); log.error(e, "[deleteApplication] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
throw new ASWException(RCode.ERROR); throw new ASWException(RCode.ERROR);
} }
@@ -729,7 +924,7 @@ public class GitServiceImpl implements IGitService {
.put("path", path) .put("path", path)
.build(); .build();
try (Git git = getGitInstance(workspaceId)) { try (Git git = this.getGitInstance(workspaceId)) {
Repository repository = git.getRepository(); Repository repository = git.getRepository();
try (TreeWalk treeWalk = new TreeWalk(repository); try (TreeWalk treeWalk = new TreeWalk(repository);
@@ -743,15 +938,8 @@ public class GitServiceImpl implements IGitService {
while (treeWalk.next()) { while (treeWalk.next()) {
if (T.StrUtil.equals(path, treeWalk.getPathString())) { if (T.StrUtil.equals(path, treeWalk.getPathString())) {
ObjectLoader loader = repository.open(treeWalk.getObjectId(0)); Map<Object, Object> fileContent = this.getFileContent(repository, treeWalk.getPathString(), treeWalk.getObjectId(0));
if (this.isBinary(treeWalk.getNameString())) { result.putAll(fileContent);
String content = Base64.getEncoder().encodeToString(loader.getBytes());
result.put("encoding", "base64");
result.put("content", content);
} else {
result.put("encoding", "");
result.put("content", T.StrUtil.utf8Str(loader.getBytes()));
}
} }
} }
} }
@@ -762,6 +950,419 @@ public class GitServiceImpl implements IGitService {
return result; return result;
} }
/**
* 获取 srcBranch tgtBranch 共同祖先之后, srcBranch 的提交记录
*
* @param workspaceId
* @param srcBranch
* @param tgtBranch
* @return
*/
@Override
public List<Map<Object, Object>> listCommitAfterMergeBase(String workspaceId, String srcBranch, String tgtBranch) {
log.info("[listCommitAfterMergeBase] [begin] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}]", workspaceId, srcBranch, tgtBranch);
List<Map<Object, Object>> commitList = T.ListUtil.list(true);
try (Git git = this.getGitInstance(workspaceId);
Repository repository = git.getRepository();) {
ObjectId branch1Id = repository.resolve(srcBranch);
ObjectId branch2Id = repository.resolve(tgtBranch);
// Find the merge base
String mergeBaseId = this.getMergeBase(repository, branch1Id, branch2Id);
log.info("[listCommitAfterMergeBase] [mergeBase: {}]", mergeBaseId);
if (T.StrUtil.isEmpty(mergeBaseId)) {
return commitList;
}
// Walk branch1's commits since the merge base
try (RevWalk revWalk = new RevWalk(repository)) {
revWalk.sort(RevSort.TOPO);
RevCommit mergeBase = revWalk.parseCommit(ObjectId.fromString(mergeBaseId));
revWalk.markStart(revWalk.parseCommit(branch1Id));
revWalk.markUninteresting(mergeBase);
for (RevCommit commit : revWalk) {
commitList.add(this.buildAswCommitInfo(commit));
List<String> parentIds = Arrays.stream(commit.getParents()).map(RevCommit::getName).collect(Collectors.toList());
if (parentIds.contains(mergeBaseId)) {
break;
}
}
}
} catch (IOException e) {
log.error(e, "[listCommitAfterMergeBase] [error]");
throw new RuntimeException(e.getMessage());
} finally {
log.info("[listCommitAfterMergeBase] [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);
}
}
/**
* 三路对比 srcBranchtgtBranch返回冲突文件详情
*
* @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
*/
private 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;
}
}
/**
* 根据 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");
}
return null;
}
/**
* 解决冲突提交到 srcBranch
* 提交指定 srcBranchtgtBranch 最新 commitId,像 merge commit 一样
*
* @param workspaceId
* @param srcBranch
* @param tgtBranch
* @param commitId
* @param commitMessage
* @param files
*/
@Override
public void resolveMrConflicts(String workspaceId, String srcBranch, String tgtBranch, String commitId, String commitMessage, List<Map<String, String>> files) {
try (Git git = this.getGitInstance(workspaceId);
Repository repository = git.getRepository();
TreeWalk treeWalk = new TreeWalk(repository);
RevWalk revWalk = new RevWalk(repository)) {
ObjectId srcBranchLatestCommitId = repository.resolve(srcBranch);
// branch tree
treeWalk.addTree(revWalk.parseTree(srcBranchLatestCommitId));
treeWalk.setRecursive(true);
DirCache newTree = DirCache.newInCore();
DirCacheBuilder newTreeBuilder = newTree.builder();
List<String> updateFilePath = files.stream().map(map -> T.MapUtil.getStr(map, "path")).collect(Collectors.toList());
while (treeWalk.next()) {
String pathString = treeWalk.getPathString();
// 先删
if (!updateFilePath.contains(pathString)) {
DirCacheEntry entry = new DirCacheEntry(pathString);
entry.setFileMode(treeWalk.getFileMode(0));
entry.setObjectId(treeWalk.getObjectId(0));
newTreeBuilder.add(entry);
}
}
// 后增
for (Map<String, String> map : files) {
String path = T.MapUtil.getStr(map, "path");
DirCacheEntry dirCacheEntry = new DirCacheEntry(path);
dirCacheEntry.setFileMode(FileMode.REGULAR_FILE);
String content = T.MapUtil.getStr(map, "content");
ObjectId objectId = this.insertBlobFileToDatabase(repository, content.getBytes());
dirCacheEntry.setObjectId(objectId);
newTreeBuilder.add(dirCacheEntry);
}
newTreeBuilder.finish();
// merge commit
ObjectId parentId1 = ObjectId.fromString(commitId);
RevCommit revCommit = revWalk.parseCommit(repository.resolve(tgtBranch));
ObjectId parentId2 = revCommit.getId();
boolean success = this.commitIndex(repository, srcBranch, newTree, parentId1, parentId2, commitMessage);
log.info("[resolveMrConflicts] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}] [commitId: {}] [success: {}]", workspaceId, srcBranch, tgtBranch, commitId, success);
} catch (IOException | ConcurrentRefUpdateException e) {
log.error(e, "[resolveMrConflicts] [error] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}] [commitId: {}]", workspaceId, srcBranch, tgtBranch, commitId);
throw new RuntimeException(e);
}
}
/**
* 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(), "asw@geedgenetworks.com"));
builder.setCommitter(new PersonIdent(loginUserEntity.getName(), "asw@geedgenetworks.com"));
// 插入新的提交对象
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
*/
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(), "asw@geedgenetworks.com");
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 * build asw commit info
* *
@@ -796,64 +1397,4 @@ public class GitServiceImpl implements IGitService {
return m; return m;
} }
/**
* 将 file object 添加到 objectDatabases,也就是 ./objects 目录下
*
* @param repository
* @param data
* @return
* @throws IOException
*/
private 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 repo
* @param branch
* @param treeId
* @param message
* @throws IOException
*/
public void createCommit(Repository repo, String branch, ObjectId treeId, String message) throws IOException {
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(), "asw@geedgenetworks.com"));
builder.setCommitter(new PersonIdent(loginUserEntity.getName(), "asw@geedgenetworks.com"));
// 插入新的提交对象
ObjectId commitId = inserter.insert(builder);
inserter.flush();
// 更新 branch 指向新的提交
if (null != branchRef) {
RefUpdate refUpdate = repo.updateRef(T.StrUtil.concat(true, LOCAL_BRANCH_PREFIX, branch));
refUpdate.setNewObjectId(commitId);
refUpdate.update();
} else {
RefUpdate refUpdate = repo.updateRef(Constants.HEAD);
refUpdate.setNewObjectId(commitId);
refUpdate.update();
}
}
}
} }

View File

@@ -149,5 +149,9 @@ INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (241, '302003', 'PLAYBOOK_INVALID_FILE', '无效文件', 'zh', '', 'admin', 1724030366000); INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (241, '302003', 'PLAYBOOK_INVALID_FILE', '无效文件', 'zh', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (242, '202004', 'PACKAGE_CANNOT_DELETE', 'The referenced package cannot be deleted', 'en', '', 'admin', 1724030366000); INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (242, '202004', 'PACKAGE_CANNOT_DELETE', 'The referenced package cannot be deleted', 'en', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (243, '202004', 'PACKAGE_CANNOT_DELETE', '以引用安装包不能删除', 'zh', '', 'admin', 1724030366000); INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (243, '202004', 'PACKAGE_CANNOT_DELETE', '以引用安装包不能删除', 'zh', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (244, '203003', 'GIT_PARENT_COMMITID_NOT_FOUND', 'Parent commitId not found', 'en', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (245, '203003', 'GIT_PARENT_COMMITID_NOT_FOUND', '未找到 parent commitId', 'zh', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (246, '203004', 'GIT_BINARY_CONFLICT_ERROR', 'Binary file conflict found; resolve conflicts in binary files manually', 'en', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (247, '203004', 'GIT_BINARY_CONFLICT_ERROR', '发现二进制文件冲突;手动解决二进制文件中的冲突', 'zh', '', 'admin', 1724030366000);
SET FOREIGN_KEY_CHECKS = 1; SET FOREIGN_KEY_CHECKS = 1;