diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
index 68fee612f..42851498b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
@@ -436,6 +436,19 @@ public abstract class ObjectReader {
// Do nothing by default, most readers don't want or need advice.
}
+ /**
+ * Advise the reader to avoid unreachable objects.
+ *
+ * While enabled the reader will skip over anything previously proven to be
+ * unreachable. This may be dangerous in the face of concurrent writes.
+ *
+ * @param avoid
+ * true to avoid unreachable objects.
+ */
+ public void setAvoidUnreachableObjects(boolean avoid) {
+ // Do nothing by default.
+ }
+
/**
* An index that can be used to speed up ObjectWalks.
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackFile.java
index 80cced84e..707488b03 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackFile.java
@@ -45,9 +45,10 @@
package org.eclipse.jgit.storage.dfs;
+import static org.eclipse.jgit.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
import static org.eclipse.jgit.storage.pack.PackExt.BITMAP_INDEX;
-import static org.eclipse.jgit.storage.pack.PackExt.PACK;
import static org.eclipse.jgit.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.storage.pack.PackExt.PACK;
import java.io.BufferedInputStream;
import java.io.EOFException;
@@ -276,8 +277,12 @@ public final class DfsPackFile {
}
}
+ final boolean isGarbage() {
+ return packDesc.getPackSource() == UNREACHABLE_GARBAGE;
+ }
+
PackBitmapIndex getBitmapIndex(DfsReader ctx) throws IOException {
- if (invalid)
+ if (invalid || isGarbage())
return null;
DfsBlockCache.Ref idxref = bitmapIndex;
if (idxref != null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java
index 7d5fb5b2a..401c483d4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java
@@ -118,6 +118,8 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
private boolean wantReadAhead;
+ private boolean avoidUnreachable;
+
private List pendingReadAhead;
DfsReader(DfsObjDatabase db) {
@@ -143,6 +145,11 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
return new DfsReader(db);
}
+ @Override
+ public void setAvoidUnreachableObjects(boolean avoid) {
+ avoidUnreachable = avoid;
+ }
+
@Override
public BitmapIndex getBitmapIndex() throws IOException {
for (DfsPackFile pack : db.getPacks()) {
@@ -169,8 +176,11 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
throws IOException {
if (id.isComplete())
return Collections.singleton(id.toObjectId());
+ boolean noGarbage = avoidUnreachable;
HashSet matches = new HashSet(4);
for (DfsPackFile pack : db.getPacks()) {
+ if (noGarbage && pack.isGarbage())
+ continue;
pack.resolve(this, matches, id, 256);
if (256 <= matches.size())
break;
@@ -182,8 +192,9 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
public boolean has(AnyObjectId objectId) throws IOException {
if (last != null && last.hasObject(this, objectId))
return true;
+ boolean noGarbage = avoidUnreachable;
for (DfsPackFile pack : db.getPacks()) {
- if (last == pack)
+ if (pack == last || (noGarbage && pack.isGarbage()))
continue;
if (pack.hasObject(this, objectId)) {
last = pack;
@@ -203,8 +214,9 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
return ldr;
}
+ boolean noGarbage = avoidUnreachable;
for (DfsPackFile pack : db.getPacks()) {
- if (pack == last)
+ if (pack == last || (noGarbage && pack.isGarbage()))
continue;
ObjectLoader ldr = pack.get(this, objectId);
if (ldr != null) {
@@ -265,6 +277,7 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
int lastIdx = 0;
DfsPackFile lastPack = packList[lastIdx];
+ boolean noGarbage = avoidUnreachable;
OBJECT_SCAN: for (T t : objectIds) {
try {
@@ -281,6 +294,8 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
if (i == lastIdx)
continue;
DfsPackFile pack = packList[i];
+ if (noGarbage && pack.isGarbage())
+ continue;
try {
long p = pack.findOffset(this, t);
if (0 < p) {
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 bc4045416..12f688658 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -787,74 +787,24 @@ public class UploadPack {
preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
if (peerHas.isEmpty())
return last;
+ if (wantAll.isEmpty() && !wantIds.isEmpty())
+ parseWants();
- List toParse = peerHas;
- HashSet peerHasSet = null;
- boolean needMissing = false;
sentReady = false;
-
- if (wantAll.isEmpty() && !wantIds.isEmpty()) {
- // We have not yet parsed the want list. Parse it now.
- peerHasSet = new HashSet(peerHas);
- int cnt = wantIds.size() + peerHasSet.size();
- toParse = new ArrayList(cnt);
- toParse.addAll(wantIds);
- toParse.addAll(peerHasSet);
- needMissing = true;
- }
-
- Set notAdvertisedWants = null;
int haveCnt = 0;
- AsyncRevObjectQueue q = walk.parseAny(toParse, needMissing);
+ walk.getObjectReader().setAvoidUnreachableObjects(true);
+ AsyncRevObjectQueue q = walk.parseAny(peerHas, false);
try {
for (;;) {
RevObject obj;
try {
obj = q.next();
} catch (MissingObjectException notFound) {
- ObjectId id = notFound.getObjectId();
- if (wantIds.contains(id)) {
- String msg = MessageFormat.format(
- JGitText.get().wantNotValid, id.name());
- throw new PackProtocolException(msg, notFound);
- }
continue;
}
if (obj == null)
break;
- // If the object is still found in wantIds, the want
- // list wasn't parsed earlier, and was done in this batch.
- //
- if (wantIds.remove(obj)) {
- if (!advertised.contains(obj) && requestPolicy != RequestPolicy.ANY) {
- if (notAdvertisedWants == null)
- notAdvertisedWants = new HashSet();
- notAdvertisedWants.add(obj);
- }
-
- if (!obj.has(WANT)) {
- obj.add(WANT);
- wantAll.add(obj);
- }
-
- if (!(obj instanceof RevCommit))
- obj.add(SATISFIED);
-
- if (obj instanceof RevTag) {
- RevObject target = walk.peel(obj);
- if (target instanceof RevCommit) {
- if (!target.has(WANT)) {
- target.add(WANT);
- wantAll.add(target);
- }
- }
- }
-
- if (!peerHasSet.contains(obj))
- continue;
- }
-
last = obj;
haveCnt++;
@@ -889,25 +839,7 @@ public class UploadPack {
}
} finally {
q.release();
- }
-
- // If the client asked for non advertised object, check our policy.
- if (notAdvertisedWants != null && !notAdvertisedWants.isEmpty()) {
- switch (requestPolicy) {
- case ADVERTISED:
- default:
- throw new PackProtocolException(MessageFormat.format(
- JGitText.get().wantNotValid,
- notAdvertisedWants.iterator().next().name()));
-
- case REACHABLE_COMMIT:
- checkNotAdvertisedWants(notAdvertisedWants);
- break;
-
- case ANY:
- // Allow whatever was asked for.
- break;
- }
+ walk.getObjectReader().setAvoidUnreachableObjects(false);
}
int missCnt = peerHas.size() - haveCnt;
@@ -952,7 +884,61 @@ public class UploadPack {
return last;
}
- private void checkNotAdvertisedWants(Set notAdvertisedWants)
+ private void parseWants() throws IOException {
+ AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
+ try {
+ List checkReachable = null;
+ RevObject obj;
+ while ((obj = q.next()) != null) {
+ if (!advertised.contains(obj)) {
+ switch (requestPolicy) {
+ case ADVERTISED:
+ default:
+ throw new PackProtocolException(MessageFormat.format(
+ JGitText.get().wantNotValid, obj));
+ case REACHABLE_COMMIT:
+ if (!(obj instanceof RevCommit)) {
+ throw new PackProtocolException(MessageFormat.format(
+ JGitText.get().wantNotValid, obj));
+ }
+ if (checkReachable == null)
+ checkReachable = new ArrayList();
+ checkReachable.add((RevCommit) obj);
+ break;
+ case ANY:
+ break;
+ }
+ }
+ want(obj);
+
+ if (!(obj instanceof RevCommit))
+ obj.add(SATISFIED);
+ if (obj instanceof RevTag) {
+ obj = walk.peel(obj);
+ if (obj instanceof RevCommit)
+ want(obj);
+ }
+ }
+ if (checkReachable != null)
+ checkNotAdvertisedWants(checkReachable);
+ wantIds.clear();
+ } catch (MissingObjectException notFound) {
+ ObjectId id = notFound.getObjectId();
+ throw new PackProtocolException(MessageFormat.format(
+ JGitText.get().wantNotValid, id.name()), notFound);
+ } finally {
+ q.release();
+ }
+ }
+
+ private void want(RevObject obj) {
+ if (!obj.has(WANT)) {
+ obj.add(WANT);
+ wantAll.add(obj);
+ }
+ }
+
+ private void checkNotAdvertisedWants(List notAdvertisedWants)
throws MissingObjectException, IncorrectObjectTypeException, IOException {
// Walk the requested commits back to the advertised commits.
// If any commit exists, a branch was deleted or rewound and
@@ -960,15 +946,8 @@ public class UploadPack {
// If the requested commit is merged into an advertised branch
// it will be marked UNINTERESTING and no commits return.
- for (RevObject o : notAdvertisedWants) {
- if (!(o instanceof RevCommit)) {
- throw new PackProtocolException(MessageFormat.format(
- JGitText.get().wantNotValid,
- notAdvertisedWants.iterator().next().name()));
- }
- walk.markStart((RevCommit) o);
- }
-
+ for (RevCommit c : notAdvertisedWants)
+ walk.markStart(c);
for (ObjectId id : advertised) {
try {
walk.markUninteresting(walk.parseCommit(id));