Browse Source

tree:<depth> should not traverse overly-deep trees

If we are traversing a tree which is too deep, then there is no need to
traverse the children. Skipping children is much faster than traversing
the possibly thousands of objects which are directly or indirectly
referenced by the tree.

Change-Id: I6d68cc1d35da48e3288b9cc80356a281ab36863d
Signed-off-by: Matthew DeVore <matvore@gmail.com>
stable-5.4
Matthew DeVore 6 years ago
parent
commit
175e66548b
  1. 7
      org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
  2. 19
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
  3. 11
      org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
  4. 11
      org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java

7
org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java

@ -45,6 +45,7 @@ import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.storage.pack.PackStatistics;
import org.eclipse.jgit.transport.UploadPack.RequestPolicy; import org.eclipse.jgit.transport.UploadPack.RequestPolicy;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
@ -76,6 +77,8 @@ public class UploadPackTest {
private TestRepository<InMemoryRepository> remote; private TestRepository<InMemoryRepository> remote;
private PackStatistics stats;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
server = newRepo("server"); server = newRepo("server");
@ -460,6 +463,7 @@ public class UploadPackTest {
ByteArrayOutputStream recv = new ByteArrayOutputStream(); ByteArrayOutputStream recv = new ByteArrayOutputStream();
up.upload(send, recv, null); up.upload(send, recv, null);
stats = up.getStatistics();
return new ByteArrayInputStream(recv.toByteArray()); return new ByteArrayInputStream(recv.toByteArray());
} }
@ -1630,6 +1634,7 @@ public class UploadPackTest {
.has(preparator.blobLowDepth.toObjectId())); .has(preparator.blobLowDepth.toObjectId()));
assertFalse(client.getObjectDatabase() assertFalse(client.getObjectDatabase()
.has(preparator.blobHighDepth.toObjectId())); .has(preparator.blobHighDepth.toObjectId()));
assertEquals(1, stats.getTreesTraversed());
} }
@Test @Test
@ -1651,6 +1656,7 @@ public class UploadPackTest {
.has(preparator.blobLowDepth.toObjectId())); .has(preparator.blobLowDepth.toObjectId()));
assertFalse(client.getObjectDatabase() assertFalse(client.getObjectDatabase()
.has(preparator.blobHighDepth.toObjectId())); .has(preparator.blobHighDepth.toObjectId()));
assertEquals(1, stats.getTreesTraversed());
} }
@Test @Test
@ -1668,6 +1674,7 @@ public class UploadPackTest {
.has(preparator.blobLowDepth.toObjectId())); .has(preparator.blobLowDepth.toObjectId()));
assertFalse(client.getObjectDatabase() assertFalse(client.getObjectDatabase()
.has(preparator.blobHighDepth.toObjectId())); .has(preparator.blobHighDepth.toObjectId()));
assertEquals(2, stats.getTreesTraversed());
} }
/** /**

19
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java

@ -2100,6 +2100,12 @@ public class PackWriter implements AutoCloseable {
/** /**
* Determines if the object should be omitted from the pack as a result of * Determines if the object should be omitted from the pack as a result of
* its depth (probably because of the tree:<depth> filter). * its depth (probably because of the tree:<depth> filter).
* <p>
* Causes {@code walker} to skip traversing the current tree, which ought to
* have just started traversal, assuming this method is called as soon as a
* new depth is reached.
* <p>
* This method increments the {@code treesTraversed} statistic.
* *
* @param obj * @param obj
* the object to check whether it should be omitted. * the object to check whether it should be omitted.
@ -2116,12 +2122,17 @@ public class PackWriter implements AutoCloseable {
// A blob is considered one level deeper than the tree that contains it. // A blob is considered one level deeper than the tree that contains it.
if (obj.getType() == OBJ_BLOB) { if (obj.getType() == OBJ_BLOB) {
treeDepth++; treeDepth++;
} else {
stats.treesTraversed++;
} }
// TODO: Do not continue traversing the tree, since its children if (filterSpec.getTreeDepthLimit() < 0 ||
// will also be too deep. treeDepth <= filterSpec.getTreeDepthLimit()) {
return filterSpec.getTreeDepthLimit() != -1 && return false;
treeDepth > filterSpec.getTreeDepthLimit(); }
walker.skipTree();
return true;
} }
// Adds the given object as an object to be packed, first performing // Adds the given object as an object to be packed, first performing

11
org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java

@ -389,6 +389,17 @@ public class ObjectWalk extends RevWalk {
} }
} }
/**
* Skips the current tree such that {@link #nextObject()} does not return
* any objects inside it. This should be called right after
* {@link #nextObject()} returns the tree.
*
* @since 5.4
*/
public void skipTree() {
currVisit.ptr = currVisit.buf.length;
}
/** /**
* Pop the next most recent object. * Pop the next most recent object.
* *

11
org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java

@ -266,6 +266,9 @@ public class PackStatistics {
/** Time in ms spent writing the pack. */ /** Time in ms spent writing the pack. */
public long timeWriting; public long timeWriting;
/** Number of trees traversed in the walk when writing the pack. */
public long treesTraversed;
/** /**
* Statistics about each object type in the pack (commits, tags, trees * Statistics about each object type in the pack (commits, tags, trees
* and blobs.) * and blobs.)
@ -585,6 +588,14 @@ public class PackStatistics {
return statistics.timeWriting; return statistics.timeWriting;
} }
/**
* @return number of trees traversed in the walk when writing the pack.
* @since 5.4
*/
public long getTreesTraversed() {
return statistics.treesTraversed;
}
/** /**
* Get total time spent processing this pack. * Get total time spent processing this pack.
* *

Loading…
Cancel
Save