Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fischer2010-07-27 03:39:37 +0000
committerChris Aniszczyk2011-08-21 21:04:23 +0000
commit9952223e0645fd7a8cddc6093a7f449c6390238d (patch)
treec00454ab2a6e13da6a65dbd28828aab01b509912
parenta1a8c6d77ec5bea13c4f5ca428e425d95e14e003 (diff)
downloadjgit-9952223e0645fd7a8cddc6093a7f449c6390238d.tar.gz
jgit-9952223e0645fd7a8cddc6093a7f449c6390238d.tar.xz
jgit-9952223e0645fd7a8cddc6093a7f449c6390238d.zip
Implement server support for shallow clones
This implements the server side of shallow clones only (i.e. git-upload-pack), not the client side. CQ: 5517 Bug: 301627 Change-Id: Ied5f501f9c8d1fe90ab2ba44fac5fa67ed0035a4 Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java176
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java243
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java20
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java105
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java70
5 files changed, 577 insertions, 37 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
new file mode 100644
index 0000000000..ad05186354
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2010, Garmin International
+ * Copyright (C) 2010, Matt Fischer <matt.fischer@garmin.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.revwalk;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+
+/**
+ * Only produce commits which are below a specified depth.
+ *
+ * @see DepthWalk
+ */
+class DepthGenerator extends Generator {
+ private final FIFORevQueue pending;
+
+ private final int depth;
+
+ private final RevWalk walk;
+
+ /**
+ * Commits which used to be shallow in the client, but which are
+ * being extended as part of this fetch. These commits should be
+ * returned to the caller as UNINTERESTING so that their blobs/trees
+ * can be marked appropriately in the pack writer.
+ */
+ private final RevFlag UNSHALLOW;
+
+ /**
+ * Commits which the normal framework has marked as UNINTERESTING,
+ * but which we now care about again. This happens if a client is
+ * extending a shallow checkout to become deeper--the new commits at
+ * the bottom of the graph need to be sent, even though they are
+ * below other commits which the client already has.
+ */
+ private final RevFlag REINTERESTING;
+
+ /**
+ * @param w
+ * @param s Parent generator
+ * @throws MissingObjectException
+ * @throws IncorrectObjectTypeException
+ * @throws IOException
+ */
+ DepthGenerator(DepthWalk w, Generator s) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ pending = new FIFORevQueue();
+ walk = (RevWalk)w;
+
+ this.depth = w.getDepth();
+ this.UNSHALLOW = w.getUnshallowFlag();
+ this.REINTERESTING = w.getReinterestingFlag();
+
+ s.shareFreeList(pending);
+
+ // Begin by sucking out all of the source's commits, and
+ // adding them to the pending queue
+ for (;;) {
+ RevCommit c = s.next();
+ if (c == null)
+ break;
+ if (((DepthWalk.Commit) c).getDepth() == 0)
+ pending.add(c);
+ }
+ }
+
+ @Override
+ int outputType() {
+ return pending.outputType() | HAS_UNINTERESTING;
+ }
+
+ @Override
+ void shareFreeList(final BlockRevQueue q) {
+ pending.shareFreeList(q);
+ }
+
+ @Override
+ RevCommit next() throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ // Perform a breadth-first descent into the commit graph,
+ // marking depths as we go. This means that if a commit is
+ // reachable by more than one route, we are guaranteed to
+ // arrive by the shortest route first.
+ for (;;) {
+ final DepthWalk.Commit c = (DepthWalk.Commit) pending.next();
+ if (c == null)
+ return null;
+
+ if ((c.flags & RevWalk.PARSED) == 0)
+ c.parseHeaders(walk);
+
+ int newDepth = c.depth + 1;
+
+ for (final RevCommit p : c.parents) {
+ DepthWalk.Commit dp = (DepthWalk.Commit) p;
+
+ // If no depth has been assigned to this commit, assign
+ // it now. Since we arrive by the shortest route first,
+ // this depth is guaranteed to be the smallest value that
+ // any path could produce.
+ if (dp.depth == -1) {
+ dp.depth = newDepth;
+
+ // If the parent is not too deep, add it to the queue
+ // so that we can produce it later
+ if (newDepth <= depth)
+ pending.add(p);
+ }
+
+ // If the current commit has become unshallowed, everything
+ // below us is new to the client. Mark its parent as
+ // re-interesting, and carry that flag downward to all
+ // of its ancestors.
+ if(c.has(UNSHALLOW) || c.has(REINTERESTING)) {
+ p.add(REINTERESTING);
+ p.flags &= ~RevWalk.UNINTERESTING;
+ }
+ }
+
+ // Produce all commits less than the depth cutoff
+ boolean produce = c.depth <= depth;
+
+ // Unshallow commits are uninteresting, but still need to be sent
+ // up to the PackWriter so that it will exclude objects correctly.
+ // All other uninteresting commits should be omitted.
+ if ((c.flags & RevWalk.UNINTERESTING) != 0 && !c.has(UNSHALLOW))
+ produce = false;
+
+ if (produce)
+ return c;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
new file mode 100644
index 0000000000..9c5eaffd7d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2010, Garmin International
+ * Copyright (C) 2010, Matt Fischer <matt.fischer@garmin.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.revwalk;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
+
+/** Interface for revision walkers that perform depth filtering. */
+public interface DepthWalk {
+ /** @return Depth to filter to. */
+ public int getDepth();
+
+ /** @return flag marking commits that should become unshallow. */
+ public RevFlag getUnshallowFlag();
+
+ /** @return flag marking commits that are interesting again. */
+ public RevFlag getReinterestingFlag();
+
+ /** RevCommit with a depth (in commits) from a root. */
+ public static class Commit extends RevCommit {
+ /** Depth of this commit in the graph, via shortest path. */
+ int depth;
+
+ /** @return depth of this commit, as found by the shortest path. */
+ public int getDepth() {
+ return depth;
+ }
+
+ /**
+ * Initialize a new commit.
+ *
+ * @param id
+ * object name for the commit.
+ */
+ protected Commit(AnyObjectId id) {
+ super(id);
+ depth = -1;
+ }
+ }
+
+ /** Subclass of RevWalk that performs depth filtering. */
+ public class RevWalk extends org.eclipse.jgit.revwalk.RevWalk implements DepthWalk {
+ private final int depth;
+
+ private final RevFlag UNSHALLOW;
+
+ private final RevFlag REINTERESTING;
+
+ /**
+ * @param repo Repository to walk
+ * @param depth Maximum depth to return
+ */
+ public RevWalk(Repository repo, int depth) {
+ super(repo);
+
+ this.depth = depth;
+ this.UNSHALLOW = newFlag("UNSHALLOW");
+ this.REINTERESTING = newFlag("REINTERESTING");
+ }
+
+ /**
+ * @param or ObjectReader to use
+ * @param depth Maximum depth to return
+ */
+ public RevWalk(ObjectReader or, int depth) {
+ super(or);
+
+ this.depth = depth;
+ this.UNSHALLOW = newFlag("UNSHALLOW");
+ this.REINTERESTING = newFlag("REINTERESTING");
+ }
+
+ /**
+ * Mark a root commit (i.e., one whose depth should be considered 0.)
+ *
+ * @param c
+ * Commit to mark
+ * @throws IOException
+ * @throws IncorrectObjectTypeException
+ * @throws MissingObjectException
+ */
+ public void markRoot(RevCommit c) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ if (c instanceof Commit)
+ ((Commit) c).depth = 0;
+ super.markStart(c);
+ }
+
+ @Override
+ protected RevCommit createCommit(AnyObjectId id) {
+ return new Commit(id);
+ }
+
+ public int getDepth() {
+ return depth;
+ }
+
+ public RevFlag getUnshallowFlag() {
+ return UNSHALLOW;
+ }
+
+ public RevFlag getReinterestingFlag() {
+ return REINTERESTING;
+ }
+ }
+
+ /** Subclass of ObjectWalk that performs depth filtering. */
+ public class ObjectWalk extends org.eclipse.jgit.revwalk.ObjectWalk implements DepthWalk {
+ private final int depth;
+
+ private final RevFlag UNSHALLOW;
+
+ private final RevFlag REINTERESTING;
+
+ /**
+ * @param repo Repository to walk
+ * @param depth Maximum depth to return
+ */
+ public ObjectWalk(Repository repo, int depth) {
+ super(repo);
+
+ this.depth = depth;
+ this.UNSHALLOW = newFlag("UNSHALLOW");
+ this.REINTERESTING = newFlag("REINTERESTING");
+ }
+
+ /**
+ * @param or Object Reader
+ * @param depth Maximum depth to return
+ */
+ public ObjectWalk(ObjectReader or, int depth) {
+ super(or);
+
+ this.depth = depth;
+ this.UNSHALLOW = newFlag("UNSHALLOW");
+ this.REINTERESTING = newFlag("REINTERESTING");
+ }
+
+ /**
+ * Mark a root commit (i.e., one whose depth should be considered 0.)
+ *
+ * @param o
+ * Commit to mark
+ * @throws IOException
+ * @throws IncorrectObjectTypeException
+ * @throws MissingObjectException
+ */
+ public void markRoot(RevObject o) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ RevObject c = o;
+ while (c instanceof RevTag) {
+ c = ((RevTag) c).getObject();
+ parseHeaders(c);
+ }
+ if (c instanceof Commit)
+ ((Commit) c).depth = 0;
+ super.markStart(o);
+ }
+
+ /**
+ * Mark an element which used to be shallow in the client, but which
+ * should now be considered a full commit. Any ancestors of this commit
+ * should be included in the walk, even if they are the ancestor of an
+ * uninteresting commit.
+ *
+ * @param c
+ * Commit to mark
+ * @throws MissingObjectException
+ * @throws IncorrectObjectTypeException
+ * @throws IOException
+ */
+ public void markUnshallow(RevObject c) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ if (c instanceof RevCommit)
+ c.add(UNSHALLOW);
+ super.markStart(c);
+ }
+
+ @Override
+ protected RevCommit createCommit(AnyObjectId id) {
+ return new Commit(id);
+ }
+
+ public int getDepth() {
+ return depth;
+ }
+
+ public RevFlag getUnshallowFlag() {
+ return UNSHALLOW;
+ }
+
+ public RevFlag getReinterestingFlag() {
+ return REINTERESTING;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java
index fbff027784..7fb958bbb6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java
@@ -132,14 +132,20 @@ class StartGenerator extends Generator {
}
walker.queue = q;
- g = new PendingGenerator(w, pending, rf, pendingOutputType);
- if (boundary) {
- // Because the boundary generator may produce uninteresting
- // commits we cannot allow the pending generator to dispose
- // of them early.
- //
- ((PendingGenerator) g).canDispose = false;
+ if (walker instanceof DepthWalk) {
+ DepthWalk dw = (DepthWalk) walker;
+ g = new DepthGenerator(dw, pending);
+ } else {
+ g = new PendingGenerator(w, pending, rf, pendingOutputType);
+
+ if (boundary) {
+ // Because the boundary generator may produce uninteresting
+ // commits we cannot allow the pending generator to dispose
+ // of them early.
+ //
+ ((PendingGenerator) g).canDispose = false;
+ }
}
if ((g.outputType() & NEEDS_REWRITE) != 0) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
index d3bd693aa8..0e34554395 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
@@ -91,6 +91,7 @@ import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
+import org.eclipse.jgit.revwalk.DepthWalk;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
@@ -195,6 +196,12 @@ public class PackWriter {
private boolean pruneCurrentObjectList;
+ private boolean shallowPack;
+
+ private int depth;
+
+ private Collection<? extends ObjectId> unshallowObjects;
+
/**
* Create writer for specified repository.
* <p>
@@ -408,6 +415,22 @@ public class PackWriter {
}
/**
+ * Configure this pack for a shallow clone.
+ *
+ * @param depth
+ * maximum depth to traverse the commit graph
+ * @param unshallow
+ * objects which used to be shallow on the client, but are being
+ * extended as part of this fetch
+ */
+ public void setShallowPack(int depth,
+ Collection<? extends ObjectId> unshallow) {
+ this.shallowPack = true;
+ this.depth = depth;
+ this.unshallowObjects = unshallow;
+ }
+
+ /**
* Returns objects number in a pack file that was created by this writer.
*
* @return number of objects in pack.
@@ -538,7 +561,7 @@ public class PackWriter {
*/
@Deprecated
public void preparePack(ProgressMonitor countingMonitor,
- final ObjectWalk walk,
+ ObjectWalk walk,
final Collection<? extends ObjectId> interestingObjects,
final Collection<? extends ObjectId> uninterestingObjects)
throws IOException {
@@ -585,7 +608,11 @@ public class PackWriter {
public void preparePack(ProgressMonitor countingMonitor,
Set<? extends ObjectId> want,
Set<? extends ObjectId> have) throws IOException {
- ObjectWalk ow = new ObjectWalk(reader);
+ ObjectWalk ow;
+ if (shallowPack)
+ ow = new DepthWalk.ObjectWalk(reader, depth);
+ else
+ ow = new ObjectWalk(reader);
preparePack(countingMonitor, ow, want, have);
}
@@ -615,12 +642,14 @@ public class PackWriter {
* when some I/O problem occur during reading objects.
*/
public void preparePack(ProgressMonitor countingMonitor,
- final ObjectWalk walk,
+ ObjectWalk walk,
final Set<? extends ObjectId> interestingObjects,
final Set<? extends ObjectId> uninterestingObjects)
throws IOException {
if (countingMonitor == null)
countingMonitor = NullProgressMonitor.INSTANCE;
+ if (shallowPack && !(walk instanceof DepthWalk.ObjectWalk))
+ walk = new DepthWalk.ObjectWalk(reader, depth);
findObjectsToPack(countingMonitor, walk, interestingObjects,
uninterestingObjects);
}
@@ -1443,9 +1472,9 @@ public class PackWriter {
if (tipToPack.containsKey(o))
o.add(inCachedPack);
- if (have.contains(o)) {
+ if (have.contains(o))
haveObjs.add(o);
- } else if (want.contains(o)) {
+ if (want.contains(o)) {
o.add(include);
wantObjs.add(o);
if (o instanceof RevTag)
@@ -1476,8 +1505,18 @@ public class PackWriter {
}
}
- for (RevObject obj : wantObjs)
- walker.markStart(obj);
+ if (walker instanceof DepthWalk.ObjectWalk) {
+ DepthWalk.ObjectWalk depthWalk = (DepthWalk.ObjectWalk) walker;
+ for (RevObject obj : wantObjs)
+ depthWalk.markRoot(obj);
+ if (unshallowObjects != null) {
+ for (ObjectId id : unshallowObjects)
+ depthWalk.markUnshallow(walker.parseAny(id));
+ }
+ } else {
+ for (RevObject obj : wantObjs)
+ walker.markStart(obj);
+ }
for (RevObject obj : haveObjs)
walker.markUninteresting(obj);
@@ -1512,36 +1551,42 @@ public class PackWriter {
countingMonitor.update(1);
}
- int commitCnt = 0;
- boolean putTagTargets = false;
- for (RevCommit cmit : commits) {
- if (!cmit.has(added)) {
- cmit.add(added);
+ if (shallowPack) {
+ for (RevCommit cmit : commits) {
addObject(cmit, 0);
- commitCnt++;
}
-
- for (int i = 0; i < cmit.getParentCount(); i++) {
- RevCommit p = cmit.getParent(i);
- if (!p.has(added) && !p.has(RevFlag.UNINTERESTING)) {
- p.add(added);
- addObject(p, 0);
+ } else {
+ int commitCnt = 0;
+ boolean putTagTargets = false;
+ for (RevCommit cmit : commits) {
+ if (!cmit.has(added)) {
+ cmit.add(added);
+ addObject(cmit, 0);
commitCnt++;
}
- }
- if (!putTagTargets && 4096 < commitCnt) {
- for (ObjectId id : tagTargets) {
- RevObject obj = walker.lookupOrNull(id);
- if (obj instanceof RevCommit
- && obj.has(include)
- && !obj.has(RevFlag.UNINTERESTING)
- && !obj.has(added)) {
- obj.add(added);
- addObject(obj, 0);
+ for (int i = 0; i < cmit.getParentCount(); i++) {
+ RevCommit p = cmit.getParent(i);
+ if (!p.has(added) && !p.has(RevFlag.UNINTERESTING)) {
+ p.add(added);
+ addObject(p, 0);
+ commitCnt++;
+ }
+ }
+
+ if (!putTagTargets && 4096 < commitCnt) {
+ for (ObjectId id : tagTargets) {
+ RevObject obj = walker.lookupOrNull(id);
+ if (obj instanceof RevCommit
+ && obj.has(include)
+ && !obj.has(RevFlag.UNINTERESTING)
+ && !obj.has(added)) {
+ obj.add(added);
+ addObject(obj, 0);
+ }
}
+ putTagTargets = true;
}
- putTagTargets = true;
}
}
commits = null;
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 a9921fd8c3..459ee6a95d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -56,6 +56,7 @@ import java.util.Set;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.lib.Constants;
@@ -65,6 +66,7 @@ import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
+import org.eclipse.jgit.revwalk.DepthWalk;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
@@ -103,6 +105,8 @@ public class UploadPack {
static final String OPTION_NO_DONE = BasePackFetchConnection.OPTION_NO_DONE;
+ static final String OPTION_SHALLOW = BasePackFetchConnection.OPTION_SHALLOW;
+
/** Database we read the objects from. */
private final Repository db;
@@ -160,6 +164,15 @@ public class UploadPack {
/** Objects on both sides, these don't have to be sent. */
private final Set<RevObject> commonBase = new HashSet<RevObject>();
+ /** Shallow commits the client already has. */
+ private final Set<ObjectId> clientShallowCommits = new HashSet<ObjectId>();
+
+ /** Shallow commits on the client which are now becoming unshallow */
+ private final List<ObjectId> unshallowCommits = new ArrayList<ObjectId>();
+
+ /** Desired depth from the client on a shallow request. */
+ private int depth;
+
/** Commit time of the oldest common commit, in seconds. */
private int oldestTime;
@@ -418,6 +431,8 @@ public class UploadPack {
else
multiAck = MultiAck.OFF;
+ if (depth != 0)
+ processShallow();
sendPack = negotiate();
} catch (PackProtocolException err) {
reportErrorDuringNegotiate(err.getMessage());
@@ -457,6 +472,39 @@ public class UploadPack {
}
}
+ private void processShallow() throws IOException {
+ DepthWalk.RevWalk depthWalk =
+ new DepthWalk.RevWalk(walk.getObjectReader(), depth);
+
+ // Find all the commits which will be shallow
+ for (ObjectId o : wantIds) {
+ try {
+ depthWalk.markRoot(depthWalk.parseCommit(o));
+ } catch (IncorrectObjectTypeException notCommit) {
+ // Ignore non-commits in this loop.
+ }
+ }
+
+ RevCommit o;
+ while ((o = depthWalk.next()) != null) {
+ DepthWalk.Commit c = (DepthWalk.Commit) o;
+
+ // Commits at the boundary which aren't already shallow in
+ // the client need to be marked as such
+ if (c.getDepth() == depth && !clientShallowCommits.contains(c))
+ pckOut.writeString("shallow " + o.name());
+
+ // Commits not on the boundary which are shallow in the client
+ // need to become unshallowed
+ if (c.getDepth() < depth && clientShallowCommits.contains(c)) {
+ unshallowCommits.add(c.copy());
+ pckOut.writeString("unshallow " + c.name());
+ }
+ }
+
+ pckOut.end();
+ }
+
/**
* Generate an advertisement of available refs and capabilities.
*
@@ -488,6 +536,7 @@ public class UploadPack {
adv.advertiseCapability(OPTION_SIDE_BAND_64K);
adv.advertiseCapability(OPTION_THIN_PACK);
adv.advertiseCapability(OPTION_NO_PROGRESS);
+ adv.advertiseCapability(OPTION_SHALLOW);
if (!biDirectionalPipe)
adv.advertiseCapability(OPTION_NO_DONE);
adv.setDerefTags(true);
@@ -509,6 +558,17 @@ public class UploadPack {
if (line == PacketLineIn.END)
break;
+
+ if (line.startsWith("deepen ")) {
+ depth = Integer.parseInt(line.substring(7));
+ continue;
+ }
+
+ if (line.startsWith("shallow ")) {
+ clientShallowCommits.add(ObjectId.fromString(line.substring(8)));
+ continue;
+ }
+
if (!line.startsWith("want ") || line.length() < 45)
throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "want", line));
@@ -536,6 +596,13 @@ public class UploadPack {
try {
line = pckIn.readString();
} catch (EOFException eof) {
+ // EOF on stateless RPC (aka smart HTTP) and non-shallow request
+ // means the client asked for the updated shallow/unshallow data,
+ // disconnected, and will try another request with actual want/have.
+ // Don't report the EOF here, its a bug in the protocol that the client
+ // just disconnects without sending an END.
+ if (!biDirectionalPipe && depth > 0)
+ return false;
throw eof;
}
@@ -887,6 +954,9 @@ public class UploadPack {
pw.setTagTargets(tagTargets);
}
+ if (depth > 0)
+ pw.setShallowPack(depth, unshallowCommits);
+
RevWalk rw = walk;
if (wantAll.isEmpty()) {
pw.preparePack(pm, wantIds, commonBase);

Back to the top