Skip to content

Commit 207dd4c

Browse files
Robin Müllertomaswolf
authored andcommitted
Fetch: add support for shallow
This adds support for shallow cloning. The CloneCommand and the FetchCommand now have the new methods setDepth, setShallowSince and addShallowExclude to tell the server that the client doesn't want to download the complete history. Bug: 475615 Change-Id: Ic80fb6efb5474543ae59be590ebe385bec21cc0d
1 parent 559be66 commit 207dd4c

25 files changed

+1066
-90
lines changed

org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@
2424
import java.net.URI;
2525
import java.net.URL;
2626
import java.text.MessageFormat;
27+
import java.time.Instant;
2728
import java.util.List;
29+
import java.util.Set;
2830

2931
import javax.servlet.http.HttpServletRequest;
3032

3133
import org.eclipse.jetty.servlet.DefaultServlet;
3234
import org.eclipse.jetty.servlet.ServletContextHandler;
3335
import org.eclipse.jetty.servlet.ServletHolder;
36+
import org.eclipse.jgit.api.Git;
3437
import org.eclipse.jgit.errors.NoRemoteRepositoryException;
3538
import org.eclipse.jgit.errors.RepositoryNotFoundException;
3639
import org.eclipse.jgit.errors.TransportException;
@@ -408,4 +411,110 @@ public void testV2HttpSubsequentResponse() throws Exception {
408411

409412
assertEquals(200, c.getResponseCode());
410413
}
414+
415+
@Test
416+
public void testCloneWithDepth() throws Exception {
417+
remoteRepository.getRepository().getConfig().setInt(
418+
"protocol", null, "version", 0);
419+
File directory = createTempDirectory("testCloneWithDepth");
420+
Git git = Git.cloneRepository()
421+
.setDirectory(directory)
422+
.setDepth(1)
423+
.setURI(smartAuthNoneURI.toString())
424+
.call();
425+
426+
assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
427+
}
428+
429+
@Test
430+
public void testCloneWithDeepenSince() throws Exception {
431+
remoteRepository.getRepository().getConfig().setInt(
432+
"protocol", null, "version", 0);
433+
RevCommit commit = remoteRepository.commit()
434+
.parent(remoteRepository.git().log().call().iterator().next())
435+
.message("Test")
436+
.add("test.txt", "Hello world")
437+
.create();
438+
remoteRepository.update(master, commit);
439+
440+
File directory = createTempDirectory("testCloneWithDeepenSince");
441+
Git git = Git.cloneRepository()
442+
.setDirectory(directory)
443+
.setShallowSince(Instant.ofEpochSecond(commit.getCommitTime()))
444+
.setURI(smartAuthNoneURI.toString())
445+
.call();
446+
447+
assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
448+
}
449+
450+
@Test
451+
public void testCloneWithDeepenNot() throws Exception {
452+
remoteRepository.getRepository().getConfig().setInt(
453+
"protocol", null, "version", 0);
454+
RevCommit commit = remoteRepository.git().log().call().iterator().next();
455+
remoteRepository.update(master, remoteRepository.commit()
456+
.parent(commit)
457+
.message("Test")
458+
.add("test.txt", "Hello world")
459+
.create());
460+
461+
File directory = createTempDirectory("testCloneWithDeepenNot");
462+
Git git = Git.cloneRepository()
463+
.setDirectory(directory)
464+
.addShallowExclude(commit.getId())
465+
.setURI(smartAuthNoneURI.toString())
466+
.call();
467+
468+
assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
469+
}
470+
471+
@Test
472+
public void testV2CloneWithDepth() throws Exception {
473+
File directory = createTempDirectory("testV2CloneWithDepth");
474+
Git git = Git.cloneRepository()
475+
.setDirectory(directory)
476+
.setDepth(1)
477+
.setURI(smartAuthNoneURI.toString())
478+
.call();
479+
480+
assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
481+
}
482+
483+
@Test
484+
public void testV2CloneWithDeepenSince() throws Exception {
485+
RevCommit commit = remoteRepository.commit()
486+
.parent(remoteRepository.git().log().call().iterator().next())
487+
.message("Test")
488+
.add("test.txt", "Hello world")
489+
.create();
490+
remoteRepository.update(master, commit);
491+
492+
File directory = createTempDirectory("testV2CloneWithDeepenSince");
493+
Git git = Git.cloneRepository()
494+
.setDirectory(directory)
495+
.setShallowSince(Instant.ofEpochSecond(commit.getCommitTime()))
496+
.setURI(smartAuthNoneURI.toString())
497+
.call();
498+
499+
assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
500+
}
501+
502+
@Test
503+
public void testV2CloneWithDeepenNot() throws Exception {
504+
RevCommit commit = remoteRepository.git().log().call().iterator().next();
505+
remoteRepository.update(master, remoteRepository.commit()
506+
.parent(commit)
507+
.message("Test")
508+
.add("test.txt", "Hello world")
509+
.create());
510+
511+
File directory = createTempDirectory("testV2CloneWithDeepenNot");
512+
Git git = Git.cloneRepository()
513+
.setDirectory(directory)
514+
.addShallowExclude(commit.getId())
515+
.setURI(smartAuthNoneURI.toString())
516+
.call();
517+
518+
assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
519+
}
411520
}

org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java

Lines changed: 234 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2011, 2013 Chris Aniszczyk <[email protected]> and others
2+
* Copyright (C) 2011, 2022 Chris Aniszczyk <[email protected]> and others
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -19,10 +19,14 @@
1919
import java.io.File;
2020
import java.io.IOException;
2121
import java.net.URISyntaxException;
22+
import java.time.Instant;
2223
import java.util.Collections;
2324
import java.util.List;
2425
import java.util.Map;
26+
import java.util.Set;
27+
import java.util.stream.Collectors;
2528
import java.util.stream.Stream;
29+
import java.util.stream.StreamSupport;
2630

2731
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
2832
import org.eclipse.jgit.api.errors.GitAPIException;
@@ -40,6 +44,7 @@
4044
import org.eclipse.jgit.lib.StoredConfig;
4145
import org.eclipse.jgit.revwalk.RevBlob;
4246
import org.eclipse.jgit.revwalk.RevCommit;
47+
import org.eclipse.jgit.revwalk.RevObject;
4348
import org.eclipse.jgit.submodule.SubmoduleStatus;
4449
import org.eclipse.jgit.submodule.SubmoduleStatusType;
4550
import org.eclipse.jgit.submodule.SubmoduleWalk;
@@ -895,6 +900,234 @@ public void testCloneWithHeadSymRefIsNonMasterCopy() throws IOException, GitAPIE
895900
assertEquals("refs/heads/test-copy", git2.getRepository().getFullBranch());
896901
}
897902

903+
@Test
904+
public void testCloneRepositoryWithDepth() throws IOException, JGitInternalException, GitAPIException {
905+
File directory = createTempDirectory("testCloneRepositoryWithDepth");
906+
CloneCommand command = Git.cloneRepository();
907+
command.setDirectory(directory);
908+
command.setURI(fileUri());
909+
command.setDepth(1);
910+
command.setBranchesToClone(Set.of("refs/heads/test"));
911+
Git git2 = command.call();
912+
addRepoToClose(git2.getRepository());
913+
914+
List<RevCommit> log = StreamSupport.stream(git2.log().all().call().spliterator(), false)
915+
.collect(Collectors.toList());
916+
assertEquals(1, log.size());
917+
RevCommit commit = log.get(0);
918+
assertEquals(Set.of(commit.getId()),
919+
git2.getRepository().getObjectDatabase().getShallowCommits());
920+
assertEquals("Second commit", commit.getFullMessage());
921+
assertEquals(0, commit.getParentCount());
922+
}
923+
924+
@Test
925+
public void testCloneRepositoryWithDepthAndAllBranches() throws IOException, JGitInternalException, GitAPIException {
926+
File directory = createTempDirectory("testCloneRepositoryWithDepthAndAllBranches");
927+
CloneCommand command = Git.cloneRepository();
928+
command.setDirectory(directory);
929+
command.setURI(fileUri());
930+
command.setDepth(1);
931+
command.setCloneAllBranches(true);
932+
Git git2 = command.call();
933+
addRepoToClose(git2.getRepository());
934+
935+
List<RevCommit> log = StreamSupport.stream(git2.log().all().call().spliterator(), false)
936+
.collect(Collectors.toList());
937+
assertEquals(2, log.size());
938+
assertEquals(log.stream().map(RevCommit::getId).collect(Collectors.toSet()),
939+
git2.getRepository().getObjectDatabase().getShallowCommits());
940+
assertEquals(List.of("Second commit", "Initial commit"),
941+
log.stream().map(RevCommit::getFullMessage).collect(Collectors.toList()));
942+
for (RevCommit commit : log) {
943+
assertEquals(0, commit.getParentCount());
944+
}
945+
}
946+
947+
@Test
948+
public void testCloneRepositoryWithDepth2() throws Exception {
949+
RevCommit parent = tr.git().log().call().iterator().next();
950+
RevCommit commit = tr.commit()
951+
.parent(parent)
952+
.message("Third commit")
953+
.add("test.txt", "Hello world")
954+
.create();
955+
tr.update("refs/heads/test", commit);
956+
957+
File directory = createTempDirectory("testCloneRepositoryWithDepth2");
958+
CloneCommand command = Git.cloneRepository();
959+
command.setDirectory(directory);
960+
command.setURI(fileUri());
961+
command.setDepth(2);
962+
command.setBranchesToClone(Set.of("refs/heads/test"));
963+
Git git2 = command.call();
964+
addRepoToClose(git2.getRepository());
965+
966+
List<RevCommit> log = StreamSupport
967+
.stream(git2.log().all().call().spliterator(), false)
968+
.collect(Collectors.toList());
969+
assertEquals(2, log.size());
970+
assertEquals(Set.of(parent.getId()),
971+
git2.getRepository().getObjectDatabase().getShallowCommits());
972+
assertEquals(List.of("Third commit", "Second commit"), log.stream()
973+
.map(RevCommit::getFullMessage).collect(Collectors.toList()));
974+
assertEquals(List.of(Integer.valueOf(1), Integer.valueOf(0)),
975+
log.stream().map(RevCommit::getParentCount)
976+
.collect(Collectors.toList()));
977+
}
978+
979+
@Test
980+
public void testCloneRepositoryWithDepthAndFetch() throws Exception {
981+
File directory = createTempDirectory("testCloneRepositoryWithDepthAndFetch");
982+
CloneCommand command = Git.cloneRepository();
983+
command.setDirectory(directory);
984+
command.setURI(fileUri());
985+
command.setDepth(1);
986+
command.setBranchesToClone(Set.of("refs/heads/test"));
987+
Git git2 = command.call();
988+
addRepoToClose(git2.getRepository());
989+
990+
RevCommit parent = tr.git().log().call().iterator().next();
991+
RevCommit commit = tr.commit()
992+
.parent(parent)
993+
.message("Third commit")
994+
.add("test.txt", "Hello world")
995+
.create();
996+
tr.update("refs/heads/test", commit);
997+
998+
git2.fetch().call();
999+
1000+
List<RevCommit> log = StreamSupport
1001+
.stream(git2.log().all().call().spliterator(), false)
1002+
.collect(Collectors.toList());
1003+
assertEquals(2, log.size());
1004+
assertEquals(Set.of(parent.getId()),
1005+
git2.getRepository().getObjectDatabase().getShallowCommits());
1006+
assertEquals(List.of("Third commit", "Second commit"), log.stream()
1007+
.map(RevCommit::getFullMessage).collect(Collectors.toList()));
1008+
assertEquals(List.of(Integer.valueOf(1), Integer.valueOf(0)),
1009+
log.stream().map(RevCommit::getParentCount)
1010+
.collect(Collectors.toList()));
1011+
}
1012+
1013+
@Test
1014+
public void testCloneRepositoryWithDepthAndFetchWithDepth() throws Exception {
1015+
File directory = createTempDirectory("testCloneRepositoryWithDepthAndFetchWithDepth");
1016+
CloneCommand command = Git.cloneRepository();
1017+
command.setDirectory(directory);
1018+
command.setURI(fileUri());
1019+
command.setDepth(1);
1020+
command.setBranchesToClone(Set.of("refs/heads/test"));
1021+
Git git2 = command.call();
1022+
addRepoToClose(git2.getRepository());
1023+
1024+
RevCommit parent = tr.git().log().call().iterator().next();
1025+
RevCommit commit = tr.commit()
1026+
.parent(parent)
1027+
.message("Third commit")
1028+
.add("test.txt", "Hello world")
1029+
.create();
1030+
tr.update("refs/heads/test", commit);
1031+
1032+
git2.fetch().setDepth(1).call();
1033+
1034+
List<RevCommit> log = StreamSupport
1035+
.stream(git2.log().all().call().spliterator(), false)
1036+
.collect(Collectors.toList());
1037+
assertEquals(2, log.size());
1038+
assertEquals(
1039+
log.stream().map(RevObject::getId).collect(Collectors.toSet()),
1040+
git2.getRepository().getObjectDatabase().getShallowCommits());
1041+
assertEquals(List.of("Third commit", "Second commit"), log.stream()
1042+
.map(RevCommit::getFullMessage).collect(Collectors.toList()));
1043+
assertEquals(List.of(Integer.valueOf(0), Integer.valueOf(0)),
1044+
log.stream().map(RevCommit::getParentCount)
1045+
.collect(Collectors.toList()));
1046+
}
1047+
1048+
@Test
1049+
public void testCloneRepositoryWithDepthAndFetchUnshallow() throws Exception {
1050+
File directory = createTempDirectory("testCloneRepositoryWithDepthAndFetchUnshallow");
1051+
CloneCommand command = Git.cloneRepository();
1052+
command.setDirectory(directory);
1053+
command.setURI(fileUri());
1054+
command.setDepth(1);
1055+
command.setBranchesToClone(Set.of("refs/heads/test"));
1056+
Git git2 = command.call();
1057+
addRepoToClose(git2.getRepository());
1058+
1059+
git2.fetch().setUnshallow(true).call();
1060+
1061+
List<RevCommit> log = StreamSupport
1062+
.stream(git2.log().all().call().spliterator(), false)
1063+
.collect(Collectors.toList());
1064+
assertEquals(2, log.size());
1065+
assertEquals(Set.of(),
1066+
git2.getRepository().getObjectDatabase().getShallowCommits());
1067+
assertEquals(List.of("Second commit", "Initial commit"), log.stream()
1068+
.map(RevCommit::getFullMessage).collect(Collectors.toList()));
1069+
assertEquals(List.of(Integer.valueOf(1), Integer.valueOf(0)),
1070+
log.stream().map(RevCommit::getParentCount)
1071+
.collect(Collectors.toList()));
1072+
}
1073+
1074+
@Test
1075+
public void testCloneRepositoryWithShallowSince() throws Exception {
1076+
RevCommit commit = tr.commit()
1077+
.parent(tr.git().log().call().iterator().next())
1078+
.message("Third commit").add("test.txt", "Hello world")
1079+
.create();
1080+
tr.update("refs/heads/test", commit);
1081+
1082+
File directory = createTempDirectory("testCloneRepositoryWithShallowSince");
1083+
CloneCommand command = Git.cloneRepository();
1084+
command.setDirectory(directory);
1085+
command.setURI(fileUri());
1086+
command.setShallowSince(Instant.ofEpochSecond(commit.getCommitTime()));
1087+
command.setBranchesToClone(Set.of("refs/heads/test"));
1088+
Git git2 = command.call();
1089+
addRepoToClose(git2.getRepository());
1090+
1091+
List<RevCommit> log = StreamSupport
1092+
.stream(git2.log().all().call().spliterator(), false)
1093+
.collect(Collectors.toList());
1094+
assertEquals(1, log.size());
1095+
assertEquals(Set.of(commit.getId()),
1096+
git2.getRepository().getObjectDatabase().getShallowCommits());
1097+
assertEquals("Third commit", log.get(0).getFullMessage());
1098+
assertEquals(0, log.get(0).getParentCount());
1099+
}
1100+
1101+
@Test
1102+
public void testCloneRepositoryWithShallowExclude() throws Exception {
1103+
RevCommit parent = tr.git().log().call().iterator().next();
1104+
tr.update("refs/heads/test",
1105+
tr.commit()
1106+
.parent(parent)
1107+
.message("Third commit")
1108+
.add("test.txt", "Hello world")
1109+
.create());
1110+
1111+
File directory = createTempDirectory("testCloneRepositoryWithShallowExclude");
1112+
CloneCommand command = Git.cloneRepository();
1113+
command.setDirectory(directory);
1114+
command.setURI(fileUri());
1115+
command.addShallowExclude(parent.getId());
1116+
command.setBranchesToClone(Set.of("refs/heads/test"));
1117+
Git git2 = command.call();
1118+
addRepoToClose(git2.getRepository());
1119+
1120+
List<RevCommit> log = StreamSupport
1121+
.stream(git2.log().all().call().spliterator(), false)
1122+
.collect(Collectors.toList());
1123+
assertEquals(1, log.size());
1124+
RevCommit commit = log.get(0);
1125+
assertEquals(Set.of(commit.getId()),
1126+
git2.getRepository().getObjectDatabase().getShallowCommits());
1127+
assertEquals("Third commit", commit.getFullMessage());
1128+
assertEquals(0, commit.getParentCount());
1129+
}
1130+
8981131
private void assertTagOption(Repository repo, TagOpt expectedTagOption)
8991132
throws URISyntaxException {
9001133
RemoteConfig remoteConfig = new RemoteConfig(

0 commit comments

Comments
 (0)