Line data Source code
1 : // Copyright (C) 2008 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 com.google.gerrit.common.Nullable; 18 : import com.google.gerrit.server.CurrentUser; 19 : import com.google.gerrit.server.git.CodeReviewCommit; 20 : import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk; 21 : import com.google.gerrit.server.query.change.InternalChangeQuery; 22 : import com.google.inject.Provider; 23 : import java.io.IOException; 24 : import java.util.Collection; 25 : import java.util.HashSet; 26 : import java.util.Iterator; 27 : import java.util.Set; 28 : import org.eclipse.jgit.revwalk.RevCommit; 29 : import org.eclipse.jgit.revwalk.RevCommitList; 30 : import org.eclipse.jgit.revwalk.RevFlag; 31 : 32 : public class MergeSorter { 33 : @Nullable private final CurrentUser caller; 34 : private final CodeReviewRevWalk rw; 35 : private final RevFlag canMergeFlag; 36 : private final Set<RevCommit> accepted; 37 : private final Provider<InternalChangeQuery> queryProvider; 38 : private final Set<CodeReviewCommit> incoming; 39 : 40 : public MergeSorter( 41 : @Nullable CurrentUser caller, 42 : CodeReviewRevWalk rw, 43 : Set<RevCommit> alreadyAccepted, 44 : RevFlag canMergeFlag, 45 : Provider<InternalChangeQuery> queryProvider, 46 53 : Set<CodeReviewCommit> incoming) { 47 53 : this.caller = caller; 48 53 : this.rw = rw; 49 53 : this.canMergeFlag = canMergeFlag; 50 53 : this.accepted = alreadyAccepted; 51 53 : this.queryProvider = queryProvider; 52 53 : this.incoming = incoming; 53 53 : } 54 : 55 : public Collection<CodeReviewCommit> sort(Collection<CodeReviewCommit> toMerge) 56 : throws IOException { 57 53 : final Set<CodeReviewCommit> heads = new HashSet<>(); 58 53 : final Set<CodeReviewCommit> sort = new HashSet<>(toMerge); 59 53 : while (!sort.isEmpty()) { 60 53 : final CodeReviewCommit n = removeOne(sort); 61 : 62 53 : rw.resetRetain(canMergeFlag); 63 53 : rw.markStart(n); 64 53 : for (RevCommit c : accepted) { 65 53 : rw.markUninteresting(c); 66 53 : } 67 : 68 : CodeReviewCommit c; 69 53 : RevCommitList<RevCommit> contents = new RevCommitList<>(); 70 53 : while ((c = rw.next()) != null) { 71 53 : if (!c.has(canMergeFlag) || !incoming.contains(c)) { 72 : // We cannot merge n as it would bring something we 73 : // aren't permitted to merge at this time. Drop n. 74 : // 75 5 : n.setStatusCode(CommitMergeStatus.MISSING_DEPENDENCY); 76 5 : n.setStatusMessage( 77 5 : CommitMergeStatus.createMissingDependencyMessage( 78 5 : caller, queryProvider, n.name(), c.name())); 79 5 : break; 80 : } 81 53 : contents.add(c); 82 : } 83 : 84 53 : if (n.getStatusCode() == CommitMergeStatus.MISSING_DEPENDENCY) { 85 5 : continue; 86 : } 87 : 88 : // Anything reachable through us is better merged by just 89 : // merging us directly. So prune our ancestors out and let 90 : // us merge instead. 91 : // 92 53 : sort.removeAll(contents); 93 53 : heads.removeAll(contents); 94 53 : heads.add(n); 95 53 : } 96 53 : return heads; 97 : } 98 : 99 : private static <T> T removeOne(Collection<T> c) { 100 53 : final Iterator<T> i = c.iterator(); 101 53 : final T r = i.next(); 102 53 : i.remove(); 103 53 : return r; 104 : } 105 : }