Support deletion of branches via REST

A branch can now be deleted by DELETE on
/projects/<project-name>/branches/<branch-id>.

Change-Id: I2c9af129f024cd2668a46dd2bd8848bd0bc0655c
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 1dc8bc8..42dfcc2 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -598,6 +598,24 @@
   }
 ----
 
+[[delete-branch]]
+Delete Branch
+~~~~~~~~~~~~~
+[verse]
+'DELETE /projects/link:#project-name[\{project-name\}]/branches/link:#branch-id[\{branch-id\}]'
+
+Deletes a branch.
+
+.Request
+----
+  DELETE /projects/MyProject/branches/stable HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 204 No Content
+----
+
 [[child-project-endpoints]]
 Child Project Endpoints
 -----------------------
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java
index 110fe15..6681d94 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.project;
 
 import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.server.project.ListBranches.BranchInfo;
 import com.google.inject.TypeLiteral;
 
@@ -32,4 +33,12 @@
   public BranchInfo getBranchInfo() {
     return branchInfo;
   }
+
+  public Branch.NameKey getBranchKey() {
+    return new Branch.NameKey(getNameKey(), branchInfo.ref);
+  }
+
+  public String getRef() {
+    return branchInfo.ref;
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
new file mode 100644
index 0000000..2978c89
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
@@ -0,0 +1,108 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.project;
+
+import com.google.gerrit.common.ChangeHooks;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.DeleteBranch.Input;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class DeleteBranch implements RestModifyView<BranchResource, Input>{
+  private static final Logger log = LoggerFactory.getLogger(CreateBranch.class);
+
+  static class Input {
+  }
+
+  private final IdentifiedUser identifiedUser;
+  private final GitRepositoryManager repoManager;
+  private final Provider<ReviewDb> dbProvider;
+  private final GitReferenceUpdated referenceUpdated;
+  private final ChangeHooks hooks;
+
+  @Inject
+  DeleteBranch(IdentifiedUser identifiedUser, GitRepositoryManager repoManager,
+      Provider<ReviewDb> dbProvider, GitReferenceUpdated referenceUpdated,
+      ChangeHooks hooks) {
+    this.identifiedUser = identifiedUser;
+    this.repoManager = repoManager;
+    this.dbProvider = dbProvider;
+    this.referenceUpdated = referenceUpdated;
+    this.hooks = hooks;
+  }
+
+  @Override
+  public Object apply(BranchResource rsrc, Input input) throws AuthException,
+      ResourceConflictException, OrmException, IOException {
+    if (!rsrc.getControl().controlForRef(rsrc.getBranchKey()).canDelete()) {
+      throw new AuthException("Cannot delete branch");
+    }
+    if (dbProvider.get().changes().byBranchOpenAll(rsrc.getBranchKey())
+        .iterator().hasNext()) {
+      throw new ResourceConflictException("branch " + rsrc.getBranchKey()
+          + " has open changes");
+    }
+
+    Repository r = repoManager.openRepository(rsrc.getNameKey());
+    try {
+      RefUpdate.Result result;
+      RefUpdate u;
+      try {
+        u = r.updateRef(rsrc.getRef());
+        u.setForceUpdate(true);
+        result = u.delete();
+      } catch (IOException e) {
+        log.error("Cannot delete " + rsrc.getBranchKey(), e);
+        throw e;
+      }
+
+      switch (result) {
+        case NEW:
+        case NO_CHANGE:
+        case FAST_FORWARD:
+        case FORCED:
+          referenceUpdated.fire(rsrc.getNameKey(), u);
+          hooks.doRefUpdatedHook(rsrc.getBranchKey(), u, identifiedUser.getAccount());
+          break;
+
+        case REJECTED_CURRENT_BRANCH:
+          log.warn("Cannot delete " + rsrc.getBranchKey() + ": " + result.name());
+          throw new ResourceConflictException("cannot delete current branch");
+
+        default:
+          log.error("Cannot delete " + rsrc.getBranchKey() + ": " + result.name());
+          throw new ResourceConflictException("cannot delete branch: " + result.name());
+      }
+    } finally {
+      r.close();
+    }
+    return Response.none();
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
index 3b44f66..82b016c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
@@ -55,6 +55,7 @@
     child(PROJECT_KIND, "branches").to(BranchesCollection.class);
     put(BRANCH_KIND).to(PutBranch.class);
     get(BRANCH_KIND).to(GetBranch.class);
+    delete(BRANCH_KIND).to(DeleteBranch.class);
     install(new FactoryModuleBuilder().build(CreateBranch.Factory.class));
 
     child(PROJECT_KIND, "dashboards").to(DashboardsCollection.class);