use of org.gitlab4j.api.models.Diff in project legend-sdlc by finos.
the class GitLabWorkspaceApi method createConflictResolution.
/**
* This method will mark create a conflict resolution branch from a given workspace branch. Assume we have workspace branch `w1`, this method will:
* 1. Create resolution branch from `master` branch (check if that's the latest)
* 2. Get all the changes of workspace branch `w1`
* 3. Copy and replace those changes to resolution branch `w1` and create a new commit out of that.
*/
private WorkspaceUpdateReport createConflictResolution(String projectId, String workspaceId, WorkspaceType workspaceType, String masterRevisionId) {
// Check if conflict resolution is happening, if it is, it means conflict resolution branch already existed, so we will
// scrap that branch and create a new one.
GitLabProjectId gitLabProjectId = parseProjectId(projectId);
RepositoryApi repositoryApi = getGitLabApi(gitLabProjectId.getGitLabMode()).getRepositoryApi();
Branch previousConflictResolutionBranch = null;
ProjectFileAccessProvider.WorkspaceAccessType conflictResolutionWorkspaceType = ProjectFileAccessProvider.WorkspaceAccessType.CONFLICT_RESOLUTION;
try {
previousConflictResolutionBranch = withRetries(() -> repositoryApi.getBranch(gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, conflictResolutionWorkspaceType)));
} catch (Exception e) {
if (!GitLabApiTools.isNotFoundGitLabApiException(e)) {
LOGGER.error("Error updating {} {} in project {}", workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel(), workspaceId, projectId);
}
}
// Delete conflict resolution workspace
if (previousConflictResolutionBranch != null) {
LOGGER.debug("Conflict resolution already happened in workspace {} in project {}, but we will recreate this conflict resolution workspace to make sure it's up to date", workspaceId, projectId);
boolean conflictResolutionBranchDeleted;
try {
conflictResolutionBranchDeleted = GitLabApiTools.deleteBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, conflictResolutionWorkspaceType), 20, 1_000);
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to delete " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId, () -> "Unknown " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " (" + workspaceId + ") or project (" + projectId + ")", () -> "Error deleting " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId);
}
if (!conflictResolutionBranchDeleted) {
throw new LegendSDLCServerException("Failed to delete " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId);
}
}
// Create conflict resolution workspace
Branch conflictResolutionBranch;
try {
conflictResolutionBranch = GitLabApiTools.createBranchFromSourceBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, conflictResolutionWorkspaceType), MASTER_BRANCH, 30, 1_000);
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to create workspace " + workspaceId + " in project " + projectId, () -> "Unknown project: " + projectId, () -> "Error creating workspace " + workspaceId + " in project " + projectId);
}
if (conflictResolutionBranch == null) {
throw new LegendSDLCServerException("Failed to create " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId);
}
// Get the changes of the current workspace
String currentWorkspaceRevisionId = this.revisionApi.getWorkspaceRevisionContext(projectId, workspaceId, workspaceType).getCurrentRevision().getId();
String workspaceCreationRevisionId;
try {
workspaceCreationRevisionId = withRetries(() -> repositoryApi.getMergeBase(gitLabProjectId.getGitLabId(), Arrays.asList(MASTER_BRANCH, currentWorkspaceRevisionId)).getId());
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to get merged base revision for revisions " + MASTER_BRANCH + ", " + currentWorkspaceRevisionId + " from project " + projectId, () -> "Could not find revisions " + MASTER_BRANCH + ", " + currentWorkspaceRevisionId + " from project " + projectId, () -> "Failed to fetch merged base information for revisions " + MASTER_BRANCH + ", " + currentWorkspaceRevisionId + " from project " + projectId);
}
CompareResults comparisonResult;
try {
comparisonResult = withRetries(() -> repositoryApi.compare(gitLabProjectId.getGitLabId(), workspaceCreationRevisionId, currentWorkspaceRevisionId, true));
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to get comparison information from revision " + workspaceCreationRevisionId + " to revision " + currentWorkspaceRevisionId + " on project" + projectId, () -> "Could not find revisions " + workspaceCreationRevisionId + " ," + currentWorkspaceRevisionId + " on project" + projectId, () -> "Failed to fetch comparison information from revision " + workspaceCreationRevisionId + " to revision " + currentWorkspaceRevisionId + " on project" + projectId);
}
List<Diff> diffs = comparisonResult.getDiffs();
// Create a new commit on conflict resolution branch
CommitsApi commitsApi = getGitLabApi(gitLabProjectId.getGitLabMode()).getCommitsApi();
ProjectFileAccessProvider.FileAccessContext projectFileAccessContext = getProjectFileAccessProvider().getProjectFileAccessContext(projectId);
ProjectFileAccessProvider.WorkspaceAccessType workspaceAccessType = ProjectFileAccessProvider.WorkspaceAccessType.WORKSPACE;
ProjectFileAccessProvider.FileAccessContext workspaceFileAccessContext = getProjectFileAccessProvider().getWorkspaceFileAccessContext(projectId, workspaceId, workspaceType, workspaceAccessType);
try {
List<CommitAction> commitActions = Lists.mutable.empty();
// NOTE: we are bringing the diffs from the workspace and applying those diffs to the project HEAD. Now, the project
// HEAD could potentially differ greatly from the workspace base revision. This means that when we create the commit
// action from the diff, we must be careful about the action type.
// Take for example DELETE: if according to the diff, we delete file A, but at project HEAD, A is already deleted
// in one of the commits between workspace base and project HEAD, such DELETE commit action will fail, this should then be
// a NO_OP. Then again, we will have to be careful when CREATE, MOVE, and UPDATE.
diffs.forEach(diff -> {
if (diff.getDeletedFile()) {
// Ensure the file to delete exists at project HEAD
ProjectFileAccessProvider.ProjectFile fileToDelete = projectFileAccessContext.getFile(diff.getOldPath());
if (fileToDelete != null) {
commitActions.add(new CommitAction().withAction(CommitAction.Action.DELETE).withFilePath(diff.getOldPath()));
}
} else if (diff.getRenamedFile()) {
// split a MOVE into a DELETE followed by an CREATE/UPDATE to handle cases when
// file to be moved is already deleted at project HEAD
// and file to be moved to is already created at project HEAD
ProjectFileAccessProvider.ProjectFile fileToDelete = projectFileAccessContext.getFile(diff.getOldPath());
ProjectFileAccessProvider.ProjectFile fileToReplace = projectFileAccessContext.getFile(diff.getNewPath());
if (fileToDelete != null) {
commitActions.add(new CommitAction().withAction(CommitAction.Action.DELETE).withFilePath(diff.getOldPath()));
}
commitActions.add(new CommitAction().withAction(fileToReplace == null ? CommitAction.Action.CREATE : CommitAction.Action.UPDATE).withFilePath(diff.getNewPath()).withEncoding(Constants.Encoding.BASE64).withContent(encodeBase64(workspaceFileAccessContext.getFile(diff.getNewPath()).getContentAsBytes())));
} else if (diff.getNewFile()) {
// If the file to be created already exists at project HEAD, change this to an UPDATE
ProjectFileAccessProvider.ProjectFile fileToCreate = projectFileAccessContext.getFile(diff.getOldPath());
commitActions.add(new CommitAction().withAction(fileToCreate == null ? CommitAction.Action.CREATE : CommitAction.Action.UPDATE).withFilePath(diff.getNewPath()).withEncoding(Constants.Encoding.BASE64).withContent(encodeBase64(workspaceFileAccessContext.getFile(diff.getNewPath()).getContentAsBytes())));
} else {
// File was updated
// If the file to be updated is deleted at project HEAD, change this to a CREATE
ProjectFileAccessProvider.ProjectFile fileToUpdate = projectFileAccessContext.getFile(diff.getOldPath());
commitActions.add(new CommitAction().withAction(fileToUpdate == null ? CommitAction.Action.CREATE : CommitAction.Action.UPDATE).withFilePath(diff.getOldPath()).withEncoding(Constants.Encoding.BASE64).withContent(encodeBase64(workspaceFileAccessContext.getFile(diff.getOldPath()).getContentAsBytes())));
}
});
commitsApi.createCommit(gitLabProjectId.getGitLabId(), this.getWorkspaceBranchName(workspaceId, workspaceType, conflictResolutionWorkspaceType), "aggregated changes for conflict resolution", null, null, getCurrentUser(), commitActions);
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to create commit on " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + workspaceId + " of project " + projectId, () -> "Unknown project: " + projectId + " or " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId, () -> "Failed to create commit in " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + workspaceId + " of project" + projectId);
}
return createWorkspaceUpdateReport(WorkspaceUpdateReportStatus.CONFLICT, masterRevisionId, conflictResolutionBranch.getCommit().getId());
}
Aggregations