use of com.google.gerrit.server.git.CodeReviewCommit in project gerrit by GerritCodeReview.
the class RebaseChangeOp method rebaseCommit.
/**
* Rebase a commit.
*
* @param ctx repo context.
* @param original the commit to rebase.
* @param base base to rebase against.
* @return the rebased commit.
* @throws MergeConflictException the rebase failed due to a merge conflict.
* @throws IOException the merge failed for another reason.
*/
private CodeReviewCommit rebaseCommit(RepoContext ctx, RevCommit original, ObjectId base, String commitMessage) throws ResourceConflictException, IOException {
RevCommit parentCommit = original.getParent(0);
if (base.equals(parentCommit)) {
throw new ResourceConflictException("Change is already up to date.");
}
ThreeWayMerger merger = newMergeUtil().newThreeWayMerger(ctx.getInserter(), ctx.getRepoView().getConfig());
merger.setBase(parentCommit);
DirCache dc = DirCache.newInCore();
if (allowConflicts && merger instanceof ResolveMerger) {
// The DirCache must be set on ResolveMerger before calling
// ResolveMerger#merge(AnyObjectId...) otherwise the entries in DirCache don't get populated.
((ResolveMerger) merger).setDirCache(dc);
}
boolean success = merger.merge(original, base);
ObjectId tree;
ImmutableSet<String> filesWithGitConflicts;
if (success) {
filesWithGitConflicts = null;
tree = merger.getResultTreeId();
} else {
List<String> conflicts = ImmutableList.of();
if (merger instanceof ResolveMerger) {
conflicts = ((ResolveMerger) merger).getUnmergedPaths();
}
if (!allowConflicts || !(merger instanceof ResolveMerger)) {
throw new MergeConflictException("The change could not be rebased due to a conflict during merge.\n\n" + MergeUtil.createConflictMessage(conflicts));
}
Map<String, MergeResult<? extends Sequence>> mergeResults = ((ResolveMerger) merger).getMergeResults();
filesWithGitConflicts = mergeResults.entrySet().stream().filter(e -> e.getValue().containsConflicts()).map(Map.Entry::getKey).collect(toImmutableSet());
tree = MergeUtil.mergeWithConflicts(ctx.getRevWalk(), ctx.getInserter(), dc, "PATCH SET", original, "BASE", ctx.getRevWalk().parseCommit(base), mergeResults);
}
CommitBuilder cb = new CommitBuilder();
cb.setTreeId(tree);
cb.setParentId(base);
cb.setAuthor(original.getAuthorIdent());
cb.setMessage(commitMessage);
if (committerIdent != null) {
cb.setCommitter(committerIdent);
} else {
cb.setCommitter(ctx.newCommitterIdent());
}
if (matchAuthorToCommitterDate) {
cb.setAuthor(new PersonIdent(cb.getAuthor(), cb.getCommitter().getWhen(), cb.getCommitter().getTimeZone()));
}
ObjectId objectId = ctx.getInserter().insert(cb);
ctx.getInserter().flush();
CodeReviewCommit commit = ((CodeReviewRevWalk) ctx.getRevWalk()).parseCommit(objectId);
commit.setFilesWithGitConflicts(filesWithGitConflicts);
return commit;
}
use of com.google.gerrit.server.git.CodeReviewCommit in project gerrit by GerritCodeReview.
the class CreateChange method createNewChange.
// TODO(issue-15517): Fix the JdkObsolete issue with Date once JGit's PersonIdent class supports
// Instants
@SuppressWarnings("JdkObsolete")
private ChangeInfo createNewChange(ChangeInput input, IdentifiedUser me, ProjectState projectState, BatchUpdate.Factory updateFactory) throws RestApiException, PermissionBackendException, IOException, ConfigInvalidException, UpdateException {
logger.atFine().log("Creating new change for target branch %s in project %s" + " (new branch = %s, base change = %s, base commit = %s)", input.branch, projectState.getName(), input.newBranch, input.baseChange, input.baseCommit);
try (Repository git = gitManager.openRepository(projectState.getNameKey());
ObjectInserter oi = git.newObjectInserter();
ObjectReader reader = oi.newReader();
CodeReviewRevWalk rw = CodeReviewCommit.newRevWalk(reader)) {
PatchSet basePatchSet = null;
List<String> groups = Collections.emptyList();
if (input.baseChange != null) {
ChangeNotes baseChange = getBaseChange(input.baseChange);
basePatchSet = psUtil.current(baseChange);
groups = basePatchSet.groups();
logger.atFine().log("base patch set = %s (groups = %s)", basePatchSet.id(), groups);
}
ObjectId parentCommit = getParentCommit(git, rw, input.branch, input.newBranch, basePatchSet, input.baseCommit, input.merge);
logger.atFine().log("parent commit = %s", parentCommit != null ? parentCommit.name() : "NULL");
RevCommit mergeTip = parentCommit == null ? null : rw.parseCommit(parentCommit);
Instant now = TimeUtil.now();
PersonIdent committer = me.newCommitterIdent(now, serverTimeZone);
PersonIdent author = input.author == null ? committer : new PersonIdent(input.author.name, input.author.email, Date.from(now), serverTimeZone);
String commitMessage = getCommitMessage(input.subject, me);
CodeReviewCommit c;
if (input.merge != null) {
// create a merge commit
c = newMergeCommit(git, oi, rw, projectState, mergeTip, input.merge, author, committer, commitMessage);
if (!c.getFilesWithGitConflicts().isEmpty()) {
logger.atFine().log("merge commit has conflicts in the following files: %s", c.getFilesWithGitConflicts());
}
} else {
// create an empty commit
c = newCommit(oi, rw, author, committer, mergeTip, commitMessage);
}
// Flush inserter so that commit becomes visible to validators
oi.flush();
Change.Id changeId = Change.id(seq.nextChangeId());
ChangeInserter ins = changeInserterFactory.create(changeId, c, input.branch);
ins.setMessage(messageForNewChange(ins.getPatchSetId(), c));
ins.setTopic(input.topic);
ins.setPrivate(input.isPrivate);
ins.setWorkInProgress(input.workInProgress || !c.getFilesWithGitConflicts().isEmpty());
ins.setGroups(groups);
if (input.validationOptions != null) {
ImmutableListMultimap.Builder<String, String> validationOptions = ImmutableListMultimap.builder();
input.validationOptions.entrySet().forEach(e -> validationOptions.put(e.getKey(), e.getValue()));
ins.setValidationOptions(validationOptions.build());
}
try (BatchUpdate bu = updateFactory.create(projectState.getNameKey(), me, now)) {
bu.setRepository(git, rw, oi);
bu.setNotify(notifyResolver.resolve(firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails));
bu.insertChange(ins);
bu.execute();
}
ChangeInfo changeInfo = jsonFactory.noOptions().format(ins.getChange());
changeInfo.containsGitConflicts = !c.getFilesWithGitConflicts().isEmpty() ? true : null;
return changeInfo;
} catch (InvalidMergeStrategyException | MergeWithConflictsNotSupportedException e) {
throw new BadRequestException(e.getMessage());
}
}
use of com.google.gerrit.server.git.CodeReviewCommit in project gerrit by GerritCodeReview.
the class CreateMergePatchSet method apply.
// TODO(issue-15517): Fix the JdkObsolete issue with Date once JGit's PersonIdent class supports
// Instants
@SuppressWarnings("JdkObsolete")
@Override
public Response<ChangeInfo> apply(ChangeResource rsrc, MergePatchSetInput in) throws IOException, RestApiException, UpdateException, PermissionBackendException {
// Not allowed to create a new patch set if the current patch set is locked.
psUtil.checkPatchSetNotLocked(rsrc.getNotes());
rsrc.permissions().check(ChangePermission.ADD_PATCH_SET);
if (in.author != null) {
permissionBackend.currentUser().project(rsrc.getProject()).ref(rsrc.getChange().getDest().branch()).check(RefPermission.FORGE_AUTHOR);
}
ProjectState projectState = projectCache.get(rsrc.getProject()).orElseThrow(illegalState(rsrc.getProject()));
projectState.checkStatePermitsWrite();
MergeInput merge = in.merge;
if (merge == null || Strings.isNullOrEmpty(merge.source)) {
throw new BadRequestException("merge.source must be non-empty");
}
if (in.author != null && (Strings.isNullOrEmpty(in.author.email) || Strings.isNullOrEmpty(in.author.name))) {
throw new BadRequestException("Author must specify name and email");
}
in.baseChange = Strings.nullToEmpty(in.baseChange).trim();
PatchSet ps = psUtil.current(rsrc.getNotes());
Change change = rsrc.getChange();
Project.NameKey project = change.getProject();
BranchNameKey dest = change.getDest();
try (Repository git = gitManager.openRepository(project);
ObjectInserter oi = git.newObjectInserter();
ObjectReader reader = oi.newReader();
CodeReviewRevWalk rw = CodeReviewCommit.newRevWalk(reader)) {
RevCommit sourceCommit = MergeUtil.resolveCommit(git, rw, merge.source);
if (!commits.canRead(projectState, git, sourceCommit)) {
throw new ResourceNotFoundException("cannot find source commit: " + merge.source + " to merge.");
}
RevCommit currentPsCommit;
List<String> groups = null;
if (!in.inheritParent && !in.baseChange.isEmpty()) {
PatchSet basePS = findBasePatchSet(in.baseChange);
currentPsCommit = rw.parseCommit(basePS.commitId());
groups = basePS.groups();
} else {
currentPsCommit = rw.parseCommit(ps.commitId());
}
Instant now = TimeUtil.now();
IdentifiedUser me = user.get().asIdentifiedUser();
PersonIdent author = in.author == null ? me.newCommitterIdent(now, serverTimeZone) : new PersonIdent(in.author.name, in.author.email, Date.from(now), serverTimeZone);
CodeReviewCommit newCommit = createMergeCommit(in, projectState, dest, git, oi, rw, currentPsCommit, sourceCommit, author, ObjectId.fromString(change.getKey().get().substring(1)));
oi.flush();
PatchSet.Id nextPsId = ChangeUtil.nextPatchSetId(ps.id());
PatchSetInserter psInserter = patchSetInserterFactory.create(rsrc.getNotes(), nextPsId, newCommit);
try (BatchUpdate bu = updateFactory.create(project, me, now)) {
bu.setRepository(git, rw, oi);
bu.setNotify(NotifyResolver.Result.none());
psInserter.setMessage(messageForChange(nextPsId, newCommit)).setWorkInProgress(!newCommit.getFilesWithGitConflicts().isEmpty()).setCheckAddPatchSetPermission(false);
if (groups != null) {
psInserter.setGroups(groups);
}
bu.addOp(rsrc.getId(), psInserter);
bu.execute();
}
ChangeJson json = jsonFactory.create(ListChangesOption.CURRENT_REVISION);
ChangeInfo changeInfo = json.format(psInserter.getChange());
changeInfo.containsGitConflicts = !newCommit.getFilesWithGitConflicts().isEmpty() ? true : null;
return Response.ok(changeInfo);
} catch (InvalidMergeStrategyException | MergeWithConflictsNotSupportedException e) {
throw new BadRequestException(e.getMessage());
}
}
use of com.google.gerrit.server.git.CodeReviewCommit in project gerrit by GerritCodeReview.
the class GitlinkOp method updateRepo.
@Override
public void updateRepo(RepoContext ctx) throws Exception {
Optional<CodeReviewCommit> commit = commitHelper.composeGitlinksCommit(branch, branchTargets);
if (commit.isPresent()) {
CodeReviewCommit c = commit.get();
ctx.addRefUpdate(c.getParent(0), c, branch.branch());
commitHelper.addBranchTip(branch, c);
}
}
use of com.google.gerrit.server.git.CodeReviewCommit in project gerrit by GerritCodeReview.
the class MergeOp method validateChangeList.
private BranchBatch validateChangeList(OpenRepo or, Collection<ChangeData> submitted) {
logger.atFine().log("Validating %d changes", submitted.size());
Set<CodeReviewCommit> toSubmit = new LinkedHashSet<>(submitted.size());
SetMultimap<ObjectId, PatchSet.Id> revisions = getRevisions(or, submitted);
SubmitType submitType = null;
ChangeData choseSubmitTypeFrom = null;
for (ChangeData cd : submitted) {
Change.Id changeId = cd.getId();
ChangeNotes notes;
Change chg;
SubmitType st;
try {
notes = cd.notes();
chg = cd.change();
st = getSubmitType(cd);
} catch (StorageException e) {
commitStatus.logProblem(changeId, e);
continue;
}
if (st == null) {
commitStatus.logProblem(changeId, "No submit type for change");
continue;
}
if (submitType == null) {
submitType = st;
choseSubmitTypeFrom = cd;
} else if (st != submitType) {
commitStatus.problem(changeId, String.format("Change has submit type %s, but previously chose submit type %s " + "from change %s in the same batch", st, submitType, choseSubmitTypeFrom.getId()));
continue;
}
if (chg.currentPatchSetId() == null) {
String msg = "Missing current patch set on change";
logger.atSevere().log("%s %s", msg, changeId);
commitStatus.problem(changeId, msg);
continue;
}
PatchSet ps;
BranchNameKey destBranch = chg.getDest();
try {
ps = cd.currentPatchSet();
} catch (StorageException e) {
commitStatus.logProblem(changeId, e);
continue;
}
if (ps == null) {
commitStatus.logProblem(changeId, "Missing patch set on change");
continue;
}
ObjectId id = ps.commitId();
if (!revisions.containsEntry(id, ps.id())) {
if (revisions.containsValue(ps.id())) {
// TODO This is actually an error, the patch set ref exists but points to a revision that
// is different from the revision that we have stored for the patch set in the change
// meta data.
commitStatus.logProblem(changeId, "Revision " + id.name() + " of patch set " + ps.number() + " does not match the revision of the patch set ref " + ps.id().toRefName());
continue;
}
// The patch set ref is not found but we want to merge the change. We can't safely do that
// if the patch set ref is missing. In a cluster setups with multiple primary nodes this can
// indicate a replication lag (e.g. the change meta data was already replicated, but the
// replication of the patch set ref is still pending).
commitStatus.logProblem(changeId, "Patch set ref " + ps.id().toRefName() + " not found. Expected patch set ref of " + ps.number() + " to point to revision " + id.name());
continue;
}
CodeReviewCommit commit;
try {
commit = or.rw.parseCommit(id);
} catch (IOException e) {
commitStatus.logProblem(changeId, e);
continue;
}
commit.setNotes(notes);
commit.setPatchsetId(ps.id());
commitStatus.put(commit);
MergeValidators mergeValidators = mergeValidatorsFactory.create();
try {
mergeValidators.validatePreMerge(or.repo, or.rw, commit, or.project, destBranch, ps.id(), caller);
} catch (MergeValidationException mve) {
commitStatus.problem(changeId, mve.getMessage());
continue;
}
commit.add(or.canMergeFlag);
toSubmit.add(commit);
}
logger.atFine().log("Submitting on this run: %s", toSubmit);
return new AutoValue_MergeOp_BranchBatch(submitType, ImmutableSet.copyOf(toSubmit));
}
Aggregations