Merge branch 'stable-3.3' into stable-3.4

* stable-3.3:
  Add step to check for collisions to magic refs
  Add branch protection check during the project import
  Set version to 3.3.0

Change-Id: I30951d25d4461d17d313682e208059b91f61e2cd
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 12acdb2..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,8 @@
 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;
 import com.googlesource.gerrit.plugins.github.notification.WebhookServlet;
@@ -51,6 +53,10 @@
 
     install(
         new FactoryModuleBuilder()
+            .implement(ProtectedBranchesCheckStep.class, ProtectedBranchesCheckStep.class)
+            .build(ProtectedBranchesCheckStep.Factory.class));
+    install(
+        new FactoryModuleBuilder()
             .implement(GitCloneStep.class, GitCloneStep.class)
             .build(GitCloneStep.Factory.class));
     install(
@@ -63,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/AbstractCloneJob.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/AbstractCloneJob.java
index 4421841..753575f 100644
--- a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/AbstractCloneJob.java
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/AbstractCloneJob.java
@@ -26,6 +26,9 @@
 
   protected String getErrorDescription(Throwable exception) {
     LOG.error("Job " + this + " FAILED", exception);
+    if (exception instanceof ProtectedBranchFoundException) {
+      return exception.getMessage();
+    }
     if (GitException.class.isAssignableFrom(exception.getClass())) {
       return ((GitException) exception).getErrorDescription();
     } else if (ProvisionException.class.isAssignableFrom(exception.getClass())) {
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 0176091..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
@@ -26,31 +26,48 @@
   public static class Provider extends HttpSessionProvider<GitImporter> {}
 
   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;
 
   @Inject
   public GitImporter(
+      ProtectedBranchesCheckStep.Factory protectedBranchesCheckFactory,
       GitCloneStep.Factory cloneFactory,
       CreateProjectStep.Factory projectFactory,
       ReplicateProjectStep.Factory replicateFactory,
+      MagicRefCheckStep.Factory magicRefCheckFactory,
       JobExecutor executor,
       IdentifiedUser user) {
     super(executor, user);
+    this.protectedBranchesCheckFactory = protectedBranchesCheckFactory;
     this.cloneFactory = cloneFactory;
     this.projectFactory = projectFactory;
     this.replicateFactory = replicateFactory;
+    this.magicRefCheckFactory = magicRefCheckFactory;
   }
 
   public void clone(int idx, String organisation, String repository, String description) {
     try {
+      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);
       GitImportJob gitCloneJob =
-          new GitImportJob(idx, organisation, repository, cloneStep, projectStep, replicateStep);
+          new GitImportJob(
+              idx,
+              organisation,
+              repository,
+              protectedBranchesCheckStep,
+              magicRefCheckStep,
+              cloneStep,
+              projectStep,
+              replicateStep);
       log.debug("New Git clone job created: " + gitCloneJob);
       schedule(idx, gitCloneJob);
     } catch (Throwable e) {
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());
+  }
+}
diff --git a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/ProtectedBranchFoundException.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/ProtectedBranchFoundException.java
new file mode 100644
index 0000000..221152f
--- /dev/null
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/ProtectedBranchFoundException.java
@@ -0,0 +1,22 @@
+// 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 ProtectedBranchFoundException extends Exception {
+
+  public ProtectedBranchFoundException(String msg) {
+    super(msg);
+  }
+}
diff --git a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/ProtectedBranchesCheckStep.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/ProtectedBranchesCheckStep.java
new file mode 100644
index 0000000..5b2f763
--- /dev/null
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/git/ProtectedBranchesCheckStep.java
@@ -0,0 +1,67 @@
+// 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.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+import com.googlesource.gerrit.plugins.github.GitHubConfig;
+import java.util.Collection;
+import java.util.List;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.kohsuke.github.GHBranch;
+
+public class ProtectedBranchesCheckStep extends ImportStep {
+
+  public interface Factory {
+    ProtectedBranchesCheckStep create(
+        @Assisted("organisation") String organisation, @Assisted("name") String repository);
+  }
+
+  @Inject
+  public ProtectedBranchesCheckStep(
+      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 {
+    Collection<GHBranch> branches = getRepository().getBranches().values();
+    progress.beginTask("Checking branch protection", branches.size());
+    List<String> protectedBranchNames = Lists.newLinkedList();
+    for (GHBranch branch : branches) {
+      if (branch.isProtected()) {
+        protectedBranchNames.add(branch.getName());
+      }
+      progress.update(1);
+    }
+    progress.endTask();
+    if (!protectedBranchNames.isEmpty()) {
+      throw new ProtectedBranchFoundException(
+          String.format(
+              "Cannot import project with protected branches, you should remove protection from:%s",
+              Joiner.on(",").join(protectedBranchNames)));
+    }
+  }
+
+  @Override
+  public boolean rollback() {
+    return true;
+  }
+}