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

Enable autoformatting #392

Merged
merged 20 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from 12 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
66 changes: 66 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,72 @@
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>2.8.1</version>
<executions>
<execution>
<id>spotless-check</id>
<!-- runs in verify phase by default -->
<goals>
<!-- can be disabled using -Dspotless.check.skip=true -->
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<java>
<eclipse>
<file>${basedir}/src/build/eclipse/formatter.xml</file>
</eclipse>

<importOrder>
<file>${basedir}/src/build/eclipse/eclipse.importorder</file>
</importOrder>
<removeUnusedImports />

<trimTrailingWhitespace />
<endWithNewline />

</java>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>ci-non-windows</id>
<activation>
<property>
<name>set.changelist</name>
</property>
<os>
<family>!windows</family>
</os>
</activation>
<build>
<plugins>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<executions>
<execution>
<id>spotless-check</id>
<!-- In CI, run check early in the build -->
<phase>process-sources</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
Expand Down
9 changes: 9 additions & 0 deletions src/build/eclipse/eclipse.importorder
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#Organize Import Order
# Import this file in Window -> Preferences -> Java -> Code Style -> Organize Imports -> Import...
0=
# This project uses simple alphabetical import order
# 1=java
# 2=javax
#
# static imports
bitwiseman marked this conversation as resolved.
Show resolved Hide resolved
3=\#
368 changes: 368 additions & 0 deletions src/build/eclipse/formatter.xml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,26 @@
package org.jenkinsci.plugins.github_branch_source;

import hudson.model.TaskListener;

import java.util.List;

/**
* Represents a strategy for constructing GitHub status notifications
*
* @since 2.3.2
*/
public abstract class AbstractGitHubNotificationStrategy {

/**
* Creates the list of {@link GitHubNotificationRequest} for the given context.
*
* @param notificationContext {@link GitHubNotificationContext} the context details
* @param listener the listener
* @param listener the listener
* @return a list of notification requests
* @since 2.3.2
*/
public abstract List<GitHubNotificationRequest> notifications(GitHubNotificationContext notificationContext, TaskListener listener);
public abstract List<GitHubNotificationRequest> notifications(
GitHubNotificationContext notificationContext,
TaskListener listener);

/**
* {@inheritDoc}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@
import hudson.Util;
import hudson.model.TaskListener;
import hudson.util.LogTaskListener;
import org.jenkinsci.plugins.github.config.GitHubServerConfig;
import org.kohsuke.github.GHRateLimit;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.RateLimitChecker;

import java.io.IOException;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jenkinsci.plugins.github.config.GitHubServerConfig;
import org.kohsuke.github.GHRateLimit;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.RateLimitChecker;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to a simple alphabetical order IIUC? Very welcome. Humans should not be reading import statements to begin with; normalizing the order minimizes the chance of stupid merge conflicts.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

humans do code review.. whilst you may not read them and you may feel they are unwanted others like me find them very useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jglick
It is my understanding that what code style guidelines there are for Jenkins say "Simple alphabetical non-static, then simple alphabetical static". Is that incorrect?

@jtnord
What you prefer the ordering be? I don't care what it is as long as I never have to tell someone in a code review to change it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So long as it is simple I would not really care. (Whether static imports should be allowed at all, and under what conditions, is a contentious topic.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jglick
The topic of static imports will not be solved by a formatter.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you prefer the ordering be? I don't care what it is as long as I never have to tell someone in a code review to change it.

I have personal preferences but I do not care too much about the order apart from always non static before static imports though, and static imports should be reserved for test code apart from those very rare special occasions.

But given as you asked, my preference is someone along the following lines.

  1. core libraries first (java, javax)
  2. then 3rd party libraries (com .edu , org)
  3. then a break
  4. then jenkins core code (hudson, jenkins, org.jenkinsci, io.jenkins)
  5. then stuff from my own project (e.g. org.jenkinsci.plugins.github_branch_source)
  6. then a break
  7. then the same but for static imports.

That said my IDE will automatically an import put it where it is configured to by its ordering (eclipse) and having per project settings is a PITA (I do not recall the last time I typed an import statement). I try to reduce diff bloat it after the fact by manually moving it if I recall :)

The topic of static imports will not be solved by a formatter.

you mean the ordering or that they should or should not be allowed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether or not they should be allowed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW my personal preference is strictly alphabetical, no line breaks, no attempt to group in any way other than | sort.


public enum ApiRateLimitChecker {

Expand All @@ -26,7 +25,8 @@ public enum ApiRateLimitChecker {
public LocalChecker getChecker(@NonNull TaskListener listener, String apiUrl) {
return new LocalChecker(listener) {
@Override
long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit, long count, long now) throws InterruptedException {
long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit, long count, long now)
throws InterruptedException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say this is less readable than before.

Suggested change
long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit, long count, long now)
throws InterruptedException {
long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit,
long count, long now) throws InterruptedException {

is more readable to me .. but 🤷 you choose somethign and it gives you something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the argument that throws should go on the same line. But whether this less readable is debatable - do you really want to debate it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can choose to disregard my opinion here and I am ok with that.
hence

🤷 you choose something and it gives you something.

auto formatters will always produce something that is worse. this is to me one (all be it a very minor) case of that.

long expiration = now;
// the buffer is how much we want to avoid using to cover unplanned over-use
int buffer = calculateBuffer(rateLimit.getLimit());
Expand All @@ -42,46 +42,48 @@ long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit, long count, long
int ideal = (int) ((rateLimit.getLimit() - buffer - burst) * resetProgress) + buffer;
if (rateLimit.getRemaining() < ideal) {
// work out how long until remaining == ideal + 0.1 * buffer (to give some spend)
double targetFraction = (rateLimit.getRemaining() - buffer * 1.1) / (rateLimit
.getLimit() - buffer - burst);
double targetFraction = (rateLimit.getRemaining() - buffer * 1.1)
/ (rateLimit.getLimit() - buffer - burst);
expiration = rateLimit.getResetDate().getTime()
- Math.max(0, (long) (targetFraction * MILLIS_PER_HOUR))
+ ENTROPY.nextInt(1000);
- Math.max(0, (long) (targetFraction * MILLIS_PER_HOUR)) + ENTROPY.nextInt(1000);
writeLog(String.format(
"Jenkins-Imposed API Limiter: Current quota for Github API usage has %d remaining (%d over budget). Next quota of %d in %s. Sleeping for %s.",
rateLimit.getRemaining(),
ideal - rateLimit.getRemaining(),
rateLimit.getLimit(),
Util.getTimeSpanString(rateLimitResetMillis),
// The GitHubRateLimitChecker adds a one second sleep to each notification loop
Util.getTimeSpanString(1000 + expiration - now)));
"Jenkins-Imposed API Limiter: Current quota for Github API usage has %d remaining (%d over budget). Next quota of %d in %s. Sleeping for %s.",
rateLimit.getRemaining(),
ideal - rateLimit.getRemaining(),
rateLimit.getLimit(),
Util.getTimeSpanString(rateLimitResetMillis),
// The GitHubRateLimitChecker adds a one second sleep to each notification loop
Util.getTimeSpanString(1000 + expiration - now)));
bitwiseman marked this conversation as resolved.
Show resolved Hide resolved
}
}
if (expiration != now) {
writeLog("Jenkins is attempting to evenly distribute GitHub API requests. To configure a different rate limiting strategy, such as having Jenkins restrict GitHub API requests only when near or above the GitHub rate limit, go to \"GitHub API usage\" under \"Configure System\" in the Jenkins settings.");
writeLog(
"Jenkins is attempting to evenly distribute GitHub API requests. To configure a different rate limiting strategy, such as having Jenkins restrict GitHub API requests only when near or above the GitHub rate limit, go to \"GitHub API usage\" under \"Configure System\" in the Jenkins settings.");
bitwiseman marked this conversation as resolved.
Show resolved Hide resolved
}
return expiration;
}
};
}
},

/**å
/**
* Restrict GitHub API requests only when near or above rate limit.
*/
ThrottleOnOver(Messages.ApiRateLimitChecker_ThrottleOnOver()) {
@Override
public LocalChecker getChecker(@NonNull TaskListener listener, String apiUrl) {
return new LocalChecker(listener) {
@Override
long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit, long count, long now) throws InterruptedException {
long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit, long count, long now)
throws InterruptedException {
// the buffer is how much we want to avoid using to cover unplanned over-use
int buffer = calculateBuffer(rateLimit.getLimit());
// check that we have at least our minimum buffer of remaining calls
if (rateLimit.getRemaining() >= buffer) {
return now;
}
writeLog("Jenkins is restricting GitHub API requests only when near or above the rate limit. To configure a different rate limiting strategy, such as having Jenkins attempt to evenly distribute GitHub API requests, go to \"GitHub API usage\" under \"Configure System\" in the Jenkins settings.");
writeLog(
"Jenkins is restricting GitHub API requests only when near or above the rate limit. To configure a different rate limiting strategy, such as having Jenkins attempt to evenly distribute GitHub API requests, go to \"GitHub API usage\" under \"Configure System\" in the Jenkins settings.");
return calculateExpirationWhenBufferExceeded(rateLimit, now, buffer);
}
};
Expand All @@ -96,12 +98,14 @@ public LocalChecker getChecker(@NonNull TaskListener listener, String apiUrl) {
if (GitHubServerConfig.GITHUB_URL.equals(apiUrl)) {
// If the GitHub public API is being used, this will fallback to ThrottleOnOver
LocalChecker checker = ThrottleOnOver.getChecker(listener, apiUrl);
checker.writeLog("GitHub throttling is disabled, which is not allowed for public GitHub usage, so ThrottleOnOver will be used instead. To configure a different rate limiting strategy, go to \"GitHub API usage\" under \"Configure System\" in the Jenkins settings.");
checker.writeLog(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be aligned to writeLog( not checker.

basically are you using a fixed offset for wrapped indentation? (ie 8 which is the lengh of checker. - in which case don't use an offset that matches to the thing you are offsetting.

otherwise you can see something like

checkz(.writeLog(
            "a very long string),
            "and something else")

and that is worse than evil.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jtnord
Same debatable point as above. The indent you are saying is missing was (as far as I can tell) intentionally removed as redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jtnord
Regarding the align to column, you can change this the formatter as noted here to be 82 and it will align to column, but it produces results that I think you'll like even less than the current results. 50 is also an option, but it ends up even worse.

But maybe I'm misunderstanding what you mean.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so given the settings that are being used are offset by a specific amount (and not column) if it hard to see when something is wrapped in something like

foo(bar(wibble(baz, bob,
        jennygoestomarket),
        markstaysathome));

Because both jennygoestomarket and markstaysathome are offset by the same amount, and when scanning the code it looks like they can be the arguments to the same function (when they are not).
Whitespace is helpful to show what things apply to (which is why we indent blocks {) to me the same applies here.
Maybe I misunderstand the settings you are applying? (but I have had many issues with eclipse when using this setting in the past)

disclaimer #1 - I much prefer projects that are set to indent on column.
disclaimer #2 I am mildly dyslexic and so this is important to me. My brain scans and reads text in a specific and different to the vast majority of people.

now - I am not a major contributor to this project and so I am not trying to enforce my standards onto the people who do work with it much more than I do (I have only had a few minor contributions) and as such I will adapt when I need to work in this project. However if this is the first step into trying to get something more standard into Jenkins ecosystem and rolling out more widely then being inclusive of all people (which includes the neuro diverse) should be taken into account, as descisions made here can make it more or less easy for people to contribute.

disclaimer #3 on my projects when I remember to switch to my coding style I indent with tabs so that people can customize their indent view to support their needs (2 for some people 8 for others, and some have 4 or even 3) but offset with spaces. this allows the code to look the same regardless of tab space setting and allow people to have a visual offset that conforms to the way they work best.

and finally disclaimer #4 all attempts at auto formatting will break somewhere. This is why I think auto formatters are evil. let the bot do a formatting review, let a human decide to reject it's findings, or to easily make it better. See the end part on disclaimer 2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jtnord

  1. The problem I've seen with with indenting to column is you end up with deeply indented code. I assume your suggestion for addressing that is to refactor the code into smaller methods, avoiding deeply nested structures. Am I correct?

  2. Neuro-atypicalness and accessibility is certainly a valid concern. I read slowly and also depend on formatting to allow me to take in code at a reasonable speed. I definitely want to be sensitive to a broad range of contributors needs. At the same time, consider that someone else might find "align to column" harder to read due to its deeper indenting causing lines to wrap more often. Which one wins? In the case of this change, I am looking to apply a standard formatting that most closely matches what is already being used. There are parts of this code base that seem to be using "align to column", but the majority of it does not.

  3. I believe this mix of tabs and spaces is referred to as "smart-tabs". It works best in "align to column" if I understand it correctly. I'm not opposed to this, but as with point two it seems a larger change. I've done like I did with no-join and created a branch using this and "align to column" settings: bitwiseman:task:task/formatting...bitwiseman:task/formatting-columns. Perhaps there's other settings that could be applied to do this better. Perhaps you as the one bringing this up could try various setting to find one that is best in your opinion?

  4. I would instead say that all attempts at formatting will be imperfect, both humans and automated systems. Auto-formatters are generally imperfect in rigid and predictable ways, whereas human formatters are imperfect in a wide range of random ways. I categorically disagree that humans should be allowed to opt-out of auto-formatting - given the option humans will choose to do things their own way and then we end up talking about "better" or "worse" formatting instead of focusing on making the code better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference would be to use the autoformatter for things it can do reliably—sort imports, remove trailing whitespace, enforce spaces vs. tabs for block indentation, normalize use of spaces around / inside tokens like ( ) =—but for more subjective things like placement of line breaks leave the choice to humans.

"GitHub throttling is disabled, which is not allowed for public GitHub usage, so ThrottleOnOver will be used instead. To configure a different rate limiting strategy, go to \"GitHub API usage\" under \"Configure System\" in the Jenkins settings.");
return checker;
} else {
return new LocalChecker(listener) {
@Override
long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit, long count, long now) throws InterruptedException {
long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit, long count, long now)
throws InterruptedException {
return now;
}
};
Expand All @@ -117,10 +121,9 @@ long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit, long count, long
/**
* Thread-local rate limit checkers.
*
* In Jenkins multiple threads can call into one {@link GitHub} instance with each wanting to
* receive logging output on a different listener. {@link RateLimitChecker} does not support
* anything like this. We use thread-local checker instances to track rate limit checking
* state for each thread.
* In Jenkins multiple threads can call into one {@link GitHub} instance with each wanting to receive logging output
* on a different listener. {@link RateLimitChecker} does not support anything like this. We use thread-local
* checker instances to track rate limit checking state for each thread.
*/
private static final ThreadLocal<LocalChecker> localRateLimitChecker = new ThreadLocal<>();

Expand Down Expand Up @@ -180,7 +183,9 @@ static void resetLocalChecker() {
* This method is the old code path for rate limit checks
*
* It has been slowly refactored until it almost matches the behavior of the GitHubRateLimitChecker.
* @deprecated rate limit checking is done automatically. Use {@link #configureThreadLocalChecker(TaskListener, GitHub)} instead.
*
* @deprecated rate limit checking is done automatically. Use
* {@link #configureThreadLocalChecker(TaskListener, GitHub)} instead.
*/
@Deprecated
public void checkApiRateLimit(TaskListener listener, GitHub gitHub) throws IOException, InterruptedException {
Expand All @@ -189,14 +194,14 @@ public void checkApiRateLimit(TaskListener listener, GitHub gitHub) throws IOExc

static final class RateLimitCheckerAdapter extends RateLimitChecker {
@Override
protected boolean checkRateLimit(GHRateLimit.Record rateLimitRecord,
long count) throws InterruptedException {
protected boolean checkRateLimit(GHRateLimit.Record rateLimitRecord, long count) throws InterruptedException {
LocalChecker checker = getLocalChecker();
if (checker == null) {
// If a checker was not configured for this thread, try our best and continue.
configureThreadLocalChecker(new LogTaskListener(LOGGER, Level.INFO), GitHubServerConfig.GITHUB_URL);
checker = getLocalChecker();
checker.writeLog("LocalChecker for rate limit was not set for this thread. Configured using system settings.");
checker.writeLog(
"LocalChecker for rate limit was not set for this thread. Configured using system settings.");
}
return checker.checkRateLimit(rateLimitRecord, count);
}
Expand All @@ -212,8 +217,7 @@ static abstract class LocalChecker {
resetExpiration();
}

protected boolean checkRateLimit(GHRateLimit.Record rateLimitRecord,
long count) throws InterruptedException {
protected boolean checkRateLimit(GHRateLimit.Record rateLimitRecord, long count) throws InterruptedException {
if (count == 0) {
resetExpiration();
}
Expand All @@ -229,7 +233,8 @@ protected boolean checkRateLimit(GHRateLimit.Record rateLimitRecord,
}

// internal for testing
abstract long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit, long count, long now) throws InterruptedException;
abstract long checkRateLimitImpl(@NonNull GHRateLimit.Record rateLimit, long count, long now)
throws InterruptedException;

void resetExpiration() {
expiration = Long.MIN_VALUE;
Expand All @@ -241,24 +246,22 @@ long calculateExpirationWhenBufferExceeded(GHRateLimit.Record rateLimit, long no
// we add a little bit of random to prevent CPU overload when the limit is due to reset but GitHub
// hasn't actually reset yet (clock synchronization is a hard problem)
if (rateLimitResetMillis < 0) {
expiration = now + ENTROPY.nextInt(
EXPIRATION_WAIT_MILLIS);
expiration = now + ENTROPY.nextInt(EXPIRATION_WAIT_MILLIS);
writeLog(String.format(
"Jenkins-Imposed API Limiter: Current quota for Github API usage has %d remaining (%d over budget). Next quota of %d due now. Sleeping for %s.",
rateLimit.getRemaining(),
buffer - rateLimit.getRemaining(),
rateLimit.getLimit(),
// The GitHubRateLimitChecker adds a one second sleep to each notification loop
Util.getTimeSpanString(1000 + expiration - now)));
"Jenkins-Imposed API Limiter: Current quota for Github API usage has %d remaining (%d over budget). Next quota of %d due now. Sleeping for %s.",
rateLimit.getRemaining(),
buffer - rateLimit.getRemaining(),
rateLimit.getLimit(),
// The GitHubRateLimitChecker adds a one second sleep to each notification loop
Util.getTimeSpanString(1000 + expiration - now)));
} else {
expiration = rateLimit.getResetDate().getTime() + ENTROPY.nextInt(
EXPIRATION_WAIT_MILLIS);
expiration = rateLimit.getResetDate().getTime() + ENTROPY.nextInt(EXPIRATION_WAIT_MILLIS);
writeLog(String.format(
"Jenkins-Imposed API Limiter: Current quota for Github API usage has %d remaining (%d over budget). Next quota of %d in %s. Sleeping until reset.",
rateLimit.getRemaining(),
buffer - rateLimit.getRemaining(),
rateLimit.getLimit(),
Util.getTimeSpanString(rateLimitResetMillis)));
"Jenkins-Imposed API Limiter: Current quota for Github API usage has %d remaining (%d over budget). Next quota of %d in %s. Sleeping until reset.",
rateLimit.getRemaining(),
buffer - rateLimit.getRemaining(),
rateLimit.getLimit(),
Util.getTimeSpanString(rateLimitResetMillis)));
}
return expiration;
}
Expand All @@ -270,10 +273,8 @@ boolean waitUntilRateLimit(long now, long expiration, long count) throws Interru
long nextNotify = now + NOTIFICATION_WAIT_MILLIS;
this.expiration = expiration;
if (count > 0) {
writeLog(String.format(
"Jenkins-Imposed API Limiter: Still sleeping, now only %s remaining.",
Util.getTimeSpanString(expiration - now)
));
writeLog(String.format("Jenkins-Imposed API Limiter: Still sleeping, now only %s remaining.",
Util.getTimeSpanString(expiration - now)));
}
if (Thread.interrupted()) {
throw new InterruptedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,21 @@
public class BranchDiscoveryTrait extends SCMSourceTrait {
/**
* None strategy.
*/
*/
public static final int NONE = 0;
/**
* Exclude branches that are also filed as PRs.
*/
*/
public static final int EXCLUDE_PRS = 1;
/**
* Only branches that are also filed as PRs.
*/
*/
public static final int ONLY_PRS = 2;
/**
* All branches.
*/
*/
public static final int ALL_BRANCHES = 3;

/**
* The strategy encoded as a bit-field.
*/
Expand Down
Loading