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
/* | |||||
* 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; | |||||
} | |||||
} | |||||
} |
/* | |||||
* 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; | |||||
} | |||||
} | |||||
} |
} | } | ||||
walker.queue = q; | 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) { | if ((g.outputType() & NEEDS_REWRITE) != 0) { |
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.lib.ThreadSafeProgressMonitor; | import org.eclipse.jgit.lib.ThreadSafeProgressMonitor; | ||||
import org.eclipse.jgit.revwalk.AsyncRevObjectQueue; | import org.eclipse.jgit.revwalk.AsyncRevObjectQueue; | ||||
import org.eclipse.jgit.revwalk.DepthWalk; | |||||
import org.eclipse.jgit.revwalk.ObjectWalk; | import org.eclipse.jgit.revwalk.ObjectWalk; | ||||
import org.eclipse.jgit.revwalk.RevCommit; | import org.eclipse.jgit.revwalk.RevCommit; | ||||
import org.eclipse.jgit.revwalk.RevFlag; | import org.eclipse.jgit.revwalk.RevFlag; | ||||
private boolean pruneCurrentObjectList; | private boolean pruneCurrentObjectList; | ||||
private boolean shallowPack; | |||||
private int depth; | |||||
private Collection<? extends ObjectId> unshallowObjects; | |||||
/** | /** | ||||
* Create writer for specified repository. | * Create writer for specified repository. | ||||
* <p> | * <p> | ||||
tagTargets = objects; | 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. | * Returns objects number in a pack file that was created by this writer. | ||||
* | * | ||||
*/ | */ | ||||
@Deprecated | @Deprecated | ||||
public void preparePack(ProgressMonitor countingMonitor, | public void preparePack(ProgressMonitor countingMonitor, | ||||
final ObjectWalk walk, | |||||
ObjectWalk walk, | |||||
final Collection<? extends ObjectId> interestingObjects, | final Collection<? extends ObjectId> interestingObjects, | ||||
final Collection<? extends ObjectId> uninterestingObjects) | final Collection<? extends ObjectId> uninterestingObjects) | ||||
throws IOException { | throws IOException { | ||||
public void preparePack(ProgressMonitor countingMonitor, | public void preparePack(ProgressMonitor countingMonitor, | ||||
Set<? extends ObjectId> want, | Set<? extends ObjectId> want, | ||||
Set<? extends ObjectId> have) throws IOException { | 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); | preparePack(countingMonitor, ow, want, have); | ||||
} | } | ||||
* when some I/O problem occur during reading objects. | * when some I/O problem occur during reading objects. | ||||
*/ | */ | ||||
public void preparePack(ProgressMonitor countingMonitor, | public void preparePack(ProgressMonitor countingMonitor, | ||||
final ObjectWalk walk, | |||||
ObjectWalk walk, | |||||
final Set<? extends ObjectId> interestingObjects, | final Set<? extends ObjectId> interestingObjects, | ||||
final Set<? extends ObjectId> uninterestingObjects) | final Set<? extends ObjectId> uninterestingObjects) | ||||
throws IOException { | throws IOException { | ||||
if (countingMonitor == null) | if (countingMonitor == null) | ||||
countingMonitor = NullProgressMonitor.INSTANCE; | countingMonitor = NullProgressMonitor.INSTANCE; | ||||
if (shallowPack && !(walk instanceof DepthWalk.ObjectWalk)) | |||||
walk = new DepthWalk.ObjectWalk(reader, depth); | |||||
findObjectsToPack(countingMonitor, walk, interestingObjects, | findObjectsToPack(countingMonitor, walk, interestingObjects, | ||||
uninterestingObjects); | uninterestingObjects); | ||||
} | } | ||||
if (tipToPack.containsKey(o)) | if (tipToPack.containsKey(o)) | ||||
o.add(inCachedPack); | o.add(inCachedPack); | ||||
if (have.contains(o)) { | |||||
if (have.contains(o)) | |||||
haveObjs.add(o); | haveObjs.add(o); | ||||
} else if (want.contains(o)) { | |||||
if (want.contains(o)) { | |||||
o.add(include); | o.add(include); | ||||
wantObjs.add(o); | wantObjs.add(o); | ||||
if (o instanceof RevTag) | if (o instanceof RevTag) | ||||
} | } | ||||
} | } | ||||
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) | for (RevObject obj : haveObjs) | ||||
walker.markUninteresting(obj); | walker.markUninteresting(obj); | ||||
countingMonitor.update(1); | 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); | 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++; | 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; | commits = null; |
import org.eclipse.jgit.JGitText; | import org.eclipse.jgit.JGitText; | ||||
import org.eclipse.jgit.errors.CorruptObjectException; | import org.eclipse.jgit.errors.CorruptObjectException; | ||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException; | |||||
import org.eclipse.jgit.errors.MissingObjectException; | import org.eclipse.jgit.errors.MissingObjectException; | ||||
import org.eclipse.jgit.errors.PackProtocolException; | import org.eclipse.jgit.errors.PackProtocolException; | ||||
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.Ref; | import org.eclipse.jgit.lib.Ref; | ||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.revwalk.AsyncRevObjectQueue; | import org.eclipse.jgit.revwalk.AsyncRevObjectQueue; | ||||
import org.eclipse.jgit.revwalk.DepthWalk; | |||||
import org.eclipse.jgit.revwalk.ObjectWalk; | import org.eclipse.jgit.revwalk.ObjectWalk; | ||||
import org.eclipse.jgit.revwalk.RevCommit; | import org.eclipse.jgit.revwalk.RevCommit; | ||||
import org.eclipse.jgit.revwalk.RevFlag; | import org.eclipse.jgit.revwalk.RevFlag; | ||||
static final String OPTION_NO_DONE = BasePackFetchConnection.OPTION_NO_DONE; | static final String OPTION_NO_DONE = BasePackFetchConnection.OPTION_NO_DONE; | ||||
static final String OPTION_SHALLOW = BasePackFetchConnection.OPTION_SHALLOW; | |||||
/** Database we read the objects from. */ | /** Database we read the objects from. */ | ||||
private final Repository db; | private final Repository db; | ||||
/** Objects on both sides, these don't have to be sent. */ | /** Objects on both sides, these don't have to be sent. */ | ||||
private final Set<RevObject> commonBase = new HashSet<RevObject>(); | 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. */ | /** Commit time of the oldest common commit, in seconds. */ | ||||
private int oldestTime; | private int oldestTime; | ||||
else | else | ||||
multiAck = MultiAck.OFF; | multiAck = MultiAck.OFF; | ||||
if (depth != 0) | |||||
processShallow(); | |||||
sendPack = negotiate(); | sendPack = negotiate(); | ||||
} catch (PackProtocolException err) { | } catch (PackProtocolException err) { | ||||
reportErrorDuringNegotiate(err.getMessage()); | reportErrorDuringNegotiate(err.getMessage()); | ||||
} | } | ||||
} | } | ||||
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. | * Generate an advertisement of available refs and capabilities. | ||||
* | * | ||||
adv.advertiseCapability(OPTION_SIDE_BAND_64K); | adv.advertiseCapability(OPTION_SIDE_BAND_64K); | ||||
adv.advertiseCapability(OPTION_THIN_PACK); | adv.advertiseCapability(OPTION_THIN_PACK); | ||||
adv.advertiseCapability(OPTION_NO_PROGRESS); | adv.advertiseCapability(OPTION_NO_PROGRESS); | ||||
adv.advertiseCapability(OPTION_SHALLOW); | |||||
if (!biDirectionalPipe) | if (!biDirectionalPipe) | ||||
adv.advertiseCapability(OPTION_NO_DONE); | adv.advertiseCapability(OPTION_NO_DONE); | ||||
adv.setDerefTags(true); | adv.setDerefTags(true); | ||||
if (line == PacketLineIn.END) | if (line == PacketLineIn.END) | ||||
break; | 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) | if (!line.startsWith("want ") || line.length() < 45) | ||||
throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "want", line)); | throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "want", line)); | ||||
try { | try { | ||||
line = pckIn.readString(); | line = pckIn.readString(); | ||||
} catch (EOFException eof) { | } 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; | throw eof; | ||||
} | } | ||||
pw.setTagTargets(tagTargets); | pw.setTagTargets(tagTargets); | ||||
} | } | ||||
if (depth > 0) | |||||
pw.setShallowPack(depth, unshallowCommits); | |||||
RevWalk rw = walk; | RevWalk rw = walk; | ||||
if (wantAll.isEmpty()) { | if (wantAll.isEmpty()) { | ||||
pw.preparePack(pm, wantIds, commonBase); | pw.preparePack(pm, wantIds, commonBase); |