diff --git a/src/main/java/org/jenkinsci/plugins/gitclient/CliGitAPIImpl.java b/src/main/java/org/jenkinsci/plugins/gitclient/CliGitAPIImpl.java index f0d4e0dc88..bc6782c657 100644 --- a/src/main/java/org/jenkinsci/plugins/gitclient/CliGitAPIImpl.java +++ b/src/main/java/org/jenkinsci/plugins/gitclient/CliGitAPIImpl.java @@ -394,7 +394,7 @@ public void execute() throws GitException, InterruptedException { if (prune) args.add("--prune"); if (shallow) { - if (depth == null){ + if (depth == null) { depth = 1; } args.add("--depth=" + depth); @@ -1067,9 +1067,11 @@ public SubmoduleUpdateCommand submoduleUpdate() { boolean recursive = false; boolean remoteTracking = false; boolean parentCredentials = false; + boolean shallow = false; String ref = null; Map submodBranch = new HashMap<>(); public Integer timeout; + Integer depth = 1; public SubmoduleUpdateCommand recursive(boolean recursive) { this.recursive = recursive; @@ -1101,6 +1103,16 @@ public SubmoduleUpdateCommand timeout(Integer timeout) { return this; } + public SubmoduleUpdateCommand shallow(boolean shallow) { + this.shallow = shallow; + return this; + } + + public SubmoduleUpdateCommand depth(Integer depth) { + this.depth = depth; + return this; + } + /** * @throws GitException if executing the Git command fails * @throws InterruptedException if called methods throw same exception @@ -1131,7 +1143,16 @@ else if (!referencePath.isDirectory()) else args.add("--reference", ref); } - + if (shallow) { + if (depth == null) { + depth = 1; + } + if (isAtLeastVersion(1, 8, 4, 0)) { + args.add("--depth=" + depth); + } else { + listener.getLogger().println("[WARNING] Git client older than 1.8.4 doesn't support shallow submodule updates. This flag is ignored."); + } + } // We need to call submodule update for each configured // submodule. Note that we can't reliably depend on the diff --git a/src/main/java/org/jenkinsci/plugins/gitclient/CloneCommand.java b/src/main/java/org/jenkinsci/plugins/gitclient/CloneCommand.java index 6c357cd8a7..3f2a0caa91 100644 --- a/src/main/java/org/jenkinsci/plugins/gitclient/CloneCommand.java +++ b/src/main/java/org/jenkinsci/plugins/gitclient/CloneCommand.java @@ -109,7 +109,7 @@ public interface CloneCommand extends GitCommand { /** * When shallow cloning, allow for a depth to be set in cases where you need more than the immediate last commit. - * Has no effect if shallow is set to false (default) + * Has no effect if shallow is set to false (default). * * @param depth number of revisions to be included in shallow clone * @return a {@link org.jenkinsci.plugins.gitclient.CloneCommand} object. diff --git a/src/main/java/org/jenkinsci/plugins/gitclient/JGitAPIImpl.java b/src/main/java/org/jenkinsci/plugins/gitclient/JGitAPIImpl.java index 2dbdde6e29..aed432c660 100644 --- a/src/main/java/org/jenkinsci/plugins/gitclient/JGitAPIImpl.java +++ b/src/main/java/org/jenkinsci/plugins/gitclient/JGitAPIImpl.java @@ -2175,7 +2175,7 @@ public void submoduleClean(boolean recursive) throws GitException { } /** - * submoduleUpdate. + * Update submodules. * * @return a {@link org.jenkinsci.plugins.gitclient.SubmoduleUpdateCommand} object. */ @@ -2216,6 +2216,20 @@ public org.jenkinsci.plugins.gitclient.SubmoduleUpdateCommand timeout(Integer ti return this; } + @Override + public org.jenkinsci.plugins.gitclient.SubmoduleUpdateCommand shallow(boolean shallow) { + if (shallow) { + listener.getLogger().println("[WARNING] JGit doesn't support shallow clone. This flag is ignored"); + } + return this; + } + + @Override + public org.jenkinsci.plugins.gitclient.SubmoduleUpdateCommand depth(Integer depth) { + listener.getLogger().println("[WARNING] JGit doesn't support shallow clone and therefore depth is meaningless. This flag is ignored"); + return this; + } + @Override public org.jenkinsci.plugins.gitclient.SubmoduleUpdateCommand useBranch(String submodule, String branchname) { return this; diff --git a/src/main/java/org/jenkinsci/plugins/gitclient/SubmoduleUpdateCommand.java b/src/main/java/org/jenkinsci/plugins/gitclient/SubmoduleUpdateCommand.java index ada0c10ad6..0216c4c698 100644 --- a/src/main/java/org/jenkinsci/plugins/gitclient/SubmoduleUpdateCommand.java +++ b/src/main/java/org/jenkinsci/plugins/gitclient/SubmoduleUpdateCommand.java @@ -58,4 +58,22 @@ public interface SubmoduleUpdateCommand extends GitCommand { * @return a {@link org.jenkinsci.plugins.gitclient.SubmoduleUpdateCommand} object. */ SubmoduleUpdateCommand timeout(Integer timeout); + + /** + * Only clone the most recent history, not preceding history. Depth of the + * shallow clone is controlled by the #depth method. + * + * @param shallow boolean controlling whether the clone is shallow (requires git>=1.8.4) + * @return a {@link org.jenkinsci.plugins.gitclient.SubmoduleUpdateCommand} object. + */ + SubmoduleUpdateCommand shallow(boolean shallow); + + /** + * When shallow cloning, allow for a depth to be set in cases where you need more than the immediate last commit. + * Has no effect if shallow is set to false (default). + * + * @param depth number of revisions to be included in shallow clone (requires git>=1.8.4) + * @return a {@link org.jenkinsci.plugins.gitclient.SubmoduleUpdateCommand} object. + */ + SubmoduleUpdateCommand depth(Integer depth); } diff --git a/src/test/java/org/jenkinsci/plugins/gitclient/GitAPITestCase.java b/src/test/java/org/jenkinsci/plugins/gitclient/GitAPITestCase.java index 1cd1b3bb41..39c2169a29 100644 --- a/src/test/java/org/jenkinsci/plugins/gitclient/GitAPITestCase.java +++ b/src/test/java/org/jenkinsci/plugins/gitclient/GitAPITestCase.java @@ -31,6 +31,7 @@ import java.io.StringWriter; import java.lang.reflect.Field; import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -556,12 +557,13 @@ public void test_clone_shallow() throws Exception assertBranchesExist(w.git.getBranches(), "master"); assertAlternatesFileNotFound(); /* JGit does not support shallow clone */ - assertEquals("isShallow?", w.igit() instanceof CliGitAPIImpl, w.cgit().isShallowRepository()); - final String shallow = ".git" + File.separator + "shallow"; - assertEquals("Shallow file existence: " + shallow, w.igit() instanceof CliGitAPIImpl, w.exists(shallow)); + boolean hasShallowCloneSupport = w.git instanceof CliGitAPIImpl && w.cgit().isAtLeastVersion(1, 5, 0, 0); + assertEquals("isShallow?", hasShallowCloneSupport, w.cgit().isShallowRepository()); + String shallow = ".git" + File.separator + "shallow"; + assertEquals("shallow file existence: " + shallow, hasShallowCloneSupport, w.exists(shallow)); } - public void test_clone_shallow_with_depth() throws IOException, InterruptedException + public void test_clone_shallow_with_depth() throws Exception { w.git.clone_().url(localMirror()).repositoryName("origin").shallow(true).depth(2).execute(); w.git.checkout("origin/master", "master"); @@ -569,8 +571,10 @@ public void test_clone_shallow_with_depth() throws IOException, InterruptedExcep assertBranchesExist(w.git.getBranches(), "master"); assertAlternatesFileNotFound(); /* JGit does not support shallow clone */ - final String shallow = ".git" + File.separator + "shallow"; - assertEquals("Shallow file existence: " + shallow, w.igit() instanceof CliGitAPIImpl, w.exists(shallow)); + boolean hasShallowCloneSupport = w.git instanceof CliGitAPIImpl && w.cgit().isAtLeastVersion(1, 5, 0, 0); + assertEquals("isShallow?", hasShallowCloneSupport, w.cgit().isShallowRepository()); + String shallow = ".git" + File.separator + "shallow"; + assertEquals("shallow file existence: " + shallow, hasShallowCloneSupport, w.exists(shallow)); } public void test_clone_shared() throws IOException, InterruptedException @@ -1401,9 +1405,11 @@ public void test_fetch_shallow() throws Exception { assertBranchesExist(w.git.getRemoteBranches(), "origin/master"); final String alternates = ".git" + File.separator + "objects" + File.separator + "info" + File.separator + "alternates"; assertFalse("Alternates file found: " + alternates, w.exists(alternates)); - /* JGit does not support shallow clone */ - final String shallow = ".git" + File.separator + "shallow"; - assertEquals("Shallow file: " + shallow, w.igit() instanceof CliGitAPIImpl, w.exists(shallow)); + /* JGit does not support shallow fetch */ + boolean hasShallowFetchSupport = w.git instanceof CliGitAPIImpl && w.cgit().isAtLeastVersion(1, 5, 0, 0); + assertEquals("isShallow?", hasShallowFetchSupport, w.cgit().isShallowRepository()); + String shallow = ".git" + File.separator + "shallow"; + assertEquals("shallow file existence: " + shallow, hasShallowFetchSupport, w.exists(shallow)); } public void test_fetch_shallow_depth() throws Exception { @@ -1414,9 +1420,11 @@ public void test_fetch_shallow_depth() throws Exception { assertBranchesExist(w.git.getRemoteBranches(), "origin/master"); final String alternates = ".git" + File.separator + "objects" + File.separator + "info" + File.separator + "alternates"; assertFalse("Alternates file found: " + alternates, w.exists(alternates)); - /* JGit does not support shallow clone */ - final String shallow = ".git" + File.separator + "shallow"; - assertEquals("Shallow file: " + shallow, w.igit() instanceof CliGitAPIImpl, w.exists(shallow)); + /* JGit does not support shallow fetch */ + boolean hasShallowFetchSupport = w.git instanceof CliGitAPIImpl && w.cgit().isAtLeastVersion(1, 5, 0, 0); + assertEquals("isShallow?", hasShallowFetchSupport, w.cgit().isShallowRepository()); + String shallow = ".git" + File.separator + "shallow"; + assertEquals("shallow file existence: " + shallow, hasShallowFetchSupport, w.exists(shallow)); } public void test_fetch_noTags() throws Exception { @@ -2586,6 +2594,43 @@ public void test_submodule_update() throws Exception { assertTrue("modules/sshkeys does not exist", w.exists("modules/sshkeys")); } assertFixSubmoduleUrlsThrows(); + + String shallow = Paths.get(".git", "modules", "module", "1", "shallow").toString(); + assertFalse("shallow file existence: " + shallow, w.exists(shallow)); + } + + public void test_submodule_update_shallow() throws Exception { + WorkingArea remote = setupRepositoryWithSubmodule(); + w.git.clone_().url("file://" + remote.file("dir-repository").getAbsolutePath()).repositoryName("origin").execute(); + w.git.checkout().branch("master").ref("origin/master").execute(); + w.git.submoduleInit(); + w.git.submoduleUpdate().shallow(true).execute(); + + boolean hasShallowSubmoduleSupport = w.git instanceof CliGitAPIImpl && w.cgit().isAtLeastVersion(1, 8, 4, 0); + + String shallow = Paths.get(".git", "modules", "submodule", "shallow").toString(); + assertEquals("shallow file existence: " + shallow, hasShallowSubmoduleSupport, w.exists(shallow)); + + int localSubmoduleCommits = w.cgit().subGit("submodule").revList("master").size(); + int remoteSubmoduleCommits = remote.cgit().subGit("dir-submodule").revList("master").size(); + assertEquals("submodule commit count didn't match", hasShallowSubmoduleSupport ? 1 : remoteSubmoduleCommits, localSubmoduleCommits); + } + + public void test_submodule_update_shallow_with_depth() throws Exception { + WorkingArea remote = setupRepositoryWithSubmodule(); + w.git.clone_().url("file://" + remote.file("dir-repository").getAbsolutePath()).repositoryName("origin").execute(); + w.git.checkout().branch("master").ref("origin/master").execute(); + w.git.submoduleInit(); + w.git.submoduleUpdate().shallow(true).depth(2).execute(); + + boolean hasShallowSubmoduleSupport = w.git instanceof CliGitAPIImpl && w.cgit().isAtLeastVersion(1, 8, 4, 0); + + String shallow = Paths.get(".git", "modules", "submodule", "shallow").toString(); + assertEquals("shallow file existence: " + shallow, hasShallowSubmoduleSupport, w.exists(shallow)); + + int localSubmoduleCommits = w.cgit().subGit("submodule").revList("master").size(); + int remoteSubmoduleCommits = remote.cgit().subGit("dir-submodule").revList("master").size(); + assertEquals("submodule commit count didn't match", hasShallowSubmoduleSupport ? 2 : remoteSubmoduleCommits, localSubmoduleCommits); } @NotImplementedInJGit @@ -4640,4 +4685,32 @@ private void withSystemLocaleReporting(String fileName, TestedCode code) throws interface TestedCode { void run() throws Exception; } + + private WorkingArea setupRepositoryWithSubmodule() throws Exception { + WorkingArea workingArea = new WorkingArea(); + + File repositoryDir = workingArea.file("dir-repository"); + File submoduleDir = workingArea.file("dir-submodule"); + + assertTrue("did not create dir " + repositoryDir.getName(), repositoryDir.mkdir()); + assertTrue("did not create dir " + submoduleDir.getName(), submoduleDir.mkdir()); + + WorkingArea submoduleWorkingArea = new WorkingArea(submoduleDir).init(); + + for (int commit = 1; commit <= 5; commit++) { + submoduleWorkingArea.touch("file", String.format("submodule content-%d", commit)); + submoduleWorkingArea.cgit().add("file"); + submoduleWorkingArea.cgit().commit(String.format("submodule commit-%d", commit)); + } + + WorkingArea repositoryWorkingArea = new WorkingArea(repositoryDir).init(); + + repositoryWorkingArea.commitEmpty("init"); + + repositoryWorkingArea.cgit().add("."); + repositoryWorkingArea.cgit().addSubmodule("file://" + submoduleDir.getAbsolutePath(), "submodule"); + repositoryWorkingArea.cgit().commit("submodule"); + + return workingArea; + } }