LCOV - code coverage report
Current view: top level - git - RefUpdateUtil.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 35 41 85.4 %
Date: 2022-11-19 15:00:39 Functions: 5 5 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2017 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.git;
      16             : 
      17             : import com.google.common.annotations.VisibleForTesting;
      18             : import java.io.IOException;
      19             : import org.eclipse.jgit.internal.JGitText;
      20             : import org.eclipse.jgit.lib.BatchRefUpdate;
      21             : import org.eclipse.jgit.lib.NullProgressMonitor;
      22             : import org.eclipse.jgit.lib.RefUpdate;
      23             : import org.eclipse.jgit.lib.Repository;
      24             : import org.eclipse.jgit.revwalk.RevWalk;
      25             : import org.eclipse.jgit.transport.ReceiveCommand;
      26             : 
      27             : /** Static utilities for working with JGit's ref update APIs. */
      28             : public class RefUpdateUtil {
      29             :   /**
      30             :    * Execute a batch ref update, throwing a checked exception if not all updates succeeded.
      31             :    *
      32             :    * <p>Creates a new {@link RevWalk} used only for this operation.
      33             :    *
      34             :    * @param bru batch update; should already have been executed.
      35             :    * @param repo repository that created {@code bru}.
      36             :    * @throws LockFailureException if the transaction was aborted due to lock failure; see {@link
      37             :    *     #checkResults(BatchRefUpdate)} for details.
      38             :    * @throws IOException if any result was not {@code OK}.
      39             :    */
      40             :   public static void executeChecked(BatchRefUpdate bru, Repository repo) throws IOException {
      41         152 :     try (RevWalk rw = new RevWalk(repo)) {
      42         152 :       executeChecked(bru, rw);
      43             :     }
      44         152 :   }
      45             : 
      46             :   /**
      47             :    * Execute a batch ref update, throwing a checked exception if not all updates succeeded.
      48             :    *
      49             :    * @param bru batch update; should already have been executed.
      50             :    * @param rw walk for executing the update.
      51             :    * @throws LockFailureException if the transaction was aborted due to lock failure; see {@link
      52             :    *     #checkResults(BatchRefUpdate)} for details.
      53             :    * @throws IOException if any result was not {@code OK}.
      54             :    */
      55             :   public static void executeChecked(BatchRefUpdate bru, RevWalk rw) throws IOException {
      56         152 :     bru.execute(rw, NullProgressMonitor.INSTANCE);
      57         152 :     checkResults(bru);
      58         152 :   }
      59             : 
      60             :   /**
      61             :    * Check results of all commands in the update batch, reducing to a single exception if there was
      62             :    * a failure.
      63             :    *
      64             :    * <p>Throws {@link LockFailureException} if at least one command failed with {@code
      65             :    * LOCK_FAILURE}, and the entire transaction was aborted, i.e. any non-{@code LOCK_FAILURE}
      66             :    * results, if there were any, failed with "transaction aborted".
      67             :    *
      68             :    * <p>In particular, if the underlying ref database does not {@link
      69             :    * org.eclipse.jgit.lib.RefDatabase#performsAtomicTransactions() perform atomic transactions},
      70             :    * then a combination of {@code LOCK_FAILURE} on one ref and {@code OK} or another result on other
      71             :    * refs will <em>not</em> throw {@code LockFailureException}.
      72             :    *
      73             :    * @param bru batch update; should already have been executed.
      74             :    * @throws LockFailureException if the transaction was aborted due to lock failure.
      75             :    * @throws IOException if any result was not {@code OK}.
      76             :    */
      77             :   @VisibleForTesting
      78             :   static void checkResults(BatchRefUpdate bru) throws IOException {
      79         153 :     if (bru.getCommands().isEmpty()) {
      80          29 :       return;
      81             :     }
      82             : 
      83         153 :     int lockFailure = 0;
      84         153 :     int aborted = 0;
      85         153 :     int failure = 0;
      86             : 
      87         153 :     for (ReceiveCommand cmd : bru.getCommands()) {
      88         153 :       if (cmd.getResult() != ReceiveCommand.Result.OK) {
      89          12 :         failure++;
      90             :       }
      91         153 :       if (cmd.getResult() == ReceiveCommand.Result.LOCK_FAILURE) {
      92          12 :         lockFailure++;
      93         153 :       } else if (cmd.getResult() == ReceiveCommand.Result.REJECTED_OTHER_REASON
      94          12 :           && JGitText.get().transactionAborted.equals(cmd.getMessage())) {
      95          12 :         aborted++;
      96             :       }
      97         153 :     }
      98             : 
      99         153 :     if (lockFailure + aborted == bru.getCommands().size()) {
     100          12 :       throw new LockFailureException("Update aborted with one or more lock failures: " + bru, bru);
     101         153 :     } else if (failure > 0) {
     102           1 :       throw new GitUpdateFailureException("Update failed: " + bru, bru);
     103             :     }
     104         153 :   }
     105             : 
     106             :   /**
     107             :    * Check results of a single ref update, throwing an exception if there was a failure.
     108             :    *
     109             :    * @param ru ref update; must already have been executed.
     110             :    * @throws IllegalArgumentException if the result was {@code NOT_ATTEMPTED}.
     111             :    * @throws LockFailureException if the result was {@code LOCK_FAILURE}.
     112             :    * @throws IOException if the result failed for another reason.
     113             :    */
     114             :   public static void checkResult(RefUpdate ru) throws IOException {
     115         151 :     RefUpdate.Result result = ru.getResult();
     116         151 :     switch (result) {
     117             :       case NOT_ATTEMPTED:
     118           0 :         throw new IllegalArgumentException("Not attempted: " + ru.getName());
     119             :       case NEW:
     120             :       case FORCED:
     121             :       case NO_CHANGE:
     122             :       case FAST_FORWARD:
     123             :       case RENAMED:
     124         151 :         return;
     125             :       case LOCK_FAILURE:
     126           1 :         throw new LockFailureException("Failed to update " + ru.getName() + ": " + result, ru);
     127             :       default:
     128             :       case IO_FAILURE:
     129             :       case REJECTED:
     130             :       case REJECTED_CURRENT_BRANCH:
     131             :       case REJECTED_MISSING_OBJECT:
     132             :       case REJECTED_OTHER_REASON:
     133           0 :         throw new GitUpdateFailureException(
     134           0 :             "Failed to update " + ru.getName() + ": " + ru.getResult(), ru);
     135             :     }
     136             :   }
     137             : 
     138             :   /**
     139             :    * Delete a single ref, throwing a checked exception on failure.
     140             :    *
     141             :    * <p>Does not require that the ref have any particular old value. Succeeds as a no-op if the ref
     142             :    * did not exist.
     143             :    *
     144             :    * @param repo repository.
     145             :    * @param refName ref name to delete.
     146             :    * @throws LockFailureException if a low-level lock failure (e.g. compare-and-swap failure)
     147             :    *     occurs.
     148             :    * @throws IOException if an error occurred.
     149             :    */
     150             :   public static void deleteChecked(Repository repo, String refName) throws IOException {
     151           1 :     RefUpdate ru = repo.updateRef(refName);
     152           1 :     ru.setForceUpdate(true);
     153           1 :     ru.setCheckConflicting(false);
     154           1 :     switch (ru.delete()) {
     155             :       case FORCED:
     156             :         // Ref was deleted.
     157           1 :         return;
     158             : 
     159             :       case NEW:
     160             :         // Ref didn't exist (yes, really).
     161           1 :         return;
     162             : 
     163             :       case LOCK_FAILURE:
     164           0 :         throw new LockFailureException("Failed to delete " + refName + ": " + ru.getResult(), ru);
     165             : 
     166             :         // Not really failures, but should not be the result of a deletion, so the best option is to
     167             :         // throw.
     168             :       case NO_CHANGE:
     169             :       case FAST_FORWARD:
     170             :       case RENAMED:
     171             :       case NOT_ATTEMPTED:
     172             : 
     173             :       case IO_FAILURE:
     174             :       case REJECTED:
     175             :       case REJECTED_CURRENT_BRANCH:
     176             :       case REJECTED_MISSING_OBJECT:
     177             :       case REJECTED_OTHER_REASON:
     178             :       default:
     179           0 :         throw new GitUpdateFailureException(
     180           0 :             "Failed to delete " + refName + ": " + ru.getResult(), ru);
     181             :     }
     182             :   }
     183             : 
     184             :   private RefUpdateUtil() {}
     185             : }

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