diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 735243f..b8947ec 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,5 +1,6 @@ -#Thu Feb 17 14:25:30 CST 2011 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.source=1.5 -org.eclipse.jdt.core.compiler.compliance=1.5 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/build.gradle b/build.gradle index 5b4650d..e716978 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,9 @@ jenkinsPlugin { // source gitHubUrl = 'https://github.com/jenkinsci/multiple-scms-plugin' + dependencies { + jenkinsPlugins 'org.jenkins-ci.plugins:scm-api:2.0.3@jar' + } // the developers section is optional, and corresponds to the POM developers section developers { developer { diff --git a/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCM.java b/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCM.java index fb105e9..a32573a 100644 --- a/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCM.java +++ b/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCM.java @@ -1,27 +1,5 @@ package org.jenkinsci.plugins.multiplescms; -import hudson.EnvVars; -import hudson.Extension; -import hudson.FilePath; -import hudson.Launcher; -import hudson.model.Action; -import hudson.model.BuildListener; -import hudson.model.Saveable; -import hudson.model.TaskListener; -import hudson.model.AbstractBuild; -import hudson.model.AbstractProject; -import hudson.model.Descriptor; -import hudson.model.Run; -import hudson.model.Hudson; -import hudson.scm.ChangeLogParser; -import hudson.scm.PollingResult; -import hudson.scm.PollingResult.Change; -import hudson.scm.SCMDescriptor; -import hudson.scm.SCMRevisionState; -import hudson.scm.NullSCM; -import hudson.scm.SCM; -import hudson.util.DescribableList; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -32,9 +10,10 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import net.sf.json.JSONArray; -import net.sf.json.JSONObject; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringEscapeUtils; @@ -42,9 +21,32 @@ import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.export.Exported; +import hudson.EnvVars; +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.Action; +import hudson.model.Descriptor; +import hudson.model.Job; +import hudson.model.Run; +import hudson.model.Saveable; +import hudson.model.TaskListener; +import hudson.scm.ChangeLogParser; +import hudson.scm.NullSCM; +import hudson.scm.PollingResult; +import hudson.scm.PollingResult.Change; +import hudson.scm.SCM; +import hudson.scm.SCMDescriptor; +import hudson.scm.SCMRevisionState; +import hudson.util.DescribableList; +import jenkins.model.Jenkins; +import net.sf.json.JSONObject; + public class MultiSCM extends SCM implements Saveable { private DescribableList> scms = - new DescribableList>(this); + new DescribableList<>(this); @DataBoundConstructor public MultiSCM(List scmList) throws IOException { @@ -57,16 +59,16 @@ public List getConfiguredSCMs() { } @Override - public SCMRevisionState calcRevisionsFromBuild(AbstractBuild build, - Launcher launcher, TaskListener listener) throws IOException, - InterruptedException { + public @CheckForNull SCMRevisionState calcRevisionsFromBuild( + @Nonnull Run build, @Nullable FilePath workspace, + @Nullable Launcher launcher, @Nonnull TaskListener listener) throws IOException, InterruptedException { + MultiSCMRevisionState revisionStates = new MultiSCMRevisionState(); - MultiSCMRevisionState revisionStates = new MultiSCMRevisionState(); - - for(SCM scm : scms) { - SCMRevisionState scmState = scm.calcRevisionsFromBuild(build, launcher, listener); - revisionStates.add(scm, build.getWorkspace(), build, scmState); - } + for(SCM scm : scms) { + SCMRevisionState scmState = scm.calcRevisionsFromBuild + (build, workspace, launcher, listener); + revisionStates.add(scm, workspace, build, scmState); + } return revisionStates; } @@ -90,7 +92,7 @@ public void buildEnvVars(AbstractBuild build, Map env) { } catch(NullPointerException npe) {} - + } } @@ -142,7 +144,7 @@ public void checkout(Run build, Launcher launcher, } scm.checkout(build, launcher, workspace, listener, subChangeLog, workspaceRevision); - List actions = build.getActions(); + List actions = build.getAllActions(); for(Action a : actions) { if(!scmActions.contains(a) && a instanceof SCMRevisionState && !(a instanceof MultiSCMRevisionState)) { scmActions.add(a); @@ -214,17 +216,17 @@ public DescriptorImpl() { // TODO Auto-generated constructor stub } - public List> getApplicableSCMs(AbstractProject project) { + public List> getApplicableSCMs(Object item) { List> scms = new ArrayList>(); - - for(SCMDescriptor scm : SCM._for(project)) { - // Filter MultiSCM itself from the list of choices. - // Theoretically it might work, but I see no practical reason to allow - // nested MultiSCM configurations. - if(!(scm instanceof DescriptorImpl) && !(scm instanceof NullSCM.DescriptorImpl)) - scms.add(scm); + if (item instanceof Job) { + for(SCMDescriptor scm : SCM._for((Job)item)) { + // Filter MultiSCM itself from the list of choices. + // Theoretically it might work, but I see no practical reason to allow + // nested MultiSCM configurations. + if(!(scm instanceof DescriptorImpl) && !(scm instanceof NullSCM.DescriptorImpl)) + scms.add(scm); + } } - return scms; } @@ -267,7 +269,8 @@ public SCM newInstance(StaplerRequest req, JSONObject formData) private static void readItem(StaplerRequest req, JSONObject obj, List dest) throws FormException { String staplerClass = obj.getString("stapler-class"); - Descriptor d = (Descriptor) Hudson.getInstance().getDescriptor(staplerClass); + @SuppressWarnings("unchecked") + Descriptor d = Jenkins.getInstance().getDescriptor(staplerClass); dest.add(d.newInstance(req, obj)); } } diff --git a/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMChangeLogParser.java b/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMChangeLogParser.java index adb25b3..ec15270 100644 --- a/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMChangeLogParser.java +++ b/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMChangeLogParser.java @@ -1,6 +1,7 @@ package org.jenkinsci.plugins.multiplescms; import hudson.model.AbstractBuild; +import hudson.model.Run; import hudson.scm.ChangeLogParser; import hudson.scm.ChangeLogSet; import hudson.scm.ChangeLogSet.Entry; @@ -55,15 +56,17 @@ public MultiSCMChangeLogParser(List scms) { private class LogSplitter extends DefaultHandler { private final MultiSCMChangeLogSet changeLogs; - private final AbstractBuild build; + private final Run build; + private RepositoryBrowser browser; private final File tempFile; private String scmClass; private StringBuffer buffer; - public LogSplitter(AbstractBuild build, String tempFilePath) { - changeLogs = new MultiSCMChangeLogSet(build); + public LogSplitter(Run build, RepositoryBrowser browser, String tempFilePath) { + changeLogs = new MultiSCMChangeLogSet(build, browser); this.tempFile= new File(tempFilePath); this.build = build; + this.browser = browser; } @Override @@ -107,16 +110,16 @@ public void endElement(String uri, String localName, String qName) /* * Due to XSTREAM serialization scmRepositoryBrowsers may be null. */ - RepositoryBrowser browser = null; - if (scmRepositoryBrowsers != null) { - browser = scmRepositoryBrowsers.get(scmClass); - } +// RepositoryBrowser browser = null; +// if (scmRepositoryBrowsers != null) { +// browser = scmRepositoryBrowsers.get(scmClass); +// } if(parser != null) { ChangeLogSet cls; if (browser != null) { cls = parser.parse(build, browser, tempFile); } else { - cls = parser.parse(build, tempFile); + cls = parser.parse(build, browser, tempFile); } changeLogs.add(scmClass, scmDisplayNames.get(scmClass), cls); @@ -139,8 +142,7 @@ public ChangeLogSet getChangeLogSets() { } @Override - public ChangeLogSet parse(AbstractBuild build, File changelogFile) - throws IOException, SAXException { + public ChangeLogSet parse(Run build, RepositoryBrowser browser, File changelogFile) throws IOException, SAXException { if(scmLogParsers == null) return ChangeLogSet.createEmpty(build); @@ -154,7 +156,7 @@ public ChangeLogSet parse(AbstractBuild build, File changelogFi throw new SAXException("Could not create parser", e); } - LogSplitter splitter = new LogSplitter(build, changelogFile.getPath() + ".temp2"); + LogSplitter splitter = new LogSplitter(build, browser, changelogFile.getPath() + ".temp2"); parser.parse(changelogFile, splitter); return splitter.getChangeLogSets(); } diff --git a/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMChangeLogSet.java b/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMChangeLogSet.java index d91f0ca..510984f 100644 --- a/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMChangeLogSet.java +++ b/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMChangeLogSet.java @@ -7,6 +7,7 @@ import java.util.List; import hudson.model.AbstractBuild; +import hudson.model.Run; import hudson.scm.RepositoryBrowser; import hudson.scm.ChangeLogSet; import hudson.scm.ChangeLogSet.Entry; @@ -17,8 +18,8 @@ public class MultiSCMChangeLogSet extends ChangeLogSet { private final HashMap changes; private final Set kinds; - protected MultiSCMChangeLogSet(AbstractBuild build) { - super(build); + protected MultiSCMChangeLogSet(Run build, RepositoryBrowser browser) { + super(build, browser); changes = new HashMap(); kinds = new HashSet(); } diff --git a/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMHeadObserver.java b/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMHeadObserver.java new file mode 100644 index 0000000..2dad710 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMHeadObserver.java @@ -0,0 +1,99 @@ +/* + * The MIT License + * + * Copyright (c) 2016 Martin Weber + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jenkinsci.plugins.multiplescms; + +import java.util.ArrayList; +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; +import jenkins.scm.api.SCMHead; +import jenkins.scm.api.SCMHeadObserver; +import jenkins.scm.api.SCMRevision; +import jenkins.scm.api.SCMSource; + +/** + * @author weber + * + */ +class MultiSCMHeadObserver extends SCMHeadObserver { + + private final List result; + private Collector currentObserver; + private SCMSource currentSource; + + /** + * + */ + public MultiSCMHeadObserver(int numSources) { + result = new ArrayList(numSources); + } + + /** + * Notified before a new SCM source will be observed. Must be invoked prior + * to {@link #observe}. + * + * @param newSource + */ + public void beginObserving(SCMSource newSource) { + if (newSource != currentSource) { + currentObserver = new SCMHeadObserver.Collector(); + currentSource = newSource; + } + } + + /** + * Notified after a SCM source has been successfully observed. Must be + * invoked after to {@link #observe}. May be not invoked, if an error + * occurred in the calling code. + */ + public void endObserving() { + result.add(currentObserver); + currentObserver = null; + currentSource = null; + } + + /* + * (non-Javadoc) + * + * @see jenkins.scm.api.SCMHeadObserver#observe(jenkins.scm.api.SCMHead, + * jenkins.scm.api.SCMRevision) + */ + @Override + public void observe(SCMHead head, SCMRevision revision) { + currentObserver.observe(head, revision); + } + + /** + * Returns the collected results. For each invocation sequence of + * {@link #beginObserving(SCMSource)}/{@link #endObserving()}, the returned + * list will contain a corresponding {@code Collector} object. + * + * + * @return the collected results. + */ + @NonNull + public List result() { + return result; + } +} diff --git a/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMSource.java b/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMSource.java new file mode 100644 index 0000000..6ea5917 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/multiplescms/MultiSCMSource.java @@ -0,0 +1,272 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2018 Martin Weber + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jenkinsci.plugins.multiplescms; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.jenkinsci.Symbol; +import org.kohsuke.stapler.DataBoundConstructor; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import hudson.Extension; +import hudson.ExtensionList; +import hudson.Util; +import hudson.model.TaskListener; +import hudson.scm.NullSCM; +import hudson.scm.SCM; +import hudson.scm.SCMDescriptor; +import jenkins.scm.api.SCMHead; +import jenkins.scm.api.SCMHeadEvent; +import jenkins.scm.api.SCMHeadObserver; +import jenkins.scm.api.SCMHeadObserver.Collector; +import jenkins.scm.api.SCMRevision; +import jenkins.scm.api.SCMSource; +import jenkins.scm.api.SCMSourceCriteria; +import jenkins.scm.api.SCMSourceDescriptor; +import jenkins.scm.impl.NullSCMSource; + +/** + * An {@code SCMSource} implementation that monitors and fetches multiple SCMs. + * + * @author Martin Weber + */ +public class MultiSCMSource extends SCMSource { + + private List scmSources; + + @DataBoundConstructor + public MultiSCMSource(@CheckForNull String id, List scmSources) { + super(id); + this.scmSources = Util.fixNull(scmSources); + } + + public List getScmSources() { + return scmSources; + } + + // @DataBoundSetter + public void setScmSources(List scmSources) { + if (scmSources != null) { + this.scmSources.addAll(scmSources); + } + } + + /* + * (non-Javadoc) + * + * @see jenkins.scm.api.SCMSource#retrieve(jenkins.scm.api.SCMHeadObserver, + * hudson.model.TaskListener) + */ + @Override + protected void retrieve(SCMSourceCriteria criteria, SCMHeadObserver observer, SCMHeadEvent event, + TaskListener listener) throws IOException, InterruptedException { + final MultiSCMHeadObserver multiObserver = new MultiSCMHeadObserver(scmSources.size()); + + // process all SCMs, but try to not delete existing sub-projects... + for (SCMSource scmSource : scmSources) { + multiObserver.beginObserving(scmSource); + scmSource.fetch(criteria, multiObserver, event, listener); + multiObserver.endObserving(); + } + + // retain only branches that exist in every source... + Collector smallestByBranchCount = null; + final List multiSCMCollectors = multiObserver.result(); + { + int smallestSize = Integer.MAX_VALUE; + for (Collector entry : multiSCMCollectors) { + int size = entry.result().size(); + if (size < smallestSize) { + smallestSize = size; + smallestByBranchCount = entry; + } + } + } + + if (smallestByBranchCount != null) { + listener.getLogger().println("Collecting branches..."); + // gather potential branches... + // NOTE: assume SCMHead has by-branch-name equality + final Set branches = new HashSet(smallestByBranchCount.result().keySet()); + // remove non-existing branches... + for (Iterator iter = branches.iterator(); iter.hasNext();) { + final SCMHead branch = iter.next(); + for (Collector collector : multiSCMCollectors) { + if (!collector.result().containsKey(branch)) { + iter.remove(); + break; + } + } + } + if(branches.isEmpty()) { + listener.getLogger().println("!!! None of the branches exists in EACH SCM."); + } else { + for (SCMHead branch : branches) { + listener.getLogger().printf("* Branch `%s` exists in each SCM.%n", branch.getName()); + } + } + listener.getLogger().println("Done collecting branches."); + // feed remaining branches into observer... + for (SCMHead branch : branches) { + final List bRevs = new ArrayList(multiSCMCollectors.size()); + for (Collector res : multiSCMCollectors) { + final SCMRevision scmRevision = res.result().get(branch); + if (scmRevision != null) { + bRevs.add(scmRevision); + } + } + final MultiSCMRevision rev = new MultiSCMRevision(branch, + bRevs.toArray(new SCMRevision[bRevs.size()])); + observer.observe(branch, rev); + } + } + } + + /* + * (non-Javadoc) + * + * @see jenkins.scm.api.SCMSource#build(jenkins.scm.api.SCMHead, + * jenkins.scm.api.SCMRevision) + */ + @Override + public SCM build(SCMHead head, SCMRevision revision) { + List ret = new ArrayList(scmSources.size()); + for (SCMSource scmSource : scmSources) { + ret.add(scmSource.build(head, revision)); + } + try { + return new MultiSCM(ret); + } catch (IOException neverThrown) { + throw new RuntimeException(neverThrown); + } + } + + /** + * Overridden for better type safety. + */ + @Override + public DescriptorImpl getDescriptor() { + return (DescriptorImpl) super.getDescriptor(); + } + + // ////////////////////////////////////////////////////////////////// + // inner classes + // ////////////////////////////////////////////////////////////////// + /** + * An {@code SCMRevision} implementation that hold multiple SCMRevisions. + * + * @author Martin Weber + */ + private static class MultiSCMRevision extends SCMRevision { + /** + * + */ + private static final long serialVersionUID = 1L; + private final SCMRevision[] revisions; + + /** + */ + protected MultiSCMRevision(SCMHead head, SCMRevision[] revisions) { + super(head); + if (revisions == null) { + throw new NullPointerException("revisions"); + } + this.revisions = revisions; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + MultiSCMRevision that = (MultiSCMRevision) o; + if (!getHead().equals(that.getHead())) { + return false; + } + if (!Arrays.equals(revisions, that.revisions)) + return false; + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(revisions); + return result; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + int iMax = revisions.length - 1; + for (int i = 0; i < revisions.length; i++) { + b.append(revisions[i]); + if (i == iMax) + break; + b.append(":"); + } + return b.toString(); + } + } // MultiSCMRevision + + @Extension + @Symbol(value = { "multipleSCMs" }) + public static class DescriptorImpl extends SCMSourceDescriptor { + @Override + public String getDisplayName() { + return "Multiple SCMs"; + } + + /** + * Returns the {@link SCMSourceDescriptor} instances that are appropriate + * for the current context. + * + * @return the {@link SCMDescriptor} instances + */ + @SuppressWarnings("unused") // used by stapler binding + public static List getScmSourceDescriptors() { + List result = new ArrayList( + ExtensionList.lookup(SCMSourceDescriptor.class)); + for (Iterator iterator = result.iterator(); iterator.hasNext();) { + SCMSourceDescriptor d = iterator.next(); + if (NullSCMSource.class.equals(d.clazz) || MultiSCMSource.class.equals(d.clazz)) { + iterator.remove(); + } + } + return result; + } + } // DescriptorImpl + +} diff --git a/src/main/resources/org/jenkinsci/plugins/multiplescms/MultiSCM/config.jelly b/src/main/resources/org/jenkinsci/plugins/multiplescms/MultiSCM/config.jelly index 9e924f4..0182126 100644 --- a/src/main/resources/org/jenkinsci/plugins/multiplescms/MultiSCM/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/multiplescms/MultiSCM/config.jelly @@ -1,15 +1,11 @@ - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/src/main/resources/org/jenkinsci/plugins/multiplescms/MultiSCMSource/config.jelly b/src/main/resources/org/jenkinsci/plugins/multiplescms/MultiSCMSource/config.jelly new file mode 100644 index 0000000..4cf2f95 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/multiplescms/MultiSCMSource/config.jelly @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/multiplescms/MultiSCMSource/help.html b/src/main/resources/org/jenkinsci/plugins/multiplescms/MultiSCMSource/help.html new file mode 100644 index 0000000..9d3aabe --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/multiplescms/MultiSCMSource/help.html @@ -0,0 +1,11 @@ +
+Looks for branches in multiple SCMs. A per-branch sub-project will be created for any branch +present in each of the configured SCMs.
+The created sub-projects will check out files from each SCM on a job run +(unlike Multibranch-pipeline, which creates sub-projects checking out from +the first SCM seen owning a branch, ignoring the other SCMs during checkout), +
+TIP: Concerning git, it will be sufficient to only specify the relevant branches in the +Filter by name behaviour of the first SCM in the list below and to leave the +default wildcard * in the additional SCMs untouched. +
\ No newline at end of file