Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [jgit-dev] Implementing shallow clones

Ok, that actually ended up being pretty easy.  Here's a new version of
the patch which I think does everything in a fairly non-hairy way.
Please let me know what you think.

--Matt

P.S.  I tried to submit this as a gerrit change, but ran into issues
uploading.  I followed all the instructions on the website to register
an account and all that, but every time I try to contact the server I
get "ssh_exchange_identification: Connection closed by remote host".
Is there something else I need to do in order for that to work?

On Tue, Aug 10, 2010 at 9:08 AM, Shawn Pearce <spearce@xxxxxxxxxxx> wrote:
> On Tue, Aug 3, 2010 at 2:18 PM, Matt Fischer <mattfischer84@xxxxxxxxx> wrote:
>> The most efficient way to do this (and what C git does) is the following:
>>
>> 1.  Set up a queue (FIFORevQueue in jgit parlance), and seed it with
>> all the root commits.  Mark them as depth 0.
>> 2.  Pop a commit from the front of the queue, and examine its depth.
>> 3.  Set all parents of this commit to depth n + 1, unless their depth
>> is already less than this.
>> 4.  Add any parents whose depth is less than the cutoff to the back of
>> the queue.
>> 5.  Produce the popped commit, and repeat from 2 until the queue is empty.
>>
>> This works because it's basically a breadth-first descent into the
>> graph.  If you have a commit that can be arrived at by two different
>> routes, you'll always get there by the shortest route first, because
>> the queue is FIFO.
>
> Oh, great idea.  I knew we had a reason for FIFORevQueue.  :-)
> (IIRC right now its unused.)
>
>> PendingGenerator almost does this, but it's built around a
>> DateRevQueue, so it's not guaranteed to get the ordering right.  It
>> kind of feels like the right way to do this would just be to
>> completely forgo use of the PendingGenerator, and have DepthGenerator
>> start directly from the root commits and spin out its own tree.
>
> I agree.
>
>> That's kind of an invasive change to StartGenerator, though--would
>> something like that be ok with you, or is there some other way to
>> integrate this algorithm into the existing structure?
>
> Yes, its fine.  But IIRC it shouldn't be that bad to insert into
> StartGenerator.  Most of the pipeline doesn't care that its a
> PendingGenerator or some other type of Generator... just that a
> Generator exists for it to wrap its stage around.  So you should be
> able to conditionally create your new DepthGenerator or the
> PendingGenerator based on the type of walk being done.
>
> --
> Shawn.
>
From ed5f501f9c8d1fe90ab2ba44fac5fa67ed0035a4 Mon Sep 17 00:00:00 2001
From: Matt Fischer <matt.fischer@xxxxxxxxxx>
Date: Mon, 26 Jul 2010 22:39:37 -0500
Subject: [PATCH] Implemented shallow clones

This implements the server side of shallow clones only (i.e. git-upload-pack),
not the client side.
---
 .../org/eclipse/jgit/revwalk/DepthGenerator.java   |  148 +++++++++++++
 .../src/org/eclipse/jgit/revwalk/DepthWalk.java    |  221 ++++++++++++++++++++
 .../org/eclipse/jgit/revwalk/StartGenerator.java   |   21 ++-
 .../org/eclipse/jgit/storage/pack/PackWriter.java  |   29 +++-
 .../src/org/eclipse/jgit/transport/UploadPack.java |   82 +++++++-
 5 files changed, 489 insertions(+), 12 deletions(-)
 create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
 create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java

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 0000000..d37194b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2010, Garmin International
+ * Copyright (C) 2010, Matt Fischer <matt.fischer@xxxxxxxxxx>
+ * 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.
+ */
+public class DepthGenerator extends Generator {
+	private final FIFORevQueue pending;
+
+	private final int outputType;
+	private final int depth;
+	private final DepthWalk.CompareMode compareMode;
+	private final RevWalk walk;
+
+	/**
+	 * @param w
+	 * @param s Parent generator
+	 * @param depth Maximum depth of commits.
+	 * @param compareMode Comparison mode
+	 * @throws MissingObjectException
+	 * @throws IncorrectObjectTypeException
+	 * @throws IOException
+	 */
+	public DepthGenerator(RevWalk w, Generator s, int depth, DepthWalk.CompareMode compareMode) throws MissingObjectException,
+	IncorrectObjectTypeException, IOException {
+		pending = new FIFORevQueue();
+		outputType = s.outputType();
+		walk = w;
+		this.depth = depth;
+		this.compareMode = compareMode;
+
+		s.shareFreeList(pending);
+
+		// Begin by sucking out all of the source's commits, and
+		// adding them to the pending queue
+		for (;;) {
+			final RevCommit c = s.next();
+			if (c == null)
+				break;
+			pending.add(c);
+		}
+	}
+
+	@Override
+	int outputType() {
+		return outputType;
+	}
+
+	@Override
+	void shareFreeList(final BlockRevQueue q) {
+		q.shareFreeList(pending);
+	}
+
+	@Override
+	RevCommit next() throws MissingObjectException,
+			IncorrectObjectTypeException, IOException {
+		for (;;) {
+			final RevCommit c = pending.next();
+			if (c == null)
+				return null;
+
+			c.parseHeaders(walk);
+
+			DepthWalk.Commit dc = (DepthWalk.Commit)c;
+			int newDepth = dc.getDepth() + 1;
+
+			for (final RevCommit p : c.parents) {
+				// Carry this child's depth up to the parent if it is
+				// less than the parent's current depth
+				DepthWalk.Commit dp = (DepthWalk.Commit)p;
+
+				if (dp.getDepth() > newDepth) {
+					dp.setDepth(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);
+				}
+			}
+
+			// Determine whether or not we will produce this commit
+			boolean produce = true;
+			switch (compareMode) {
+			case EQUAL:
+				produce = (dc.getDepth() == depth);
+				break;
+
+			case LESS_THAN_EQUAL:
+				produce = (dc.getDepth() <= depth);
+				break;
+			}
+
+			if (produce)
+				return c;
+			else
+				continue;
+		}
+	}
+}
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 0000000..3ce8c86
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2010, Garmin International
+ * Copyright (C) 2010, Matt Fischer <matt.fischer@xxxxxxxxxx>
+ * 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 which perform depth filtering
+ */
+public interface DepthWalk {
+
+	/**
+	 * Comparison modes for depth testing
+	 */
+	public enum CompareMode {
+		/** Commits must be exactly equal to the specified depth */
+		EQUAL,
+
+		/** Commits must be less than or equal to the specified depth */
+		LESS_THAN_EQUAL
+	}
+
+	/**
+	 * @return Depth to filter to
+	 */
+	public int getDepth();
+
+	/**
+	 * @return Comparison mode for this walker
+	 */
+	public CompareMode getCompareMode();
+
+	/**
+	 * Wrapper object for RevCommit which adds a depth tag.
+	 */
+	public class Commit extends RevCommit {
+
+		private int depth;
+
+		/**
+		 * @param id
+		 */
+		protected Commit(AnyObjectId id) {
+			super(id);
+			depth = Integer.MAX_VALUE;
+		}
+
+		/**
+		 * Set the depth of this commit
+		 * @param depth Distance to nearest root in commit graph
+		 */
+		public void setDepth(int depth) {
+			this.depth = depth;
+		}
+
+		/**
+		 * @return The depth of this commit
+		 */
+		public int getDepth() {
+			return depth;
+		}
+	}
+
+	/**
+	 * Subclass of RevWalk which performs depth filtering
+	 */
+	public class RevWalk extends org.eclipse.jgit.revwalk.RevWalk implements DepthWalk {
+		private final int depth;
+		private final CompareMode compareMode;
+
+		/**
+		 * @param repo Repository to walk
+		 * @param depth Maximum depth to return
+		 * @param compareMode Comparison mode
+		 */
+		public RevWalk(Repository repo, int depth, CompareMode compareMode) {
+			super(repo);
+
+			this.depth = depth;
+			this.compareMode = compareMode;
+		}
+
+		/** Mark a commit as a root (i.e., depth 0)
+		 * @param c Commit to mark
+		 * @throws MissingObjectException
+		 * @throws IncorrectObjectTypeException
+		 * @throws IOException
+		 */
+		public void markStart(RevCommit c) throws MissingObjectException,
+		IncorrectObjectTypeException, IOException {
+			if (c instanceof Commit) {
+				((Commit)c).setDepth(0);
+			}
+
+			super.markStart(c);
+		}
+
+		@Override
+		protected RevCommit createCommit(final AnyObjectId id) {
+			// Wrap this commit with a depth tag
+			return new Commit(id);
+		}
+
+		public int getDepth() {
+			return depth;
+		}
+
+		public CompareMode getCompareMode() {
+			return compareMode;
+		}
+	}
+
+	/**
+	 * Subclass of ObjectWalk which performs depth filtering
+	 */
+	public class ObjectWalk extends org.eclipse.jgit.revwalk.ObjectWalk implements DepthWalk {
+		private final int depth;
+		private final CompareMode compareMode;
+
+		/**
+		 * @param repo Repository to walk
+		 * @param depth Maximum depth to return
+		 * @param compareMode Comparison mode
+		 */
+		public ObjectWalk(Repository repo, int depth, CompareMode compareMode) {
+			super(repo);
+
+			this.depth = depth;
+			this.compareMode = compareMode;
+		}
+
+		/**
+		 * @param or Object Reader
+		 * @param depth Maximum depth to return
+		 * @param compareMode Comparison mode
+		 */
+		public ObjectWalk(ObjectReader or, int depth, CompareMode compareMode) {
+			super(or);
+
+			this.depth = depth;
+			this.compareMode = compareMode;
+		}
+
+		/** Mark a commit as a root (i.e., depth 0)
+		 * @param c Commit to mark
+		 * @throws MissingObjectException
+		 * @throws IncorrectObjectTypeException
+		 * @throws IOException
+		 */
+		public void markStart(RevObject c) throws MissingObjectException,
+		IncorrectObjectTypeException, IOException {
+			if (c instanceof Commit) {
+				((Commit)c).setDepth(0);
+			}
+
+			super.markStart(c);
+		}
+
+		@Override
+		protected RevCommit createCommit(final AnyObjectId id) {
+			// Wrap this commit with a depth tag
+			return new Commit(id);
+		}
+
+		public int getDepth() {
+			return depth;
+		}
+
+		public CompareMode getCompareMode() {
+			return compareMode;
+		}
+	}
+}
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 5e778a4..4c71282 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java
@@ -54,6 +54,7 @@
 import org.eclipse.jgit.revwalk.filter.AndRevFilter;
 import org.eclipse.jgit.revwalk.filter.RevFilter;
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.jgit.revwalk.DepthWalk;
 
 /**
  * Initial RevWalk generator that bootstraps a new walk.
@@ -130,14 +131,20 @@ RevCommit next() throws MissingObjectException,
 		}
 
 		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(w, pending, dw.getDepth(), dw.getCompareMode());
+		} 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 df5594c..6603dea 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
@@ -84,6 +84,7 @@
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
+import org.eclipse.jgit.revwalk.DepthWalk;
 import org.eclipse.jgit.revwalk.ObjectWalk;
 import org.eclipse.jgit.revwalk.RevFlag;
 import org.eclipse.jgit.revwalk.RevObject;
@@ -376,7 +377,24 @@ public void preparePack(ProgressMonitor countingMonitor,
 		if (countingMonitor == null)
 			countingMonitor = NullProgressMonitor.INSTANCE;
 		ObjectWalk walker = setUpWalker(interestingObjects,
-				uninterestingObjects);
+				uninterestingObjects, 0);
+		findObjectsToPack(countingMonitor, walker);
+	}
+
+	/**
+	 * @param countingMonitor
+	 * @param interestingObjects
+	 * @param depth
+	 * @throws IOException
+	 */
+	public void preparePack(ProgressMonitor countingMonitor,
+			final Collection<? extends ObjectId> interestingObjects,
+			int depth)
+			throws IOException {
+		if (countingMonitor == null)
+			countingMonitor = NullProgressMonitor.INSTANCE;
+		ObjectWalk walker = setUpWalker(interestingObjects,
+				null, depth);
 		findObjectsToPack(countingMonitor, walker);
 	}
 
@@ -947,10 +965,15 @@ private void writeChecksum(PackOutputStream out) throws IOException {
 
 	private ObjectWalk setUpWalker(
 			final Collection<? extends ObjectId> interestingObjects,
-			final Collection<? extends ObjectId> uninterestingObjects)
+			final Collection<? extends ObjectId> uninterestingObjects,
+			int depth)
 			throws MissingObjectException, IOException,
 			IncorrectObjectTypeException {
-		final ObjectWalk walker = new ObjectWalk(reader);
+		final ObjectWalk walker;
+		if (depth > 0)
+			walker = new DepthWalk.ObjectWalk(reader, depth - 1, DepthWalk.CompareMode.LESS_THAN_EQUAL);
+		else
+			walker = new ObjectWalk(reader);
 		walker.setRetainBody(false);
 		walker.sort(RevSort.COMMIT_TIME_DESC);
 		if (thin)
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 16d56df..d459789 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -63,6 +63,7 @@
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.DepthWalk;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevFlag;
 import org.eclipse.jgit.revwalk.RevFlagSet;
@@ -97,6 +98,8 @@
 
 	static final String OPTION_NO_PROGRESS = BasePackFetchConnection.OPTION_NO_PROGRESS;
 
+	static final String OPTION_SHALLOW = BasePackFetchConnection.OPTION_SHALLOW;
+
 	/** Database we read the objects from. */
 	private final Repository db;
 
@@ -151,6 +154,12 @@
 	/** Objects on both sides, these don't have to be sent. */
 	private final List<RevObject> commonBase = new ArrayList<RevObject>();
 
+	/** Shallow commits that we will be sending. */
+	private final List<RevCommit> localShallowCommits = new ArrayList<RevCommit>();
+
+	/** Shallow commits the remote already has. */
+	private final List<RevCommit> remoteShallowCommits = new ArrayList<RevCommit>();
+
 	/** null if {@link #commonBase} should be examined again. */
 	private Boolean okToGiveUp;
 
@@ -170,6 +179,8 @@
 
 	private MultiAck multiAck = MultiAck.OFF;
 
+	private int depth = 0;
+
 	/**
 	 * Create a new pack upload for an open repository.
 	 *
@@ -347,8 +358,59 @@ else if (options.contains(OPTION_MULTI_ACK))
 		else
 			multiAck = MultiAck.OFF;
 
-		if (negotiate())
+		if (depth != 0) {
+			processShallow();
+		}
+
+		if (negotiate()) {
 			sendPack();
+		}
+	}
+
+	private void processShallow() throws IOException {
+		DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk(db, depth - 1, DepthWalk.CompareMode.EQUAL);
+
+		// Find all the commits which will be shallow
+		for (RevCommit c : wantCommits) {
+			RevCommit commit = depthWalk.parseCommit(c);
+			depthWalk.markStart(commit);
+		}
+
+		RevObject o;
+
+		// Find all commits at the boundary, and mark them as shallow
+		while ((o = depthWalk.next()) != null) {
+			localShallowCommits.add((RevCommit)o);
+
+			boolean found = false;
+			for (RevCommit c : remoteShallowCommits) {
+				if (c.name() == o.name()) {
+					found = true;
+					break;
+				}
+			}
+
+			if (!found) {
+				pckOut.writeString("shallow " + o.name());
+			}
+		}
+
+		// Unshallow any commits which we're expanding on
+		for (RevCommit remote : remoteShallowCommits) {
+			boolean found = false;
+			for(RevCommit local : localShallowCommits) {
+				if(remote.name() == local.name()) {
+					found = true;
+					break;
+				}
+			}
+
+			if (!found) {
+				pckOut.writeString("unshallow " + remote.name());
+			}
+		}
+
+		pckOut.end();
 	}
 
 	/**
@@ -369,8 +431,10 @@ public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException {
 		adv.advertiseCapability(OPTION_SIDE_BAND_64K);
 		adv.advertiseCapability(OPTION_THIN_PACK);
 		adv.advertiseCapability(OPTION_NO_PROGRESS);
+		adv.advertiseCapability(OPTION_SHALLOW);
 		adv.setDerefTags(true);
 		refs = refFilter.filter(db.getAllRefs());
+
 		adv.send(refs);
 		adv.end();
 	}
@@ -389,6 +453,16 @@ private void recvWants() throws IOException {
 
 			if (line == PacketLineIn.END)
 				break;
+
+			if (line.startsWith("deepen ")) {
+				depth = Integer.parseInt(line.substring(7));
+				continue;
+			}
+			if (line.startsWith("shallow ")) {
+				final ObjectId id = ObjectId.fromString(line.substring(8));
+				remoteShallowCommits.add(walk.parseCommit(id));
+				continue;
+			}
 			if (!line.startsWith("want ") || line.length() < 45)
 				throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "want", line));
 
@@ -588,7 +662,11 @@ private void sendPack() throws IOException {
 		try {
 			pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
 			pw.setThin(options.contains(OPTION_THIN_PACK));
-			pw.preparePack(pm, wantAll, commonBase);
+			if(depth > 0) {
+				pw.preparePack(pm, wantAll, depth);
+			} else {
+				pw.preparePack(pm, wantAll, commonBase);
+			}
 			if (options.contains(OPTION_INCLUDE_TAG)) {
 				for (final Ref r : refs.values()) {
 					final RevObject o;
-- 
1.6.0.6


Back to the top