{
+
+ /**
+ * Stapler constructor.
+ *
+ * @param extension the {@link PartialCloneFilter}
+ */
+ @DataBoundConstructor
+ public PartialCloneFilterTrait(PartialCloneFilter extension) {
+ super(extension);
+ }
+
+ /**
+ * Our {@link hudson.model.Descriptor}
+ */
+ @Extension
+ @Symbol("partialCloneFilter")
+ public static class DescriptorImpl extends GitSCMExtensionTraitDescriptor {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDisplayName() {
+ return "Partial clone";
+ }
+ }
+}
diff --git a/src/main/resources/hudson/plugins/git/extensions/impl/PartialCloneFilter/config.groovy b/src/main/resources/hudson/plugins/git/extensions/impl/PartialCloneFilter/config.groovy
new file mode 100644
index 0000000000..6383965ce9
--- /dev/null
+++ b/src/main/resources/hudson/plugins/git/extensions/impl/PartialCloneFilter/config.groovy
@@ -0,0 +1,7 @@
+package hudson.plugins.git.extensions.impl.PartialCloneFilter
+
+def f = namespace(lib.FormTagLib)
+
+f.entry(title:_("Filter specification"), field:"filterSpec") {
+ f.textbox()
+}
diff --git a/src/main/resources/hudson/plugins/git/extensions/impl/PartialCloneFilter/help.html b/src/main/resources/hudson/plugins/git/extensions/impl/PartialCloneFilter/help.html
new file mode 100644
index 0000000000..9614997740
--- /dev/null
+++ b/src/main/resources/hudson/plugins/git/extensions/impl/PartialCloneFilter/help.html
@@ -0,0 +1,4 @@
+
+ Specify an object filter for clone and fetch.
+ If set, the repository is converted to a partial clone.
+
diff --git a/src/test/java/hudson/plugins/git/CredentialsUserRemoteConfigTest.java b/src/test/java/hudson/plugins/git/CredentialsUserRemoteConfigTest.java
index 1027e2f773..4b20dca986 100644
--- a/src/test/java/hudson/plugins/git/CredentialsUserRemoteConfigTest.java
+++ b/src/test/java/hudson/plugins/git/CredentialsUserRemoteConfigTest.java
@@ -114,6 +114,7 @@ private String randomPipelineExtensions() {
// Inverse build chooser will find nothing to build and fails the test
// "[$class: 'BuildChooserSetting', buildChooser: [$class: 'InverseBuildChooser']]",
"[$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [[path: 'src'], [path: 'Makefile']]]",
+ "[$class: 'PartialCloneFilter', filterSpec: 'blob:none'",
"[$class: 'AuthorInChangelog']",
"[$class: 'BuildChooserSetting', buildChooser: [$class: 'DefaultBuildChooser']]",
"[$class: 'BuildSingleRevisionOnly']",
@@ -160,6 +161,7 @@ private String randomPipelineExtensions() {
"pruneTags(false)",
"pruneTags(true)",
"sparseCheckout(sparseCheckoutPaths: [[path: 'src'], [path: 'Makefile']])",
+ "partialCloneFilter('blob:none')",
"submodule(disableSubmodules: true)",
"submodule(depth: 1, shallow: true)",
"submodule(parentCredentials: true, recursiveSubmodules: true, threads: 13)",
diff --git a/src/test/java/hudson/plugins/git/extensions/impl/PartialCloneFilterTest.java b/src/test/java/hudson/plugins/git/extensions/impl/PartialCloneFilterTest.java
new file mode 100644
index 0000000000..47950d91f0
--- /dev/null
+++ b/src/test/java/hudson/plugins/git/extensions/impl/PartialCloneFilterTest.java
@@ -0,0 +1,211 @@
+package hudson.plugins.git.extensions.impl;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+
+import hudson.model.Run;
+import hudson.model.TaskListener;
+import hudson.plugins.git.GitException;
+import hudson.plugins.git.GitSCM;
+import java.util.List;
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.URIish;
+import org.jenkinsci.plugins.gitclient.CloneCommand;
+import org.jenkinsci.plugins.gitclient.FetchCommand;
+import org.jenkinsci.plugins.gitclient.GitClient;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class PartialCloneFilterTest {
+
+ private static final String BLOBLESS_FILTER_SPEC = "blob:none";
+
+ private PartialCloneFilter bloblessPartialCloneFilter;
+
+ @BeforeEach
+ void beforeEach() {
+ bloblessPartialCloneFilter = new PartialCloneFilter(BLOBLESS_FILTER_SPEC);
+ }
+
+ @Test
+ void testGetFilterSpec() {
+ assertThat(bloblessPartialCloneFilter.getFilterSpec(), is(BLOBLESS_FILTER_SPEC));
+ }
+
+ @Test
+ void testDecorateCloneCommand() throws Exception {
+ GitSCM scm = null;
+ Run build = null;
+ GitClient git = null;
+ TaskListener listener = null;
+ MyCloneCommand cmd = new MyCloneCommand();
+ bloblessPartialCloneFilter.decorateCloneCommand(scm, build, git, listener, cmd);
+ assertThat(cmd.getFilterSpec(), is(BLOBLESS_FILTER_SPEC));
+ }
+
+ @Test
+ void testDecorateFetchCommand() throws Exception {
+ GitSCM scm = null;
+ Run build = null;
+ GitClient git = null;
+ TaskListener listener = null;
+ MyFetchCommand cmd = new MyFetchCommand();
+ bloblessPartialCloneFilter.decorateFetchCommand(scm, build, git, listener, cmd);
+ assertThat(cmd.getFilterSpec(), is(BLOBLESS_FILTER_SPEC));
+ }
+
+ @Test
+ void equalsContract() {
+ EqualsVerifier.forClass(PartialCloneFilter.class).usingGetClass().verify();
+ }
+
+ @Test
+ void testHashCode() {
+ PartialCloneFilter bloblessPartialCloneFilterCopy = new PartialCloneFilter(BLOBLESS_FILTER_SPEC);
+ assertThat(bloblessPartialCloneFilter.hashCode(), is(bloblessPartialCloneFilterCopy.hashCode()));
+ assertThat(bloblessPartialCloneFilter, is(bloblessPartialCloneFilterCopy));
+ }
+
+ @Test
+ void testToString() {
+ assertThat(
+ bloblessPartialCloneFilter.toString(),
+ is("PartialCloneFilter{filterSpec=" + BLOBLESS_FILTER_SPEC + "}"));
+ }
+
+ private static class MyCloneCommand implements CloneCommand {
+
+ private String filterSpec;
+
+ String getFilterSpec() {
+ return filterSpec;
+ }
+
+ @Override
+ public CloneCommand url(String url) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand repositoryName(String name) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand shallow() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand shallow(boolean shallow) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand shared() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand shared(boolean shared) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand reference(String reference) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand timeout(Integer timeout) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand noCheckout() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand tags(boolean tags) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand refspecs(List refspecs) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand depth(Integer depth) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CloneCommand filter(String filterSpec) {
+ this.filterSpec = filterSpec;
+ return this;
+ }
+
+ @Override
+ public void execute() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+ }
+
+ private static class MyFetchCommand implements FetchCommand {
+
+ private String filterSpec;
+
+ String getFilterSpec() {
+ return filterSpec;
+ }
+
+ @Override
+ public FetchCommand from(URIish remote, List refspecs) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public FetchCommand prune() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public FetchCommand prune(boolean prune) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public FetchCommand shallow(boolean shallow) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public FetchCommand timeout(Integer timeout) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public FetchCommand tags(boolean tags) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public FetchCommand depth(Integer depth) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public FetchCommand filter(String filterSpec) {
+ this.filterSpec = filterSpec;
+ return this;
+ }
+
+ @Override
+ public void execute() throws GitException, InterruptedException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+ }
+}
diff --git a/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceTest.java b/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceTest.java
index 1524ebab11..63677fb441 100644
--- a/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceTest.java
+++ b/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceTest.java
@@ -1221,6 +1221,11 @@ public FetchCommand depth(Integer integer) {
return this;
}
+ @Override
+ public FetchCommand filter(String filterSpec) {
+ return this;
+ }
+
@Override
public void execute() throws GitException, InterruptedException {
fetchCommand.execute();
diff --git a/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceWantTagsTest.java b/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceWantTagsTest.java
index 2a7b87ca76..17cdf37e66 100644
--- a/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceWantTagsTest.java
+++ b/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceWantTagsTest.java
@@ -228,6 +228,11 @@ public FetchCommand depth(Integer integer) {
return this;
}
+ @Override
+ public FetchCommand filter(String filterSpec) {
+ return this;
+ }
+
@Override
public void execute() throws GitException, InterruptedException {
fetchCommand.execute();