Add step to check for collisions to magic refs

Importing a repository from github, containing refs starting with the
magic ref prefix (refs/for or refs/meta) will lead to errors where
any push to that repository would fail with:

fatal: One or more refs/for/ names blocks change upload

Prevent repositories containing refs starting with refs/for or refs/meta
from being imported and provide a meaningful error message to the user
so that they can fix the issue and try again.

Bug: Issue 14059
Change-Id: I812e77624a6c7663d6efdec0694fef37b8b8ee4b
diff --git a/README.md b/README.md
index 018f4d0..d01069d 100644
--- a/README.md
+++ b/README.md
@@ -190,3 +190,22 @@
 
 After the installation, Eclipse must be restarted and compilation
 errors should disappear.
+
+### Notes
+
+#### Magic refs
+
+Before importing a repository from github, this plugin checks that its git refs
+do not clash with Gerrit magic refs, since importing those refs would prevent
+users from creating change requests.
+
+Attempting to import repositories having refs starting with `refs/for/` or
+`refs/meta` will fail with an error message.
+For example:
+
+```text
+Found 2 ref(s): Please remove or rename the following refs and try again:
+  refs/for/foo, refs/meta/bar
+```
+
+More information on Gerrit magic refs can be found [here](https://gerrit-review.googlesource.com/Documentation/intro-user.html#upload-change)
\ No newline at end of file
diff --git a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/GuiceHttpModule.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/GuiceHttpModule.java
index 4e6448b..f75030d 100644
--- a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/GuiceHttpModule.java
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/GuiceHttpModule.java
@@ -24,6 +24,7 @@
 import com.googlesource.gerrit.plugins.github.git.GitCloneStep;
 import com.googlesource.gerrit.plugins.github.git.GitHubRepository;
 import com.googlesource.gerrit.plugins.github.git.GitImporter;
+import com.googlesource.gerrit.plugins.github.git.MagicRefCheckStep;
 import com.googlesource.gerrit.plugins.github.git.ProtectedBranchesCheckStep;
 import com.googlesource.gerrit.plugins.github.git.PullRequestImportJob;
 import com.googlesource.gerrit.plugins.github.git.ReplicateProjectStep;
@@ -68,6 +69,10 @@
             .build(ReplicateProjectStep.Factory.class));
     install(
         new FactoryModuleBuilder()
+            .implement(MagicRefCheckStep.class, MagicRefCheckStep.class)
+            .build(MagicRefCheckStep.Factory.class));
+    install(
+        new FactoryModuleBuilder()
             .implement(PullRequestImportJob.class, PullRequestImportJob.class)
             .build(PullRequestImportJob.Factory.class));
     install(
diff --git a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/GitImporter.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/GitImporter.java
index 66a7b5c..ff81411 100644
--- a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/GitImporter.java
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/GitImporter.java
@@ -27,6 +27,7 @@
 
   private static final Logger log = LoggerFactory.getLogger(GitImporter.class);
   private final ProtectedBranchesCheckStep.Factory protectedBranchesCheckFactory;
+  private final MagicRefCheckStep.Factory magicRefCheckFactory;
   private final GitCloneStep.Factory cloneFactory;
   private final CreateProjectStep.Factory projectFactory;
   private final ReplicateProjectStep.Factory replicateFactory;
@@ -37,6 +38,7 @@
       GitCloneStep.Factory cloneFactory,
       CreateProjectStep.Factory projectFactory,
       ReplicateProjectStep.Factory replicateFactory,
+      MagicRefCheckStep.Factory magicRefCheckFactory,
       JobExecutor executor,
       IdentifiedUser user) {
     super(executor, user);
@@ -44,6 +46,7 @@
     this.cloneFactory = cloneFactory;
     this.projectFactory = projectFactory;
     this.replicateFactory = replicateFactory;
+    this.magicRefCheckFactory = magicRefCheckFactory;
   }
 
   public void clone(int idx, String organisation, String repository, String description) {
@@ -51,6 +54,7 @@
       ProtectedBranchesCheckStep protectedBranchesCheckStep =
           protectedBranchesCheckFactory.create(organisation, repository);
       GitCloneStep cloneStep = cloneFactory.create(organisation, repository);
+      MagicRefCheckStep magicRefCheckStep = magicRefCheckFactory.create(organisation, repository);
       CreateProjectStep projectStep =
           projectFactory.create(organisation, repository, description, user.getUserName().get());
       ReplicateProjectStep replicateStep = replicateFactory.create(organisation, repository);
@@ -60,6 +64,7 @@
               organisation,
               repository,
               protectedBranchesCheckStep,
+              magicRefCheckStep,
               cloneStep,
               projectStep,
               replicateStep);
diff --git a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/MagicRefCheckStep.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/MagicRefCheckStep.java
new file mode 100644
index 0000000..616a31c
--- /dev/null
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/MagicRefCheckStep.java
@@ -0,0 +1,72 @@
+// Copyright (C) 2021 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.googlesource.gerrit.plugins.github.git;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.server.util.MagicBranch;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+import com.googlesource.gerrit.plugins.github.GitHubConfig;
+import java.util.List;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.kohsuke.github.GHRef;
+
+public class MagicRefCheckStep extends ImportStep {
+  public interface Factory {
+    MagicRefCheckStep create(
+        @Assisted("organisation") String organisation, @Assisted("name") String repository);
+  }
+
+  @Inject
+  public MagicRefCheckStep(
+      GitHubConfig config,
+      GitHubRepository.Factory gitHubRepoFactory,
+      @Assisted("organisation") String organisation,
+      @Assisted("name") String repository) {
+    super(config.gitHubUrl, organisation, repository, gitHubRepoFactory);
+  }
+
+  @Override
+  public void doImport(ProgressMonitor progress) throws Exception {
+    try {
+      GHRef[] allRefs = getRepository().getRefs();
+      progress.beginTask("Checking magic refs", allRefs.length);
+
+      List<String> offendingRefs = Lists.newLinkedList();
+      for (GHRef ref : allRefs) {
+        if (MagicBranch.isMagicBranch(ref.getRef())
+            || ref.getRef().startsWith(RefNames.REFS_META)) {
+          offendingRefs.add(ref.getRef());
+        }
+        progress.update(1);
+      }
+
+      if (!offendingRefs.isEmpty()) {
+        throw new MagicRefFoundException(
+            String.format(
+                "Found %d ref(s): Please remove or rename the following ref(s) and try again: %s",
+                offendingRefs.size(), Joiner.on(", ").join(offendingRefs)));
+      }
+    } finally {
+      progress.endTask();
+    }
+  }
+
+  @Override
+  public boolean rollback() {
+    return true;
+  }
+}
diff --git a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/MagicRefFoundException.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/MagicRefFoundException.java
new file mode 100644
index 0000000..263ee9d
--- /dev/null
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/MagicRefFoundException.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 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.googlesource.gerrit.plugins.github.git;
+
+public class MagicRefFoundException extends GitException {
+
+  public MagicRefFoundException(String message) {
+    super(message);
+  }
+
+  @Override
+  public String getErrorDescription() {
+    return String.format("Clash with Gerrit magic refs. %s", getMessage());
+  }
+}