use of com.google.gerrit.exceptions.StorageException in project gerrit by GerritCodeReview.
the class MergeOp method merge.
/**
* Merges the given change.
*
* <p>Depending on the server configuration, more changes may be affected, e.g. by submission of a
* topic or via superproject subscriptions. All affected changes are integrated using the projects
* integration strategy.
*
* @param change the change to be merged.
* @param caller the identity of the caller
* @param checkSubmitRules whether the prolog submit rules should be evaluated
* @param submitInput parameters regarding the merge
* @throws RestApiException if an error occurred.
* @throws PermissionBackendException if permissions can't be checked
* @throws IOException an error occurred reading from NoteDb.
* @return the merged change
*/
public Change merge(Change change, IdentifiedUser caller, boolean checkSubmitRules, SubmitInput submitInput, boolean dryrun) throws RestApiException, UpdateException, IOException, ConfigInvalidException, PermissionBackendException {
this.submitInput = submitInput;
this.notify = notifyResolver.resolve(firstNonNull(submitInput.notify, NotifyHandling.ALL), submitInput.notifyDetails);
this.dryrun = dryrun;
this.caller = caller;
this.ts = TimeUtil.now();
this.submissionId = new SubmissionId(change);
try (TraceContext traceContext = TraceContext.open().addTag(RequestId.Type.SUBMISSION_ID, new RequestId(submissionId.toString()))) {
openRepoManager();
logger.atFine().log("Beginning integration of %s", change);
try {
ChangeSet indexBackedChangeSet = mergeSuperSet.setMergeOpRepoManager(orm).completeChangeSet(change, caller, /* includingTopicClosure= */
false);
if (!indexBackedChangeSet.ids().contains(change.getId())) {
// indexBackedChangeSet contains only open changes, if the change is missing in this set
// it might be that the change was concurrently submitted in the meantime.
change = changeDataFactory.create(change).reloadChange();
if (!change.isNew()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
}
throw new IllegalStateException(String.format("change %s missing from %s", change.getId(), indexBackedChangeSet));
}
if (indexBackedChangeSet.furtherHiddenChanges()) {
throw new AuthException("A change to be submitted with " + change.getId() + " is not visible");
}
logger.atFine().log("Calculated to merge %s", indexBackedChangeSet);
// Reload ChangeSet so that we don't rely on (potentially) stale index data for merging
ChangeSet noteDbChangeSet = reloadChanges(indexBackedChangeSet);
// At this point, any change that isn't new can be filtered out since they were only here
// in the first place due to stale index.
List<ChangeData> filteredChanges = new ArrayList<>();
for (ChangeData changeData : noteDbChangeSet.changes()) {
if (!changeData.change().getStatus().equals(Status.NEW)) {
logger.atFine().log("Change %s has status %s due to stale index, so it is skipped during submit", changeData.getId(), changeData.change().getStatus().name());
continue;
}
filteredChanges.add(changeData);
}
// There are no hidden changes (or else we would have thrown AuthException above).
ChangeSet filteredNoteDbChangeSet = new ChangeSet(filteredChanges, /* hiddenChanges= */
ImmutableList.of());
// Count cross-project submissions outside of the retry loop. The chance of a single project
// failing increases with the number of projects, so the failure count would be inflated if
// this metric were incremented inside of integrateIntoHistory.
int projects = filteredNoteDbChangeSet.projects().size();
if (projects > 1) {
topicMetrics.topicSubmissions.increment();
}
SubmissionExecutor submissionExecutor = new SubmissionExecutor(dryrun, superprojectUpdateSubmissionListeners);
RetryTracker retryTracker = new RetryTracker();
retryHelper.changeUpdate("integrateIntoHistory", updateFactory -> {
long attempt = retryTracker.lastAttemptNumber + 1;
boolean isRetry = attempt > 1;
if (isRetry) {
logger.atFine().log("Retrying, attempt #%d; skipping merged changes", attempt);
this.ts = TimeUtil.now();
openRepoManager();
}
this.commitStatus = new CommitStatus(filteredNoteDbChangeSet, isRetry);
if (checkSubmitRules) {
logger.atFine().log("Checking submit rules and state");
checkSubmitRulesAndState(filteredNoteDbChangeSet, isRetry);
} else {
logger.atFine().log("Bypassing submit rules");
bypassSubmitRulesAndRequirements(filteredNoteDbChangeSet);
}
integrateIntoHistory(filteredNoteDbChangeSet, submissionExecutor);
return null;
}).listener(retryTracker).defaultTimeoutMultiplier(filteredNoteDbChangeSet.projects().size() * 2).retryOn(t -> t instanceof RuntimeException).call();
submissionExecutor.afterExecutions(orm);
if (projects > 1) {
topicMetrics.topicSubmissionsCompleted.increment();
}
// (e.g. caller provided a change that was already merged).
return updatedChanges.containsKey(change.getId()) ? updatedChanges.get(change.getId()) : change;
} catch (IOException e) {
// Anything before the merge attempt is an error
throw new StorageException(e);
}
}
}
use of com.google.gerrit.exceptions.StorageException in project gerrit by GerritCodeReview.
the class MergeOp method checkSubmitRulesAndState.
private void checkSubmitRulesAndState(ChangeSet cs, boolean allowMerged) throws ResourceConflictException {
checkArgument(!cs.furtherHiddenChanges(), "checkSubmitRulesAndState called for topic with hidden change");
for (ChangeData cd : cs.changes()) {
try {
if (!cd.change().isNew()) {
if (!(cd.change().isMerged() && allowMerged)) {
commitStatus.problem(cd.getId(), "Change " + cd.getId() + " is " + ChangeUtil.status(cd.change()));
}
} else if (cd.change().isWorkInProgress()) {
commitStatus.problem(cd.getId(), "Change " + cd.getId() + " is work in progress");
} else {
checkSubmitRequirements(cd);
}
} catch (ResourceConflictException e) {
commitStatus.problem(cd.getId(), e.getMessage());
} catch (StorageException e) {
String msg = "Error checking submit rules for change";
logger.atWarning().withCause(e).log("%s %s", msg, cd.getId());
commitStatus.problem(cd.getId(), msg);
}
}
commitStatus.maybeFailVerbose();
}
use of com.google.gerrit.exceptions.StorageException in project gerrit by GerritCodeReview.
the class MergeOp method getRevisions.
private SetMultimap<ObjectId, PatchSet.Id> getRevisions(OpenRepo or, Collection<ChangeData> cds) {
try {
List<String> refNames = new ArrayList<>(cds.size());
for (ChangeData cd : cds) {
Change c = cd.change();
if (c != null) {
refNames.add(c.currentPatchSetId().toRefName());
}
}
SetMultimap<ObjectId, PatchSet.Id> revisions = MultimapBuilder.hashKeys(cds.size()).hashSetValues(1).build();
for (Map.Entry<String, Ref> e : or.repo.getRefDatabase().exactRef(refNames.toArray(new String[refNames.size()])).entrySet()) {
revisions.put(e.getValue().getObjectId(), PatchSet.Id.fromRef(e.getKey()));
}
return revisions;
} catch (IOException | StorageException e) {
throw new StorageException("Failed to validate changes", e);
}
}
use of com.google.gerrit.exceptions.StorageException in project gerrit by GerritCodeReview.
the class MergeOp method bypassSubmitRulesAndRequirements.
private void bypassSubmitRulesAndRequirements(ChangeSet cs) {
checkArgument(!cs.furtherHiddenChanges(), "cannot bypass submit rules for topic with hidden change");
for (ChangeData cd : cs.changes()) {
Change change = cd.change();
if (change == null) {
throw new StorageException("Change not found");
}
if (change.isClosed()) {
// No need to check submit rules if the change is closed.
continue;
}
List<SubmitRecord> records = new ArrayList<>(getSubmitRecords(cd));
SubmitRecord forced = new SubmitRecord();
forced.status = SubmitRecord.Status.FORCED;
records.add(forced);
cd.setSubmitRecords(submitRuleOptions(/* allowClosed= */
false), records);
// Also bypass submit requirements. Mark them as forced.
Map<SubmitRequirement, SubmitRequirementResult> forcedSRs = cd.submitRequirementsIncludingLegacy().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toBuilder().forced(Optional.of(true)).build()));
cd.setSubmitRequirements(forcedSRs);
}
}
use of com.google.gerrit.exceptions.StorageException in project gerrit by GerritCodeReview.
the class SubmitDryRun method run.
public boolean run(@Nullable CurrentUser caller, SubmitType submitType, Repository repo, CodeReviewRevWalk rw, BranchNameKey destBranch, ObjectId tip, ObjectId toMerge, Set<RevCommit> alreadyAccepted) throws NoSuchProjectException, IOException {
CodeReviewCommit tipCommit = rw.parseCommit(tip);
CodeReviewCommit toMergeCommit = rw.parseCommit(toMerge);
RevFlag canMerge = rw.newFlag("CAN_MERGE");
toMergeCommit.add(canMerge);
Arguments args = new Arguments(repo, rw, mergeUtilFactory.create(getProject(destBranch)), new MergeSorter(caller, rw, alreadyAccepted, canMerge, queryProvider, ImmutableSet.of(toMergeCommit)));
switch(submitType) {
case CHERRY_PICK:
return CherryPick.dryRun(args, tipCommit, toMergeCommit);
case FAST_FORWARD_ONLY:
return FastForwardOnly.dryRun(args, tipCommit, toMergeCommit);
case MERGE_ALWAYS:
return MergeAlways.dryRun(args, tipCommit, toMergeCommit);
case MERGE_IF_NECESSARY:
return MergeIfNecessary.dryRun(args, tipCommit, toMergeCommit);
case REBASE_IF_NECESSARY:
return RebaseIfNecessary.dryRun(args, repo, tipCommit, toMergeCommit);
case REBASE_ALWAYS:
return RebaseAlways.dryRun(args, repo, tipCommit, toMergeCommit);
case INHERIT:
default:
String errorMsg = "No submit strategy for: " + submitType;
logger.atSevere().log("%s", errorMsg);
throw new StorageException(errorMsg);
}
}
Aggregations