Skip to content

Commit

Permalink
Merge pull request #183 from hrubi/expand-branch-spec
Browse files Browse the repository at this point in the history
[FIXED JENKINS-17417] Expand Branch Specifier
  • Loading branch information
mc1arke committed Feb 3, 2014
2 parents ec52156 + 4fc7f44 commit 9f8bc3b
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 28 deletions.
54 changes: 33 additions & 21 deletions src/main/java/hudson/plugins/git/BranchSpec.java
@@ -1,5 +1,6 @@
package hudson.plugins.git;

import hudson.EnvVars;
import hudson.Extension;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
Expand Down Expand Up @@ -27,7 +28,6 @@ public class BranchSpec extends AbstractDescribableImpl<BranchSpec> implements S
private static final long serialVersionUID = -6177158367915899356L;

private String name;
private transient Pattern pattern;

public String getName() {
return name;
Expand All @@ -48,54 +48,69 @@ public BranchSpec(String name) {
}

public String toString() {
return pattern + " (" + name + ")";
return name;
}

public boolean matches(String item) {
return getPattern().matcher(item).matches();
EnvVars env = new EnvVars();
return matches(item, env);
}

public boolean matches(String item, EnvVars env) {
return getPattern(env).matcher(item).matches();
}

public List<String> filterMatching(Collection<String> branches) {
EnvVars env = new EnvVars();
return filterMatching(branches, env);
}

public List<String> filterMatching(Collection<String> branches, EnvVars env) {
List<String> items = new ArrayList<String>();

for(String b : branches) {
if(matches(b))
if(matches(b, env))
items.add(b);
}

return items;
}

public List<Branch> filterMatchingBranches(Collection<Branch> branches) {
EnvVars env = new EnvVars();
return filterMatchingBranches(branches, env);
}

public List<Branch> filterMatchingBranches(Collection<Branch> branches, EnvVars env) {
List<Branch> items = new ArrayList<Branch>();

for(Branch b : branches) {
if(matches(b.getName()))
if(matches(b.getName(), env))
items.add(b);
}

return items;
}

private String getExpandedName(EnvVars env) {
return env.expand(name);
}

private Pattern getPattern() {
// return the saved pattern if available
if (pattern != null)
return pattern;

private Pattern getPattern(EnvVars env) {
String expandedName = getExpandedName(env);
// use regex syntax directly if name starts with colon
if (name.startsWith(":") && name.length() > 1) {
String regexSubstring = name.substring(1, name.length());
pattern = Pattern.compile(regexSubstring);
return pattern;
if (expandedName.startsWith(":") && expandedName.length() > 1) {
String regexSubstring = expandedName.substring(1, expandedName.length());
return Pattern.compile(regexSubstring);
}

// if an unqualified branch was given add a "*/" so it will match branches
// from remote repositories as the user probably intended
String qualifiedName;
if (!name.contains("**") && !name.contains("/"))
qualifiedName = "*/" + name;
if (!expandedName.contains("**") && !expandedName.contains("/"))
qualifiedName = "*/" + expandedName;
else
qualifiedName = name;
qualifiedName = expandedName;

// build a pattern into this builder
StringBuilder builder = new StringBuilder();
Expand Down Expand Up @@ -140,10 +155,7 @@ private Pattern getPattern() {
builder.append("[^/]*");
}

// save the pattern
pattern = Pattern.compile(builder.toString());

return pattern;
return Pattern.compile(builder.toString());
}

@Extension
Expand Down
13 changes: 8 additions & 5 deletions src/main/java/hudson/plugins/git/util/DefaultBuildChooser.java
@@ -1,6 +1,7 @@
package hudson.plugins.git.util;

import hudson.Extension;
import hudson.EnvVars;
import hudson.model.TaskListener;
import hudson.plugins.git.*;
import hudson.remoting.VirtualChannel;
Expand Down Expand Up @@ -48,7 +49,7 @@ public Collection<Revision> getCandidateRevisions(boolean isPollCall, String sin
// if the branch name contains more wildcards then the simple usecase
// does not apply and we need to skip to the advanced usecase
if (singleBranch == null || singleBranch.contains("*"))
return getAdvancedCandidateRevisions(isPollCall,listener,new GitUtils(listener,git),data);
return getAdvancedCandidateRevisions(isPollCall,listener,new GitUtils(listener,git),data, context);

// check if we're trying to build a specific commit
// this only makes sense for a build, there is no
Expand Down Expand Up @@ -185,7 +186,9 @@ private Revision objectId2Revision(String singleBranch, ObjectId sha1) {
* @throws IOException
* @throws GitException
*/
private List<Revision> getAdvancedCandidateRevisions(boolean isPollCall, TaskListener listener, GitUtils utils, BuildData data) throws GitException, IOException, InterruptedException {
private List<Revision> getAdvancedCandidateRevisions(boolean isPollCall, TaskListener listener, GitUtils utils, BuildData data, BuildChooserContext context) throws GitException, IOException, InterruptedException {
EnvVars env = context.getBuild().getEnvironment();

// 1. Get all the (branch) revisions that exist
List<Revision> revs = new ArrayList<Revision>(utils.getAllBranchRevisions());
verbose(listener, "Starting with all the branches: {0}", revs);
Expand All @@ -200,7 +203,7 @@ private List<Revision> getAdvancedCandidateRevisions(boolean isPollCall, TaskLis
Branch b = j.next();
boolean keep = false;
for (BranchSpec bspec : gitSCM.getBranches()) {
if (bspec.matches(b.getName())) {
if (bspec.matches(b.getName(), env)) {
keep = true;
break;
}
Expand All @@ -216,7 +219,7 @@ private List<Revision> getAdvancedCandidateRevisions(boolean isPollCall, TaskLis
if (r.getBranches().size() > 1) {
for (Iterator<Branch> j = r.getBranches().iterator(); j.hasNext();) {
Branch b = j.next();
if (HEAD.matches(b.getName())) {
if (HEAD.matches(b.getName(), env)) {
verbose(listener, "Ignoring {0} because there''s named branch for this revision", b.getName());
j.remove();
}
Expand Down Expand Up @@ -259,7 +262,7 @@ private List<Revision> getAdvancedCandidateRevisions(boolean isPollCall, TaskLis
// with fast-forward merges between branches
if (!isPollCall && revs.isEmpty() && lastBuiltRevision != null) {
verbose(listener, "Nothing seems worth building, so falling back to the previously built revision: {0}", data.getLastBuiltRevision());
return Collections.singletonList(utils.sortBranchesForRevision(lastBuiltRevision, gitSCM.getBranches()));
return Collections.singletonList(utils.sortBranchesForRevision(lastBuiltRevision, gitSCM.getBranches(), env));
}

// 5. sort them by the date of commit, old to new
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/hudson/plugins/git/util/GitUtils.java
Expand Up @@ -82,13 +82,18 @@ public Revision getRevisionForSHA1(ObjectId sha1) throws GitException, IOExcepti
}

public Revision sortBranchesForRevision(Revision revision, List<BranchSpec> branchOrder) {
EnvVars env = new EnvVars();
return sortBranchesForRevision(revision, branchOrder, env);
}

public Revision sortBranchesForRevision(Revision revision, List<BranchSpec> branchOrder, EnvVars env) {
ArrayList<Branch> orderedBranches = new ArrayList<Branch>(revision.getBranches().size());
ArrayList<Branch> revisionBranches = new ArrayList<Branch>(revision.getBranches());

for(BranchSpec branchSpec : branchOrder) {
for (Iterator<Branch> i = revisionBranches.iterator(); i.hasNext();) {
Branch b = i.next();
if (branchSpec.matches(b.getName())) {
if (branchSpec.matches(b.getName(), env)) {
i.remove();
orderedBranches.add(b);
}
Expand Down
@@ -1,6 +1,7 @@
package hudson.plugins.git.util;

import hudson.Extension;
import hudson.EnvVars;
import hudson.model.TaskListener;
import hudson.plugins.git.*;
import hudson.remoting.VirtualChannel;
Expand Down Expand Up @@ -41,6 +42,7 @@ public Collection<Revision> getCandidateRevisions(boolean isPollCall,
String singleBranch, GitClient git, TaskListener listener,
BuildData buildData, BuildChooserContext context) throws GitException, IOException, InterruptedException {

EnvVars env = context.getBuild().getEnvironment();
GitUtils utils = new GitUtils(listener, git);
List<Revision> branchRevs = new ArrayList<Revision>(utils.getAllBranchRevisions());
List<BranchSpec> specifiedBranches = gitSCM.getBranches();
Expand All @@ -56,7 +58,7 @@ public Collection<Revision> getCandidateRevisions(boolean isPollCall,
// Check whether this branch matches a branch spec from the job config
for (BranchSpec spec : specifiedBranches) {
// If the branch matches, throw it away as we do *not* want to build it
if (spec.matches(branch.getName()) || HEAD.matches(branch.getName())) {
if (spec.matches(branch.getName(), env) || HEAD.matches(branch.getName(), env)) {
j.remove();
break;
}
Expand Down
55 changes: 55 additions & 0 deletions src/test/java/hudson/plugins/git/TestBranchSpec.java
@@ -1,5 +1,8 @@
package hudson.plugins.git;

import hudson.EnvVars;
import java.util.HashMap;

import junit.framework.Assert;
import junit.framework.TestCase;

Expand Down Expand Up @@ -46,6 +49,58 @@ public void testMatch() {
Assert.assertFalse(p.matches("origin/my-branch/b1"));
}

public void testMatchEnv() {
HashMap<String, String> envMap = new HashMap<String, String>();
envMap.put("master", "master");
envMap.put("origin", "origin");
envMap.put("dev", "dev");
envMap.put("magnayn", "magnayn");
envMap.put("mybranch", "my.branch");
envMap.put("anyLong", "**");
envMap.put("anyShort", "*");
EnvVars env = new EnvVars(envMap);

BranchSpec l = new BranchSpec("${master}");
Assert.assertTrue(l.matches("origin/master", env));
Assert.assertFalse(l.matches("origin/something/master", env));
Assert.assertFalse(l.matches("master", env));
Assert.assertFalse(l.matches("dev", env));


BranchSpec est = new BranchSpec("${origin}/*/${dev}");

Assert.assertFalse(est.matches("origintestdev", env));
Assert.assertTrue(est.matches("origin/test/dev", env));
Assert.assertFalse(est.matches("origin/test/release", env));
Assert.assertFalse(est.matches("origin/test/somthing/release", env));

BranchSpec s = new BranchSpec("${origin}/*");

Assert.assertTrue(s.matches("origin/master", env));

BranchSpec m = new BranchSpec("**/${magnayn}/*");

Assert.assertTrue(m.matches("origin/magnayn/b1", env));
Assert.assertTrue(m.matches("remote/origin/magnayn/b1", env));

BranchSpec n = new BranchSpec("*/${mybranch}/*");

Assert.assertTrue(n.matches("origin/my.branch/b1", env));
Assert.assertFalse(n.matches("origin/my-branch/b1", env));
Assert.assertFalse(n.matches("remote/origin/my.branch/b1", env));

BranchSpec o = new BranchSpec("${anyLong}");

Assert.assertTrue(o.matches("origin/my.branch/b1", env));
Assert.assertTrue(o.matches("origin/my-branch/b1", env));
Assert.assertTrue(o.matches("remote/origin/my.branch/b1", env));

BranchSpec p = new BranchSpec("${anyShort}");

Assert.assertTrue(p.matches("origin/x", env));
Assert.assertFalse(p.matches("origin/my-branch/b1", env));
}

public void testEmptyName() {
BranchSpec branchSpec = new BranchSpec("");
assertEquals("**",branchSpec.getName());
Expand Down

0 comments on commit 9f8bc3b

Please sign in to comment.