summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java87
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java35
2 files changed, 120 insertions, 2 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
index 908bddfa97..a8127abd36 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
@@ -4,24 +4,33 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.storage.dfs.DfsGarbageCollector;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.UploadPack.RequestPolicy;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.eclipse.jgit.transport.resolver.UploadPackFactory;
+import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
/**
* Tests for server upload-pack utilities.
*/
public class UploadPackTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
private URIish uri;
private TestProtocol<Object> testProtocol;
@@ -51,6 +60,25 @@ public class UploadPackTest {
return new InMemoryRepository(new DfsRepositoryDescription(name));
}
+ private void generateBitmaps(InMemoryRepository repo) throws Exception {
+ new DfsGarbageCollector(repo).pack(null);
+ repo.scanForRepoChanges();
+ }
+
+ private static TestProtocol<Object> generateReachableCommitUploadPackProtocol() {
+ return new TestProtocol<>(
+ new UploadPackFactory<Object>() {
+ @Override
+ public UploadPack create(Object req, Repository db)
+ throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ UploadPack up = new UploadPack(db);
+ up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
+ return up;
+ }
+ }, null);
+ }
+
@Test
public void testFetchParentOfShallowCommit() throws Exception {
RevCommit commit0 = remote.commit().message("0").create();
@@ -83,4 +111,63 @@ public class UploadPackTest {
assertTrue(client.hasObject(commit0.toObjectId()));
}
}
+
+ @Test
+ public void testFetchUnreachableBlobWithBitmap() throws Exception {
+ RevBlob blob = remote.blob("foo");
+ remote.commit(remote.tree(remote.file("foo", blob)));
+ generateBitmaps(server);
+
+ testProtocol = generateReachableCommitUploadPackProtocol();
+ uri = testProtocol.register(ctx, server);
+
+ assertFalse(client.hasObject(blob.toObjectId()));
+
+ try (Transport tn = testProtocol.open(uri, client, "server")) {
+ thrown.expect(TransportException.class);
+ thrown.expectMessage(Matchers.containsString(
+ "want " + blob.name() + " not valid"));
+ tn.fetch(NullProgressMonitor.INSTANCE,
+ Collections.singletonList(new RefSpec(blob.name())));
+ }
+ }
+
+ @Test
+ public void testFetchReachableBlobWithBitmap() throws Exception {
+ RevBlob blob = remote.blob("foo");
+ RevCommit commit = remote.commit(remote.tree(remote.file("foo", blob)));
+ remote.update("master", commit);
+ generateBitmaps(server);
+
+ testProtocol = generateReachableCommitUploadPackProtocol();
+ uri = testProtocol.register(ctx, server);
+
+ assertFalse(client.hasObject(blob.toObjectId()));
+
+ try (Transport tn = testProtocol.open(uri, client, "server")) {
+ tn.fetch(NullProgressMonitor.INSTANCE,
+ Collections.singletonList(new RefSpec(blob.name())));
+ assertTrue(client.hasObject(blob.toObjectId()));
+ }
+ }
+
+ @Test
+ public void testFetchReachableBlobWithoutBitmap() throws Exception {
+ RevBlob blob = remote.blob("foo");
+ RevCommit commit = remote.commit(remote.tree(remote.file("foo", blob)));
+ remote.update("master", commit);
+
+ testProtocol = generateReachableCommitUploadPackProtocol();
+ uri = testProtocol.register(ctx, server);
+
+ assertFalse(client.hasObject(blob.toObjectId()));
+
+ try (Transport tn = testProtocol.open(uri, client, "server")) {
+ thrown.expect(TransportException.class);
+ thrown.expectMessage(Matchers.containsString(
+ "want " + blob.name() + " not valid"));
+ tn.fetch(NullProgressMonitor.INSTANCE,
+ Collections.singletonList(new RefSpec(blob.name())));
+ }
+ }
}
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 cf070c6348..cab532288b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -77,14 +77,18 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.lib.BitmapIndex;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
+import org.eclipse.jgit.revwalk.BitmapWalker;
import org.eclipse.jgit.revwalk.DepthWalk;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -1315,6 +1319,18 @@ public class UploadPack {
}
}
+ private static void checkNotAdvertisedWantsUsingBitmap(ObjectReader reader,
+ BitmapIndex bitmapIndex, List<ObjectId> notAdvertisedWants,
+ Set<ObjectId> reachableFrom) throws IOException {
+ BitmapWalker bitmapWalker = new BitmapWalker(new ObjectWalk(reader), bitmapIndex, null);
+ BitmapBuilder reachables = bitmapWalker.findObjects(reachableFrom, null, false);
+ for (ObjectId oid : notAdvertisedWants) {
+ if (!reachables.contains(oid)) {
+ throw new WantNotValidException(oid);
+ }
+ }
+ }
+
private static void checkNotAdvertisedWants(UploadPack up,
List<ObjectId> notAdvertisedWants, Set<ObjectId> reachableFrom)
throws MissingObjectException, IncorrectObjectTypeException, IOException {
@@ -1324,13 +1340,28 @@ public class UploadPack {
// into an advertised branch it will be marked UNINTERESTING and no commits
// return.
- try (RevWalk walk = new RevWalk(up.getRevWalk().getObjectReader())) {
+ ObjectReader reader = up.getRevWalk().getObjectReader();
+ try (RevWalk walk = new RevWalk(reader)) {
AsyncRevObjectQueue q = walk.parseAny(notAdvertisedWants, true);
try {
RevObject obj;
while ((obj = q.next()) != null) {
- if (!(obj instanceof RevCommit))
+ if (!(obj instanceof RevCommit)) {
+ // If unadvertized non-commits are requested, use
+ // bitmaps. If there are no bitmaps, instead of
+ // incurring the expense of a manual walk, reject
+ // the request.
+ BitmapIndex bitmapIndex = reader.getBitmapIndex();
+ if (bitmapIndex != null) {
+ checkNotAdvertisedWantsUsingBitmap(
+ reader,
+ bitmapIndex,
+ notAdvertisedWants,
+ reachableFrom);
+ return;
+ }
throw new WantNotValidException(obj);
+ }
walk.markStart((RevCommit) obj);
}
} catch (MissingObjectException notFound) {