blob: 36de70e71a7f81abce65455009bc6607b7d2e530 [file] [log] [blame]
// Copyright (C) 2012 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.git.strategy;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Sets;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.change.RebaseChangeOp;
import com.google.gerrit.server.extensions.events.ChangeMerged;
import com.google.gerrit.server.git.BatchUpdate;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.git.EmailMerge;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.LabelNormalizer;
import com.google.gerrit.server.git.MergeOp.CommitStatus;
import com.google.gerrit.server.git.MergeSorter;
import com.google.gerrit.server.git.MergeTip;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.git.SubmoduleOp;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.util.RequestId;
import com.google.inject.Module;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* Base class that submit strategies must extend.
* <p>
* A submit strategy for a certain {@link SubmitType} defines how the submitted
* commits should be merged.
*/
public abstract class SubmitStrategy {
public static Module module() {
return new FactoryModule() {
@Override
protected void configure() {
factory(SubmitStrategy.Arguments.Factory.class);
}
};
}
static class Arguments {
interface Factory {
Arguments create(
SubmitType submitType,
Branch.NameKey destBranch,
CommitStatus commits,
CodeReviewRevWalk rw,
IdentifiedUser caller,
MergeTip mergeTip,
ObjectInserter inserter,
Repository repo,
RevFlag canMergeFlag,
ReviewDb db,
Set<RevCommit> alreadyAccepted,
RequestId submissionId,
NotifyHandling notifyHandling,
SubmoduleOp submoduleOp);
}
final AccountCache accountCache;
final ApprovalsUtil approvalsUtil;
final BatchUpdate.Factory batchUpdateFactory;
final ChangeControl.GenericFactory changeControlFactory;
final ChangeMerged changeMerged;
final ChangeMessagesUtil cmUtil;
final EmailMerge.Factory mergedSenderFactory;
final GitRepositoryManager repoManager;
final LabelNormalizer labelNormalizer;
final PatchSetInfoFactory patchSetInfoFactory;
final PatchSetUtil psUtil;
final ProjectCache projectCache;
final PersonIdent serverIdent;
final RebaseChangeOp.Factory rebaseFactory;
final TagCache tagCache;
final Branch.NameKey destBranch;
final CodeReviewRevWalk rw;
final CommitStatus commits;
final IdentifiedUser caller;
final MergeTip mergeTip;
final ObjectInserter inserter;
final Repository repo;
final RevFlag canMergeFlag;
final ReviewDb db;
final Set<RevCommit> alreadyAccepted;
final RequestId submissionId;
final SubmitType submitType;
final NotifyHandling notifyHandling;
final SubmoduleOp submoduleOp;
final ProjectState project;
final MergeSorter mergeSorter;
final MergeUtil mergeUtil;
@AssistedInject
Arguments(
AccountCache accountCache,
ApprovalsUtil approvalsUtil,
BatchUpdate.Factory batchUpdateFactory,
ChangeControl.GenericFactory changeControlFactory,
ChangeMerged changeMerged,
ChangeMessagesUtil cmUtil,
EmailMerge.Factory mergedSenderFactory,
GitRepositoryManager repoManager,
LabelNormalizer labelNormalizer,
MergeUtil.Factory mergeUtilFactory,
PatchSetInfoFactory patchSetInfoFactory,
PatchSetUtil psUtil,
@GerritPersonIdent PersonIdent serverIdent,
ProjectCache projectCache,
RebaseChangeOp.Factory rebaseFactory,
TagCache tagCache,
@Assisted Branch.NameKey destBranch,
@Assisted CommitStatus commits,
@Assisted CodeReviewRevWalk rw,
@Assisted IdentifiedUser caller,
@Assisted MergeTip mergeTip,
@Assisted ObjectInserter inserter,
@Assisted Repository repo,
@Assisted RevFlag canMergeFlag,
@Assisted ReviewDb db,
@Assisted Set<RevCommit> alreadyAccepted,
@Assisted RequestId submissionId,
@Assisted SubmitType submitType,
@Assisted NotifyHandling notifyHandling,
@Assisted SubmoduleOp submoduleOp) {
this.accountCache = accountCache;
this.approvalsUtil = approvalsUtil;
this.batchUpdateFactory = batchUpdateFactory;
this.changeControlFactory = changeControlFactory;
this.changeMerged = changeMerged;
this.mergedSenderFactory = mergedSenderFactory;
this.repoManager = repoManager;
this.cmUtil = cmUtil;
this.labelNormalizer = labelNormalizer;
this.patchSetInfoFactory = patchSetInfoFactory;
this.psUtil = psUtil;
this.projectCache = projectCache;
this.rebaseFactory = rebaseFactory;
this.tagCache = tagCache;
this.serverIdent = serverIdent;
this.destBranch = destBranch;
this.commits = commits;
this.rw = rw;
this.caller = caller;
this.mergeTip = mergeTip;
this.inserter = inserter;
this.repo = repo;
this.canMergeFlag = canMergeFlag;
this.db = db;
this.alreadyAccepted = alreadyAccepted;
this.submissionId = submissionId;
this.submitType = submitType;
this.notifyHandling = notifyHandling;
this.submoduleOp = submoduleOp;
this.project = checkNotNull(projectCache.get(destBranch.getParentKey()),
"project not found: %s", destBranch.getParentKey());
this.mergeSorter = new MergeSorter(rw, alreadyAccepted, canMergeFlag);
this.mergeUtil = mergeUtilFactory.create(project);
}
}
final Arguments args;
SubmitStrategy(Arguments args) {
this.args = checkNotNull(args);
}
/**
* Add operations to a batch update that execute this submit strategy.
* <p>
* Guarantees exactly one op is added to the update for each change in the
* input set.
*
* @param bu batch update to add operations to.
* @param toMerge the set of submitted commits that should be merged using
* this submit strategy. Implementations are responsible for ordering of
* commits, and will not modify the input in place.
* @throws IntegrationException if an error occurred initializing the
* operations (as opposed to an error during execution, which will be
* reported only when the batch update executes the operations).
*/
public final void addOps(BatchUpdate bu, Set<CodeReviewCommit> toMerge)
throws IntegrationException {
List<SubmitStrategyOp> ops = buildOps(toMerge);
Set<CodeReviewCommit> added = Sets.newHashSetWithExpectedSize(ops.size());
for (SubmitStrategyOp op : ops) {
added.add(op.getCommit());
}
// First add ops for any implicitly merged changes.
List<CodeReviewCommit> difference =
new ArrayList<>(Sets.difference(toMerge, added));
Collections.reverse(difference);
for (CodeReviewCommit c : difference) {
bu.addOp(c.change().getId(), new ImplicitIntegrateOp(args, c));
}
// Then ops for explicitly merged changes
for (SubmitStrategyOp op : ops) {
bu.addOp(op.getId(), op);
}
}
protected abstract List<SubmitStrategyOp> buildOps(
Collection<CodeReviewCommit> toMerge) throws IntegrationException;
}