Browse files

Add 'Inverse' choosing strategy which ignores specified branches.

Also add documentation and improve the existing strategy documentation.
  • Loading branch information...
1 parent e4dcece commit e3a33d122fe17e9001107c1ee1adbc6cd5570440 @orrc orrc committed Sep 26, 2011
View
113 src/main/java/hudson/plugins/git/util/InverseBuildChooser.java
@@ -0,0 +1,113 @@
+package hudson.plugins.git.util;
+
+import hudson.Extension;
+import hudson.model.TaskListener;
+import hudson.plugins.git.Branch;
+import hudson.plugins.git.BranchSpec;
+import hudson.plugins.git.GitException;
+import hudson.plugins.git.IGitAPI;
+import hudson.plugins.git.Messages;
+import hudson.plugins.git.Revision;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.kohsuke.stapler.DataBoundConstructor;
+
+/**
+ * Git build chooser which will select all branches <b>except</b> for those which match the
+ * configured branch specifiers.
+ * <p>
+ * e.g. If <tt>&#x2a;&#x2a;/master</tt> and <tt>&#x2a;&#x2a;/release-&#x2a;</tt> are configured as
+ * "Branches to build" then any branches matching those patterns <b>will not</b> be built, unless
+ * another branch points to the same revision.
+ * <p>
+ * This is useful, for example, when you have jobs building your <tt>master</tt> and various
+ * <tt>release</tt> branches and you want a second job which builds all new feature branches &mdash;
+ * i.e. branches which do not match these patterns &mdash; without redundantly building
+ * <tt>master</tt> and the release branches again each time they change.
+ *
+ * @author Christopher Orr
+ */
+public class InverseBuildChooser extends BuildChooser {
+
+ /* Ignore symbolic default branch ref. */
+ private transient final BranchSpec HEAD = new BranchSpec("*/HEAD");
+
+ @DataBoundConstructor
+ public InverseBuildChooser() {
+ }
+
+ @Override
+ public Collection<Revision> getCandidateRevisions(boolean isPollCall,
+ String singleBranch, IGitAPI git, TaskListener listener,
+ BuildData buildData) throws GitException, IOException {
+
+ GitUtils utils = new GitUtils(listener, git);
+ List<Revision> branchRevs = new ArrayList<Revision>(utils.getAllBranchRevisions());
+ List<BranchSpec> specifiedBranches = gitSCM.getBranches();
+
+ // Iterate over all the revisions pointed to by branches in the repository
+ for (Iterator<Revision> i = branchRevs.iterator(); i.hasNext(); ) {
+ Revision revision = i.next();
+
+ // Iterate over each branch for this revision
+ for (Iterator<Branch> j = revision.getBranches().iterator(); j.hasNext(); ) {
+ Branch branch = j.next();
+
+ // 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())) {
+ j.remove();
+ break;
+ }
+ }
+ }
+
+ // If we discarded all branches for this revision, ignore the whole revision
+ if (revision.getBranches().isEmpty()) {
+ i.remove();
+ }
+ }
+
+ // Filter out branch revisions that aren't leaves
+ branchRevs = utils.filterTipBranches(branchRevs);
+
+ // Filter our branch revisions that have already been built
+ for (Iterator<Revision> i = branchRevs.iterator(); i.hasNext(); ) {
+ Revision r = i.next();
+ if (buildData.hasBeenBuilt(r.getSha1())) {
+ i.remove();
+ }
+ }
+
+ // If we're in a build (not an SCM poll) and nothing new was found, run the last build again
+ if (!isPollCall && branchRevs.isEmpty()) {
+ // Warn the user that they've done something crazy like excluding all branches
+ listener.getLogger().println(Messages.BuildChooser_Inverse_EverythingExcluded());
+ if (buildData.getLastBuiltRevision() != null) {
+ return Collections.singletonList(buildData.getLastBuiltRevision());
+ }
+ }
+
+ // Sort revisions by the date of commit, old to new, to ensure fairness in scheduling
+ Collections.sort(branchRevs,new CommitTimeComparator(utils.git.getRepository()));
+ return branchRevs;
+ }
+
+ @Extension
+ public static final class DescriptorImpl extends BuildChooserDescriptor {
+ @Override
+ public String getDisplayName() {
+ return Messages.BuildChooser_Inverse();
+ }
+ }
+
+ private static final long serialVersionUID = 1L;
+
+}
View
2 src/main/resources/hudson/plugins/git/Messages.properties
@@ -0,0 +1,2 @@
+BuildChooser_Inverse=Inverse
+BuildChooser_Inverse_EverythingExcluded=All current git branches were excluded from being built. Either your branch specifiers are too broad or you should be using the "Default" choosing strategy.
View
21 src/main/webapp/choosingStrategy.html
@@ -1,4 +1,21 @@
<div>
- Which strategy to use when selecting revision for building.
- Default will search HEADs for different branches.
+ Specifies the strategy Jenkins will use to decide which revision to build.
+ <dl>
+ <dt>Default</dt>
+ <dd>
+ For each branch which matches the "Branches to build" (or all branches, if none are listed),
+ this strategy will select those whose most recent commit has not already been built.<br>
+ If multiple branches match these criteria, the oldest will be selected.
+ </dd>
+ <dt>Inverse</dt>
+ <dd>
+ This does the opposite of the "Default" strategy &mdash; any branches listed under "Branches
+ to build" will <strong>not</strong> be built. For example, entering the pattern
+ <tt>*/master</tt> will cause Jenkins to build any changes found on any branch, so long as the
+ branch name does <em>not</em> match this pattern.<br>
+ If multiple branches match these criteria, the oldest will be selected.
+ </dd>
+ </dl>
+ For details on any other strategies that may be listed in the drop-down box above, refer to the
+ respective plugin's documentation.
</div>

0 comments on commit e3a33d1

Please sign in to comment.