diff --git a/src/main/java/hudson/plugins/git/GitSCM.java b/src/main/java/hudson/plugins/git/GitSCM.java index 5ccac995db..34cf3d287e 100644 --- a/src/main/java/hudson/plugins/git/GitSCM.java +++ b/src/main/java/hudson/plugins/git/GitSCM.java @@ -664,6 +664,37 @@ private String getSingleBranch(EnvVars env) { return branch; } + /** + * If any branch can match a multiple remote branches, then workspace polling is required. + * + * Returns {@code true} if any branches require workspace polling. + */ + private boolean branchesRequireWorkspaceForPolling(@NonNull EnvVars env, @CheckForNull TaskListener listener) { + for (BranchSpec branchSpec : getBranches()) { + final String branch = branchSpec.getName(); + final String expandedBranch = env.expand(branch); + + // If the branch contains a wildcard anywhere other than a leading */ or is empty (which is intepreted as + // **), it can match multiple remote branches. + final String strippedBranch = expandedBranch.replaceAll("^\\*/", ""); + if (strippedBranch.equals("") || strippedBranch.contains("*")) { + if (listener != null) { + final PrintStream log = listener.getLogger(); + + if (branch.equals(expandedBranch)) { + log.printf("Branch '%s' requires workspace for polling%n", branch); + } else { + log.printf("Branch '%s' (expanded to '%s') requires workspace for polling%n", + branch, expandedBranch); + } + } + return true; + } + } + + return false; + } + @Override public SCMRevisionState calcRevisionsFromBuild(Run abstractBuild, FilePath workspace, Launcher launcher, TaskListener taskListener) throws IOException, InterruptedException { return SCMRevisionState.NONE; @@ -672,15 +703,15 @@ public SCMRevisionState calcRevisionsFromBuild(Run abstractBuild, FilePath @Override public boolean requiresWorkspaceForPolling() { // TODO would need to use hudson.plugins.git.util.GitUtils.getPollEnvironment - return requiresWorkspaceForPolling(new EnvVars()); + return requiresWorkspaceForPolling(new EnvVars(), null); } /* Package protected for test access */ - boolean requiresWorkspaceForPolling(EnvVars environment) { + boolean requiresWorkspaceForPolling(EnvVars environment, TaskListener listener) { for (GitSCMExtension ext : getExtensions()) { if (ext.requiresWorkspaceForPolling()) return true; } - return getSingleBranch(environment) == null; + return branchesRequireWorkspaceForPolling(environment, listener); } @Override @@ -715,7 +746,7 @@ private PollingResult compareRemoteRevisionWithImpl(Job project, Launcher final String singleBranch = getSingleBranch(pollEnv); - if (!requiresWorkspaceForPolling(pollEnv)) { + if (!requiresWorkspaceForPolling(pollEnv, listener)) { final EnvVars environment = project instanceof AbstractProject ap ? GitUtils.getPollEnvironment(ap, workspace, launcher, listener, false) : new EnvVars(); diff --git a/src/test/java/hudson/plugins/git/AbstractGitTestCase.java b/src/test/java/hudson/plugins/git/AbstractGitTestCase.java index 5b3cd44207..d5f7e2985f 100644 --- a/src/test/java/hudson/plugins/git/AbstractGitTestCase.java +++ b/src/test/java/hudson/plugins/git/AbstractGitTestCase.java @@ -193,7 +193,8 @@ protected FreeStyleProject setupProject(List branches, boolean autho if (credential != null) { project.getBuildersList().add(new HasCredentialBuilder(credential.getId())); } - scm.getExtensions().add(new DisableRemotePoll()); // don't work on a file:// repository + if (!fastRemotePoll) + scm.getExtensions().add(new DisableRemotePoll()); if (relativeTargetDir!=null) scm.getExtensions().add(new RelativeTargetDirectory(relativeTargetDir)); if (excludedUsers!=null) diff --git a/src/test/java/hudson/plugins/git/GitSCMTest.java b/src/test/java/hudson/plugins/git/GitSCMTest.java index f9cf6fee2e..c8813cf1be 100644 --- a/src/test/java/hudson/plugins/git/GitSCMTest.java +++ b/src/test/java/hudson/plugins/git/GitSCMTest.java @@ -334,6 +334,8 @@ public void testBasicRemotePoll() throws Exception { assumeTrue("Test class max time " + MAX_SECONDS_FOR_THESE_TESTS + " exceeded", isTimeAvailable()); // FreeStyleProject project = setupProject("master", true, false); FreeStyleProject project = setupProject("master", false, null, null, null, true, null); + assertFalse("scm should not require workspace for polling", project.getScm().requiresWorkspaceForPolling()); + // create initial commit and then run the build against it: final String commitFile1 = "commitFile1"; commit(commitFile1, johnDoe, "Commit number 1"); @@ -2628,6 +2630,30 @@ public void testPolling_CanDoRemotePollingIfOneBranchButMultipleRepositories() t assertFalse(pollingResult.hasChanges()); } + @Test + public void testPolling_CanDoRemotePollingIfMultipleBranches() throws Exception { + assumeTrue("Test class max time " + MAX_SECONDS_FOR_THESE_TESTS + " exceeded", isTimeAvailable()); + FreeStyleProject project = createFreeStyleProject(); + List branchSpecs = Arrays.asList( + new BranchSpec("*/master"), + new BranchSpec("*/stable")); + GitSCM scm = new GitSCM(createRemoteRepositories(), + branchSpecs, null, null, + Collections.emptyList()); + project.setScm(scm); + assertFalse("scm should not require workspace for polling", scm.requiresWorkspaceForPolling()); + + commit("commitFile1", johnDoe, "Commit number 1"); + git.branch("stable"); + + FreeStyleBuild first_build = project.scheduleBuild2(0, new Cause.UserIdCause()).get(); + r.assertBuildStatus(Result.SUCCESS, first_build); + + first_build.getWorkspace().deleteContents(); + PollingResult pollingResult = scm.poll(project, null, first_build.getWorkspace(), listener, null); + assertFalse(pollingResult.hasChanges()); + } + @Issue("JENKINS-24467") @Test public void testPolling_environmentValueAsEnvironmentContributingAction() throws Exception { diff --git a/src/test/java/hudson/plugins/git/GitSCMUnitTest.java b/src/test/java/hudson/plugins/git/GitSCMUnitTest.java index 96533247c7..71e5982fc9 100644 --- a/src/test/java/hudson/plugins/git/GitSCMUnitTest.java +++ b/src/test/java/hudson/plugins/git/GitSCMUnitTest.java @@ -261,6 +261,30 @@ public void testRequiresWorkspaceForPollingMultiBranch() throws Exception { List branches = new ArrayList<>(); branches.add(new BranchSpec("master")); branches.add(new BranchSpec("origin/master")); + GitSCM bigGitSCM = new GitSCM(createRepoList(repoURL, null), + branches, + null, null, Collections.emptyList()); + assertFalse(bigGitSCM.requiresWorkspaceForPolling()); + } + + @Test + public void testRequiresWorkspaceForPollingMultiBranchWithWildcardRemoteName() throws Exception { + /* Multi-branch use case */ + List branches = new ArrayList<>(); + branches.add(new BranchSpec("master")); + branches.add(new BranchSpec("*/master")); + GitSCM bigGitSCM = new GitSCM(createRepoList(repoURL, null), + branches, + null, null, Collections.emptyList()); + assertFalse(bigGitSCM.requiresWorkspaceForPolling()); + } + + @Test + public void testRequiresWorkspaceForPollingMultiBranchWithWildcardSuffix() throws Exception { + /* Multi-branch use case */ + List branches = new ArrayList<>(); + branches.add(new BranchSpec("master")); + branches.add(new BranchSpec("*/stable*")); GitSCM bigGitSCM = new GitSCM(createRepoList(repoURL, null), branches, null, null, Collections.emptyList()); @@ -275,7 +299,7 @@ public void testRequiresWorkspaceForPollingEmptyBranchName() throws Exception { GitSCM bigGitSCM = new GitSCM(createRepoList(repoURL, null), Collections.singletonList(new BranchSpec("${A}")), null, null, Collections.emptyList()); - assertFalse(bigGitSCM.requiresWorkspaceForPolling(env)); + assertTrue(bigGitSCM.requiresWorkspaceForPolling(env, null)); } @Test