diff --git a/src/main/java/hudson/plugins/git/BranchSpec.java b/src/main/java/hudson/plugins/git/BranchSpec.java
index 2c6ce09367..fba6efdaeb 100644
--- a/src/main/java/hudson/plugins/git/BranchSpec.java
+++ b/src/main/java/hudson/plugins/git/BranchSpec.java
@@ -159,6 +159,9 @@ private Pattern getPattern(EnvVars env, String repositoryName) {
String regexSubstring = expandedName.substring(1, expandedName.length());
return Pattern.compile(regexSubstring);
}
+ if (expandedName.startsWith("refs/changes/")) {
+ return Pattern.compile(Pattern.quote(expandedName));
+ }
if (repositoryName != null) {
// remove the "refs/.../" stuff from the branch-spec if necessary
String pattern = cutRefs(expandedName)
diff --git a/src/main/java/jenkins/plugins/git/GitSCMFileSystem.java b/src/main/java/jenkins/plugins/git/GitSCMFileSystem.java
index 2165734450..071091a0fb 100644
--- a/src/main/java/jenkins/plugins/git/GitSCMFileSystem.java
+++ b/src/main/java/jenkins/plugins/git/GitSCMFileSystem.java
@@ -59,6 +59,9 @@
import java.util.concurrent.locks.Lock;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
import jenkins.scm.api.SCMFile;
import jenkins.scm.api.SCMFileSystem;
import jenkins.scm.api.SCMHead;
@@ -267,6 +270,9 @@ public boolean supports(SCM source) {
|| gscm.getBranches().get(0).getName().matches(
"^((\\Q" + Constants.R_TAGS + "\\E.*)|([^/]+)|(\\*/[^/*]+(/[^/*]+)*))$"
)
+ || gscm.getBranches().get(0).getName().matches(
+ "^((\\Qrefs/changes/\\E.*)|([^/]+)|(\\*/[^/*]+(/[^/*]+)*))$"
+ )
);
// we only support where the branch spec is obvious and not a wildcard
}
@@ -287,40 +293,82 @@ public boolean supportsDescriptor(SCMSourceDescriptor descriptor) {
}
static class HeadNameResult {
- final String headName;
- final String prefix;
-
- private HeadNameResult(String headName, String prefix) {
- this.headName = headName;
- this.prefix = prefix;
+ final String remoteHeadName;
+ final String refspec;
+ final SCMRevision rev;
+
+ private HeadNameResult(String remoteHeadName, String refspec, SCMRevision rev) {
+ this.remoteHeadName = remoteHeadName;
+ this.refspec = refspec;
+ this.rev = rev;
}
static HeadNameResult calculate(@NonNull BranchSpec branchSpec,
@CheckForNull SCMRevision rev,
- @CheckForNull EnvVars env) {
+ @CheckForNull String refSpec,
+ @CheckForNull EnvVars env,
+ @CheckForNull String remoteName) {
+
String branchSpecExpandedName = branchSpec.getName();
if (env != null) {
branchSpecExpandedName = env.expand(branchSpecExpandedName);
}
+ String refspecExpandedName = refSpec;
+ if (env != null && refspecExpandedName != null) {
+ refspecExpandedName = env.expand(refspecExpandedName);
+ }
+ // default to a branch (refs/heads)
String prefix = Constants.R_HEADS;
+ // check for a tag
if (branchSpecExpandedName.startsWith(Constants.R_TAGS)) {
prefix = Constants.R_TAGS;
+ } else if (branchSpecExpandedName.startsWith("refs/changes")) {
+ prefix = "refs/changes/";
+ } else {
+ // check for FETCH_HEAD
+ if (branchSpecExpandedName.equals(Constants.FETCH_HEAD) && refspecExpandedName != null &&
+ !refspecExpandedName.equals("")) {
+ prefix = null;
+ } else {
+ // check for commit-id
+ final String regex = "^[a-fA-F0-9]{40}$";
+ final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
+ final Matcher matcher = pattern.matcher(branchSpecExpandedName);
+
+ if (matcher.find()) {
+ // commit-id
+ prefix = null;
+ rev = new AbstractGitSCMSource.SCMRevisionImpl(new SCMHead(branchSpecExpandedName), branchSpecExpandedName);
+ }
+ }
}
- String headName;
+ String calculatedHeadName = branchSpecExpandedName;
if (rev != null && env != null) {
- headName = env.expand(rev.getHead().getName());
+ calculatedHeadName = env.expand(rev.getHead().getName());
} else {
- if (branchSpecExpandedName.startsWith(prefix)) {
- headName = branchSpecExpandedName.substring(prefix.length());
+ if (prefix != null && branchSpecExpandedName.startsWith(prefix)) {
+ calculatedHeadName = branchSpecExpandedName.substring(prefix.length());
} else if (branchSpecExpandedName.startsWith("*/")) {
- headName = branchSpecExpandedName.substring(2);
+ calculatedHeadName = branchSpecExpandedName.substring(2);
+ }
+ }
+
+ if (refspecExpandedName == null || refspecExpandedName.equals("")) {
+ if (prefix.equals(Constants.R_TAGS)) {
+ refspecExpandedName = "+" + prefix + calculatedHeadName + ":" + prefix + calculatedHeadName;
} else {
- headName = branchSpecExpandedName;
+ refspecExpandedName = "+" + prefix + calculatedHeadName + ":" + Constants.R_REMOTES + remoteName + "/" + calculatedHeadName;
}
}
- return new HeadNameResult(headName, prefix);
+
+ String remoteHead = calculatedHeadName;
+ if (prefix != null && prefix.equals(Constants.R_HEADS)) {
+ remoteHead = Constants.R_REMOTES + remoteName + "/" + calculatedHeadName;
+ }
+
+ return new HeadNameResult(remoteHead, refspecExpandedName, rev);
}
}
@@ -397,16 +445,11 @@ public SCMFileSystem build(@NonNull Item owner, @NonNull SCM scm, @CheckForNull
listener.getLogger().println("URI syntax exception for '" + remoteName + "' " + ex);
}
- HeadNameResult headNameResult = HeadNameResult.calculate(branchSpec, rev, env);
+ HeadNameResult headNameResult = HeadNameResult.calculate(branchSpec, rev, config.getRefspec(), env, remoteName);
+ client.fetch_().prune(true).from(remoteURI, Collections.singletonList(new RefSpec(headNameResult.refspec))).execute();
- client.fetch_().prune(true).from(remoteURI, Collections.singletonList(new RefSpec(
- "+" + headNameResult.prefix + headNameResult.headName + ":" + Constants.R_REMOTES + remoteName + "/"
- + headNameResult.headName))).execute();
-
- listener.getLogger().println("Done.");
- return new GitSCMFileSystem(client, remote, Constants.R_REMOTES + remoteName + "/" + headNameResult.headName, (AbstractGitSCMSource.SCMRevisionImpl) rev);
- } catch (GitException x) {
- throw new IOException(x);
+ listener.getLogger().println("Done with " + remoteName + " using " + headNameResult.remoteHeadName + ".");
+ return new GitSCMFileSystem(client, remote, headNameResult.remoteHeadName, (AbstractGitSCMSource.SCMRevisionImpl) headNameResult.rev);
} finally {
cacheLock.unlock();
}
diff --git a/src/main/resources/hudson/plugins/git/BranchSpec/help-name.html b/src/main/resources/hudson/plugins/git/BranchSpec/help-name.html
index 7c39aee1fd..3548b4846e 100644
--- a/src/main/resources/hudson/plugins/git/BranchSpec/help-name.html
+++ b/src/main/resources/hudson/plugins/git/BranchSpec/help-name.html
@@ -47,6 +47,10 @@
Tracks/checks out the specified tag.
E.g. refs/tags/git-2.3.0
+
refs/changes/<gerritChange>refs/changes/91/45391/1
+ <commitId>5062ac843f2b947733e6a3b105977056821bd352, 5062ac84, ...
diff --git a/src/test/java/hudson/plugins/git/BranchSpecTest.java b/src/test/java/hudson/plugins/git/BranchSpecTest.java
index 835cf16a5b..27b82e6bac 100644
--- a/src/test/java/hudson/plugins/git/BranchSpecTest.java
+++ b/src/test/java/hudson/plugins/git/BranchSpecTest.java
@@ -149,6 +149,14 @@ void testUsesRefsHeads() {
assertFalse(m.matches("remote/origin/jane"));
}
+ @Test
+ public void testMatchesGerritChangeRef() {
+ BranchSpec spec = new BranchSpec("refs/changes/91/45391/1");
+
+ assertTrue(spec.matches("refs/changes/91/45391/1"));
+ assertFalse(spec.matches("refs/heads/refs/changes/91/45391/1"));
+ }
+
@Test
void testUsesJavaPatternDirectlyIfPrefixedWithColon() {
BranchSpec m = new BranchSpec(":^(?!(origin/prefix)).*");
diff --git a/src/test/java/jenkins/plugins/git/GitSCMFileSystemTest.java b/src/test/java/jenkins/plugins/git/GitSCMFileSystemTest.java
index ffee98ff89..6bef3b8ff7 100644
--- a/src/test/java/jenkins/plugins/git/GitSCMFileSystemTest.java
+++ b/src/test/java/jenkins/plugins/git/GitSCMFileSystemTest.java
@@ -32,11 +32,15 @@
import hudson.plugins.git.GitException;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
import java.util.TreeSet;
+import hudson.plugins.git.UserRemoteConfig;
import jenkins.plugins.git.junit.jupiter.WithGitSampleRepo;
import jenkins.scm.api.SCMFile;
import jenkins.scm.api.SCMFileSystem;
@@ -68,6 +72,8 @@
import static org.junit.jupiter.api.Assertions.*;
import static org.hamcrest.MatcherAssert.assertThat;
+import jenkins.plugins.git.GitSCMFileSystem.BuilderImpl.HeadNameResult;
+
/**
* Tests for {@link AbstractGitSCMSource}
*/
@@ -410,6 +416,10 @@ void create_SCMFileSystem_from_tag() throws Exception {
sampleRepo.git("commit", "--all", "--message=dev");
sampleRepo.git("tag", "v1.0");
SCMFileSystem fs = SCMFileSystem.of(r.createFreeStyleProject(), new GitSCM(GitSCM.createRepoList(sampleRepo.toString(), null), Collections.singletonList(new BranchSpec("refs/tags/v1.0")), null, null, Collections.emptyList()));
+ assertEquals("modified", getFileContent(fs, "dir/subdir/file", "modified"));
+ }
+
+ public String getFileContent(SCMFileSystem fs, String path, String expectedContent) throws IOException, InterruptedException {
assertThat(fs, notNullValue());
assertThat(fs.getRoot(), notNullValue());
Iterable