Merge "Remove Autocomplete noDebounce option"
diff --git a/java/com/google/gerrit/server/restapi/change/RebaseChain.java b/java/com/google/gerrit/server/restapi/change/RebaseChain.java
index db94db2..34a2623 100644
--- a/java/com/google/gerrit/server/restapi/change/RebaseChain.java
+++ b/java/com/google/gerrit/server/restapi/change/RebaseChain.java
@@ -245,6 +245,9 @@
     try (Repository repo = repoManager.openRepository(tipRsrc.getProject());
         RevWalk rw = new RevWalk(repo)) {
       List<PatchSetData> chain = getChainForCurrentPatchSet(tipRsrc);
+      if (chain.size() <= 1) {
+        return description;
+      }
       PatchSetData oldestAncestor = chain.get(0);
       if (rebaseUtil.canRebase(
           oldestAncestor.patchSet(), oldestAncestor.data().change().getDest(), repo, rw)) {
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteRef.java b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
index 5a84f69..b7fe46e 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteRef.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
 import static com.google.gerrit.entities.RefNames.isConfigRef;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.BRANCH_MODIFICATION;
 import static java.lang.String.format;
 import static org.eclipse.jgit.lib.Constants.R_REFS;
 import static org.eclipse.jgit.lib.Constants.R_TAGS;
@@ -41,6 +42,7 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.project.RefValidationHelper;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.update.context.RefUpdateContext;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -102,56 +104,58 @@
    */
   public void deleteSingleRef(ProjectState projectState, String ref, @Nullable String prefix)
       throws IOException, ResourceConflictException, AuthException, PermissionBackendException {
-    if (prefix != null && !ref.startsWith(R_REFS)) {
-      ref = prefix + ref;
-    }
+    try (RefUpdateContext ctx = RefUpdateContext.open(BRANCH_MODIFICATION)) {
+      if (prefix != null && !ref.startsWith(R_REFS)) {
+        ref = prefix + ref;
+      }
 
-    projectState.checkStatePermitsWrite();
-    permissionBackend
-        .currentUser()
-        .project(projectState.getNameKey())
-        .ref(ref)
-        .check(RefPermission.DELETE);
+      projectState.checkStatePermitsWrite();
+      permissionBackend
+          .currentUser()
+          .project(projectState.getNameKey())
+          .ref(ref)
+          .check(RefPermission.DELETE);
 
-    try (Repository repository = repoManager.openRepository(projectState.getNameKey())) {
-      RefUpdate.Result result;
-      RefUpdate u = repository.updateRef(ref);
-      u.setExpectedOldObjectId(repository.exactRef(ref).getObjectId());
-      u.setNewObjectId(ObjectId.zeroId());
-      u.setForceUpdate(true);
-      refDeletionValidator.validateRefOperation(
-          projectState.getName(),
-          identifiedUser.get(),
-          u,
-          /* pushOptions */ ImmutableListMultimap.of());
-      result = u.delete();
+      try (Repository repository = repoManager.openRepository(projectState.getNameKey())) {
+        RefUpdate.Result result;
+        RefUpdate u = repository.updateRef(ref);
+        u.setExpectedOldObjectId(repository.exactRef(ref).getObjectId());
+        u.setNewObjectId(ObjectId.zeroId());
+        u.setForceUpdate(true);
+        refDeletionValidator.validateRefOperation(
+            projectState.getName(),
+            identifiedUser.get(),
+            u,
+            /* pushOptions */ ImmutableListMultimap.of());
+        result = u.delete();
 
-      switch (result) {
-        case NEW:
-        case NO_CHANGE:
-        case FAST_FORWARD:
-        case FORCED:
-          referenceUpdated.fire(
-              projectState.getNameKey(),
-              u,
-              ReceiveCommand.Type.DELETE,
-              identifiedUser.get().state());
-          break;
+        switch (result) {
+          case NEW:
+          case NO_CHANGE:
+          case FAST_FORWARD:
+          case FORCED:
+            referenceUpdated.fire(
+                projectState.getNameKey(),
+                u,
+                ReceiveCommand.Type.DELETE,
+                identifiedUser.get().state());
+            break;
 
-        case REJECTED_CURRENT_BRANCH:
-          logger.atFine().log("Cannot delete current branch %s: %s", ref, result.name());
-          throw new ResourceConflictException("cannot delete current branch");
+          case REJECTED_CURRENT_BRANCH:
+            logger.atFine().log("Cannot delete current branch %s: %s", ref, result.name());
+            throw new ResourceConflictException("cannot delete current branch");
 
-        case LOCK_FAILURE:
-          throw new LockFailureException(String.format("Cannot delete %s", ref), u);
-        case IO_FAILURE:
-        case NOT_ATTEMPTED:
-        case REJECTED:
-        case RENAMED:
-        case REJECTED_MISSING_OBJECT:
-        case REJECTED_OTHER_REASON:
-        default:
-          throw new StorageException(String.format("Cannot delete %s: %s", ref, result.name()));
+          case LOCK_FAILURE:
+            throw new LockFailureException(String.format("Cannot delete %s", ref), u);
+          case IO_FAILURE:
+          case NOT_ATTEMPTED:
+          case REJECTED:
+          case RENAMED:
+          case REJECTED_MISSING_OBJECT:
+          case REJECTED_OTHER_REASON:
+          default:
+            throw new StorageException(String.format("Cannot delete %s: %s", ref, result.name()));
+        }
       }
     }
   }
diff --git a/java/com/google/gerrit/server/update/context/RefUpdateContext.java b/java/com/google/gerrit/server/update/context/RefUpdateContext.java
index d1c5ff8..3ac0f0d 100644
--- a/java/com/google/gerrit/server/update/context/RefUpdateContext.java
+++ b/java/com/google/gerrit/server/update/context/RefUpdateContext.java
@@ -88,7 +88,7 @@
     ACCOUNTS_UPDATE,
     /** A ref is updated as a part of direct push. */
     DIRECT_PUSH,
-    /** A ref is updated as a part of explicit branch update operation. */
+    /** A ref is updated as a part of explicit branch or ref update operation. */
     BRANCH_MODIFICATION,
     /** A ref is updated as a part of explicit tag update operation. */
     TAG_MODIFICATION,
@@ -106,7 +106,13 @@
     /** A ref is updated as a part of versioned meta data change. */
     VERSIONED_META_DATA_CHANGE,
     /** A ref is updated as a part of commit-ban operation. */
-    BAN_COMMIT
+    BAN_COMMIT,
+    /**
+     * A ref is updated inside a plugin.
+     *
+     * <p>If a plugin updates one of a special refs - it must also open a nested context.
+     */
+    PLUGIN,
   }
 
   /** Opens a provided context. */
diff --git a/java/com/google/gerrit/testing/InMemoryRepositoryManager.java b/java/com/google/gerrit/testing/InMemoryRepositoryManager.java
index 4fe6ff7..650c218 100644
--- a/java/com/google/gerrit/testing/InMemoryRepositoryManager.java
+++ b/java/com/google/gerrit/testing/InMemoryRepositoryManager.java
@@ -26,6 +26,7 @@
 import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.INIT_REPO;
 import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.MERGE_CHANGE;
 import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.OFFLINE_OPERATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.PLUGIN;
 import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.REPO_SEQ;
 import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.TAG_MODIFICATION;
 import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.VERSIONED_META_DATA_CHANGE;
@@ -160,6 +161,8 @@
             RefUpdateContext.hasOpen(MERGE_CHANGE)
                 || RefUpdateContext.hasOpen(RefUpdateType.BRANCH_MODIFICATION)
                 || RefUpdateContext.hasOpen(RefUpdateType.UPDATE_SUPERPROJECT)
+                // Plugin can update any ref
+                || RefUpdateContext.hasOpen(PLUGIN)
                 || isTestRepoCall(),
             "Ordinary ref '%s' is updated outside of the expected operation. Wrap code in the correct RefUpdateContext or add the ref as a special ref.",
             refName);