Add configuration for backup branch names

This enables backup branches to follow two different naming schemes:
it can be backed up by a time/date stamp, or it can use an
incrementing counter to create a unique name.  The time/date stamp
option is default to preserve legacy behavior.

Change-Id: I7493e9d020055685da409119ac64a46f6af6cf07
diff --git a/src/main/java/com/googlesource/gerrit/plugins/refprotection/BackupRef.java b/src/main/java/com/googlesource/gerrit/plugins/refprotection/BackupRef.java
index b35e930..e4d7fc8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/refprotection/BackupRef.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/refprotection/BackupRef.java
@@ -23,14 +23,20 @@
  */
 package com.googlesource.gerrit.plugins.refprotection;
 
+import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.events.GitReferenceUpdatedListener.Event;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.project.CreateBranch;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -47,6 +53,9 @@
   private static final Logger log =
       LoggerFactory.getLogger(BackupRef.class);
   private final CreateBranch.Factory createBranchFactory;
+  @Inject private static PluginConfigFactory cfg;
+  @Inject private static GitRepositoryManager repoManager;
+  @Inject @PluginName private static String pluginName;
 
   @Inject
   BackupRef(CreateBranch.Factory createBranchFactory) {
@@ -55,7 +64,7 @@
 
   public void createBackup(Event event, ProjectResource project) {
     String refName = event.getRefName();
-    String backupRef = get(refName);
+    String backupRef = get(project, refName);
 
     // No-op if the backup branch name is same as the original
     if (backupRef.equals(refName)) {
@@ -74,7 +83,16 @@
     }
   }
 
-  static String get(String refName) {
+  static String get(ProjectResource project, String refName) {
+    if (cfg.getFromGerritConfig(pluginName).getBoolean("useTimestamp", true)) {
+      return getTimestampBranch(refName);
+    }
+    else {
+      return getSequentialBranch(project, refName);
+    }
+  }
+
+  private static String getTimestampBranch(String refName) {
     if (refName.startsWith(R_HEADS) || refName.startsWith(R_TAGS)) {
       return String.format("%s-%s",
           R_BACKUPS + refName.replaceFirst(R_REFS, ""),
@@ -83,4 +101,27 @@
 
     return refName;
   }
+
+  private static String getSequentialBranch(ProjectResource project, String branchName) {
+    Integer rev = 1;
+    String deletedName = branchName.replaceFirst(R_REFS, "");
+    try (Repository git = repoManager.openRepository(project.getNameKey())) {
+      for (Ref ref : git.getAllRefs().values()) {
+        String name = ref.getName();
+        if (name.startsWith(R_BACKUPS + deletedName + "/")) {
+          Integer thisNum =
+              Integer.parseInt(name.substring(name.lastIndexOf('/') + 1));
+          if (thisNum >= rev) {
+            rev = thisNum + 1;
+          }
+        }
+      }
+    } catch (RepositoryNotFoundException e) {
+      log.error("Repository does not exist", e);
+    } catch (IOException e) {
+      log.error("Could not determine latest revision of deleted branch", e);
+    }
+
+    return R_BACKUPS + deletedName + "/" + rev;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/refprotection/RefProtectionModule.java b/src/main/java/com/googlesource/gerrit/plugins/refprotection/RefProtectionModule.java
index 96d250c..48de79a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/refprotection/RefProtectionModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/refprotection/RefProtectionModule.java
@@ -32,5 +32,6 @@
   protected void configure() {
     DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(
         RefUpdateListener.class);
+    requestStaticInjection(BackupRef.class);
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/refprotection/BackupRefNameTest.java b/src/test/java/com/googlesource/gerrit/plugins/refprotection/BackupRefNameTest.java
index 0ad095f..d38f3b5 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/refprotection/BackupRefNameTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/refprotection/BackupRefNameTest.java
@@ -7,22 +7,22 @@
 public class BackupRefNameTest {
 
   @Test
-  public void backupRefNameForTag() throws Exception {
-    String name = BackupRef.get("refs/tags/v1.0");
+  public void backupTimestampRefNameForTag() throws Exception {
+    String name = BackupRef.get(null, "refs/tags/v1.0");
     String expected_prefix = BackupRef.R_BACKUPS + "tags/v1.0-";
     assertThat(name).startsWith(expected_prefix);
   }
 
   @Test
-  public void backupRefNameForBranch() throws Exception {
-    String name = BackupRef.get("refs/heads/master");
+  public void backupTimestampRefNameForBranch() throws Exception {
+    String name = BackupRef.get(null, "refs/heads/master");
     String expected_prefix = BackupRef.R_BACKUPS + "heads/master-";
     assertThat(name).startsWith(expected_prefix);
   }
 
   @Test
-  public void backupRefNameForUnsupportedNamespace() throws Exception {
+  public void backupTimestampRefNameForUnsupportedNamespace() throws Exception {
     String ref = "refs/changes/45/12345/1";
-    assertThat(BackupRef.get(ref)).isEqualTo(ref);
+    assertThat(BackupRef.get(null, ref)).isEqualTo(ref);
   }
 }