UploadPack: Use more relevant refs first in object reachability check

The bitmap-bassed object reachability checker, tries to find the objects
in the first starter, then adding the second starter... and so on. This
rewards passing the most popular refs first.

Order the refs with heads first, then tags, then others (e.g. changes)
for the object reachability checker. Using streams, delay also the
resolution of the ref to RevObject until necessary.

Change-Id: I9414b76754d7c0ffee1e2eeed6939895c8e92cbe
Signed-off-by: Ivan Frade <ifrade@google.com>
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 cf504d2..9889015 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -1903,7 +1903,6 @@
 
 		try (RevWalk walk = new RevWalk(reader)) {
 			walk.setRetainBody(false);
-			Set<ObjectId> reachableFrom = refIdSet(visibleRefs);
 			// Missing "wants" throw exception here
 			List<RevObject> wantsAsObjs = objectIdsToRevObjects(walk,
 					notAdvertisedWants);
@@ -1930,12 +1929,15 @@
 				}
 
 				try (ObjectWalk objWalk = walk.toObjectWalkWithSameObjects()) {
-					List<RevObject> havesAsObjs = objectIdsToRevObjects(objWalk,
-							reachableFrom);
+					Stream<RevObject> startersAsObjs = importantRefsFirst(visibleRefs)
+							.map(UploadPack::refToObjectId)
+							.map(objId -> objectIdToRevObject(objWalk, objId))
+							.filter(Objects::nonNull); // Ignore missing tips
+
 					ObjectReachabilityChecker reachabilityChecker = objWalk
 							.createObjectReachabilityChecker();
 					Optional<RevObject> unreachable = reachabilityChecker
-							.areAllReachable(wantsAsObjs, havesAsObjs.stream());
+							.areAllReachable(wantsAsObjs, startersAsObjs);
 					if (unreachable.isPresent()) {
 						throw new WantNotValidException(unreachable.get());
 					}
@@ -2007,6 +2009,29 @@
 		}
 	}
 
+	/**
+	 * Translate an object id to a RevObject.
+	 *
+	 * @param walk
+	 *            walk on the relevant object storage
+	 * @param objectId
+	 *            Object Id
+	 * @return RevObject instance or null if the object is missing
+	 */
+	@Nullable
+	private static RevObject objectIdToRevObject(RevWalk walk,
+			ObjectId objectId) {
+		if (objectId == null) {
+			return null;
+		}
+
+		try {
+			return walk.parseAny(objectId);
+		} catch (IOException e) {
+			return null;
+		}
+	}
+
 	// Resolve the ObjectIds into RevObjects. Any missing object raises an
 	// exception
 	private static List<RevObject> objectIdsToRevObjects(RevWalk walk,