SubmoduleOp: Allow all branches to be subscribed

This eases the way of handling many different branches for the
superproject subscription. As the superproject subscriptions
are not yet in a release, we can still play around with them.

When a user sees "refs = refs/heads/*:refs/heads/*" they cannot tell
whether this is 1:1 matching or any match to any match without
consulting the documentation. Differentiate the matching
strategy by keys, such that we have "all = <refspec>" as well
as "matching = <refspec>".

The "all" key implies there is no interaction between the
wildcards on the left and right side, e.g.

  all=refs/heads/*:refs/heads*

indicates that any branch can be subscribed to any other branch
in the given superproject.

"matching" however substitutes the wildcard on the right
with the captured value on the left.

  matching=refs/heads/*:refs/heads*

allows a subscription of refs/heads/foo in the submodule to the
refs/heads/foo in the superproject.

Change-Id: I84d3a72c00f76570798880adf54ce56f974466ff
This commit is contained in:
Stefan Beller
2016-08-04 16:13:11 -07:00
parent 14a81007ba
commit bb444175a6
8 changed files with 226 additions and 104 deletions

View File

@@ -86,7 +86,7 @@ the subscribe capabilities in the 'project.config' file:
and add the following lines:
----
[allowSuperproject "<superproject>"]
refs = <refspec>
matching = <refspec>
----
where the 'superproject' should be the exact project name of the superproject.
The refspec defines which branches of the submodule are allowed to be
@@ -104,7 +104,7 @@ The configuration is inherited from parent projects, such that you can have
a configuration in the "All-Projects" project like:
----
[allowSuperproject "my-only-superproject"]
refs = refs/heads/*:refs/heads/*
matching = refs/heads/*:refs/heads/*
----
and then you don't have to worry about configuring the individual projects
any more. Child projects cannot negate the parent's configuration.
@@ -147,14 +147,17 @@ a `branch` field and a url pointing to this server.
[[acl_refspec]]
=== The RefSpec in the allowSuperproject section
The RefSpec for defining the branch level access for subscriptions look similar
to Git style RefSpecs used for pushing in Git. Regular expressions
as found in the ACL configuration are not supported. The most restrictive
RefSpec is allowing one specific branch of the submodule to be subscribed
to one specific branch of the superproject via:
There are two options for specifying which branches can be subscribed
to. The most common is to set `allowSuperproject.<superproject>.matching`
to a Git-style refspec, which has the same syntax as the refspecs used
for pushing in Git. Regular expressions as found in the ACL configuration
are not supported.
The most restrictive refspec is allowing one specific branch of the
submodule to be subscribed to one specific branch of the superproject:
----
[allowSuperproject "<superproject>"]
refs = refs/heads/<submodule-branch>:refs/heads/<superproject-branch>
matching = refs/heads/<submodule-branch>:refs/heads/<superproject-branch>
----
If you want to allow for a 1:1 mapping, i.e. 'master' maps to 'master',
@@ -162,14 +165,24 @@ If you want to allow for a 1:1 mapping, i.e. 'master' maps to 'master',
'stable':
----
[allowSuperproject "<superproject>"]
refs = refs/heads/*:refs/heads/*
matching = refs/heads/*:refs/heads/*
----
If you want to enable a branch to be subscribed to any other branch of
the superproject, omit the second part of the RefSpec:
To allow all refs matching one pattern to subscribe to all refs
matching another pattern, set `allowSuperproject.<superproject>.all`
to the patterns concatenated with a colon. For example, to make a
single branch available for subscription from all branches of the
superproject:
----
[allowSuperproject "<superproject>"]
refs = refs/heads/<submodule-branch>
all = refs/heads/<submodule-branch>:refs/heads/*
----
To make all branches available for subscription from all branches of
the superproject:
----
[allowSuperproject "<superproject>"]
all = refs/heads/*:refs/heads/*
----
=== Subscription Limitations

View File

@@ -130,18 +130,25 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
return pushChangeTo(repo, "refs/heads/" + branch, "some change", "");
}
protected void allowSubmoduleSubscription(String submodule, String subBranch,
String superproject, String superBranch) throws Exception {
protected void allowSubmoduleSubscription(String submodule,
String subBranch, String superproject, String superBranch, boolean match)
throws Exception {
Project.NameKey sub = new Project.NameKey(name(submodule));
Project.NameKey superName = new Project.NameKey(name(superproject));
try (MetaDataUpdate md = metaDataUpdateFactory.create(sub)) {
md.setMessage("Added superproject subscription");
ProjectConfig pc = ProjectConfig.read(md);
SubscribeSection s = new SubscribeSection(superName);
String refspec;
if (superBranch == null) {
s.addRefSpec(subBranch);
refspec = subBranch;
} else {
s.addRefSpec(subBranch + ":" + superBranch);
refspec = subBranch + ":" + superBranch;
}
if (match) {
s.addMatchingRefSpec(refspec);
} else {
s.addMultiMatchRefSpec(refspec);
}
pc.addSubscribeSection(s);
ObjectId oldId = pc.getRevision();
@@ -151,6 +158,13 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
}
}
protected void allowMatchingSubmoduleSubscription(String submodule,
String subBranch, String superproject, String superBranch)
throws Exception {
allowSubmoduleSubscription(submodule, subBranch, superproject,
superBranch, true);
}
protected void createSubmoduleSubscription(TestRepository<?> repo, String branch,
String subscribeToRepo, String subscribeToBranch) throws Exception {
Config config = new Config();

View File

@@ -44,7 +44,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubscriptionWithoutGlobalServerSetting() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
createSubmoduleSubscription(superRepo, "master",
@@ -70,7 +70,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubscriptionToEmptyRepo() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
createSubmoduleSubscription(superRepo, "master",
@@ -87,7 +87,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubscriptionToExistingRepo() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
pushChangeTo(subRepo, "master");
@@ -104,8 +104,8 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubscriptionWildcardACLForSingleBranch() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
// master is allowed to be subscribed to any superprojects branch:
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
// master is allowed to be subscribed to master branch only:
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", null);
// create 'branch':
pushChangeTo(superRepo, "branch");
@@ -120,14 +120,14 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
expectToHaveSubmoduleState(superRepo, "master",
"subscribed-to-project", subHEAD);
expectToHaveSubmoduleState(superRepo, "branch",
"subscribed-to-project", subHEAD);
assertThat(hasSubmodule(superRepo, "branch",
"subscribed-to-project")).isFalse();
}
@Test
public void testSubscriptionWildcardACLForMissingProject() throws Exception {
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/*",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/*",
"not-existing-super-project", "refs/heads/*");
pushChangeTo(subRepo, "master");
}
@@ -136,7 +136,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubscriptionWildcardACLForMissingBranch() throws Exception {
createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/*",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/*",
"super-project", "refs/heads/*");
pushChangeTo(subRepo, "foo");
}
@@ -145,7 +145,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubscriptionWildcardACLForMissingGitmodules() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/*",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/*",
"super-project", "refs/heads/*");
pushChangeTo(superRepo, "master");
pushChangeTo(subRepo, "master");
@@ -156,7 +156,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
// any branch is allowed to be subscribed to the same superprojects branch:
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/*",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/*",
"super-project", "refs/heads/*");
// create 'branch' in both repos:
@@ -188,12 +188,53 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
"subscribed-to-project", subHEAD3);
}
@Test
public void testSubscriptionWildcardACLForManyBranches() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
// Any branch is allowed to be subscribed to any superproject branch:
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/*",
"super-project", null, false);
pushChangeTo(superRepo, "branch");
pushChangeTo(subRepo, "another-branch");
createSubmoduleSubscription(superRepo, "branch",
"subscribed-to-project", "another-branch");
ObjectId subHEAD = pushChangeTo(subRepo, "another-branch");
expectToHaveSubmoduleState(superRepo, "branch",
"subscribed-to-project", subHEAD);
}
@Test
public void testSubscriptionWildcardACLOneToManyBranches() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
// Any branch is allowed to be subscribed to any superproject branch:
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/*", false);
pushChangeTo(superRepo, "branch");
createSubmoduleSubscription(superRepo, "branch",
"subscribed-to-project", "master");
ObjectId subHEAD = pushChangeTo(subRepo, "master");
expectToHaveSubmoduleState(superRepo, "branch",
"subscribed-to-project", subHEAD);
createSubmoduleSubscription(superRepo, "branch",
"subscribed-to-project", "branch");
pushChangeTo(subRepo, "branch");
// no change expected, as only master is subscribed:
expectToHaveSubmoduleState(superRepo, "branch",
"subscribed-to-project", subHEAD);
}
@Test
@GerritConfig(name = "submodule.verboseSuperprojectUpdate", value = "false")
public void testSubmoduleShortCommitMessage() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
pushChangeTo(subRepo, "master");
@@ -220,7 +261,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubmoduleSubjectCommitMessage() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
pushChangeTo(subRepo, "master");
@@ -248,7 +289,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubmoduleCommitMessage() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
pushChangeTo(subRepo, "master");
@@ -276,7 +317,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubscriptionUnsubscribe() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
pushChangeTo(subRepo, "master");
@@ -303,7 +344,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
pushChangeTo(subRepo, "master");
@@ -329,7 +370,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubscriptionToDifferentBranches() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/foo",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/foo",
"super-project", "refs/heads/master");
createSubmoduleSubscription(superRepo, "master",
@@ -345,9 +386,9 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testBranchCircularSubscription() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
allowSubmoduleSubscription("super-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("super-project", "refs/heads/master",
"subscribed-to-project", "refs/heads/master");
pushChangeTo(subRepo, "master");
@@ -370,9 +411,9 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
allowSubmoduleSubscription("super-project", "refs/heads/dev",
allowMatchingSubmoduleSubscription("super-project", "refs/heads/dev",
"subscribed-to-project", "refs/heads/dev");
pushChangeTo(subRepo, "master");
@@ -414,7 +455,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubscriptionFailOnWrongProjectACL() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"wrong-super-project", "refs/heads/master");
pushChangeTo(subRepo, "master");
@@ -429,7 +470,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testSubscriptionFailOnWrongBranchACL() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/wrong-branch");
pushChangeTo(subRepo, "master");
@@ -448,7 +489,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project",
new Project.NameKey(name("config-repo2")));
allowSubmoduleSubscription("config-repo", "refs/heads/*",
allowMatchingSubmoduleSubscription("config-repo", "refs/heads/*",
"super-project", "refs/heads/*");
pushChangeTo(subRepo, "master");
@@ -463,7 +504,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void testAllowedButNotSubscribed() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
pushChangeTo(subRepo, "master");
@@ -490,7 +531,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
TestRepository<?> subRepo = createProjectWithPush(
"nested/subscribed-to-project");
// master is allowed to be subscribed to any superprojects branch:
allowSubmoduleSubscription("nested/subscribed-to-project",
allowMatchingSubmoduleSubscription("nested/subscribed-to-project",
"refs/heads/master", "super-project", null);
pushChangeTo(subRepo, "master");

View File

@@ -58,7 +58,7 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
public void testSubscriptionUpdateOfManyChanges() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
@@ -113,7 +113,7 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
public void testSubscriptionUpdateIncludingChangeInSuperproject() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
@@ -181,11 +181,11 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
TestRepository<?> sub2 = createProjectWithPush("sub2");
TestRepository<?> sub3 = createProjectWithPush("sub3");
allowSubmoduleSubscription("sub1", "refs/heads/master",
allowMatchingSubmoduleSubscription("sub1", "refs/heads/master",
"super-project", "refs/heads/master");
allowSubmoduleSubscription("sub2", "refs/heads/master",
allowMatchingSubmoduleSubscription("sub2", "refs/heads/master",
"super-project", "refs/heads/master");
allowSubmoduleSubscription("sub3", "refs/heads/master",
allowMatchingSubmoduleSubscription("sub3", "refs/heads/master",
"super-project", "refs/heads/master");
Config config = new Config();
@@ -227,7 +227,7 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> sub = createProjectWithPush("sub");
allowSubmoduleSubscription("sub", "refs/heads/master",
allowMatchingSubmoduleSubscription("sub", "refs/heads/master",
"super-project", "refs/heads/master");
Config config = new Config();
@@ -261,7 +261,7 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
TestRepository<?> sub = createProjectWithPush("sub");
TestRepository<?> standAlone = createProjectWithPush("standalone");
allowSubmoduleSubscription("sub", "refs/heads/master",
allowMatchingSubmoduleSubscription("sub", "refs/heads/master",
"super-project", "refs/heads/master");
createSubmoduleSubscription(superRepo, "master", "sub", "master");
@@ -301,9 +301,9 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
TestRepository<?> midRepo = createProjectWithPush("mid-project");
TestRepository<?> bottomRepo = createProjectWithPush("bottom-project");
allowSubmoduleSubscription("mid-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("mid-project", "refs/heads/master",
"top-project", "refs/heads/master");
allowSubmoduleSubscription("bottom-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("bottom-project", "refs/heads/master",
"mid-project", "refs/heads/master");
createSubmoduleSubscription(topRepo, "master", "mid-project", "master");
@@ -332,11 +332,11 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
TestRepository<?> midRepo = createProjectWithPush("mid-project");
TestRepository<?> bottomRepo = createProjectWithPush("bottom-project");
allowSubmoduleSubscription("mid-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("mid-project", "refs/heads/master",
"top-project", "refs/heads/master");
allowSubmoduleSubscription("bottom-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("bottom-project", "refs/heads/master",
"mid-project", "refs/heads/master");
allowSubmoduleSubscription("bottom-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("bottom-project", "refs/heads/master",
"top-project", "refs/heads/master");
createSubmoduleSubscription(midRepo, "master", "bottom-project", "master");
@@ -373,11 +373,11 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
createSubmoduleSubscription(topRepo, "master", "mid-project", "master");
createSubmoduleSubscription(bottomRepo, "master", "top-project", "master");
allowSubmoduleSubscription("bottom-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("bottom-project", "refs/heads/master",
"mid-project", "refs/heads/master");
allowSubmoduleSubscription("mid-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("mid-project", "refs/heads/master",
"top-project", "refs/heads/master");
allowSubmoduleSubscription("top-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("top-project", "refs/heads/master",
"bottom-project", "refs/heads/master");
ObjectId bottomMasterHead =
@@ -401,9 +401,9 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
allowSubmoduleSubscription("super-project", "refs/heads/dev",
allowMatchingSubmoduleSubscription("super-project", "refs/heads/dev",
"subscribed-to-project", "refs/heads/dev");
pushChangeTo(subRepo, "dev");

View File

@@ -29,20 +29,28 @@ import java.util.List;
@GwtIncompatible("Unemulated org.eclipse.jgit.transport.RefSpec")
public class SubscribeSection {
private final List<RefSpec> refSpecs;
private final List<RefSpec> multiMatchRefSpecs;
private final List<RefSpec> matchingRefSpecs;
private final Project.NameKey project;
public SubscribeSection(Project.NameKey p) {
project = p;
refSpecs = new ArrayList<>();
matchingRefSpecs = new ArrayList<>();
multiMatchRefSpecs = new ArrayList<>();
}
public void addRefSpec(RefSpec spec) {
refSpecs.add(spec);
public void addMatchingRefSpec(RefSpec spec) {
matchingRefSpecs.add(spec);
}
public void addRefSpec(String spec) {
refSpecs.add(new RefSpec(spec));
public void addMatchingRefSpec(String spec) {
RefSpec r = new RefSpec(spec);
matchingRefSpecs.add(r);
}
public void addMultiMatchRefSpec(String spec) {
RefSpec r = new RefSpec(spec, RefSpec.WildcardMode.ALLOW_MISMATCH);
multiMatchRefSpecs.add(r);
}
public Project.NameKey getProject() {
@@ -57,7 +65,12 @@ public class SubscribeSection {
* @return if the branch could trigger a superproject update
*/
public boolean appliesTo(Branch.NameKey branch) {
for (RefSpec r : refSpecs) {
for (RefSpec r : matchingRefSpecs) {
if (r.matchSource(branch.get())) {
return true;
}
}
for (RefSpec r : multiMatchRefSpecs) {
if (r.matchSource(branch.get())) {
return true;
}
@@ -65,8 +78,12 @@ public class SubscribeSection {
return false;
}
public Collection<RefSpec> getRefSpecs() {
return Collections.unmodifiableCollection(refSpecs);
public Collection<RefSpec> getMatchingRefSpecs() {
return Collections.unmodifiableCollection(matchingRefSpecs);
}
public Collection<RefSpec> getMultiMatchRefSpecs() {
return Collections.unmodifiableCollection(multiMatchRefSpecs);
}
@Override
@@ -74,10 +91,19 @@ public class SubscribeSection {
StringBuilder ret = new StringBuilder();
ret.append("[SubscribeSection, project=");
ret.append(project);
ret.append(", refs=[");
for (RefSpec r : refSpecs) {
ret.append(r.toString());
ret.append(", ");
if (!matchingRefSpecs.isEmpty()) {
ret.append(", matching=[");
for (RefSpec r : matchingRefSpecs) {
ret.append(r.toString());
ret.append(", ");
}
}
if (!multiMatchRefSpecs.isEmpty()) {
ret.append(", all=[");
for (RefSpec r : multiMatchRefSpecs) {
ret.append(r.toString());
ret.append(", ");
}
}
ret.append("]");
return ret.toString();

View File

@@ -132,7 +132,8 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private static final String KEY_STATE = "state";
private static final String SUBSCRIBE_SECTION = "allowSuperproject";
private static final String SUBSCRIBE_REFS = "refs";
private static final String SUBSCRIBE_MATCH_REFS = "matching";
private static final String SUBSCRIBE_MULTI_MATCH_REFS = "all";
private static final String DASHBOARD = "dashboard";
private static final String KEY_DEFAULT = "default";
@@ -848,8 +849,12 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
Project.NameKey p = new Project.NameKey(projectName);
SubscribeSection ss = new SubscribeSection(p);
for (String s : rc.getStringList(SUBSCRIBE_SECTION,
projectName, SUBSCRIBE_REFS)) {
ss.addRefSpec(s);
projectName, SUBSCRIBE_MULTI_MATCH_REFS)) {
ss.addMultiMatchRefSpec(s);
}
for (String s : rc.getStringList(SUBSCRIBE_SECTION,
projectName, SUBSCRIBE_MATCH_REFS)) {
ss.addMatchingRefSpec(s);
}
subscribeSections.put(p, ss);
}
@@ -1238,8 +1243,13 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private void saveSubscribeSections(Config rc) {
for (Project.NameKey p : subscribeSections.keySet()) {
SubscribeSection s = subscribeSections.get(p);
for (RefSpec r : s.getRefSpecs()) {
rc.setString(SUBSCRIBE_SECTION, p.get(), SUBSCRIBE_REFS, r.toString());
for (RefSpec r : s.getMatchingRefSpecs()) {
rc.setString(SUBSCRIBE_SECTION, p.get(),
SUBSCRIBE_MATCH_REFS, r.toString());
}
for (RefSpec r : s.getMultiMatchRefSpecs()) {
rc.setString(SUBSCRIBE_SECTION, p.get(),
SUBSCRIBE_MULTI_MATCH_REFS, r.toString());
}
}
}

View File

@@ -62,6 +62,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
@@ -222,33 +223,50 @@ public class SubmoduleOp {
private Collection<Branch.NameKey> getDestinationBranches(Branch.NameKey src,
SubscribeSection s) throws IOException {
Collection<Branch.NameKey> ret = new ArrayList<>();
Collection<Branch.NameKey> ret = new HashSet<>();
logDebug("Inspecting SubscribeSection " + s);
for (RefSpec r : s.getRefSpecs()) {
logDebug("Inspecting ref " + r);
if (r.matchSource(src.get())) {
if (r.getDestination() == null) {
// no need to care for wildcard, as we matched already
OpenRepo or;
try {
or = orm.openRepo(s.getProject(), false);
} catch (NoSuchProjectException e) {
// A project listed a non existent project to be allowed
// to subscribe to it. Allow this for now.
continue;
}
for (RefSpec r : s.getMatchingRefSpecs()) {
logDebug("Inspecting [matching] ref " + r);
if (!r.matchSource(src.get())) {
continue;
}
if (r.isWildcard()) {
// refs/heads/*[:refs/somewhere/*]
ret.add(new Branch.NameKey(s.getProject(),
r.expandFromSource(src.get()).getDestination()));
} else {
// e.g. refs/heads/master[:refs/heads/stable]
String dest = r.getDestination();
if (dest == null) {
dest = r.getSource();
}
ret.add(new Branch.NameKey(s.getProject(), dest));
}
}
for (Ref ref : or.repo.getRefDatabase().getRefs(
RefNames.REFS_HEADS).values()) {
ret.add(new Branch.NameKey(s.getProject(), ref.getName()));
}
} else if (r.isWildcard()) {
// refs/heads/*:refs/heads/*
ret.add(new Branch.NameKey(s.getProject(),
r.expandFromSource(src.get()).getDestination()));
} else {
// e.g. refs/heads/master:refs/heads/stable
ret.add(new Branch.NameKey(s.getProject(), r.getDestination()));
for (RefSpec r : s.getMultiMatchRefSpecs()) {
logDebug("Inspecting [all] ref " + r);
if (!r.matchSource(src.get())) {
continue;
}
OpenRepo or;
try {
or = orm.openRepo(s.getProject(), false);
} catch (NoSuchProjectException e) {
// A project listed a non existent project to be allowed
// to subscribe to it. Allow this for now, i.e. no exception is
// thrown.
continue;
}
for (Ref ref : or.repo.getRefDatabase().getRefs(
RefNames.REFS_HEADS).values()) {
if (r.getDestination() != null && !r.matchDestination(ref.getName())) {
continue;
}
Branch.NameKey b = new Branch.NameKey(s.getProject(), ref.getName());
if (!ret.contains(b)) {
ret.add(b);
}
}
}

View File

@@ -79,10 +79,10 @@ public class Schema_120 extends SchemaVersion {
}
RefSpec newRefSpec = new RefSpec(subbranch.get() + ":" + superBranch.get());
if (!s.getRefSpecs().contains(newRefSpec)) {
if (!s.getMatchingRefSpecs().contains(newRefSpec)) {
// For the migration we use only exact RefSpecs, we're not trying to
// generalize it.
s.addRefSpec(newRefSpec);
s.addMatchingRefSpec(newRefSpec);
}
pc.commit(md);