PUSH_MERGE should evaluate against refs/heads/*

Any "PUSH_MERGE" on "refs/for/*" will be automatially migrated
when the config is loaded.

Change-Id: I0069d7f73f2e13532831adc43aabf92ebd43d9e8
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 9e45c41..bff4094 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -628,13 +628,6 @@
 by Gerrit. By granting `Push` without `Push Merge Commit`, the only
 merges that enter the system will be those created by Gerrit.
 
-The reference name connected to a `Push Merge Commit` entry must always
-be prefixed with `refs/for/`, for example `refs/for/refs/heads/BRANCH`.
-This applies even for an entry that complements a `Push` entry for
-`refs/heads/BRANCH` that allows direct pushes of non-merge commits, and
-the intention of the `Push Merge Commit` entry is to allow direct pushes
-of merge commits.
-
 
 [[category_push_annotated]]
 [[category_create_annotated]]
@@ -915,7 +908,7 @@
 
 * xref:category_read[`Read`] on 'refs/heads/\*' and 'refs/tags/*'
 * xref:category_push[`Create Review`] to 'refs/heads/*'
-* xref:category_push_merge[`Push merge commit`] to 'refs/for/refs/heads/*'
+* xref:category_push_merge[`Push merge commit`] to 'refs/heads/*'
 * xref:category_forge_author[`Forge Author Identity`] to 'refs/heads/*'
 * link:config-labels.html#label_Code-Review[`Label: Code-Review`] with range '-2' to '+2' for 'refs/heads/*'
 * link:config-labels.html#label_Verified[`Label: Verified`] with range '-1' to '+1' for 'refs/heads/*'
diff --git a/java/com/google/gerrit/server/permissions/RefControl.java b/java/com/google/gerrit/server/permissions/RefControl.java
index c2d6aab..bf3a133 100644
--- a/java/com/google/gerrit/server/permissions/RefControl.java
+++ b/java/com/google/gerrit/server/permissions/RefControl.java
@@ -150,7 +150,7 @@
 
   /** @return true if this user can submit merge patch sets to this ref */
   private boolean canUploadMerges() {
-    return projectControl.controlForRef("refs/for/" + refName).canPerform(Permission.PUSH_MERGE);
+    return canPerform(Permission.PUSH_MERGE);
   }
 
   /** @return true if the user can update the reference as a fast-forward. */
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index a588709..7bb5afe 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -779,6 +779,12 @@
       migratedAs.addPermission(perm);
       return true;
     }
+    if (isRefsForExclusively(refName) && perm.getName().equals(Permission.PUSH_MERGE)) {
+      // No need to migrate PUSH_MERGE on refs/*
+      AccessSection migratedAs = getAccessSection(unRefsFor(refName), true);
+      migratedAs.addPermission(perm);
+      return true;
+    }
     return false;
   }
 
diff --git a/java/com/google/gerrit/server/schema/AllProjectsCreator.java b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
index 4a914a6..8c5cabd 100644
--- a/java/com/google/gerrit/server/schema/AllProjectsCreator.java
+++ b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
@@ -162,7 +162,6 @@
       AccessSection heads = config.getAccessSection(AccessSection.HEADS, true);
       AccessSection tags = config.getAccessSection("refs/tags/*", true);
       AccessSection meta = config.getAccessSection(RefNames.REFS_CONFIG, true);
-      AccessSection magic = config.getAccessSection("refs/for/" + AccessSection.ALL, true);
 
       grant(config, cap, GlobalCapability.ADMINISTRATE_SERVER, admin);
       grant(config, all, Permission.READ, admin, anonymous);
@@ -185,6 +184,7 @@
       grant(config, heads, cr, -2, 2, admin, owners);
       grant(config, heads, Permission.CREATE, admin, owners);
       grant(config, heads, Permission.PUSH, admin, owners);
+      grant(config, all, Permission.PUSH_MERGE, registered);
       grant(config, heads, Permission.SUBMIT, admin, owners);
       grant(config, heads, Permission.FORGE_AUTHOR, registered);
       grant(config, heads, Permission.FORGE_COMMITTER, admin, owners);
@@ -194,8 +194,6 @@
       grant(config, tags, Permission.CREATE_TAG, admin, owners);
       grant(config, tags, Permission.CREATE_SIGNED_TAG, admin, owners);
 
-      grant(config, magic, Permission.PUSH_MERGE, registered);
-
       meta.getPermission(Permission.READ, true).setExclusiveGroup(true);
       grant(config, meta, Permission.READ, admin, owners);
       grant(config, meta, cr, -2, 2, admin, owners);
diff --git a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
index 4c33516..5950348 100644
--- a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
+++ b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
@@ -693,6 +693,24 @@
   }
 
   @Test
+  public void readConfigAddPushMergeRefsForStarIsMigrated() throws Exception {
+    RevCommit rev =
+        tr.commit()
+            .add("groups", group(developers))
+            .add("project.config", "[access \"refs/for/*\"]\n" + "  pushMerge = group Developers\n")
+            .create();
+
+    ProjectConfig cfg = read(rev);
+    AccessSection as = cfg.getAccessSection("refs/for/*");
+    assertThat(as).isNull();
+    as = cfg.getAccessSection("refs/*");
+    assertThat(as).isNotNull();
+    PermissionRule rule = new PermissionRule(developers);
+    assertThat(as.getPermission(Permission.PUSH_MERGE, false).getRules())
+        .isEqualTo(Lists.newArrayList(rule));
+  }
+
+  @Test
   public void readCommentLinkMatchButNoHtmlOrLink() throws Exception {
     RevCommit rev =
         tr.commit()