Support exclusion of ref patterns
Currently the plugin can only exclude individual refs. Support excluding
ref patterns for cases like automation creating refs/heads/auto-<uuid>
refs in the manifest repository.
Define the patterns on a git-style (only one '*' in the name). It is
consistent with other patterns in the conf and allows us to use the
RefSpec class for the matching.
Change-Id: I2de497ec2d276604230cf2430812a3776a27b58a
diff --git a/java/Documentation/about.md b/java/Documentation/about.md
index 54a5d34..95152c6 100644
--- a/java/Documentation/about.md
+++ b/java/Documentation/about.md
@@ -50,20 +50,22 @@
For the destination branch, you may also specify `refs/heads/*` to copy all
branches in the manifest repository. In this case the `srcRef` field is not
-required (it will be ignored if present). Specific branches can be excluded with
-the `exclude` option. `exclude` value is a comma-separated list of fully
-qualified refs (i.e. with the `refs/heads/` prefix).
+required (it will be ignored if present).
+
+Branches that match the srcRef can still be skipped with the `exclude`
+option. Its value is a comma-separated list of refs or ref patterns. Both of
+them fully qualified (i.e. with the `refs/heads/` prefix). Note that git ref
+patterns allow only one '*'.
```
[superproject "submodules:refs/heads/*"]
srcRepo = platforms/manifest
srcPath = manifest.xml
- exclude = refs/heads/test,refs/heads/ignoreme
+ exclude = refs/heads/ignoreme, refs/heads/*-release, refs/heads/auto-*
```
-This plugin bypasses visibility restrictions, so edits to the manifest
-repo can be used to reveal existence of hidden repositories or
-branches.
+This plugin bypasses visibility restrictions, so edits to the manifest repo can
+be used to reveal existence of hidden repositories or branches.
MANUAL TRIGGER
diff --git a/java/com/googlesource/gerrit/plugins/supermanifest/ConfigEntry.java b/java/com/googlesource/gerrit/plugins/supermanifest/ConfigEntry.java
index 09ac53c..681063f 100644
--- a/java/com/googlesource/gerrit/plugins/supermanifest/ConfigEntry.java
+++ b/java/com/googlesource/gerrit/plugins/supermanifest/ConfigEntry.java
@@ -22,11 +22,12 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
-import java.util.Set;
import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RefSpec.WildcardMode;
public class ConfigEntry {
public static final String SECTION_NAME = "superproject";
@@ -108,6 +109,7 @@
srcRefsExcluded =
Stream.of(nullToEmpty(cfg.getString(SECTION_NAME, name, "exclude")).split(","))
.map(String::trim)
+ .filter(s -> !s.isEmpty())
.collect(ImmutableSet.toImmutableSet());
xmlPath = cfg.getString(SECTION_NAME, name, "srcPath");
@@ -209,13 +211,15 @@
return destBranch;
}
- /**
- * Refs that should not be copied
- *
- * @return the refs listed in the "exclude" option
- */
- public Set<String> getSrcRefsExcluded() {
- return srcRefsExcluded;
+ public boolean excludesRef(String refName) {
+ for (String excluded : srcRefsExcluded) {
+ // ALLOW_MISMATCH allows '*' only in one side (source in this case)
+ RefSpec excludedSpec = new RefSpec(excluded, WildcardMode.ALLOW_MISMATCH);
+ if (excludedSpec.matchSource(refName)) {
+ return true;
+ }
+ }
+ return false;
}
enum ToolType {
diff --git a/java/com/googlesource/gerrit/plugins/supermanifest/SuperManifestRefUpdatedListener.java b/java/com/googlesource/gerrit/plugins/supermanifest/SuperManifestRefUpdatedListener.java
index bfaca2e..fa3d400 100644
--- a/java/com/googlesource/gerrit/plugins/supermanifest/SuperManifestRefUpdatedListener.java
+++ b/java/com/googlesource/gerrit/plugins/supermanifest/SuperManifestRefUpdatedListener.java
@@ -390,7 +390,8 @@
continue;
}
- if (c.srcRefsExcluded.contains(refName)) {
+ if (c.excludesRef(refName)) {
+ info("Skipping %s: it matches exclude conditions.", refName);
continue;
}
relevantConfigs.add(c);
diff --git a/javatests/com/googlesource/gerrit/plugins/supermanifest/ConfigEntryTest.java b/javatests/com/googlesource/gerrit/plugins/supermanifest/ConfigEntryTest.java
index 7017680..19e006d 100644
--- a/javatests/com/googlesource/gerrit/plugins/supermanifest/ConfigEntryTest.java
+++ b/javatests/com/googlesource/gerrit/plugins/supermanifest/ConfigEntryTest.java
@@ -44,6 +44,7 @@
assertThat(entry.getSrcRepoKey()).isEqualTo(Project.nameKey("manifest"));
assertThat(entry.getSrcRef()).isEqualTo("refs/heads/nyc-src");
assertThat(entry.getXmlPath()).isEqualTo("default.xml");
+ assertThat(entry.excludesRef("refs/heads/master")).isFalse();
}
@Test
@@ -135,9 +136,11 @@
cfg.fromText(builder.toString());
ConfigEntry entry = new ConfigEntry(cfg, "superproject:refs/heads/*");
-
- assertThat(entry.srcRefsExcluded)
- .containsExactly("refs/heads/a", "refs/heads/b", "refs/heads/c");
+ assertThat(entry.excludesRef("refs/heads/a")).isTrue();
+ assertThat(entry.excludesRef("refs/heads/b")).isTrue();
+ assertThat(entry.excludesRef("refs/heads/c")).isTrue();
+ assertThat(entry.excludesRef("refs/tags/r1")).isFalse();
+ assertThat(entry.excludesRef("refs/heads/aaa")).isFalse();
}
@Test
@@ -155,9 +158,39 @@
cfg.fromText(builder.toString());
ConfigEntry entry = new ConfigEntry(cfg, "superproject:refs/heads/*");
+ assertThat(entry.excludesRef("refs/heads/a")).isTrue();
+ assertThat(entry.excludesRef("refs/heads/b")).isTrue();
+ assertThat(entry.excludesRef("refs/heads/c")).isTrue();
+ assertThat(entry.excludesRef("refs/tags/r1")).isFalse();
+ assertThat(entry.excludesRef("refs/heads/aaa")).isFalse();
+ }
- assertThat(entry.srcRefsExcluded)
- .containsExactly("refs/heads/a", "refs/heads/b", "refs/heads/c");
+ @Test
+ public void excluded_patterns() throws ConfigInvalidException {
+ StringBuilder builder =
+ new StringBuilder(
+ getBasicConf(
+ "superproject",
+ "refs/heads/*",
+ "manifest",
+ "refs/heads/nyc-src",
+ "default.xml"))
+ .append(" exclude = refs/heads/a*, refs/heads/*-release \n");
+ Config cfg = new Config();
+ cfg.fromText(builder.toString());
+
+ ConfigEntry entry = new ConfigEntry(cfg, "superproject:refs/heads/*");
+ // Excluded
+ assertThat(entry.excludesRef("refs/heads/aa")).isTrue();
+ assertThat(entry.excludesRef("refs/heads/a-something")).isTrue();
+ assertThat(entry.excludesRef("refs/heads/b-release")).isTrue();
+ assertThat(entry.excludesRef("refs/heads/bb-release")).isTrue();
+
+ // Non excluded
+ assertThat(entry.excludesRef("refs/heads/a")).isFalse();
+ assertThat(entry.excludesRef("refs/heads/master")).isFalse();
+ assertThat(entry.excludesRef("refs/heads/c-release-c")).isFalse();
+ assertThat(entry.excludesRef("refs/tags/aa")).isFalse();
}
private String getBasicConf(