aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java109
-rw-r--r--org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java10
-rw-r--r--org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java20
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java235
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java39
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java6
-rw-r--r--org.eclipse.jgit/.settings/.api_filters16
-rw-r--r--org.eclipse.jgit/BUILD6
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java92
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java133
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java23
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java80
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java23
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java159
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java60
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java20
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java116
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java56
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java49
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java88
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java96
29 files changed, 1333 insertions, 174 deletions
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
index 3438c52c8d..6b067273a9 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
@@ -24,13 +24,16 @@ import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.text.MessageFormat;
+import java.time.Instant;
import java.util.List;
+import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.errors.NoRemoteRepositoryException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.errors.TransportException;
@@ -408,4 +411,110 @@ public class HttpClientTests extends AllFactoriesHttpTestCase {
assertEquals(200, c.getResponseCode());
}
+
+ @Test
+ public void testCloneWithDepth() throws Exception {
+ remoteRepository.getRepository().getConfig().setInt(
+ "protocol", null, "version", 0);
+ File directory = createTempDirectory("testCloneWithDepth");
+ Git git = Git.cloneRepository()
+ .setDirectory(directory)
+ .setDepth(1)
+ .setURI(smartAuthNoneURI.toString())
+ .call();
+
+ assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+ }
+
+ @Test
+ public void testCloneWithDeepenSince() throws Exception {
+ remoteRepository.getRepository().getConfig().setInt(
+ "protocol", null, "version", 0);
+ RevCommit commit = remoteRepository.commit()
+ .parent(remoteRepository.git().log().call().iterator().next())
+ .message("Test")
+ .add("test.txt", "Hello world")
+ .create();
+ remoteRepository.update(master, commit);
+
+ File directory = createTempDirectory("testCloneWithDeepenSince");
+ Git git = Git.cloneRepository()
+ .setDirectory(directory)
+ .setShallowSince(Instant.ofEpochSecond(commit.getCommitTime()))
+ .setURI(smartAuthNoneURI.toString())
+ .call();
+
+ assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+ }
+
+ @Test
+ public void testCloneWithDeepenNot() throws Exception {
+ remoteRepository.getRepository().getConfig().setInt(
+ "protocol", null, "version", 0);
+ RevCommit commit = remoteRepository.git().log().call().iterator().next();
+ remoteRepository.update(master, remoteRepository.commit()
+ .parent(commit)
+ .message("Test")
+ .add("test.txt", "Hello world")
+ .create());
+
+ File directory = createTempDirectory("testCloneWithDeepenNot");
+ Git git = Git.cloneRepository()
+ .setDirectory(directory)
+ .addShallowExclude(commit.getId())
+ .setURI(smartAuthNoneURI.toString())
+ .call();
+
+ assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+ }
+
+ @Test
+ public void testV2CloneWithDepth() throws Exception {
+ File directory = createTempDirectory("testV2CloneWithDepth");
+ Git git = Git.cloneRepository()
+ .setDirectory(directory)
+ .setDepth(1)
+ .setURI(smartAuthNoneURI.toString())
+ .call();
+
+ assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+ }
+
+ @Test
+ public void testV2CloneWithDeepenSince() throws Exception {
+ RevCommit commit = remoteRepository.commit()
+ .parent(remoteRepository.git().log().call().iterator().next())
+ .message("Test")
+ .add("test.txt", "Hello world")
+ .create();
+ remoteRepository.update(master, commit);
+
+ File directory = createTempDirectory("testV2CloneWithDeepenSince");
+ Git git = Git.cloneRepository()
+ .setDirectory(directory)
+ .setShallowSince(Instant.ofEpochSecond(commit.getCommitTime()))
+ .setURI(smartAuthNoneURI.toString())
+ .call();
+
+ assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+ }
+
+ @Test
+ public void testV2CloneWithDeepenNot() throws Exception {
+ RevCommit commit = remoteRepository.git().log().call().iterator().next();
+ remoteRepository.update(master, remoteRepository.commit()
+ .parent(commit)
+ .message("Test")
+ .add("test.txt", "Hello world")
+ .create());
+
+ File directory = createTempDirectory("testV2CloneWithDeepenNot");
+ Git git = Git.cloneRepository()
+ .setDirectory(directory)
+ .addShallowExclude(commit.getId())
+ .setURI(smartAuthNoneURI.toString())
+ .call();
+
+ assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+ }
}
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
index 22e19979e6..873d430675 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
@@ -30,16 +30,18 @@ public class AccessEvent {
private final Map<String, String[]> parameters;
- private final int status;
+ private int status;
- private final Map<String, String> responseHeaders;
+ private Map<String, String> responseHeaders;
- AccessEvent(Request req, Response rsp) {
+ AccessEvent(Request req) {
method = req.getMethod();
uri = req.getRequestURI();
requestHeaders = cloneHeaders(req);
parameters = clone(req.getParameterMap());
+ }
+ void setResponse(Response rsp) {
status = rsp.getStatus();
responseHeaders = cloneHeaders(rsp);
}
@@ -141,7 +143,7 @@ public class AccessEvent {
* @return first value of the response header; null if not sent.
*/
public String getResponseHeader(String name) {
- return responseHeaders.get(name);
+ return responseHeaders != null ? responseHeaders.get(name) : null;
}
/** {@inheritDoc} */
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
index a86edd2f39..04cb2428a2 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
@@ -87,19 +87,23 @@ class TestRequestLog extends HandlerWrapper {
}
}
+ AccessEvent event = null;
+ if (DispatcherType.REQUEST
+ .equals(baseRequest.getDispatcherType())) {
+ event = new AccessEvent((Request) request);
+ synchronized (events) {
+ events.add(event);
+ }
+ }
+
super.handle(target, baseRequest, request, response);
- if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
- log((Request) request, (Response) response);
+ if (event != null) {
+ event.setResponse((Response) response);
+ }
} finally {
active.release();
}
}
-
- private void log(Request request, Response response) {
- synchronized (events) {
- events.add(new AccessEvent(request, response));
- }
- }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
index c928d2ad22..6053c8c568 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2013 Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2011, 2022 Chris Aniszczyk <caniszczyk@gmail.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -19,10 +19,14 @@ import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
+import java.time.Instant;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
import org.eclipse.jgit.api.errors.GitAPIException;
@@ -40,6 +44,7 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.submodule.SubmoduleStatus;
import org.eclipse.jgit.submodule.SubmoduleStatusType;
import org.eclipse.jgit.submodule.SubmoduleWalk;
@@ -895,6 +900,234 @@ public class CloneCommandTest extends RepositoryTestCase {
assertEquals("refs/heads/test-copy", git2.getRepository().getFullBranch());
}
+ @Test
+ public void testCloneRepositoryWithDepth() throws IOException, JGitInternalException, GitAPIException {
+ File directory = createTempDirectory("testCloneRepositoryWithDepth");
+ CloneCommand command = Git.cloneRepository();
+ command.setDirectory(directory);
+ command.setURI(fileUri());
+ command.setDepth(1);
+ command.setBranchesToClone(Set.of("refs/heads/test"));
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+
+ List<RevCommit> log = StreamSupport.stream(git2.log().all().call().spliterator(), false)
+ .collect(Collectors.toList());
+ assertEquals(1, log.size());
+ RevCommit commit = log.get(0);
+ assertEquals(Set.of(commit.getId()),
+ git2.getRepository().getObjectDatabase().getShallowCommits());
+ assertEquals("Second commit", commit.getFullMessage());
+ assertEquals(0, commit.getParentCount());
+ }
+
+ @Test
+ public void testCloneRepositoryWithDepthAndAllBranches() throws IOException, JGitInternalException, GitAPIException {
+ File directory = createTempDirectory("testCloneRepositoryWithDepthAndAllBranches");
+ CloneCommand command = Git.cloneRepository();
+ command.setDirectory(directory);
+ command.setURI(fileUri());
+ command.setDepth(1);
+ command.setCloneAllBranches(true);
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+
+ List<RevCommit> log = StreamSupport.stream(git2.log().all().call().spliterator(), false)
+ .collect(Collectors.toList());
+ assertEquals(2, log.size());
+ assertEquals(log.stream().map(RevCommit::getId).collect(Collectors.toSet()),
+ git2.getRepository().getObjectDatabase().getShallowCommits());
+ assertEquals(List.of("Second commit", "Initial commit"),
+ log.stream().map(RevCommit::getFullMessage).collect(Collectors.toList()));
+ for (RevCommit commit : log) {
+ assertEquals(0, commit.getParentCount());
+ }
+ }
+
+ @Test
+ public void testCloneRepositoryWithDepth2() throws Exception {
+ RevCommit parent = tr.git().log().call().iterator().next();
+ RevCommit commit = tr.commit()
+ .parent(parent)
+ .message("Third commit")
+ .add("test.txt", "Hello world")
+ .create();
+ tr.update("refs/heads/test", commit);
+
+ File directory = createTempDirectory("testCloneRepositoryWithDepth2");
+ CloneCommand command = Git.cloneRepository();
+ command.setDirectory(directory);
+ command.setURI(fileUri());
+ command.setDepth(2);
+ command.setBranchesToClone(Set.of("refs/heads/test"));
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+
+ List<RevCommit> log = StreamSupport
+ .stream(git2.log().all().call().spliterator(), false)
+ .collect(Collectors.toList());
+ assertEquals(2, log.size());
+ assertEquals(Set.of(parent.getId()),
+ git2.getRepository().getObjectDatabase().getShallowCommits());
+ assertEquals(List.of("Third commit", "Second commit"), log.stream()
+ .map(RevCommit::getFullMessage).collect(Collectors.toList()));
+ assertEquals(List.of(Integer.valueOf(1), Integer.valueOf(0)),
+ log.stream().map(RevCommit::getParentCount)
+ .collect(Collectors.toList()));
+ }
+
+ @Test
+ public void testCloneRepositoryWithDepthAndFetch() throws Exception {
+ File directory = createTempDirectory("testCloneRepositoryWithDepthAndFetch");
+ CloneCommand command = Git.cloneRepository();
+ command.setDirectory(directory);
+ command.setURI(fileUri());
+ command.setDepth(1);
+ command.setBranchesToClone(Set.of("refs/heads/test"));
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+
+ RevCommit parent = tr.git().log().call().iterator().next();
+ RevCommit commit = tr.commit()
+ .parent(parent)
+ .message("Third commit")
+ .add("test.txt", "Hello world")
+ .create();
+ tr.update("refs/heads/test", commit);
+
+ git2.fetch().call();
+
+ List<RevCommit> log = StreamSupport
+ .stream(git2.log().all().call().spliterator(), false)
+ .collect(Collectors.toList());
+ assertEquals(2, log.size());
+ assertEquals(Set.of(parent.getId()),
+ git2.getRepository().getObjectDatabase().getShallowCommits());
+ assertEquals(List.of("Third commit", "Second commit"), log.stream()
+ .map(RevCommit::getFullMessage).collect(Collectors.toList()));
+ assertEquals(List.of(Integer.valueOf(1), Integer.valueOf(0)),
+ log.stream().map(RevCommit::getParentCount)
+ .collect(Collectors.toList()));
+ }
+
+ @Test
+ public void testCloneRepositoryWithDepthAndFetchWithDepth() throws Exception {
+ File directory = createTempDirectory("testCloneRepositoryWithDepthAndFetchWithDepth");
+ CloneCommand command = Git.cloneRepository();
+ command.setDirectory(directory);
+ command.setURI(fileUri());
+ command.setDepth(1);
+ command.setBranchesToClone(Set.of("refs/heads/test"));
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+
+ RevCommit parent = tr.git().log().call().iterator().next();
+ RevCommit commit = tr.commit()
+ .parent(parent)
+ .message("Third commit")
+ .add("test.txt", "Hello world")
+ .create();
+ tr.update("refs/heads/test", commit);
+
+ git2.fetch().setDepth(1).call();
+
+ List<RevCommit> log = StreamSupport
+ .stream(git2.log().all().call().spliterator(), false)
+ .collect(Collectors.toList());
+ assertEquals(2, log.size());
+ assertEquals(
+ log.stream().map(RevObject::getId).collect(Collectors.toSet()),
+ git2.getRepository().getObjectDatabase().getShallowCommits());
+ assertEquals(List.of("Third commit", "Second commit"), log.stream()
+ .map(RevCommit::getFullMessage).collect(Collectors.toList()));
+ assertEquals(List.of(Integer.valueOf(0), Integer.valueOf(0)),
+ log.stream().map(RevCommit::getParentCount)
+ .collect(Collectors.toList()));
+ }
+
+ @Test
+ public void testCloneRepositoryWithDepthAndFetchUnshallow() throws Exception {
+ File directory = createTempDirectory("testCloneRepositoryWithDepthAndFetchUnshallow");
+ CloneCommand command = Git.cloneRepository();
+ command.setDirectory(directory);
+ command.setURI(fileUri());
+ command.setDepth(1);
+ command.setBranchesToClone(Set.of("refs/heads/test"));
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+
+ git2.fetch().setUnshallow(true).call();
+
+ List<RevCommit> log = StreamSupport
+ .stream(git2.log().all().call().spliterator(), false)
+ .collect(Collectors.toList());
+ assertEquals(2, log.size());
+ assertEquals(Set.of(),
+ git2.getRepository().getObjectDatabase().getShallowCommits());
+ assertEquals(List.of("Second commit", "Initial commit"), log.stream()
+ .map(RevCommit::getFullMessage).collect(Collectors.toList()));
+ assertEquals(List.of(Integer.valueOf(1), Integer.valueOf(0)),
+ log.stream().map(RevCommit::getParentCount)
+ .collect(Collectors.toList()));
+ }
+
+ @Test
+ public void testCloneRepositoryWithShallowSince() throws Exception {
+ RevCommit commit = tr.commit()
+ .parent(tr.git().log().call().iterator().next())
+ .message("Third commit").add("test.txt", "Hello world")
+ .create();
+ tr.update("refs/heads/test", commit);
+
+ File directory = createTempDirectory("testCloneRepositoryWithShallowSince");
+ CloneCommand command = Git.cloneRepository();
+ command.setDirectory(directory);
+ command.setURI(fileUri());
+ command.setShallowSince(Instant.ofEpochSecond(commit.getCommitTime()));
+ command.setBranchesToClone(Set.of("refs/heads/test"));
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+
+ List<RevCommit> log = StreamSupport
+ .stream(git2.log().all().call().spliterator(), false)
+ .collect(Collectors.toList());
+ assertEquals(1, log.size());
+ assertEquals(Set.of(commit.getId()),
+ git2.getRepository().getObjectDatabase().getShallowCommits());
+ assertEquals("Third commit", log.get(0).getFullMessage());
+ assertEquals(0, log.get(0).getParentCount());
+ }
+
+ @Test
+ public void testCloneRepositoryWithShallowExclude() throws Exception {
+ RevCommit parent = tr.git().log().call().iterator().next();
+ tr.update("refs/heads/test",
+ tr.commit()
+ .parent(parent)
+ .message("Third commit")
+ .add("test.txt", "Hello world")
+ .create());
+
+ File directory = createTempDirectory("testCloneRepositoryWithShallowExclude");
+ CloneCommand command = Git.cloneRepository();
+ command.setDirectory(directory);
+ command.setURI(fileUri());
+ command.addShallowExclude(parent.getId());
+ command.setBranchesToClone(Set.of("refs/heads/test"));
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+
+ List<RevCommit> log = StreamSupport
+ .stream(git2.log().all().call().spliterator(), false)
+ .collect(Collectors.toList());
+ assertEquals(1, log.size());
+ RevCommit commit = log.get(0);
+ assertEquals(Set.of(commit.getId()),
+ git2.getRepository().getObjectDatabase().getShallowCommits());
+ assertEquals("Third commit", commit.getFullMessage());
+ assertEquals(0, commit.getParentCount());
+ }
+
private void assertTagOption(Repository repo, TagOpt expectedTagOption)
throws URISyntaxException {
RemoteConfig remoteConfig = new RemoteConfig(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
index c0db83a820..b2a4af30ab 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 Google LLC. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -15,6 +15,7 @@ import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
import static org.eclipse.jgit.transport.ObjectIdMatcher.hasOnlyObjectIds;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -133,6 +134,42 @@ public class ProtocolV0ParserTest {
}
@Test
+ public void testRecvWantsDeepenSince()
+ throws PackProtocolException, IOException {
+ PacketLineIn pckIn = formatAsPacketLine(
+ "want 4624442d68ee402a94364191085b77137618633e\n",
+ "want f900c8326a43303685c46b279b9f70411bff1a4b\n",
+ "deepen-since 1652773020\n",
+ PacketLineIn.end());
+ ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig());
+ FetchV0Request request = parser.recvWants(pckIn);
+ assertTrue(request.getClientCapabilities().isEmpty());
+ assertEquals(1652773020, request.getDeepenSince());
+ assertThat(request.getWantIds(),
+ hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
+ "f900c8326a43303685c46b279b9f70411bff1a4b"));
+ }
+
+ @Test
+ public void testRecvWantsDeepenNots()
+ throws PackProtocolException, IOException {
+ PacketLineIn pckIn = formatAsPacketLine(
+ "want 4624442d68ee402a94364191085b77137618633e\n",
+ "want f900c8326a43303685c46b279b9f70411bff1a4b\n",
+ "deepen-not 856d5138d7269a483efe276d4a6b5c25b4fbb1a4\n",
+ "deepen-not heads/refs/test\n",
+ PacketLineIn.end());
+ ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig());
+ FetchV0Request request = parser.recvWants(pckIn);
+ assertTrue(request.getClientCapabilities().isEmpty());
+ assertThat(request.getDeepenNots(), contains("856d5138d7269a483efe276d4a6b5c25b4fbb1a4",
+ "heads/refs/test"));
+ assertThat(request.getWantIds(),
+ hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
+ "f900c8326a43303685c46b279b9f70411bff1a4b"));
+ }
+
+ @Test
public void testRecvWantsShallow()
throws PackProtocolException, IOException {
PacketLineIn pckIn = formatAsPacketLine(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
index 837bdce919..167b5b72c6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 Google LLC. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -152,7 +152,7 @@ public class ProtocolV2ParserTest {
assertThat(request.getClientShallowCommits(),
hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
- assertTrue(request.getDeepenNotRefs().isEmpty());
+ assertTrue(request.getDeepenNots().isEmpty());
assertEquals(15, request.getDepth());
assertTrue(request.getClientCapabilities()
.contains(GitProtocolConstants.OPTION_DEEPEN_RELATIVE));
@@ -171,7 +171,7 @@ public class ProtocolV2ParserTest {
assertThat(request.getClientShallowCommits(),
hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
- assertThat(request.getDeepenNotRefs(),
+ assertThat(request.getDeepenNots(),
hasItems("a08595f76159b09d57553e37a5123f1091bb13e7"));
}
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 6eb8bd3732..8aa84f3ac1 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -13,7 +13,13 @@
<filter id="336695337">
<message_arguments>
<message_argument value="org.eclipse.jgit.lib.ObjectDatabase"/>
- <message_argument value="getApproximateObjectCount()"/>
+ <message_argument value="getShallowCommits()"/>
+ </message_arguments>
+ </filter>
+ <filter id="336695337">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.ObjectDatabase"/>
+ <message_argument value="setShallowCommits(Set&lt;ObjectId&gt;)"/>
</message_arguments>
</filter>
</resource>
@@ -60,14 +66,6 @@
</message_arguments>
</filter>
</resource>
- <resource path="src/org/eclipse/jgit/transport/BasePackPushConnection.java" type="org.eclipse.jgit.transport.BasePackPushConnection">
- <filter id="338792546">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.transport.BasePackPushConnection"/>
- <message_argument value="noRepository()"/>
- </message_arguments>
- </filter>
- </resource>
<resource path="src/org/eclipse/jgit/transport/PushConfig.java" type="org.eclipse.jgit.transport.PushConfig">
<filter id="338722907">
<message_arguments>
diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD
index 04873b0c72..e806e7d6d0 100644
--- a/org.eclipse.jgit/BUILD
+++ b/org.eclipse.jgit/BUILD
@@ -23,6 +23,12 @@ java_library(
"//lib:javaewah",
"//lib:slf4j-api",
],
+ javacopts = [
+ "-Xep:ReferenceEquality:OFF",
+ "-Xep:StringEquality:OFF",
+ "-Xep:TypeParameterUnusedInFormals:OFF",
+ "-Xep:DefaultCharset:OFF",
+ ]
)
genrule(
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index 66adad5151..f3ecadd6e5 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -38,7 +38,7 @@ badIgnorePatternFull=File {0} line {1}: cannot parse pattern ''{2}'': {3}
badObjectType=Bad object type: {0}
badRef=Bad ref: {0}: {1}
badSectionEntry=Bad section entry: {0}
-badShallowLine=Bad shallow line: {0}
+badShallowLine=Shallow file ''{0}'' has bad line: {1}
bareRepositoryNoWorkdirAndIndex=Bare Repository has neither a working tree, nor an index
base85invalidChar=Invalid base-85 character: 0x{0}
base85length=Base-85 encoded data must have a length that is a multiple of 5
@@ -237,6 +237,8 @@ deletedOrphanInPackDir=Deleted orphaned file {}
deleteRequiresZeroNewId=Delete requires new ID to be zero
deleteTagUnexpectedResult=Delete tag returned unexpected result {0}
deletingNotSupported=Deleting {0} not supported.
+depthMustBeAt1=Depth must be >= 1
+depthWithUnshallow=Depth and unshallow can\'t be used together
destinationIsNotAWildcard=Destination is not a wildcard.
detachedHeadDetected=HEAD is detached
diffToolNotGivenError=No diff tool provided and no defaults configured.
@@ -518,6 +520,7 @@ notFound=not found.
nothingToFetch=Nothing to fetch.
nothingToPush=Nothing to push.
notMergedExceptionMessage=Branch was not deleted as it has not been merged yet; use the force option to delete it anyway
+notShallowedUnshallow=The server sent a unshallow for a commit that wasn''t marked as shallow: {0}
noXMLParserAvailable=No XML parser available.
objectAtHasBadZlibStream=Object at {0} in {1} has bad zlib stream
objectIsCorrupt=Object {0} is corrupt: {1}
@@ -587,6 +590,7 @@ pushNotPermitted=push not permitted
pushOptionsNotSupported=Push options not supported; received {0}
rawLogMessageDoesNotParseAsLogEntry=Raw log message does not parse as log entry
readConfigFailed=Reading config file ''{0}'' failed
+readShallowFailed=Reading shallow file ''{0}'' failed
readFileStoreAttributesFailed=Reading FileStore attributes from user config failed
readerIsRequired=Reader is required
readingObjectsFromLocalRepositoryFailed=reading objects from local repository failed: {0}
@@ -662,6 +666,7 @@ serviceNotEnabledNoName=Service not enabled
serviceNotPermitted={1} not permitted on ''{0}''
sha1CollisionDetected=SHA-1 collision detected on {0}
shallowCommitsAlreadyInitialized=Shallow commits have already been initialized
+shallowNotSupported=The server does not support shallow
shallowPacksRequireDepthWalk=Shallow packs require a DepthWalk
shortCompressedStreamAt=Short compressed stream at {0}
shortReadOfBlock=Short read of block.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index 3aa711455b..1f979a938c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2017 Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2011, 2022 Chris Aniszczyk <caniszczyk@gmail.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -13,10 +13,13 @@ import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.text.MessageFormat;
+import java.time.Instant;
+import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
@@ -91,6 +94,12 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
private TagOpt tagOption;
+ private Integer depth;
+
+ private Instant shallowSince;
+
+ private List<String> shallowExcludes = new ArrayList<>();
+
private enum FETCH_TYPE {
MULTIPLE_BRANCHES, ALL_BRANCHES, MIRROR
}
@@ -306,6 +315,11 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
fetchAll ? TagOpt.FETCH_TAGS : TagOpt.AUTO_FOLLOW);
}
command.setInitialBranch(branch);
+ if (depth != null) {
+ command.setDepth(depth.intValue());
+ }
+ command.setShallowSince(shallowSince);
+ command.setShallowExcludes(shallowExcludes);
configure(command);
return command.call();
@@ -737,6 +751,82 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
return this;
}
+ /**
+ * Creates a shallow clone with a history truncated to the specified number
+ * of commits.
+ *
+ * @param depth
+ * the depth
+ * @return {@code this}
+ *
+ * @since 6.3
+ */
+ public CloneCommand setDepth(int depth) {
+ if (depth < 1) {
+ throw new IllegalArgumentException(JGitText.get().depthMustBeAt1);
+ }
+ this.depth = Integer.valueOf(depth);
+ return this;
+ }
+
+ /**
+ * Creates a shallow clone with a history after the specified time.
+ *
+ * @param shallowSince
+ * the timestammp; must not be {@code null}
+ * @return {@code this}
+ *
+ * @since 6.3
+ */
+ public CloneCommand setShallowSince(@NonNull OffsetDateTime shallowSince) {
+ this.shallowSince = shallowSince.toInstant();
+ return this;
+ }
+
+ /**
+ * Creates a shallow clone with a history after the specified time.
+ *
+ * @param shallowSince
+ * the timestammp; must not be {@code null}
+ * @return {@code this}
+ *
+ * @since 6.3
+ */
+ public CloneCommand setShallowSince(@NonNull Instant shallowSince) {
+ this.shallowSince = shallowSince;
+ return this;
+ }
+
+ /**
+ * Creates a shallow clone with a history, excluding commits reachable from
+ * a specified remote branch or tag.
+ *
+ * @param shallowExclude
+ * the ref or commit; must not be {@code null}
+ * @return {@code this}
+ *
+ * @since 6.3
+ */
+ public CloneCommand addShallowExclude(@NonNull String shallowExclude) {
+ shallowExcludes.add(shallowExclude);
+ return this;
+ }
+
+ /**
+ * Creates a shallow clone with a history, excluding commits reachable from
+ * a specified remote branch or tag.
+ *
+ * @param shallowExclude
+ * the commit; must not be {@code null}
+ * @return {@code this}
+ *
+ * @since 6.3
+ */
+ public CloneCommand addShallowExclude(@NonNull ObjectId shallowExclude) {
+ shallowExcludes.add(shallowExclude.name());
+ return this;
+ }
+
private static void validateDirs(File directory, File gitDir, boolean bare)
throws IllegalStateException {
if (directory != null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
index 90c1515b06..84bee36204 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2022 Chris Aniszczyk <caniszczyk@gmail.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -14,10 +14,13 @@ import static java.util.stream.Collectors.toList;
import java.io.IOException;
import java.net.URISyntaxException;
import java.text.MessageFormat;
+import java.time.Instant;
+import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidConfigurationException;
@@ -76,6 +79,14 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
private String initialBranch;
+ private Integer depth;
+
+ private Instant deepenSince;
+
+ private List<String> shallowExcludes = new ArrayList<>();
+
+ private boolean unshallow;
+
/**
* Callback for status of fetch operation.
*
@@ -156,11 +167,9 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
walk.getPath());
// When the fetch mode is "yes" we always fetch. When the
- // mode
- // is "on demand", we only fetch if the submodule's revision
- // was
- // updated to an object that is not currently present in the
- // submodule.
+ // mode is "on demand", we only fetch if the submodule's
+ // revision was updated to an object that is not currently
+ // present in the submodule.
if ((recurseMode == FetchRecurseSubmodulesMode.ON_DEMAND
&& !submoduleRepo.getObjectDatabase()
.has(walk.getObjectId()))
@@ -209,6 +218,17 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
if (tagOption != null)
transport.setTagOpt(tagOption);
transport.setFetchThin(thin);
+ if (depth != null) {
+ transport.setDepth(depth);
+ }
+ if (unshallow) {
+ if (depth != null) {
+ throw new IllegalStateException(JGitText.get().depthWithUnshallow);
+ }
+ transport.setDepth(Constants.INFINITE_DEPTH);
+ }
+ transport.setDeepenSince(deepenSince);
+ transport.setDeepenNots(shallowExcludes);
configure(transport);
FetchResult result = transport.fetch(monitor,
applyOptions(refSpecs), initialBranch);
@@ -542,4 +562,105 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
this.isForceUpdate = force;
return this;
}
+
+ /**
+ * Limits fetching to the specified number of commits from the tip of each
+ * remote branch history.
+ *
+ * @param depth
+ * the depth
+ * @return {@code this}
+ *
+ * @since 6.3
+ */
+ public FetchCommand setDepth(int depth) {
+ if (depth < 1) {
+ throw new IllegalArgumentException(JGitText.get().depthMustBeAt1);
+ }
+ this.depth = Integer.valueOf(depth);
+ return this;
+ }
+
+ /**
+ * Deepens or shortens the history of a shallow repository to include all
+ * reachable commits after a specified time.
+ *
+ * @param shallowSince
+ * the timestammp; must not be {@code null}
+ * @return {@code this}
+ *
+ * @since 6.3
+ */
+ public FetchCommand setShallowSince(@NonNull OffsetDateTime shallowSince) {
+ this.deepenSince = shallowSince.toInstant();
+ return this;
+ }
+
+ /**
+ * Deepens or shortens the history of a shallow repository to include all
+ * reachable commits after a specified time.
+ *
+ * @param shallowSince
+ * the timestammp; must not be {@code null}
+ * @return {@code this}
+ *
+ * @since 6.3
+ */
+ public FetchCommand setShallowSince(@NonNull Instant shallowSince) {
+ this.deepenSince = shallowSince;
+ return this;
+ }
+
+ /**
+ * Deepens or shortens the history of a shallow repository to exclude
+ * commits reachable from a specified remote branch or tag.
+ *
+ * @param shallowExclude
+ * the ref or commit; must not be {@code null}
+ * @return {@code this}
+ *
+ * @since 6.3
+ */
+ public FetchCommand addShallowExclude(@NonNull String shallowExclude) {
+ shallowExcludes.add(shallowExclude);
+ return this;
+ }
+
+ /**
+ * Creates a shallow clone with a history, excluding commits reachable from
+ * a specified remote branch or tag.
+ *
+ * @param shallowExclude
+ * the commit; must not be {@code null}
+ * @return {@code this}
+ *
+ * @since 6.3
+ */
+ public FetchCommand addShallowExclude(@NonNull ObjectId shallowExclude) {
+ shallowExcludes.add(shallowExclude.name());
+ return this;
+ }
+
+ /**
+ * If the source repository is complete, converts a shallow repository to a
+ * complete one, removing all the limitations imposed by shallow
+ * repositories.
+ *
+ * If the source repository is shallow, fetches as much as possible so that
+ * the current repository has the same history as the source repository.
+ *
+ * @param unshallow
+ * whether to unshallow or not
+ * @return {@code this}
+ *
+ * @since 6.3
+ */
+ public FetchCommand setUnshallow(boolean unshallow) {
+ this.unshallow = unshallow;
+ return this;
+ }
+
+ void setShallowExcludes(List<String> shallowExcludes) {
+ this.shallowExcludes = shallowExcludes;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index efdb8e42e3..964debcccb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -265,6 +265,8 @@ public class JGitText extends TranslationBundle {
/***/ public String deleteRequiresZeroNewId;
/***/ public String deleteTagUnexpectedResult;
/***/ public String deletingNotSupported;
+ /***/ public String depthMustBeAt1;
+ /***/ public String depthWithUnshallow;
/***/ public String destinationIsNotAWildcard;
/***/ public String detachedHeadDetected;
/***/ public String diffToolNotGivenError;
@@ -546,6 +548,7 @@ public class JGitText extends TranslationBundle {
/***/ public String nothingToFetch;
/***/ public String nothingToPush;
/***/ public String notMergedExceptionMessage;
+ /***/ public String notShallowedUnshallow;
/***/ public String noXMLParserAvailable;
/***/ public String objectAtHasBadZlibStream;
/***/ public String objectIsCorrupt;
@@ -615,6 +618,7 @@ public class JGitText extends TranslationBundle {
/***/ public String pushOptionsNotSupported;
/***/ public String rawLogMessageDoesNotParseAsLogEntry;
/***/ public String readConfigFailed;
+ /***/ public String readShallowFailed;
/***/ public String readFileStoreAttributesFailed;
/***/ public String readerIsRequired;
/***/ public String readingObjectsFromLocalRepositoryFailed;
@@ -690,6 +694,7 @@ public class JGitText extends TranslationBundle {
/***/ public String serviceNotPermitted;
/***/ public String sha1CollisionDetected;
/***/ public String shallowCommitsAlreadyInitialized;
+ /***/ public String shallowNotSupported;
/***/ public String shallowPacksRequireDepthWalk;
/***/ public String shortCompressedStreamAt;
/***/ public String shortReadOfBlock;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
index 99da222395..5a8207ed01 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
@@ -1,3 +1,12 @@
+/*
+ * Copyright (C) 2011, 2022 Google Inc. and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
package org.eclipse.jgit.internal.storage.dfs;
import java.io.ByteArrayOutputStream;
@@ -6,13 +15,16 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefDatabase;
/**
@@ -98,6 +110,7 @@ public class InMemoryRepository extends DfsRepository {
public static class MemObjDatabase extends DfsObjDatabase {
private List<DfsPackDescription> packs = new ArrayList<>();
private int blockSize;
+ private Set<ObjectId> shallowCommits = Collections.emptySet();
MemObjDatabase(DfsRepository repo) {
super(repo, new DfsReaderOptions());
@@ -167,6 +180,16 @@ public class InMemoryRepository extends DfsRepository {
}
@Override
+ public Set<ObjectId> getShallowCommits() throws IOException {
+ return shallowCommits;
+ }
+
+ @Override
+ public void setShallowCommits(Set<ObjectId> shallowCommits) {
+ this.shallowCommits = shallowCommits;
+ }
+
+ @Override
public long getApproximateObjectCount() {
long count = 0;
for (DfsPackDescription p : packs) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
index 094fdc1559..9272bf3f59 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2010, JetBrains s.r.o. and others
+ * Copyright (C) 2010, 2022 JetBrains s.r.o. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -117,10 +117,15 @@ class CachedObjectDirectory extends FileObjectDatabase {
}
@Override
- Set<ObjectId> getShallowCommits() throws IOException {
+ public Set<ObjectId> getShallowCommits() throws IOException {
return wrapped.getShallowCommits();
}
+ @Override
+ public void setShallowCommits(Set<ObjectId> shallowCommits) throws IOException {
+ wrapped.setShallowCommits(shallowCommits);
+ }
+
private CachedObjectDirectory[] myAlternates() {
if (alts == null) {
ObjectDirectory.AlternateHandle[] src = wrapped.myAlternates();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
index 01dd27d9fb..e97ed393a1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Google Inc. and others
+ * Copyright (C) 2010, 2022 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -50,8 +50,6 @@ abstract class FileObjectDatabase extends ObjectDatabase {
abstract FS getFS();
- abstract Set<ObjectId> getShallowCommits() throws IOException;
-
abstract void selectObjectRepresentation(PackWriter packer,
ObjectToPack otp, WindowCursor curs) throws IOException;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index 06c8cad3ac..7699439128 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, Google Inc. and others
+ * Copyright (C) 2009, 2022 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -19,6 +19,7 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.OutputStream;
import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -560,31 +561,82 @@ public class ObjectDirectory extends FileObjectDatabase {
}
@Override
- Set<ObjectId> getShallowCommits() throws IOException {
+ public Set<ObjectId> getShallowCommits() throws IOException {
if (shallowFile == null || !shallowFile.isFile())
return Collections.emptySet();
if (shallowFileSnapshot == null
|| shallowFileSnapshot.isModified(shallowFile)) {
- shallowCommitsIds = new HashSet<>();
+ try {
+ shallowCommitsIds = FileUtils.readWithRetries(shallowFile,
+ f -> {
+ FileSnapshot newSnapshot = FileSnapshot.save(f);
+ HashSet<ObjectId> result = new HashSet<>();
+ try (BufferedReader reader = open(f)) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (!ObjectId.isId(line)) {
+ throw new IOException(
+ MessageFormat.format(JGitText
+ .get().badShallowLine,
+ f.getAbsolutePath(),
+ line));
+
+ }
+ result.add(ObjectId.fromString(line));
+ }
+ }
+ shallowFileSnapshot = newSnapshot;
+ return result;
+ });
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IOException(
+ MessageFormat.format(JGitText.get().readShallowFailed,
+ shallowFile.getAbsolutePath()),
+ e);
+ }
+ }
- try (BufferedReader reader = open(shallowFile)) {
- String line;
- while ((line = reader.readLine()) != null) {
- try {
- shallowCommitsIds.add(ObjectId.fromString(line));
- } catch (IllegalArgumentException ex) {
- throw new IOException(MessageFormat
- .format(JGitText.get().badShallowLine, line),
- ex);
+ return shallowCommitsIds;
+ }
+
+ @Override
+ public void setShallowCommits(Set<ObjectId> shallowCommits) throws IOException {
+ this.shallowCommitsIds = shallowCommits;
+ LockFile lock = new LockFile(shallowFile);
+ if (!lock.lock()) {
+ throw new IOException(MessageFormat.format(JGitText.get().lockError,
+ shallowFile.getAbsolutePath()));
+ }
+
+ try {
+ if (shallowCommits.isEmpty()) {
+ if (shallowFile.isFile()) {
+ shallowFile.delete();
+ }
+ } else {
+ try (OutputStream out = lock.getOutputStream()) {
+ for (ObjectId shallowCommit : shallowCommits) {
+ byte[] buf = new byte[Constants.OBJECT_ID_STRING_LENGTH + 1];
+ shallowCommit.copyTo(buf, 0);
+ buf[Constants.OBJECT_ID_STRING_LENGTH] = '\n';
+ out.write(buf);
}
+ } finally {
+ lock.commit();
}
}
+ } finally {
+ lock.unlock();
+ }
+ if (shallowCommits.isEmpty()) {
+ shallowFileSnapshot = FileSnapshot.DIRTY;
+ } else {
shallowFileSnapshot = FileSnapshot.save(shallowFile);
}
-
- return shallowCommitsIds;
}
void closeAllPackHandles(File packFile) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index cf2e69dbb5..30a0074195 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2008, Google Inc.
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2017, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2006, 2022, Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -747,6 +747,13 @@ public final class Constants {
*/
public static final String LOCK_SUFFIX = ".lock"; //$NON-NLS-1$
+ /**
+ * Depth used to unshallow a repository
+ *
+ * @since 6.3
+ */
+ public static final int INFINITE_DEPTH = 0x7fffffff;
+
private Constants() {
// Hide the default constructor
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
index 70009cba35..1c0f436090 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, Google Inc. and others
+ * Copyright (C) 2009, 2022 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -11,6 +11,7 @@
package org.eclipse.jgit.lib;
import java.io.IOException;
+import java.util.Set;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
@@ -72,6 +73,26 @@ public abstract class ObjectDatabase implements AutoCloseable {
public abstract ObjectReader newReader();
/**
+ * @return the shallow commits of the current repository
+ *
+ * @throws IOException the database could not be read
+ *
+ * @since 6.3
+ */
+ public abstract Set<ObjectId> getShallowCommits() throws IOException;
+
+ /**
+ * Update the shallow commits of the current repository
+ *
+ * @param shallowCommits the new shallow commits
+ *
+ * @throws IOException the database could not be updated
+ *
+ * @since 6.3
+ */
+ public abstract void setShallowCommits(Set<ObjectId> shallowCommits) throws IOException;
+
+ /**
* Close any resources held by this database.
*/
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
index 3f167ccce2..be36d2b834 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
@@ -12,16 +12,30 @@
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DELIM;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_NOT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_SINCE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DONE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_END;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ERR;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_HAVE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_UNSHALLOW;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_WANT;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
+import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Set;
import org.eclipse.jgit.errors.PackProtocolException;
@@ -32,6 +46,7 @@ import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectDatabase;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ProgressMonitor;
@@ -76,7 +91,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
/**
* Maximum number of 'have' lines to send before giving up.
* <p>
- * During {@link #negotiate(ProgressMonitor)} we send at most this many
+ * During {@link #negotiate(ProgressMonitor, boolean, Set)} we send at most this many
* commits to the remote peer as 'have' lines without an ACK response before
* we give up.
*/
@@ -210,6 +225,12 @@ public abstract class BasePackFetchConnection extends BasePackConnection
private int maxHaves;
+ private Integer depth;
+
+ private Instant deepenSince;
+
+ private List<String> deepenNots;
+
/**
* RPC state, if {@link BasePackConnection#statelessRPC} is true or protocol
* V2 is used.
@@ -246,6 +267,9 @@ public abstract class BasePackFetchConnection extends BasePackConnection
includeTags = transport.getTagOpt() != TagOpt.NO_TAGS;
thinPack = transport.isFetchThin();
filterSpec = transport.getFilterSpec();
+ depth = transport.getDepth();
+ deepenSince = transport.getDeepenSince();
+ deepenNots = transport.getDeepenNots();
if (local != null) {
walk = new RevWalk(local);
@@ -385,9 +409,17 @@ public abstract class BasePackFetchConnection extends BasePackConnection
}
PacketLineOut output = statelessRPC ? pckState : pckOut;
if (sendWants(want, output)) {
+ boolean mayHaveShallow = depth != null || deepenSince != null || !deepenNots.isEmpty();
+ Set<ObjectId> shallowCommits = local.getObjectDatabase().getShallowCommits();
+ if (isCapableOf(GitProtocolConstants.CAPABILITY_SHALLOW)) {
+ sendShallow(shallowCommits, output);
+ } else if (mayHaveShallow) {
+ throw new PackProtocolException(JGitText.get().shallowNotSupported);
+ }
output.end();
outNeedsEnd = false;
- negotiate(monitor);
+
+ negotiate(monitor, mayHaveShallow, shallowCommits);
clearState();
@@ -424,10 +456,18 @@ public abstract class BasePackFetchConnection extends BasePackConnection
for (String capability : getCapabilitiesV2(capabilities)) {
pckState.writeString(capability);
}
+
if (!sendWants(want, pckState)) {
// We already have everything we wanted.
return;
}
+
+ Set<ObjectId> shallowCommits = local.getObjectDatabase().getShallowCommits();
+ if (capabilities.contains(GitProtocolConstants.CAPABILITY_SHALLOW)) {
+ sendShallow(shallowCommits, pckState);
+ } else if (depth != null || deepenSince != null || !deepenNots.isEmpty()) {
+ throw new PackProtocolException(JGitText.get().shallowNotSupported);
+ }
// If we send something, we always close it properly ourselves.
outNeedsEnd = false;
@@ -455,10 +495,21 @@ public abstract class BasePackFetchConnection extends BasePackConnection
clearState();
String line = pckIn.readString();
// If we sent a done, we may have an error reply here.
- if (sentDone && line.startsWith("ERR ")) { //$NON-NLS-1$
+ if (sentDone && line.startsWith(PACKET_ERR)) {
throw new RemoteRepositoryException(uri, line.substring(4));
}
- // "shallow-info", "wanted-refs", and "packfile-uris" would have to be
+
+ if (GitProtocolConstants.SECTION_SHALLOW_INFO.equals(line)) {
+ line = handleShallowUnshallow(shallowCommits, pckIn);
+ if (!PacketLineIn.isDelimiter(line)) {
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().expectedGot, PACKET_DELIM,
+ line));
+ }
+ line = pckIn.readString();
+ }
+
+ // "wanted-refs" and "packfile-uris" would have to be
// handled here in that order.
if (!GitProtocolConstants.SECTION_PACKFILE.equals(line)) {
throw new PackProtocolException(
@@ -494,7 +545,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
if (c == null) {
break;
}
- output.writeString("have " + c.getId().name() + '\n'); //$NON-NLS-1$
+ output.writeString(PACKET_HAVE + c.getId().name() + '\n');
n++;
if (n % 10 == 0 && monitor.isCancelled()) {
throw new CancelledException();
@@ -505,7 +556,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
|| (fetchState.hadAcks
&& fetchState.havesWithoutAck > MAX_HAVES)
|| fetchState.havesTotal > maxHaves) {
- output.writeString("done\n"); //$NON-NLS-1$
+ output.writeString(PACKET_DONE + '\n');
output.end();
return true;
}
@@ -572,11 +623,12 @@ public abstract class BasePackFetchConnection extends BasePackConnection
if (gotReady) {
if (!PacketLineIn.isDelimiter(line)) {
throw new PackProtocolException(MessageFormat
- .format(JGitText.get().expectedGot, "0001", line)); //$NON-NLS-1$
+ .format(JGitText.get().expectedGot, PACKET_DELIM,
+ line));
}
} else if (!PacketLineIn.isEnd(line)) {
throw new PackProtocolException(MessageFormat
- .format(JGitText.get().expectedGot, "0000", line)); //$NON-NLS-1$
+ .format(JGitText.get().expectedGot, PACKET_END, line));
}
return gotReady;
}
@@ -672,21 +724,23 @@ public abstract class BasePackFetchConnection extends BasePackConnection
if (objectId == null) {
continue;
}
- try {
- if (walk.parseAny(objectId).has(REACHABLE)) {
- // We already have this object. Asking for it is
- // not a very good idea.
- //
- continue;
+ // if depth is set we need to fetch the objects even if they are already available
+ if (transport.getDepth() == null) {
+ try {
+ if (walk.parseAny(objectId).has(REACHABLE)) {
+ // We already have this object. Asking for it is
+ // not a very good idea.
+ //
+ continue;
+ }
+ } catch (IOException err) {
+ // Its OK, we don't have it, but we want to fix that
+ // by fetching the object from the other side.
}
- } catch (IOException err) {
- // Its OK, we don't have it, but we want to fix that
- // by fetching the object from the other side.
}
final StringBuilder line = new StringBuilder(46);
- line.append("want "); //$NON-NLS-1$
- line.append(objectId.name());
+ line.append(PACKET_WANT).append(objectId.name());
if (first && TransferConfig.ProtocolVersion.V0
.equals(getProtocolVersion())) {
line.append(enableCapabilities());
@@ -773,8 +827,8 @@ public abstract class BasePackFetchConnection extends BasePackConnection
return line.toString();
}
- private void negotiate(ProgressMonitor monitor) throws IOException,
- CancelledException {
+ private void negotiate(ProgressMonitor monitor, boolean mayHaveShallow, Set<ObjectId> shallowCommits)
+ throws IOException, CancelledException {
final MutableObjectId ackId = new MutableObjectId();
int resultsPending = 0;
int havesSent = 0;
@@ -795,7 +849,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
}
ObjectId o = c.getId();
- pckOut.writeString("have " + o.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ pckOut.writeString(PACKET_HAVE + o.name() + '\n');
havesSent++;
havesSinceLastContinue++;
@@ -898,7 +952,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
// loop above while in the middle of a request. This allows us
// to just write done immediately.
//
- pckOut.writeString("done\n"); //$NON-NLS-1$
+ pckOut.writeString(PACKET_DONE + '\n');
pckOut.flush();
}
@@ -911,6 +965,14 @@ public abstract class BasePackFetchConnection extends BasePackConnection
resultsPending++;
}
+ if (mayHaveShallow) {
+ String line = handleShallowUnshallow(shallowCommits, pckIn);
+ if (!PacketLineIn.isEnd(line)) {
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().expectedGot, PACKET_END, line));
+ }
+ }
+
READ_RESULT: while (resultsPending > 0 || multiAck != MultiAck.OFF) {
final AckNackResult anr = pckIn.readACK(ackId);
resultsPending--;
@@ -992,7 +1054,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
private void markCommon(RevObject obj, AckNackResult anr, boolean useState)
throws IOException {
if (useState && anr == AckNackResult.ACK_COMMON && !obj.has(STATE)) {
- pckState.writeString("have " + obj.name() + '\n'); //$NON-NLS-1$
+ pckState.writeString(PACKET_HAVE + obj.name() + '\n');
obj.add(STATE);
}
obj.add(COMMON);
@@ -1025,6 +1087,55 @@ public abstract class BasePackFetchConnection extends BasePackConnection
}
}
+ private void sendShallow(Set<ObjectId> shallowCommits, PacketLineOut output)
+ throws IOException {
+ for (ObjectId shallowCommit : shallowCommits) {
+ output.writeString(PACKET_SHALLOW + shallowCommit.name());
+ }
+
+ if (depth != null) {
+ output.writeString(PACKET_DEEPEN + depth);
+ }
+
+ if (deepenSince != null) {
+ output.writeString(
+ PACKET_DEEPEN_SINCE + deepenSince.getEpochSecond());
+ }
+
+ if (deepenNots != null) {
+ for (String deepenNotRef : deepenNots) {
+ output.writeString(PACKET_DEEPEN_NOT + deepenNotRef);
+ }
+ }
+ }
+
+ private String handleShallowUnshallow(
+ Set<ObjectId> advertisedShallowCommits, PacketLineIn input)
+ throws IOException {
+ String line = input.readString();
+ ObjectDatabase objectDatabase = local.getObjectDatabase();
+ HashSet<ObjectId> newShallowCommits = new HashSet<>(
+ advertisedShallowCommits);
+ while (!PacketLineIn.isDelimiter(line) && !PacketLineIn.isEnd(line)) {
+ if (line.startsWith(PACKET_SHALLOW)) {
+ newShallowCommits.add(ObjectId
+ .fromString(line.substring(PACKET_SHALLOW.length())));
+ } else if (line.startsWith(PACKET_UNSHALLOW)) {
+ ObjectId unshallow = ObjectId
+ .fromString(line.substring(PACKET_UNSHALLOW.length()));
+ if (!advertisedShallowCommits.contains(unshallow)) {
+ throw new PackProtocolException(MessageFormat.format(
+ JGitText.get().notShallowedUnshallow,
+ unshallow.name()));
+ }
+ newShallowCommits.remove(unshallow);
+ }
+ line = input.readString();
+ }
+ objectDatabase.setShallowCommits(newShallowCommits);
+ return line;
+ }
+
/**
* Notification event delivered just before the pack is received from the
* network. This event can be used by RPC such as {@link org.eclipse.jgit.transport.TransportHttp} to
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
index 7bface49d9..28c3b6a0fa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2022 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -393,7 +393,7 @@ class FetchProcess {
ow.markUninteresting(ow.parseAny(ref.getObjectId()));
ow.checkConnectivity();
}
- return true;
+ return transport.getDepth() == null; // if depth is set we need to request objects that are already available
} catch (MissingObjectException e) {
return false;
} catch (IOException e) {
@@ -516,8 +516,10 @@ class FetchProcess {
}
if (spec.getDestination() != null) {
final TrackingRefUpdate tru = createUpdate(spec, newId);
- if (newId.equals(tru.getOldObjectId()))
+ // if depth is set we need to update the ref
+ if (newId.equals(tru.getOldObjectId()) && transport.getDepth() == null) {
return;
+ }
localUpdates.add(tru);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
index 9ebc722ffe..0663c5141c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 Google LLC. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -35,7 +35,7 @@ abstract class FetchRequest {
final int deepenSince;
- final List<String> deepenNotRefs;
+ final List<String> deepenNots;
@Nullable
final String agent;
@@ -53,7 +53,7 @@ abstract class FetchRequest {
* the filter spec
* @param clientCapabilities
* capabilities sent in the request
- * @param deepenNotRefs
+ * @param deepenNots
* Requests that the shallow clone/fetch should be cut at these
* specific revisions instead of a depth.
* @param deepenSince
@@ -66,14 +66,14 @@ abstract class FetchRequest {
@NonNull Set<ObjectId> clientShallowCommits,
@NonNull FilterSpec filterSpec,
@NonNull Set<String> clientCapabilities, int deepenSince,
- @NonNull List<String> deepenNotRefs, @Nullable String agent) {
+ @NonNull List<String> deepenNots, @Nullable String agent) {
this.wantIds = requireNonNull(wantIds);
this.depth = depth;
this.clientShallowCommits = requireNonNull(clientShallowCommits);
this.filterSpec = requireNonNull(filterSpec);
this.clientCapabilities = requireNonNull(clientCapabilities);
this.deepenSince = deepenSince;
- this.deepenNotRefs = requireNonNull(deepenNotRefs);
+ this.deepenNots = requireNonNull(deepenNots);
this.agent = agent;
}
@@ -148,8 +148,8 @@ abstract class FetchRequest {
* @return refs received in "deepen-not" lines.
*/
@NonNull
- List<String> getDeepenNotRefs() {
- return deepenNotRefs;
+ List<String> getDeepenNots() {
+ return deepenNots;
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
index 91adb5e6ac..4decb79513 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 Google LLC. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -11,9 +11,10 @@ package org.eclipse.jgit.transport;
import static java.util.Objects.requireNonNull;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import org.eclipse.jgit.annotations.NonNull;
@@ -28,15 +29,20 @@ final class FetchV0Request extends FetchRequest {
FetchV0Request(@NonNull Set<ObjectId> wantIds, int depth,
@NonNull Set<ObjectId> clientShallowCommits,
@NonNull FilterSpec filterSpec,
- @NonNull Set<String> clientCapabilities, @Nullable String agent) {
+ @NonNull Set<String> clientCapabilities, int deepenSince,
+ @NonNull List<String> deepenNotRefs, @Nullable String agent) {
super(wantIds, depth, clientShallowCommits, filterSpec,
- clientCapabilities, 0, Collections.emptyList(), agent);
+ clientCapabilities, deepenSince, deepenNotRefs, agent);
}
static final class Builder {
int depth;
+ final List<String> deepenNots = new ArrayList<>();
+
+ int deepenSince;
+
final Set<ObjectId> wantIds = new HashSet<>();
final Set<ObjectId> clientShallowCommits = new HashSet<>();
@@ -68,6 +74,50 @@ final class FetchV0Request extends FetchRequest {
}
/**
+ * @return depth set in the request (via a "deepen" line). Defaulting to
+ * 0 if not set.
+ */
+ int getDepth() {
+ return depth;
+ }
+
+ /**
+ * @return true if there has been at least one "deepen not" line in the
+ * request so far
+ */
+ boolean hasDeepenNots() {
+ return !deepenNots.isEmpty();
+ }
+
+ /**
+ * @param deepenNot
+ * reference received in a "deepen not" line
+ * @return this builder
+ */
+ Builder addDeepenNot(String deepenNot) {
+ deepenNots.add(deepenNot);
+ return this;
+ }
+
+ /**
+ * @param value
+ * Unix timestamp received in a "deepen since" line
+ * @return this builder
+ */
+ Builder setDeepenSince(int value) {
+ deepenSince = value;
+ return this;
+ }
+
+ /**
+ * @return shallow since value, sent before in a "deepen since" line. 0
+ * by default.
+ */
+ int getDeepenSince() {
+ return deepenSince;
+ }
+
+ /**
* @param shallowOid
* object id received in a "shallow" line
* @return this builder
@@ -110,7 +160,7 @@ final class FetchV0Request extends FetchRequest {
FetchV0Request build() {
return new FetchV0Request(wantIds, depth, clientShallowCommits,
- filterSpec, clientCaps, agent);
+ filterSpec, clientCaps, deepenSince, deepenNots, agent);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
index 50fb9d2262..446a3bcc79 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 Google LLC. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -50,7 +50,7 @@ public final class FetchV2Request extends FetchRequest {
@NonNull List<String> wantedRefs,
@NonNull Set<ObjectId> wantIds,
@NonNull Set<ObjectId> clientShallowCommits, int deepenSince,
- @NonNull List<String> deepenNotRefs, int depth,
+ @NonNull List<String> deepenNots, int depth,
@NonNull FilterSpec filterSpec,
boolean doneReceived, boolean waitForDone,
@NonNull Set<String> clientCapabilities,
@@ -58,7 +58,7 @@ public final class FetchV2Request extends FetchRequest {
boolean sidebandAll, @NonNull List<String> packfileUriProtocols) {
super(wantIds, depth, clientShallowCommits, filterSpec,
clientCapabilities, deepenSince,
- deepenNotRefs, agent);
+ deepenNots, agent);
this.peerHas = requireNonNull(peerHas);
this.wantedRefs = requireNonNull(wantedRefs);
this.doneReceived = doneReceived;
@@ -140,7 +140,7 @@ public final class FetchV2Request extends FetchRequest {
final Set<ObjectId> clientShallowCommits = new HashSet<>();
- final List<String> deepenNotRefs = new ArrayList<>();
+ final List<String> deepenNots = new ArrayList<>();
final Set<String> clientCapabilities = new HashSet<>();
@@ -240,17 +240,17 @@ public final class FetchV2Request extends FetchRequest {
* @return true if there has been at least one "deepen not" line in the
* request so far
*/
- boolean hasDeepenNotRefs() {
- return !deepenNotRefs.isEmpty();
+ boolean hasDeepenNots() {
+ return !deepenNots.isEmpty();
}
/**
- * @param deepenNotRef
+ * @param deepenNot
* reference received in a "deepen not" line
* @return this builder
*/
- Builder addDeepenNotRef(String deepenNotRef) {
- deepenNotRefs.add(deepenNotRef);
+ Builder addDeepenNot(String deepenNot) {
+ deepenNots.add(deepenNot);
return this;
}
@@ -350,7 +350,7 @@ public final class FetchV2Request extends FetchRequest {
*/
FetchV2Request build() {
return new FetchV2Request(peerHas, wantedRefs, wantIds,
- clientShallowCommits, deepenSince, deepenNotRefs,
+ clientShallowCommits, deepenSince, deepenNots,
depth, filterSpec, doneReceived, waitForDone, clientCapabilities,
agent, Collections.unmodifiableList(serverOptions),
sidebandAll,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
index aaa9308ac3..be14e92d07 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2008, 2013 Google Inc.
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2022 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -234,6 +234,13 @@ public final class GitProtocolConstants {
public static final String CAPABILITY_SERVER_OPTION = "server-option"; //$NON-NLS-1$
/**
+ * The server supports the receiving of shallow options.
+ *
+ * @since 6.3
+ */
+ public static final String CAPABILITY_SHALLOW = "shallow"; //$NON-NLS-1$
+
+ /**
* Option for passing application-specific options to the server.
*
* @since 5.2
@@ -308,6 +315,13 @@ public final class GitProtocolConstants {
public static final String SECTION_PACKFILE = "packfile"; //$NON-NLS-1$
/**
+ * Protocol V2 shallow-info section header.
+ *
+ * @since 6.3
+ */
+ public static final String SECTION_SHALLOW_INFO = "shallow-info"; //$NON-NLS-1$
+
+ /**
* Protocol announcement for protocol version 1. This is the same as V0,
* except for this initial line.
*
@@ -329,6 +343,106 @@ public final class GitProtocolConstants {
*/
public static final String VERSION_2_REQUEST = "version=2"; //$NON-NLS-1$
+ /**
+ * The flush packet.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_FLUSH = "0000"; //$NON-NLS-1$
+
+ /**
+ * An alias for {@link #PACKET_FLUSH}. "Flush" is the name used in the C git
+ * documentation; the Java implementation calls this "end" in several
+ * places.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_END = PACKET_FLUSH;
+
+ /**
+ * The delimiter packet in protocol V2.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_DELIM = "0001"; //$NON-NLS-1$
+
+ /**
+ * A "deepen" packet beginning.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_DEEPEN = "deepen "; //$NON-NLS-1$
+
+ /**
+ * A "deepen-not" packet beginning.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_DEEPEN_NOT = "deepen-not "; //$NON-NLS-1$
+
+ /**
+ * A "deepen-since" packet beginning.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_DEEPEN_SINCE = "deepen-since "; //$NON-NLS-1$
+
+ /**
+ * An "ACK" packet beginning.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_ACK = "ACK "; //$NON-NLS-1$
+
+ /**
+ * A "done" packet beginning.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_DONE = "done"; //$NON-NLS-1$
+
+ /**
+ * A "ERR" packet beginning.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_ERR = "ERR "; //$NON-NLS-1$
+
+ /**
+ * A "have" packet beginning.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_HAVE = "have "; //$NON-NLS-1$
+
+ /**
+ * A "shallow" packet beginning.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_SHALLOW = OPTION_SHALLOW + ' ';
+
+ /**
+ * A "shallow" packet beginning.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_UNSHALLOW = "unshallow "; //$NON-NLS-1$
+
+ /**
+ * A "want" packet beginning.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_WANT = "want "; //$NON-NLS-1$
+
+ /**
+ * A "want-ref" packet beginning.
+ *
+ * @since 6.3
+ */
+ public static final String PACKET_WANT_REF = OPTION_WANT_REF + ' ';
+
enum MultiAck {
OFF, CONTINUE, DETAILED;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
index f8c51c180f..21a492577f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 Google LLC. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -10,6 +10,11 @@
package org.eclipse.jgit.transport;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_NOT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_SINCE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_WANT;
import java.io.EOFException;
import java.io.IOException;
@@ -70,20 +75,56 @@ final class ProtocolV0Parser {
break;
}
- if (line.startsWith("deepen ")) { //$NON-NLS-1$
- int depth = Integer.parseInt(line.substring(7));
+ if (line.startsWith(PACKET_DEEPEN)) {
+ int depth = Integer
+ .parseInt(line.substring(PACKET_DEEPEN.length()));
if (depth <= 0) {
throw new PackProtocolException(
MessageFormat.format(JGitText.get().invalidDepth,
Integer.valueOf(depth)));
}
+ if (reqBuilder.getDeepenSince() != 0) {
+ throw new PackProtocolException(
+ JGitText.get().deepenSinceWithDeepen);
+ }
+ if (reqBuilder.hasDeepenNots()) {
+ throw new PackProtocolException(
+ JGitText.get().deepenNotWithDeepen);
+ }
reqBuilder.setDepth(depth);
continue;
}
- if (line.startsWith("shallow ")) { //$NON-NLS-1$
+ if (line.startsWith(PACKET_DEEPEN_NOT)) {
+ reqBuilder.addDeepenNot(
+ line.substring(PACKET_DEEPEN_NOT.length()));
+ if (reqBuilder.getDepth() != 0) {
+ throw new PackProtocolException(
+ JGitText.get().deepenNotWithDeepen);
+ }
+ continue;
+ }
+
+ if (line.startsWith(PACKET_DEEPEN_SINCE)) {
+ // TODO: timestamps should be long
+ int ts = Integer
+ .parseInt(line.substring(PACKET_DEEPEN_SINCE.length()));
+ if (ts <= 0) {
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().invalidTimestamp, line));
+ }
+ if (reqBuilder.getDepth() != 0) {
+ throw new PackProtocolException(
+ JGitText.get().deepenSinceWithDeepen);
+ }
+ reqBuilder.setDeepenSince(ts);
+ continue;
+ }
+
+ if (line.startsWith(PACKET_SHALLOW)) {
reqBuilder.addClientShallowCommit(
- ObjectId.fromString(line.substring(8)));
+ ObjectId.fromString(
+ line.substring(PACKET_SHALLOW.length())));
continue;
}
@@ -101,7 +142,7 @@ final class ProtocolV0Parser {
continue;
}
- if (!line.startsWith("want ") || line.length() < 45) { //$NON-NLS-1$
+ if (!line.startsWith(PACKET_WANT) || line.length() < 45) {
throw new PackProtocolException(MessageFormat
.format(JGitText.get().expectedGot, "want", line)); //$NON-NLS-1$
}
@@ -115,7 +156,8 @@ final class ProtocolV0Parser {
}
}
- reqBuilder.addWantId(ObjectId.fromString(line.substring(5)));
+ reqBuilder.addWantId(
+ ObjectId.fromString(line.substring(PACKET_WANT.length())));
isFirst = false;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
index 6cec4b9a3f..b38deb69c0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 Google LLC. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -20,7 +20,14 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_AL
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE;
-import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WANT_REF;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_NOT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_SINCE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DONE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_HAVE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_WANT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_WANT_REF;
import java.io.IOException;
import java.text.MessageFormat;
@@ -115,15 +122,17 @@ final class ProtocolV2Parser {
boolean filterReceived = false;
for (String line2 : pckIn.readStrings()) {
- if (line2.startsWith("want ")) { //$NON-NLS-1$
- reqBuilder.addWantId(ObjectId.fromString(line2.substring(5)));
+ if (line2.startsWith(PACKET_WANT)) {
+ reqBuilder.addWantId(ObjectId
+ .fromString(line2.substring(PACKET_WANT.length())));
} else if (transferConfig.isAllowRefInWant()
- && line2.startsWith(OPTION_WANT_REF + " ")) { //$NON-NLS-1$
+ && line2.startsWith(PACKET_WANT_REF)) {
reqBuilder.addWantedRef(
- line2.substring(OPTION_WANT_REF.length() + 1));
- } else if (line2.startsWith("have ")) { //$NON-NLS-1$
- reqBuilder.addPeerHas(ObjectId.fromString(line2.substring(5)));
- } else if (line2.equals("done")) { //$NON-NLS-1$
+ line2.substring(PACKET_WANT_REF.length()));
+ } else if (line2.startsWith(PACKET_HAVE)) {
+ reqBuilder.addPeerHas(ObjectId
+ .fromString(line2.substring(PACKET_HAVE.length())));
+ } else if (line2.equals(PACKET_DONE)) {
reqBuilder.setDoneReceived();
} else if (line2.equals(OPTION_WAIT_FOR_DONE)) {
reqBuilder.setWaitForDone();
@@ -135,11 +144,13 @@ final class ProtocolV2Parser {
reqBuilder.addClientCapability(OPTION_INCLUDE_TAG);
} else if (line2.equals(OPTION_OFS_DELTA)) {
reqBuilder.addClientCapability(OPTION_OFS_DELTA);
- } else if (line2.startsWith("shallow ")) { //$NON-NLS-1$
+ } else if (line2.startsWith(PACKET_SHALLOW)) {
reqBuilder.addClientShallowCommit(
- ObjectId.fromString(line2.substring(8)));
- } else if (line2.startsWith("deepen ")) { //$NON-NLS-1$
- int parsedDepth = Integer.parseInt(line2.substring(7));
+ ObjectId.fromString(
+ line2.substring(PACKET_SHALLOW.length())));
+ } else if (line2.startsWith(PACKET_DEEPEN)) {
+ int parsedDepth = Integer
+ .parseInt(line2.substring(PACKET_DEEPEN.length()));
if (parsedDepth <= 0) {
throw new PackProtocolException(
MessageFormat.format(JGitText.get().invalidDepth,
@@ -149,21 +160,23 @@ final class ProtocolV2Parser {
throw new PackProtocolException(
JGitText.get().deepenSinceWithDeepen);
}
- if (reqBuilder.hasDeepenNotRefs()) {
+ if (reqBuilder.hasDeepenNots()) {
throw new PackProtocolException(
JGitText.get().deepenNotWithDeepen);
}
reqBuilder.setDepth(parsedDepth);
- } else if (line2.startsWith("deepen-not ")) { //$NON-NLS-1$
- reqBuilder.addDeepenNotRef(line2.substring(11));
+ } else if (line2.startsWith(PACKET_DEEPEN_NOT)) {
+ reqBuilder.addDeepenNot(
+ line2.substring(PACKET_DEEPEN_NOT.length()));
if (reqBuilder.getDepth() != 0) {
throw new PackProtocolException(
JGitText.get().deepenNotWithDeepen);
}
} else if (line2.equals(OPTION_DEEPEN_RELATIVE)) {
reqBuilder.addClientCapability(OPTION_DEEPEN_RELATIVE);
- } else if (line2.startsWith("deepen-since ")) { //$NON-NLS-1$
- int ts = Integer.parseInt(line2.substring(13));
+ } else if (line2.startsWith(PACKET_DEEPEN_SINCE)) {
+ int ts = Integer.parseInt(
+ line2.substring(PACKET_DEEPEN_SINCE.length()));
if (ts <= 0) {
throw new PackProtocolException(MessageFormat
.format(JGitText.get().invalidTimestamp, line2));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index 2542105c07..b70eedca63 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -20,6 +20,8 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_QUIET;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ERR;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
@@ -1247,7 +1249,7 @@ public class ReceivePack {
public void sendAdvertisedRefs(RefAdvertiser adv)
throws IOException, ServiceMayNotContinueException {
if (advertiseError != null) {
- adv.writeOne("ERR " + advertiseError); //$NON-NLS-1$
+ adv.writeOne(PACKET_ERR + advertiseError);
return;
}
@@ -1255,7 +1257,7 @@ public class ReceivePack {
advertiseRefsHook.advertiseRefs(this);
} catch (ServiceMayNotContinueException fail) {
if (fail.getMessage() != null) {
- adv.writeOne("ERR " + fail.getMessage()); //$NON-NLS-1$
+ adv.writeOne(PACKET_ERR + fail.getMessage());
fail.setOutput();
}
throw fail;
@@ -1339,8 +1341,9 @@ public class ReceivePack {
break;
}
- if (line.length() >= 48 && line.startsWith("shallow ")) { //$NON-NLS-1$
- parseShallow(line.substring(8, 48));
+ int len = PACKET_SHALLOW.length() + 40;
+ if (line.length() >= len && line.startsWith(PACKET_SHALLOW)) {
+ parseShallow(line.substring(PACKET_SHALLOW.length(), len));
continue;
}
@@ -1795,9 +1798,9 @@ public class ReceivePack {
@Override
void sendString(String s) throws IOException {
if (reportStatus) {
- pckOut.writeString(s + "\n"); //$NON-NLS-1$
+ pckOut.writeString(s + '\n');
} else if (msgOut != null) {
- msgOut.write(Constants.encode(s + "\n")); //$NON-NLS-1$
+ msgOut.write(Constants.encode(s + '\n'));
}
}
};
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index 3222d6330c..7cea998474 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -27,6 +27,7 @@ import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.MessageFormat;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -784,6 +785,12 @@ public abstract class Transport implements AutoCloseable {
private PrePushHook prePush;
+ private Integer depth;
+
+ private Instant deepenSince;
+
+ private List<String> deepenNots = new ArrayList<>();
+
@Nullable
TransferConfig.ProtocolVersion protocol;
@@ -1086,6 +1093,83 @@ public abstract class Transport implements AutoCloseable {
filterSpec = requireNonNull(filter);
}
+
+ /**
+ * Retrieves the depth for a shallow clone.
+ *
+ * @return the depth, or {@code null} if none set
+ * @since 6.3
+ */
+ public final Integer getDepth() {
+ return depth;
+ }
+
+ /**
+ * Limits fetching to the specified number of commits from the tip of each
+ * remote branch history.
+ *
+ * @param depth
+ * the depth
+ * @since 6.3
+ */
+ public final void setDepth(int depth) {
+ if (depth < 1) {
+ throw new IllegalArgumentException(JGitText.get().depthMustBeAt1);
+ }
+ this.depth = Integer.valueOf(depth);
+ }
+
+ /**
+ * Limits fetching to the specified number of commits from the tip of each
+ * remote branch history.
+ *
+ * @param depth
+ * the depth, or {@code null} to unset the depth
+ * @since 6.3
+ */
+ public final void setDepth(Integer depth) {
+ if (depth != null && depth.intValue() < 1) {
+ throw new IllegalArgumentException(JGitText.get().depthMustBeAt1);
+ }
+ this.depth = depth;
+ }
+
+ /**
+ * @return the deepen-since for a shallow clone
+ * @since 6.3
+ */
+ public final Instant getDeepenSince() {
+ return deepenSince;
+ }
+
+ /**
+ * Deepen or shorten the history of a shallow repository to include all reachable commits after a specified time.
+ *
+ * @param deepenSince the deepen-since. Must not be {@code null}
+ * @since 6.3
+ */
+ public final void setDeepenSince(@NonNull Instant deepenSince) {
+ this.deepenSince = deepenSince;
+ }
+
+ /**
+ * @return the deepen-not for a shallow clone
+ * @since 6.3
+ */
+ public final List<String> getDeepenNots() {
+ return deepenNots;
+ }
+
+ /**
+ * Deepen or shorten the history of a shallow repository to exclude commits reachable from a specified remote branch or tag.
+ *
+ * @param deepenNots the deepen-not. Must not be {@code null}
+ * @since 6.3
+ */
+ public final void setDeepenNots(@NonNull List<String> deepenNots) {
+ this.deepenNots = deepenNots;
+ }
+
/**
* Apply provided remote configuration on this transport.
*
@@ -1230,7 +1314,7 @@ public abstract class Transport implements AutoCloseable {
* @param toFetch
* specification of refs to fetch locally. May be null or the
* empty collection to use the specifications from the
- * RemoteConfig. May contains regular and negative
+ * RemoteConfig. May contains regular and negative
* {@link RefSpec}s. Source for each regular RefSpec can't
* be null.
* @return information describing the tracking refs updated.
@@ -1266,7 +1350,7 @@ public abstract class Transport implements AutoCloseable {
* @param toFetch
* specification of refs to fetch locally. May be null or the
* empty collection to use the specifications from the
- * RemoteConfig. May contains regular and negative
+ * RemoteConfig. May contain regular and negative
* {@link RefSpec}s. Source for each regular RefSpec can't
* be null.
* @param branch
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index dcd806a3da..65dbf12b2f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2020 Google Inc. and others
+ * Copyright (C) 2008, 2022 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -10,6 +10,7 @@
package org.eclipse.jgit.transport;
+import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.requireNonNull;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
@@ -35,6 +36,12 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ACK;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DONE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ERR;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_HAVE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_UNSHALLOW;
import static org.eclipse.jgit.transport.GitProtocolConstants.VERSION_2_REQUEST;
import static org.eclipse.jgit.util.RefMap.toRefMap;
@@ -1033,6 +1040,7 @@ public class UploadPack implements Closeable {
// writing a response. Buffer the response until then.
PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
List<ObjectId> unshallowCommits = new ArrayList<>();
+ List<ObjectId> deepenNots = emptyList();
FetchRequest req;
try {
if (biDirectionalPipe)
@@ -1071,13 +1079,15 @@ public class UploadPack implements Closeable {
verifyClientShallow(req.getClientShallowCommits());
}
- if (req.getDepth() != 0 || req.getDeepenSince() != 0) {
+ deepenNots = parseDeepenNots(req.getDeepenNots());
+ if (req.getDepth() != 0 || req.getDeepenSince() != 0 || !req.getDeepenNots().isEmpty()) {
computeShallowsAndUnshallows(req, shallow -> {
- pckOut.writeString("shallow " + shallow.name() + '\n'); //$NON-NLS-1$
+ pckOut.writeString(PACKET_SHALLOW + shallow.name() + '\n');
}, unshallow -> {
- pckOut.writeString("unshallow " + unshallow.name() + '\n'); //$NON-NLS-1$
+ pckOut.writeString(
+ PACKET_UNSHALLOW + unshallow.name() + '\n');
unshallowCommits.add(unshallow);
- }, Collections.emptyList());
+ }, deepenNots);
pckOut.end();
}
@@ -1109,7 +1119,7 @@ public class UploadPack implements Closeable {
if (sendPack) {
sendPack(accumulator, req, refs == null ? null : refs.values(),
- unshallowCommits, Collections.emptyList(), pckOut);
+ unshallowCommits, deepenNots, pckOut);
}
}
@@ -1188,15 +1198,7 @@ public class UploadPack implements Closeable {
// TODO(ifrade): Refactor to pass around the Request object, instead of
// copying data back to class fields
- List<ObjectId> deepenNots = new ArrayList<>();
- for (String s : req.getDeepenNotRefs()) {
- Ref ref = findRef(s);
- if (ref == null) {
- throw new PackProtocolException(MessageFormat
- .format(JGitText.get().invalidRefName, s));
- }
- deepenNots.add(ref.getObjectId());
- }
+ List<ObjectId> deepenNots = parseDeepenNots(req.getDeepenNots());
Map<String, ObjectId> wantedRefs = wantedRefs(req);
// TODO(ifrade): Avoid mutating the parsed request.
@@ -1206,7 +1208,7 @@ public class UploadPack implements Closeable {
boolean sectionSent = false;
boolean mayHaveShallow = req.getDepth() != 0
|| req.getDeepenSince() != 0
- || !req.getDeepenNotRefs().isEmpty();
+ || !req.getDeepenNots().isEmpty();
List<ObjectId> shallowCommits = new ArrayList<>();
List<ObjectId> unshallowCommits = new ArrayList<>();
@@ -1232,7 +1234,7 @@ public class UploadPack implements Closeable {
GitProtocolConstants.SECTION_ACKNOWLEDGMENTS + '\n');
for (ObjectId id : req.getPeerHas()) {
if (walk.getObjectReader().has(id)) {
- pckOut.writeString("ACK " + id.getName() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ pckOut.writeString(PACKET_ACK + id.getName() + '\n');
}
}
processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
@@ -1250,12 +1252,13 @@ public class UploadPack implements Closeable {
if (mayHaveShallow) {
if (sectionSent)
pckOut.writeDelim();
- pckOut.writeString("shallow-info\n"); //$NON-NLS-1$
+ pckOut.writeString(
+ GitProtocolConstants.SECTION_SHALLOW_INFO + '\n');
for (ObjectId o : shallowCommits) {
- pckOut.writeString("shallow " + o.getName() + '\n'); //$NON-NLS-1$
+ pckOut.writeString(PACKET_SHALLOW + o.getName() + '\n');
}
for (ObjectId o : unshallowCommits) {
- pckOut.writeString("unshallow " + o.getName() + '\n'); //$NON-NLS-1$
+ pckOut.writeString(PACKET_UNSHALLOW + o.getName() + '\n');
}
sectionSent = true;
}
@@ -1319,7 +1322,7 @@ public class UploadPack implements Closeable {
.format(JGitText.get().missingObject, oid.name()), e);
}
- pckOut.writeString(oid.getName() + " " + size); //$NON-NLS-1$
+ pckOut.writeString(oid.getName() + ' ' + size);
}
pckOut.end();
@@ -1391,7 +1394,7 @@ public class UploadPack implements Closeable {
protocolV2Hook
.onCapabilities(CapabilitiesV2Request.builder().build());
for (String s : getV2CapabilityAdvertisement()) {
- pckOut.writeString(s + "\n"); //$NON-NLS-1$
+ pckOut.writeString(s + '\n');
}
pckOut.end();
@@ -1618,7 +1621,7 @@ public class UploadPack implements Closeable {
*/
public void sendMessage(String what) {
try {
- msgOut.write(Constants.encode(what + "\n")); //$NON-NLS-1$
+ msgOut.write(Constants.encode(what + '\n'));
} catch (IOException e) {
// Ignore write failures.
}
@@ -1725,24 +1728,26 @@ public class UploadPack implements Closeable {
if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
pckOut.writeString("NAK\n"); //$NON-NLS-1$
if (noDone && sentReady) {
- pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ pckOut.writeString(PACKET_ACK + last.name() + '\n');
return true;
}
if (!biDirectionalPipe)
return false;
pckOut.flush();
- } else if (line.startsWith("have ") && line.length() == 45) { //$NON-NLS-1$
- peerHas.add(ObjectId.fromString(line.substring(5)));
+ } else if (line.startsWith(PACKET_HAVE)
+ && line.length() == PACKET_HAVE.length() + 40) {
+ peerHas.add(ObjectId
+ .fromString(line.substring(PACKET_HAVE.length())));
accumulator.haves++;
- } else if (line.equals("done")) { //$NON-NLS-1$
+ } else if (line.equals(PACKET_DONE)) {
last = processHaveLines(peerHas, last, pckOut, accumulator, Option.NONE);
if (commonBase.isEmpty())
pckOut.writeString("NAK\n"); //$NON-NLS-1$
else if (multiAck != MultiAck.OFF)
- pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ pckOut.writeString(PACKET_ACK + last.name() + '\n');
return true;
@@ -1803,14 +1808,15 @@ public class UploadPack implements Closeable {
//
switch (multiAck) {
case OFF:
- if (commonBase.size() == 1)
- out.writeString("ACK " + obj.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (commonBase.size() == 1) {
+ out.writeString(PACKET_ACK + obj.name() + '\n');
+ }
break;
case CONTINUE:
- out.writeString("ACK " + obj.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ out.writeString(PACKET_ACK + obj.name() + " continue\n"); //$NON-NLS-1$
break;
case DETAILED:
- out.writeString("ACK " + obj.name() + " common\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ out.writeString(PACKET_ACK + obj.name() + " common\n"); //$NON-NLS-1$
break;
}
}
@@ -1849,11 +1855,11 @@ public class UploadPack implements Closeable {
break;
case CONTINUE:
out.writeString(
- "ACK " + id.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ PACKET_ACK + id.name() + " continue\n"); //$NON-NLS-1$
break;
case DETAILED:
out.writeString(
- "ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ PACKET_ACK + id.name() + " ready\n"); //$NON-NLS-1$
readySent = true;
break;
}
@@ -1866,7 +1872,7 @@ public class UploadPack implements Closeable {
if (multiAck == MultiAck.DETAILED && !didOkToGiveUp
&& okToGiveUp()) {
ObjectId id = peerHas.get(peerHas.size() - 1);
- out.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ out.writeString(PACKET_ACK + id.name() + " ready\n"); //$NON-NLS-1$
readySent = true;
}
@@ -2476,6 +2482,24 @@ public class UploadPack implements Closeable {
}
}
+ private List<ObjectId> parseDeepenNots(List<String> deepenNots)
+ throws IOException {
+ List<ObjectId> result = new ArrayList<>();
+ for (String s : deepenNots) {
+ if (ObjectId.isId(s)) {
+ result.add(ObjectId.fromString(s));
+ } else {
+ Ref ref = findRef(s);
+ if (ref == null) {
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().invalidRefName, s));
+ }
+ result.add(ref.getObjectId());
+ }
+ }
+ return result;
+ }
+
private static class ResponseBufferedOutputStream extends OutputStream {
private final OutputStream rawOut;
@@ -2539,7 +2563,7 @@ public class UploadPack implements Closeable {
@Override
public void writeError(String message) throws IOException {
new PacketLineOut(requireNonNull(rawOut))
- .writeString("ERR " + message + '\n'); //$NON-NLS-1$
+ .writeString(PACKET_ERR + message + '\n');
}
}
}