Line data Source code
1 : // Copyright (C) 2015 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.submit; 16 : 17 : import static com.google.gerrit.server.project.ProjectCache.noSuchProject; 18 : import static java.util.stream.Collectors.toSet; 19 : 20 : import com.google.common.collect.ImmutableSet; 21 : import com.google.common.collect.Streams; 22 : import com.google.common.flogger.FluentLogger; 23 : import com.google.gerrit.common.Nullable; 24 : import com.google.gerrit.entities.BranchNameKey; 25 : import com.google.gerrit.exceptions.StorageException; 26 : import com.google.gerrit.extensions.client.SubmitType; 27 : import com.google.gerrit.server.CurrentUser; 28 : import com.google.gerrit.server.git.CodeReviewCommit; 29 : import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk; 30 : import com.google.gerrit.server.git.MergeUtil; 31 : import com.google.gerrit.server.git.MergeUtilFactory; 32 : import com.google.gerrit.server.project.NoSuchProjectException; 33 : import com.google.gerrit.server.project.ProjectCache; 34 : import com.google.gerrit.server.project.ProjectState; 35 : import com.google.gerrit.server.query.change.InternalChangeQuery; 36 : import com.google.inject.Inject; 37 : import com.google.inject.Provider; 38 : import java.io.IOException; 39 : import java.util.Collection; 40 : import java.util.HashSet; 41 : import java.util.Objects; 42 : import java.util.Set; 43 : import org.eclipse.jgit.lib.Constants; 44 : import org.eclipse.jgit.lib.ObjectId; 45 : import org.eclipse.jgit.lib.Ref; 46 : import org.eclipse.jgit.lib.Repository; 47 : import org.eclipse.jgit.revwalk.RevCommit; 48 : import org.eclipse.jgit.revwalk.RevFlag; 49 : import org.eclipse.jgit.revwalk.RevObject; 50 : import org.eclipse.jgit.revwalk.RevTag; 51 : import org.eclipse.jgit.revwalk.RevWalk; 52 : 53 : /** Dry run of a submit strategy. */ 54 : public class SubmitDryRun { 55 150 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 56 : 57 : static class Arguments { 58 : final Repository repo; 59 : final CodeReviewRevWalk rw; 60 : final MergeUtil mergeUtil; 61 : final MergeSorter mergeSorter; 62 : 63 21 : Arguments(Repository repo, CodeReviewRevWalk rw, MergeUtil mergeUtil, MergeSorter mergeSorter) { 64 21 : this.repo = repo; 65 21 : this.rw = rw; 66 21 : this.mergeUtil = mergeUtil; 67 21 : this.mergeSorter = mergeSorter; 68 21 : } 69 : } 70 : 71 : public static Set<ObjectId> getAlreadyAccepted(Repository repo) throws IOException { 72 21 : return Streams.concat( 73 21 : repo.getRefDatabase().getRefsByPrefix(Constants.R_HEADS).stream(), 74 21 : repo.getRefDatabase().getRefsByPrefix(Constants.R_TAGS).stream()) 75 21 : .map(Ref::getObjectId) 76 21 : .filter(Objects::nonNull) 77 21 : .collect(toSet()); 78 : } 79 : 80 : public static Set<RevCommit> getAlreadyAccepted(Repository repo, RevWalk rw) throws IOException { 81 21 : Set<RevCommit> accepted = new HashSet<>(); 82 21 : addCommits(getAlreadyAccepted(repo), rw, accepted); 83 21 : return accepted; 84 : } 85 : 86 : public static void addCommits(Iterable<ObjectId> ids, RevWalk rw, Collection<RevCommit> out) 87 : throws IOException { 88 21 : for (ObjectId id : ids) { 89 21 : RevObject obj = rw.parseAny(id); 90 21 : if (obj instanceof RevTag) { 91 0 : obj = rw.peel(obj); 92 : } 93 21 : if (obj instanceof RevCommit) { 94 21 : out.add((RevCommit) obj); 95 : } 96 21 : } 97 21 : } 98 : 99 : private final ProjectCache projectCache; 100 : private final MergeUtilFactory mergeUtilFactory; 101 : private final Provider<InternalChangeQuery> queryProvider; 102 : 103 : @Inject 104 : SubmitDryRun( 105 : ProjectCache projectCache, 106 : MergeUtilFactory mergeUtilFactory, 107 150 : Provider<InternalChangeQuery> queryProvider) { 108 150 : this.projectCache = projectCache; 109 150 : this.mergeUtilFactory = mergeUtilFactory; 110 150 : this.queryProvider = queryProvider; 111 150 : } 112 : 113 : public boolean run( 114 : @Nullable CurrentUser caller, 115 : SubmitType submitType, 116 : Repository repo, 117 : CodeReviewRevWalk rw, 118 : BranchNameKey destBranch, 119 : ObjectId tip, 120 : ObjectId toMerge, 121 : Set<RevCommit> alreadyAccepted) 122 : throws NoSuchProjectException, IOException { 123 21 : CodeReviewCommit tipCommit = rw.parseCommit(tip); 124 21 : CodeReviewCommit toMergeCommit = rw.parseCommit(toMerge); 125 21 : RevFlag canMerge = rw.newFlag("CAN_MERGE"); 126 21 : toMergeCommit.add(canMerge); 127 21 : Arguments args = 128 : new Arguments( 129 : repo, 130 : rw, 131 21 : mergeUtilFactory.create(getProject(destBranch)), 132 : new MergeSorter( 133 : caller, 134 : rw, 135 : alreadyAccepted, 136 : canMerge, 137 : queryProvider, 138 21 : ImmutableSet.of(toMergeCommit))); 139 : 140 21 : switch (submitType) { 141 : case CHERRY_PICK: 142 3 : return CherryPick.dryRun(args, tipCommit, toMergeCommit); 143 : case FAST_FORWARD_ONLY: 144 2 : return FastForwardOnly.dryRun(args, tipCommit, toMergeCommit); 145 : case MERGE_ALWAYS: 146 2 : return MergeAlways.dryRun(args, tipCommit, toMergeCommit); 147 : case MERGE_IF_NECESSARY: 148 21 : return MergeIfNecessary.dryRun(args, tipCommit, toMergeCommit); 149 : case REBASE_IF_NECESSARY: 150 2 : return RebaseIfNecessary.dryRun(args, repo, tipCommit, toMergeCommit); 151 : case REBASE_ALWAYS: 152 2 : return RebaseAlways.dryRun(args, repo, tipCommit, toMergeCommit); 153 : case INHERIT: 154 : default: 155 0 : String errorMsg = "No submit strategy for: " + submitType; 156 0 : logger.atSevere().log("%s", errorMsg); 157 0 : throw new StorageException(errorMsg); 158 : } 159 : } 160 : 161 : private ProjectState getProject(BranchNameKey branch) throws NoSuchProjectException { 162 21 : return projectCache.get(branch.project()).orElseThrow(noSuchProject(branch.project())); 163 : } 164 : }