Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'Inverse' choosing strategy which ignores specified branches #45

Merged
merged 4 commits into from
Oct 25, 2011
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import hudson.plugins.git.GitException;
import hudson.plugins.git.GitSCM;
import hudson.plugins.git.IGitAPI;
import hudson.plugins.git.Messages;
import hudson.plugins.git.Revision;
import org.kohsuke.stapler.DataBoundConstructor;
import org.eclipse.jgit.lib.ObjectId;
Expand Down Expand Up @@ -227,7 +228,7 @@ private void verbose(TaskListener listener, String format, Object... args) {
public static final class DescriptorImpl extends BuildChooserDescriptor {
@Override
public String getDisplayName() {
return "Default";
return Messages.BuildChooser_Default();
}

@Override
Expand Down
115 changes: 115 additions & 0 deletions src/main/java/hudson/plugins/git/util/InverseBuildChooser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
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 static 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);

// Warn the user that they've done something crazy such as excluding all branches
if (branchRevs.isEmpty()) {
listener.getLogger().println(Messages.BuildChooser_Inverse_EverythingExcluded());
}

// Filter out 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() && buildData.getLastBuiltRevision() != null) {
listener.getLogger().println(Messages.BuildChooser_BuildingLastRevision());
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;

}
4 changes: 4 additions & 0 deletions src/main/resources/hudson/plugins/git/Messages.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BuildChooser_Default=Default
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.
BuildChooser_BuildingLastRevision=No new revisions were found; the most-recently built branch will be built again.
21 changes: 19 additions & 2 deletions src/main/webapp/choosingStrategy.html
Original file line number Diff line number Diff line change
@@ -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>