Browse Source

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>
tags/v1.1.0.201109011030-rc2
Matt Fischer 13 years ago
parent
commit
9952223e06

+ 176
- 0
org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java View File

@@ -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;
}
}
}

+ 243
- 0
org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java View File

@@ -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;
}
}
}

+ 13
- 7
org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java View File

@@ -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) {

+ 75
- 30
org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java View File

@@ -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>
@@ -407,6 +414,22 @@ public class PackWriter {
tagTargets = objects;
}

/**
* 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.
*
@@ -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;

+ 70
- 0
org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java View File

@@ -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);

Loading…
Cancel
Save