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

          Line data    Source code
       1             : // Copyright (C) 2016 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.common.base.Preconditions.checkState;
      18             : import static java.util.Objects.requireNonNull;
      19             : 
      20             : import com.google.common.base.CharMatcher;
      21             : import com.google.common.collect.ImmutableSet;
      22             : import com.google.common.flogger.FluentLogger;
      23             : import com.google.gerrit.entities.Change;
      24             : import com.google.gerrit.extensions.api.changes.SubmitInput;
      25             : import com.google.gerrit.extensions.restapi.ResourceConflictException;
      26             : import com.google.gerrit.server.change.TestSubmitInput;
      27             : import com.google.gerrit.server.git.CodeReviewCommit;
      28             : import com.google.gerrit.server.submit.MergeOp.CommitStatus;
      29             : import com.google.gerrit.server.update.BatchUpdateListener;
      30             : import java.util.ArrayList;
      31             : import java.util.Collection;
      32             : import java.util.List;
      33             : import java.util.Set;
      34             : import org.eclipse.jgit.revwalk.RevCommit;
      35             : 
      36             : public class SubmitStrategyListener implements BatchUpdateListener {
      37          53 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      38             : 
      39             :   private final Collection<SubmitStrategy> strategies;
      40             :   private final CommitStatus commitStatus;
      41             :   private final boolean failAfterRefUpdates;
      42             : 
      43             :   public SubmitStrategyListener(
      44          53 :       SubmitInput input, Collection<SubmitStrategy> strategies, CommitStatus commitStatus) {
      45          53 :     this.strategies = strategies;
      46          53 :     this.commitStatus = commitStatus;
      47          53 :     if (input instanceof TestSubmitInput) {
      48           8 :       failAfterRefUpdates = ((TestSubmitInput) input).failAfterRefUpdates;
      49             :     } else {
      50          53 :       failAfterRefUpdates = false;
      51             :     }
      52          53 :   }
      53             : 
      54             :   @Override
      55             :   public void afterUpdateRepos() throws ResourceConflictException {
      56          53 :     markCleanMerges();
      57          53 :     List<Change.Id> alreadyMerged = checkCommitStatus();
      58          53 :     findUnmergedChanges(alreadyMerged);
      59          53 :   }
      60             : 
      61             :   @Override
      62             :   public void afterUpdateRefs() throws ResourceConflictException {
      63          53 :     if (failAfterRefUpdates) {
      64           0 :       throw new ResourceConflictException("Failing after ref updates");
      65             :     }
      66          53 :   }
      67             : 
      68             :   private void findUnmergedChanges(List<Change.Id> alreadyMerged) throws ResourceConflictException {
      69          53 :     for (SubmitStrategy strategy : strategies) {
      70          53 :       if (strategy instanceof CherryPick) {
      71             :         // Can't do this sanity check for CherryPick since:
      72             :         // * CherryPick might have picked a subset of changes
      73             :         // * CherryPick might have status SKIPPED_IDENTICAL_TREE
      74           7 :         continue;
      75             :       }
      76          53 :       SubmitStrategy.Arguments args = strategy.args;
      77          53 :       Set<Change.Id> unmerged =
      78          53 :           args.mergeUtil.findUnmergedChanges(
      79          53 :               args.commitStatus.getChangeIds(args.destBranch),
      80             :               args.rw,
      81             :               args.canMergeFlag,
      82          53 :               args.mergeTip.getInitialTip(),
      83          53 :               args.mergeTip.getCurrentTip(),
      84             :               alreadyMerged);
      85          53 :       checkState(unmerged.isEmpty(), "changes not reachable from new branch tip: %s", unmerged);
      86          53 :     }
      87          53 :     commitStatus.maybeFailVerbose();
      88          53 :   }
      89             : 
      90             :   private void markCleanMerges() {
      91          53 :     for (SubmitStrategy strategy : strategies) {
      92          53 :       SubmitStrategy.Arguments args = strategy.args;
      93          53 :       RevCommit initialTip = args.mergeTip.getInitialTip();
      94          53 :       args.mergeUtil.markCleanMerges(
      95             :           args.rw,
      96             :           args.canMergeFlag,
      97          53 :           args.mergeTip.getCurrentTip(),
      98          53 :           initialTip == null ? ImmutableSet.of() : ImmutableSet.of(initialTip));
      99          53 :     }
     100          53 :   }
     101             : 
     102             :   private List<Change.Id> checkCommitStatus() throws ResourceConflictException {
     103          53 :     List<Change.Id> alreadyMerged = new ArrayList<>(commitStatus.getChangeIds().size());
     104          53 :     for (Change.Id id : commitStatus.getChangeIds()) {
     105          53 :       CodeReviewCommit commit = commitStatus.get(id);
     106          53 :       CommitMergeStatus s = commit != null ? commit.getStatusCode() : null;
     107          53 :       requireNonNull(
     108          53 :           s, String.format("change %d: change not processed by merge strategy", id.get()));
     109             : 
     110          53 :       if (commit.getStatusMessage().isPresent()) {
     111           5 :         logger.atFine().log(
     112             :             "change %d: Status for commit %s is %s. %s",
     113           5 :             id.get(), commit.name(), s, commit.getStatusMessage().get());
     114             :       } else {
     115          53 :         logger.atFine().log("change %d: Status for commit %s is %s.", id.get(), commit.name(), s);
     116             :       }
     117          53 :       switch (s) {
     118             :         case CLEAN_MERGE:
     119             :         case CLEAN_REBASE:
     120             :         case CLEAN_PICK:
     121             :         case SKIPPED_IDENTICAL_TREE:
     122          53 :           break; // Merge strategy accepted this change.
     123             : 
     124             :         case ALREADY_MERGED:
     125             :           // Already an ancestor of tip.
     126           7 :           alreadyMerged.add(commit.getPatchsetId().changeId());
     127           7 :           break;
     128             : 
     129             :         case PATH_CONFLICT:
     130             :         case REBASE_MERGE_CONFLICT:
     131             :         case MANUAL_RECURSIVE_MERGE:
     132             :         case CANNOT_CHERRY_PICK_ROOT:
     133             :         case CANNOT_REBASE_ROOT:
     134             :         case NOT_FAST_FORWARD:
     135             :         case EMPTY_COMMIT:
     136             :         case MISSING_DEPENDENCY:
     137             :         case FAST_FORWARD_INDEPENDENT_CHANGES:
     138             :           // TODO(dborowitz): Reformat these messages to be more appropriate for
     139             :           // short problem descriptions.
     140           8 :           String message = s.getDescription();
     141           8 :           if (commit.getStatusMessage().isPresent()) {
     142           5 :             message += " " + commit.getStatusMessage().get();
     143             :           }
     144           8 :           commitStatus.problem(id, CharMatcher.is('\n').collapseFrom(message, ' '));
     145           8 :           break;
     146             : 
     147             :         default:
     148           0 :           commitStatus.problem(id, "unspecified merge failure: " + s);
     149             :           break;
     150             :       }
     151          53 :     }
     152          53 :     commitStatus.maybeFailVerbose();
     153          53 :     return alreadyMerged;
     154             :   }
     155             : 
     156             :   @Override
     157             :   public void afterUpdateChanges() throws ResourceConflictException {
     158          53 :     commitStatus.maybeFail("Error updating status");
     159          53 :   }
     160             : }

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