LCOV - code coverage report
Current view: top level - server/submit - RebaseSorter.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 59 60 98.3 %
Date: 2022-11-19 15:00:39 Functions: 5 5 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2012 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.common.flogger.FluentLogger;
      18             : import com.google.gerrit.entities.BranchNameKey;
      19             : import com.google.gerrit.exceptions.StorageException;
      20             : import com.google.gerrit.server.CurrentUser;
      21             : import com.google.gerrit.server.git.CodeReviewCommit;
      22             : import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
      23             : import com.google.gerrit.server.query.change.ChangeData;
      24             : import com.google.gerrit.server.query.change.InternalChangeQuery;
      25             : import com.google.inject.Provider;
      26             : import java.io.IOException;
      27             : import java.util.ArrayList;
      28             : import java.util.Collection;
      29             : import java.util.Collections;
      30             : import java.util.HashSet;
      31             : import java.util.Iterator;
      32             : import java.util.List;
      33             : import java.util.Set;
      34             : import org.eclipse.jgit.revwalk.RevCommit;
      35             : import org.eclipse.jgit.revwalk.RevFlag;
      36             : 
      37             : public class RebaseSorter {
      38          53 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      39             : 
      40             :   private final CurrentUser caller;
      41             :   private final CodeReviewRevWalk rw;
      42             :   private final RevFlag canMergeFlag;
      43             :   private final RevCommit initialTip;
      44             :   private final Set<RevCommit> alreadyAccepted;
      45             :   private final Provider<InternalChangeQuery> queryProvider;
      46             :   private final Set<CodeReviewCommit> incoming;
      47             : 
      48             :   public RebaseSorter(
      49             :       CurrentUser caller,
      50             :       CodeReviewRevWalk rw,
      51             :       RevCommit initialTip,
      52             :       Set<RevCommit> alreadyAccepted,
      53             :       RevFlag canMergeFlag,
      54             :       Provider<InternalChangeQuery> queryProvider,
      55          53 :       Set<CodeReviewCommit> incoming) {
      56          53 :     this.caller = caller;
      57          53 :     this.rw = rw;
      58          53 :     this.canMergeFlag = canMergeFlag;
      59          53 :     this.initialTip = initialTip;
      60          53 :     this.alreadyAccepted = alreadyAccepted;
      61          53 :     this.queryProvider = queryProvider;
      62          53 :     this.incoming = incoming;
      63          53 :   }
      64             : 
      65             :   public List<CodeReviewCommit> sort(Collection<CodeReviewCommit> toSort) throws IOException {
      66           5 :     final List<CodeReviewCommit> sorted = new ArrayList<>();
      67           5 :     final Set<CodeReviewCommit> sort = new HashSet<>(toSort);
      68           5 :     while (!sort.isEmpty()) {
      69           5 :       final CodeReviewCommit n = removeOne(sort);
      70             : 
      71           5 :       rw.resetRetain(canMergeFlag);
      72           5 :       rw.markStart(n);
      73           5 :       if (initialTip != null) {
      74           5 :         rw.markUninteresting(initialTip);
      75             :       }
      76             : 
      77             :       CodeReviewCommit c;
      78           5 :       final List<CodeReviewCommit> contents = new ArrayList<>();
      79           5 :       while ((c = rw.next()) != null) {
      80           5 :         if (!c.has(canMergeFlag) || !incoming.contains(c)) {
      81           3 :           if (isAlreadyMerged(c, n.change().getDest())) {
      82           3 :             rw.markUninteresting(c);
      83             :           } else {
      84             :             // We cannot merge n as it would bring something we
      85             :             // aren't permitted to merge at this time. Drop n.
      86             :             //
      87           2 :             n.setStatusCode(CommitMergeStatus.MISSING_DEPENDENCY);
      88           2 :             n.setStatusMessage(
      89           2 :                 CommitMergeStatus.createMissingDependencyMessage(
      90           2 :                     caller, queryProvider, n.name(), c.name()));
      91             :           }
      92             :           // Stop RevWalk because c is either a merged commit or a missing
      93             :           // dependency. Not need to walk further.
      94           2 :           break;
      95             :         }
      96           5 :         contents.add(c);
      97             :       }
      98             : 
      99           5 :       if (n.getStatusCode() == CommitMergeStatus.MISSING_DEPENDENCY) {
     100           2 :         continue;
     101             :       }
     102             : 
     103           5 :       sort.removeAll(contents);
     104           5 :       Collections.reverse(contents);
     105           5 :       sorted.removeAll(contents);
     106           5 :       sorted.addAll(contents);
     107           5 :     }
     108           5 :     return sorted;
     109             :   }
     110             : 
     111             :   private boolean isAlreadyMerged(CodeReviewCommit commit, BranchNameKey dest) throws IOException {
     112           3 :     try (CodeReviewRevWalk mirw = CodeReviewCommit.newRevWalk(rw.getObjectReader())) {
     113           3 :       mirw.reset();
     114           3 :       mirw.markStart(commit);
     115             :       // check if the commit is merged in other branches
     116           3 :       for (RevCommit accepted : alreadyAccepted) {
     117           3 :         if (mirw.isMergedInto(mirw.parseCommit(commit), mirw.parseCommit(accepted))) {
     118           3 :           logger.atFine().log(
     119           3 :               "Dependency %s merged into branch head %s.", commit.getName(), accepted.getName());
     120           3 :           return true;
     121             :         }
     122           3 :       }
     123             : 
     124             :       // check if the commit associated change is merged in the same branch
     125           2 :       List<ChangeData> changes = queryProvider.get().byCommit(commit);
     126           2 :       for (ChangeData change : changes) {
     127           2 :         if (change.change().isMerged() && change.change().getDest().equals(dest)) {
     128           2 :           logger.atFine().log(
     129           2 :               "Dependency %s associated with merged change %s.", commit.getName(), change.getId());
     130           2 :           return true;
     131             :         }
     132           2 :       }
     133           2 :       return false;
     134           3 :     } catch (StorageException e) {
     135           0 :       throw new IOException(e);
     136             :     }
     137             :   }
     138             : 
     139             :   private static <T> T removeOne(Collection<T> c) {
     140           5 :     final Iterator<T> i = c.iterator();
     141           5 :     final T r = i.next();
     142           5 :     i.remove();
     143           5 :     return r;
     144             :   }
     145             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750