blob: 37d5295d73363f592850c4e0155cf330e22a4f2e [file] [log] [blame]
// Copyright (C) 2016 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.project;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.MergeableInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.InMemoryInserter;
import com.google.gerrit.server.git.MergeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.merge.Merger;
import org.eclipse.jgit.merge.ResolveMerger;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.kohsuke.args4j.Option;
import java.io.IOException;
/**
* Check the mergeability at current branch for a git object references expression.
*/
public class CheckMergeability implements RestReadView<BranchResource> {
private String source;
private String strategy;
private SubmitType submitType;
private final Provider<ReviewDb> db;
@Option(name = "--source", metaVar = "COMMIT",
usage = "the source reference to merge, which could be any git object "
+ "references expression, refer to "
+ "org.eclipse.jgit.lib.Repository#resolve(String)",
required = true)
public void setSource(String source) {
this.source = source;
}
@Option(name = "--strategy", metaVar = "STRATEGY",
usage = "name of the merge strategy, refer to "
+ "org.eclipse.jgit.merge.MergeStrategy")
public void setStrategy(String strategy) {
this.strategy = strategy;
}
private final GitRepositoryManager gitManager;
@Inject
CheckMergeability(GitRepositoryManager gitManager,
@GerritServerConfig Config cfg,
Provider<ReviewDb> db) {
this.gitManager = gitManager;
this.strategy = MergeUtil.getMergeStrategy(cfg).getName();
this.submitType = cfg.getEnum("project", null, "submitType",
SubmitType.MERGE_IF_NECESSARY);
this.db = db;
}
@Override
public MergeableInfo apply(BranchResource resource)
throws IOException, BadRequestException, ResourceNotFoundException {
if (!(submitType.equals(SubmitType.MERGE_ALWAYS) ||
submitType.equals(SubmitType.MERGE_IF_NECESSARY))) {
throw new BadRequestException(
"Submit type: " + submitType + " is not supported");
}
MergeableInfo result = new MergeableInfo();
result.submitType = submitType;
result.strategy = strategy;
try (Repository git = gitManager.openRepository(resource.getNameKey());
RevWalk rw = new RevWalk(git);
ObjectInserter inserter = new InMemoryInserter(git)) {
Merger m = MergeUtil.newMerger(git, inserter, strategy);
Ref destRef = git.getRefDatabase().exactRef(resource.getRef());
if (destRef == null) {
throw new ResourceNotFoundException(resource.getRef());
}
RevCommit targetCommit = rw.parseCommit(destRef.getObjectId());
RevCommit sourceCommit = MergeUtil.resolveCommit(git, rw, source);
if (!resource.getControl().canReadCommit(db.get(), git, sourceCommit)) {
throw new BadRequestException(
"do not have read permission for: " + source);
}
if (rw.isMergedInto(sourceCommit, targetCommit)) {
result.mergeable = true;
result.commitMerged = true;
result.contentMerged = true;
return result;
}
if (m.merge(false, targetCommit, sourceCommit)) {
result.mergeable = true;
result.commitMerged = false;
result.contentMerged = m.getResultTreeId().equals(targetCommit.getTree());
} else {
result.mergeable = false;
if (m instanceof ResolveMerger) {
result.conflicts = ((ResolveMerger) m).getUnmergedPaths();
}
}
} catch (IllegalArgumentException e) {
throw new BadRequestException(e.getMessage());
}
return result;
}
}