blob: 391d9562bb4a5484c4481ecb07af01c5cb7c58d3 [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.submit;
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
/** Dry run of a submit strategy. */
public class SubmitDryRun {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
static class Arguments {
final Repository repo;
final CodeReviewRevWalk rw;
final MergeUtil mergeUtil;
final MergeSorter mergeSorter;
Arguments(Repository repo, CodeReviewRevWalk rw, MergeUtil mergeUtil, MergeSorter mergeSorter) {
this.repo = repo;
this.rw = rw;
this.mergeUtil = mergeUtil;
this.mergeSorter = mergeSorter;
}
}
public static Set<ObjectId> getAlreadyAccepted(Repository repo) throws IOException {
return Streams.concat(
repo.getRefDatabase().getRefsByPrefix(Constants.R_HEADS).stream(),
repo.getRefDatabase().getRefsByPrefix(Constants.R_TAGS).stream())
.map(Ref::getObjectId)
.filter(Objects::nonNull)
.collect(toSet());
}
public static Set<RevCommit> getAlreadyAccepted(Repository repo, RevWalk rw) throws IOException {
Set<RevCommit> accepted = new HashSet<>();
addCommits(getAlreadyAccepted(repo), rw, accepted);
return accepted;
}
public static void addCommits(Iterable<ObjectId> ids, RevWalk rw, Collection<RevCommit> out)
throws IOException {
for (ObjectId id : ids) {
RevObject obj = rw.parseAny(id);
if (obj instanceof RevTag) {
obj = rw.peel(obj);
}
if (obj instanceof RevCommit) {
out.add((RevCommit) obj);
}
}
}
private final ProjectCache projectCache;
private final MergeUtil.Factory mergeUtilFactory;
private final Provider<InternalChangeQuery> queryProvider;
@Inject
SubmitDryRun(
ProjectCache projectCache,
MergeUtil.Factory mergeUtilFactory,
Provider<InternalChangeQuery> queryProvider) {
this.projectCache = projectCache;
this.mergeUtilFactory = mergeUtilFactory;
this.queryProvider = queryProvider;
}
public boolean run(
@Nullable CurrentUser caller,
SubmitType submitType,
Repository repo,
CodeReviewRevWalk rw,
Branch.NameKey destBranch,
ObjectId tip,
ObjectId toMerge,
Set<RevCommit> alreadyAccepted)
throws IntegrationException, 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(errorMsg);
throw new IntegrationException(errorMsg);
}
}
private ProjectState getProject(Branch.NameKey branch) throws NoSuchProjectException {
ProjectState p = projectCache.get(branch.getParentKey());
if (p == null) {
throw new NoSuchProjectException(branch.getParentKey());
}
return p;
}
}