Add ability to back up as tag

This creates the functionality required to create a tag instead of
a branch, which also allows us to store audit information, such as
who triggered the deletion or the push.

Change-Id: I31fd05675a33a6eba20dd7cf6a3703fbfd981ead
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 aab80bb..93f0227 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/refprotection/BackupRef.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/refprotection/BackupRef.java
@@ -36,8 +36,13 @@
 
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.TagBuilder;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -66,32 +71,81 @@
 
   public void createBackup(RefUpdatedEvent event, ProjectResource project) {
     String refName = event.getRefName();
-    String backupRef = get(project, refName);
-
-    // No-op if the backup branch name is same as the original
-    if (backupRef.equals(refName)) {
-      return;
-    }
 
     try (Repository git = repoManager.openRepository(project.getNameKey())) {
-      try (RevWalk revWalk = new RevWalk(git)) {
-        CreateBranch.Input input = new CreateBranch.Input();
-        input.ref = backupRef;
-        // We need to parse the commit to ensure if it's a tag, we get the
-        // commit the tag points to!
-        input.revision =
-            ObjectId.toString(revWalk.parseCommit(ObjectId.fromString(event.refUpdate.oldRev))
-                .getId());
+      String backupRef = get(project, refName);
 
-        try {
-          createBranchFactory.create(backupRef).apply(project, input);
-        } catch (BadRequestException | AuthException | ResourceConflictException
-            | IOException e) {
-          log.error(e.getMessage(), e);
+      // No-op if the backup branch name is same as the original
+      if (backupRef.equals(refName)) {
+        return;
+      }
+
+      try (RevWalk revWalk = new RevWalk(git)) {
+        if (cfg.getFromGerritConfig(pluginName).getBoolean("createTag",
+            false)) {
+          TagBuilder tag = new TagBuilder();
+          tag.setTagger(
+              new PersonIdent(event.submitter.name, event.submitter.email));
+          tag.setObjectId(revWalk
+              .parseCommit(ObjectId.fromString(event.refUpdate.oldRev)));
+          String update = "Non-fast-forward update to";
+          if (event.refUpdate.newRev.equals(ObjectId.zeroId().getName())) {
+            update = "Deleted";
+          }
+          String type = "branch";
+          if (event.refUpdate.refName.startsWith(R_TAGS)) {
+            type = "tag";
+          }
+          tag.setMessage(update + " " + type + " " + event.refUpdate.refName);
+          tag.setTag(backupRef);
+
+          ObjectInserter inserter = git.newObjectInserter();
+          ObjectId tagId = inserter.insert(tag);
+          inserter.flush();
+          RefUpdate tagRef = git.updateRef(tag.getTag());
+          tagRef.setNewObjectId(tagId);
+          tagRef.setRefLogMessage("tagged deleted branch/tag " + tag.getTag(),
+              false);
+          tagRef.setForceUpdate(false);
+          Result result = tagRef.update();
+          switch (result) {
+            case NEW:
+            case FORCED:
+              log.debug("Successfully created backup tag");
+              break;
+
+            case LOCK_FAILURE:
+              log.error("Failed to lock repository while creating backup tag");
+              break;
+
+            case REJECTED:
+              log.error("Tag already exists while creating backup tag");
+              break;
+
+            default:
+              log.error("Unknown error while creating backup tag");
+          }
+        } else {
+          CreateBranch.Input input = new CreateBranch.Input();
+          input.ref = backupRef;
+          // We need to parse the commit to ensure if it's a tag, we get the
+          // commit the tag points to!
+          input.revision = ObjectId.toString(
+              revWalk.parseCommit(ObjectId.fromString(event.refUpdate.oldRev))
+                  .getId());
+
+          try {
+            createBranchFactory.create(backupRef).apply(project, input);
+          } catch (BadRequestException | AuthException
+              | ResourceConflictException | IOException e) {
+            log.error(e.getMessage(), e);
+          }
         }
       }
+    } catch (RepositoryNotFoundException e) {
+      log.error("Repository does not exist", e);
     } catch (IOException e) {
-      log.error(e.getMessage(), e);
+      log.error("Could not open repository", e);
     }
   }