summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Frade <ifrade@google.com>2020-04-03 21:48:34 -0700
committerIvan Frade <ifrade@google.com>2020-04-28 16:56:32 -0700
commit20bb3124218ed93956450fb30a95b2277db590c5 (patch)
tree4718b35b77ac939d06a7f5dc5518590f538e5bcc
parentae26fa19b7945960fc6e8bf2ab64ec128836ea6d (diff)
downloadjgit-20bb3124218ed93956450fb30a95b2277db590c5.tar.gz
jgit-20bb3124218ed93956450fb30a95b2277db590c5.zip
revwalk: Extract ObjectReachabilityChecker interface
Extract ObjectReachabilityChecker interface from the walk-based implementation, to add a bitmapped based implementation later. Refactor the test case to use it for both implementations. Change-Id: Iaac7c6b037723811956ac22625f27d3b4d742139 Signed-off-by: Ivan Frade <ifrade@google.com>
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectReachabilityTestCase.java143
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java138
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectReachabilityChecker.java48
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityChecker.java92
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java3
5 files changed, 231 insertions, 193 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectReachabilityTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectReachabilityTestCase.java
new file mode 100644
index 0000000000..267b163f43
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectReachabilityTestCase.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020, 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.revwalk;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
+import org.junit.Before;
+import org.junit.Test;
+
+public abstract class ObjectReachabilityTestCase
+ extends LocalDiskRepositoryTestCase {
+
+ private TestRepository<FileRepository> repo;
+ private AddressableRevCommit baseCommit;
+ private AddressableRevCommit branchACommit;
+ private AddressableRevCommit branchBCommit;
+ private AddressableRevCommit mergeCommit;
+
+ abstract ObjectReachabilityChecker getChecker(
+ TestRepository<FileRepository> repository) throws Exception;
+
+ // Pair of commit and blob inside it
+ protected static class AddressableRevCommit {
+ RevCommit commit;
+
+ RevBlob blob;
+
+ AddressableRevCommit(RevCommit commit, RevBlob blob) {
+ this.commit = commit;
+ this.blob = blob;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ FileRepository db = createWorkRepository();
+ repo = new TestRepository<>(db);
+ prepareRepo();
+ }
+
+ @Test
+ public void blob_in_base_reachable_from_branches() throws Exception {
+ ObjectReachabilityChecker checker = getChecker(repo);
+
+ RevObject baseBlob = baseCommit.blob;
+ assertReachable("reachable from one branch", checker.areAllReachable(
+ Arrays.asList(baseBlob), Stream.of(branchACommit.commit)));
+ assertReachable("reachable from another branch",
+ checker.areAllReachable(
+ Arrays.asList(baseBlob),
+ Stream.of(branchBCommit.commit)));
+ }
+
+ @Test
+ public void blob_reachable_from_owning_commit() throws Exception {
+ ObjectReachabilityChecker checker = getChecker(repo);
+
+ RevObject branchABlob = branchACommit.blob;
+ assertReachable("reachable from itself",
+ checker.areAllReachable(Arrays.asList(branchABlob),
+ Stream.of(branchACommit.commit)));
+ }
+
+ @Test
+ public void blob_in_branch_reachable_from_merge() throws Exception {
+ ObjectReachabilityChecker checker = getChecker(repo);
+
+ RevObject branchABlob = branchACommit.blob;
+ assertReachable("reachable from merge", checker.areAllReachable(
+ Arrays.asList(branchABlob), Stream.of(mergeCommit.commit)));
+ }
+
+ @Test
+ public void blob_unreachable_from_earlier_commit() throws Exception {
+ ObjectReachabilityChecker checker = getChecker(repo);
+
+ RevObject branchABlob = branchACommit.blob;
+ assertUnreachable("unreachable from earlier commit",
+ checker.areAllReachable(Arrays.asList(branchABlob),
+ Stream.of(baseCommit.commit)));
+ }
+
+ @Test
+ public void blob_unreachable_from_parallel_branch() throws Exception {
+ ObjectReachabilityChecker checker = getChecker(repo);
+
+ RevObject branchABlob = branchACommit.blob;
+ assertUnreachable("unreachable from another branch",
+ checker.areAllReachable(Arrays.asList(branchABlob),
+ Stream.of(branchBCommit.commit)));
+ }
+
+ private void prepareRepo() throws Exception {
+ baseCommit = createCommit("base");
+ branchACommit = createCommit("branchA", baseCommit);
+ branchBCommit = createCommit("branchB", baseCommit);
+ mergeCommit = createCommit("merge", branchACommit, branchBCommit);
+
+ // Bitmaps are generated from references
+ repo.update("refs/heads/a", branchACommit.commit);
+ repo.update("refs/heads/b", branchBCommit.commit);
+ repo.update("refs/heads/merge", mergeCommit.commit);
+ }
+
+ private AddressableRevCommit createCommit(String blobPath, AddressableRevCommit... parents) throws Exception {
+ RevBlob blob = repo.blob(blobPath + " content");
+ CommitBuilder commitBuilder = repo.commit();
+ for (int i = 0; i < parents.length; i++) {
+ commitBuilder.parent(parents[i].commit);
+ }
+ commitBuilder.add(blobPath, blob);
+
+ RevCommit commit = commitBuilder.create();
+ return new AddressableRevCommit(commit, blob);
+ }
+
+ private static void assertReachable(String msg, Optional<RevObject> result) {
+ assertFalse(msg, result.isPresent());
+ }
+
+ private static void assertUnreachable(String msg, Optional<RevObject> result) {
+ assertTrue(msg, result.isPresent());
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java
index f8fc0c9719..b1c9556df8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java
@@ -9,149 +9,17 @@
*/
package org.eclipse.jgit.revwalk;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Arrays;
-import java.util.Optional;
-import java.util.stream.Stream;
-
import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
-import org.junit.Before;
-import org.junit.Test;
public class PedestrianObjectReachabilityTest
- extends LocalDiskRepositoryTestCase {
+ extends ObjectReachabilityTestCase {
- PedestrianObjectReachabilityChecker getChecker(
+ @Override
+ ObjectReachabilityChecker getChecker(
TestRepository<FileRepository> repository)
throws Exception {
return new PedestrianObjectReachabilityChecker(
repository.getRevWalk().toObjectWalkWithSameObjects());
}
-
- private TestRepository<FileRepository> repo;
-
- private AddressableRevCommit baseCommit;
-
- private AddressableRevCommit branchACommit;
-
- private AddressableRevCommit branchBCommit;
-
- private AddressableRevCommit mergeCommit;
-
- /** {@inheritDoc} */
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- FileRepository db = createWorkRepository();
- repo = new TestRepository<>(db);
- prepareRepo();
- }
-
- @Test
- public void blob_in_base_reachable_from_branches() throws Exception {
- PedestrianObjectReachabilityChecker checker = getChecker(repo);
-
- RevObject baseBlob = baseCommit.blob;
- assertReachable("reachable from one branch", checker.areAllReachable(
- Arrays.asList(baseBlob), Stream.of(branchACommit.commit)));
- assertReachable("reachable from another branch",
- checker.areAllReachable(
- Arrays.asList(baseBlob), Stream.of(branchBCommit.commit)));
- }
-
- @Test
- public void blob_reachable_from_owning_commit() throws Exception {
- PedestrianObjectReachabilityChecker checker = getChecker(repo);
-
- RevObject branchABlob = branchACommit.blob;
- assertReachable("reachable from itself",
- checker.areAllReachable(Arrays.asList(branchABlob),
- Stream.of(branchACommit.commit)));
- }
-
- @Test
- public void blob_in_branch_reachable_from_merge() throws Exception {
- PedestrianObjectReachabilityChecker checker = getChecker(repo);
-
- RevObject branchABlob = branchACommit.blob;
- assertReachable("reachable from merge", checker.areAllReachable(
- Arrays.asList(branchABlob), Stream.of(mergeCommit.commit)));
- }
-
- @Test
- public void blob_unreachable_from_earlier_commit() throws Exception {
- PedestrianObjectReachabilityChecker checker = getChecker(repo);
-
- RevObject branchABlob = branchACommit.blob;
- assertUnreachable("unreachable from earlier commit",
- checker.areAllReachable(Arrays.asList(branchABlob),
- Stream.of(baseCommit.commit)));
- }
-
- @Test
- public void blob_unreachable_from_parallel_branch() throws Exception {
- PedestrianObjectReachabilityChecker checker = getChecker(repo);
-
- RevObject branchABlob = branchACommit.blob;
- assertUnreachable("unreachable from another branch",
- checker.areAllReachable(Arrays.asList(branchABlob),
- Stream.of(branchBCommit.commit)));
- }
-
- private void prepareRepo()
- throws Exception {
- baseCommit = createCommit("base");
- branchACommit = createCommit("branchA", baseCommit);
- branchBCommit = createCommit("branchB", baseCommit);
- mergeCommit = createCommit("merge", branchACommit, branchBCommit);
-
- // Bitmaps are generated from references
- // TODO(ifrade): These are not needed now, but will be when we use these
- // tests with the bitmap implementation.
- repo.update("refs/heads/a", branchACommit.commit);
- repo.update("refs/heads/b", branchBCommit.commit);
- repo.update("refs/heads/merge", mergeCommit.commit);
- }
-
- private AddressableRevCommit createCommit(String blobPath,
- AddressableRevCommit... parents) throws Exception {
- RevBlob blob = repo.blob(blobPath + " content");
- CommitBuilder commitBuilder = repo.commit();
- for (int i = 0; i < parents.length; i++) {
- commitBuilder.parent(parents[i].commit);
- }
- commitBuilder.add(blobPath, blob);
-
- RevCommit commit = commitBuilder.create();
- return new AddressableRevCommit(commit, blob);
- }
-
- // Pair of commit and blob inside it
- static class AddressableRevCommit {
- RevCommit commit;
-
- RevBlob blob;
-
- AddressableRevCommit(RevCommit commit, RevBlob blob) {
- this.commit = commit;
- this.blob = blob;
- }
- }
-
- private static void assertReachable(String msg,
- Optional<RevObject> result) {
- assertFalse(msg, result.isPresent());
- }
-
- private static void assertUnreachable(String msg,
- Optional<RevObject> result) {
- assertTrue(msg, result.isPresent());
- }
-
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectReachabilityChecker.java
new file mode 100644
index 0000000000..48e908e884
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectReachabilityChecker.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020, 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.revwalk;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+/**
+ * Checks if all objects are reachable from certain starting points.
+ *
+ * This is an expensive check that browses commits, trees, blobs and tags. For
+ * reachability just between commits see {@link ReachabilityChecker}
+ * implementations.
+ *
+ * @since 5.8
+ */
+public interface ObjectReachabilityChecker {
+
+ /**
+ * Checks that all targets are reachable from the starters.
+ *
+ * @implSpec Missing or invalid objects are reported as illegal state.
+ * Caller should have found them while translating ObjectIds into
+ * RevObjects. They can only happen here if the caller is mixing
+ * revwalks.
+ *
+ * @param targets
+ * objects to check for reachability from the starters
+ * @param starters
+ * objects known to be reachable to the caller
+ * @return Optional a single unreachable target if there are any (there
+ * could be more). Empty optional means all targets are reachable.
+ * @throws IOException
+ * Cannot access underlying storage
+ */
+ Optional<RevObject> areAllReachable(Collection<RevObject> targets,
+ Stream<RevObject> starters) throws IOException;
+
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityChecker.java
index 6e027ceafb..90cb2fac4d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityChecker.java
@@ -10,27 +10,19 @@
package org.eclipse.jgit.revwalk;
import java.io.IOException;
+import java.io.InvalidObjectException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Optional;
import java.util.stream.Stream;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.AnyObjectId;
/**
* Checks if all objects are reachable from certain starting points doing a
* walk.
- *
- * This is an expensive check that browses commits, trees, blobs and tags. For
- * reachability just between commits see {@link ReachabilityChecker}
- * implementations.
- *
- * TODO(ifrade): This class won't be public when the interface is introduced.
- * Skipping the @since.
*/
-public class PedestrianObjectReachabilityChecker {
+public class PedestrianObjectReachabilityChecker implements ObjectReachabilityChecker {
private ObjectWalk walk;
/**
@@ -44,60 +36,46 @@ public class PedestrianObjectReachabilityChecker {
}
/**
- * Checks that all targets are reachable from the starters.
- *
- * @param targets
- * objects we want to reach from the starters
- * @param starters
- * objects known to be reachable to the caller
- * @return Optional with an unreachable target if there is any (there could
- * be more than one). Empty optional means all targets are
- * reachable.
- * @throws MissingObjectException
- * An object was missing. This should not happen as the caller
- * checked this while doing
- * {@link RevWalk#parseAny(AnyObjectId)} to convert ObjectIds to
- * RevObjects.
- * @throws IncorrectObjectTypeException
- * Incorrect object type. As with missing objects, this should
- * not happen if the caller used
- * {@link RevWalk#parseAny(AnyObjectId)}.
- * @throws IOException
- * Cannot access underlying storage
+ * {@inheritDoc}
*/
+ @Override
public Optional<RevObject> areAllReachable(Collection<RevObject> targets,
- Stream<RevObject> starters) throws MissingObjectException,
- IncorrectObjectTypeException, IOException {
- walk.reset();
- walk.sort(RevSort.TOPO);
- for (RevObject target : targets) {
- walk.markStart(target);
- }
+ Stream<RevObject> starters) throws IOException {
+ try {
+ walk.reset();
+ walk.sort(RevSort.TOPO);
+ for (RevObject target : targets) {
+ walk.markStart(target);
+ }
- Iterator<RevObject> iterator = starters.iterator();
- while (iterator.hasNext()) {
- RevObject o = iterator.next();
- walk.markUninteresting(o);
+ Iterator<RevObject> iterator = starters.iterator();
+ while (iterator.hasNext()) {
+ RevObject o = iterator.next();
+ walk.markUninteresting(o);
- RevObject peeled = walk.peel(o);
- if (peeled instanceof RevCommit) {
- // By default, for performance reasons, ObjectWalk does not mark
- // a tree as uninteresting when we mark a commit. Mark it
- // ourselves so that we can determine reachability exactly.
- walk.markUninteresting(((RevCommit) peeled).getTree());
+ RevObject peeled = walk.peel(o);
+ if (peeled instanceof RevCommit) {
+ // By default, for performance reasons, ObjectWalk does not
+ // mark
+ // a tree as uninteresting when we mark a commit. Mark it
+ // ourselves so that we can determine reachability exactly.
+ walk.markUninteresting(((RevCommit) peeled).getTree());
+ }
}
- }
- RevCommit commit = walk.next();
- if (commit != null) {
- return Optional.of(commit);
- }
+ RevCommit commit = walk.next();
+ if (commit != null) {
+ return Optional.of(commit);
+ }
- RevObject object = walk.nextObject();
- if (object != null) {
- return Optional.of(object);
- }
+ RevObject object = walk.nextObject();
+ if (object != null) {
+ return Optional.of(object);
+ }
- return Optional.empty();
+ return Optional.empty();
+ } catch (MissingObjectException | InvalidObjectException e) {
+ throw new IllegalStateException(e);
+ }
}
}
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 0552909c51..cfa6958ef9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -79,6 +79,7 @@ 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.ObjectReachabilityChecker;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.PedestrianObjectReachabilityChecker;
import org.eclipse.jgit.revwalk.ReachabilityChecker;
@@ -1939,7 +1940,7 @@ public class UploadPack {
try (ObjectWalk objWalk = walk.toObjectWalkWithSameObjects()) {
List<RevObject> havesAsObjs = objectIdsToRevObjects(
objWalk, reachableFrom);
- PedestrianObjectReachabilityChecker reachabilityChecker = new PedestrianObjectReachabilityChecker(
+ ObjectReachabilityChecker reachabilityChecker = new PedestrianObjectReachabilityChecker(
objWalk);
Optional<RevObject> unreachable = reachabilityChecker
.areAllReachable(wantsAsObjs,