blob: 240235780df2db792b3951f251d81effa59b5aa6 [file] [log] [blame]
// Copyright (C) 2011 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 com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.UPDATE_SUPERPROJECT;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.submit.MergeOpRepoManager.OpenRepo;
import com.google.gerrit.server.update.BatchUpdates;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Map;
import org.eclipse.jgit.transport.ReceiveCommand;
public class SubmoduleOp {
@Singleton
public static class Factory {
private final BatchUpdates batchUpdates;
private final SubscriptionGraph.Factory subscriptionGraphFactory;
private final SubmoduleCommits.Factory submoduleCommitsFactory;
@Inject
Factory(
BatchUpdates batchUpdates,
SubscriptionGraph.Factory subscriptionGraphFactory,
SubmoduleCommits.Factory submoduleCommitsFactory) {
this.batchUpdates = batchUpdates;
this.subscriptionGraphFactory = subscriptionGraphFactory;
this.submoduleCommitsFactory = submoduleCommitsFactory;
}
public SubmoduleOp create(
Map<BranchNameKey, ReceiveCommand> updatedBranches, MergeOpRepoManager orm)
throws SubmoduleConflictException {
return new SubmoduleOp(
batchUpdates,
updatedBranches,
orm,
subscriptionGraphFactory.compute(updatedBranches.keySet(), orm),
submoduleCommitsFactory.create(orm));
}
}
private final BatchUpdates batchUpdates;
private final Map<BranchNameKey, ReceiveCommand> updatedBranches;
private final MergeOpRepoManager orm;
private final SubscriptionGraph subscriptionGraph;
private final SubmoduleCommits submoduleCommits;
private final UpdateOrderCalculator updateOrderCalculator;
private SubmoduleOp(
BatchUpdates batchUpdates,
Map<BranchNameKey, ReceiveCommand> updatedBranches,
MergeOpRepoManager orm,
SubscriptionGraph subscriptionGraph,
SubmoduleCommits submoduleCommits) {
this.batchUpdates = batchUpdates;
this.updatedBranches = updatedBranches;
this.orm = orm;
this.subscriptionGraph = subscriptionGraph;
this.submoduleCommits = submoduleCommits;
this.updateOrderCalculator = new UpdateOrderCalculator(subscriptionGraph);
}
public void updateSuperProjects(boolean dryrun) throws RestApiException {
ImmutableSet<Project.NameKey> projects = updateOrderCalculator.getProjectsInOrder();
if (projects == null) {
return;
}
if (dryrun) {
// On dryrun, the refs hasn't been updated.
// force the new tips on submoduleCommits
forceRefTips(updatedBranches, submoduleCommits);
}
LinkedHashSet<Project.NameKey> superProjects = new LinkedHashSet<>();
try {
GitlinkOp.Factory gitlinkOpFactory =
new GitlinkOp.Factory(submoduleCommits, subscriptionGraph);
for (Project.NameKey project : projects) {
// only need superprojects
if (subscriptionGraph.isAffectedSuperProject(project)) {
superProjects.add(project);
// get a new BatchUpdate for the super project
OpenRepo or = orm.getRepo(project);
for (BranchNameKey branch : subscriptionGraph.getAffectedSuperBranches(project)) {
or.getUpdate().addRepoOnlyOp(gitlinkOpFactory.create(branch));
}
}
}
try (RefUpdateContext ctx = RefUpdateContext.open(UPDATE_SUPERPROJECT)) {
batchUpdates.execute(
orm.batchUpdates(superProjects, /* refLogMessage= */ "merged"),
ImmutableList.of(),
dryrun);
}
} catch (UpdateException | IOException | NoSuchProjectException e) {
throw new StorageException("Cannot update gitlinks", e);
}
}
private void forceRefTips(
Map<BranchNameKey, ReceiveCommand> updatedBranches, SubmoduleCommits submoduleCommits) {
// This is dryrun, all commands succeeded (no need to filter success).
for (Map.Entry<BranchNameKey, ReceiveCommand> updateBranch : updatedBranches.entrySet()) {
try {
ReceiveCommand command = updateBranch.getValue();
if (command.getType() == ReceiveCommand.Type.DELETE) {
continue;
}
BranchNameKey branchNameKey = updateBranch.getKey();
OpenRepo openRepo = orm.getRepo(branchNameKey.project());
CodeReviewCommit fakeTip = openRepo.rw.parseCommit(command.getNewId());
submoduleCommits.addBranchTip(branchNameKey, fakeTip);
} catch (NoSuchProjectException | IOException e) {
throw new StorageException("Cannot find branch tip target in dryrun", e);
}
}
}
}