Subject: [PATCH v2 24/25] upload-pack: make check_reachable_object() return unreachable list if asked

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Thu, 4 Feb 2016 16:04:00 +0700

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 object.h      |  2 +-
 upload-pack.c | 51 ++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 45 insertions(+), 8 deletions(-)

diff --git a/object.h b/object.h
index f8b6442..614a006 100644
--- a/object.h
+++ b/object.h
@@ -31,7 +31,7 @@ struct object_array {
  * revision.h:      0---------10                                26
  * fetch-pack.c:    0---4
  * walker.c:        0-2
- * upload-pack.c:               11----------------19
+ * upload-pack.c:       4       11----------------19
  * builtin/blame.c:               12-13
  * bisect.c:                               16
  * bundle.c:                               16
diff --git a/upload-pack.c b/upload-pack.c
index a72ffc2..1270aa3 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -451,8 +451,16 @@ static int is_our_ref(struct object *o)
 			(ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
 	return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
 }
-
-static int check_unreachable(struct object_array *src)
+/*
+ * If reachable is NULL, return 1 if there is no unreachable object,
+ * zero otherwise.
+ *
+ * If reachable is not NULL, it's filled with reachable objects.
+ * Return value is irrelevant. The caller has to compare reachable and
+ * src to find out if there's any unreachable object.
+ */
+static int check_unreachable(struct object_array *reachable,
+			     struct object_array *src)
 {
 	static const char *argv[] = {
 		"rev-list", "--stdin", NULL,
@@ -484,6 +492,8 @@ static int check_unreachable(struct object_array *src)
 		o = get_indexed_object(--i);
 		if (!o)
 			continue;
+		if (reachable && o->type == OBJ_COMMIT)
+			o->flags &= ~TMP_MARK;
 		if (!is_our_ref(o))
 			continue;
 		memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
@@ -493,8 +503,13 @@ static int check_unreachable(struct object_array *src)
 	namebuf[40] = '\n';
 	for (i = 0; i < src->nr; i++) {
 		o = src->objects[i].item;
-		if (is_our_ref(o))
+		if (is_our_ref(o)) {
+			if (reachable)
+				add_object_array(o, NULL, reachable);
 			continue;
+		}
+		if (reachable && o->type == OBJ_COMMIT)
+			o->flags |= TMP_MARK;
 		memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
 		if (write_in_full(cmd.in, namebuf, 41) < 0)
 			return 0;
@@ -507,9 +522,31 @@ static int check_unreachable(struct object_array *src)
 	 * The commits out of the rev-list are not ancestors of
 	 * our ref.
 	 */
-	i = read_in_full(cmd.out, namebuf, 1);
-	if (i)
-		return 0;
+	if (!reachable) {
+		i = read_in_full(cmd.out, namebuf, 1);
+		if (i)
+			return 0;
+	} else {
+		while ((i = read_in_full(cmd.out, namebuf, 41)) == 41) {
+			struct object_id sha1;
+
+			if (namebuf[40] != '\n' || get_oid_hex(namebuf, &sha1))
+				break;
+
+			o = lookup_object(sha1.hash);
+			if (o && o->type == OBJ_COMMIT) {
+				o->flags &= ~TMP_MARK;
+			}
+		}
+		for (i = get_max_object_index(); 0 < i; ) {
+			o = get_indexed_object(--i);
+			if (o && o->type == OBJ_COMMIT &&
+			    (o->flags & TMP_MARK)) {
+				add_object_array(o, NULL, reachable);
+				o->flags &= ~TMP_MARK;
+			}
+		}
+	}
 	close(cmd.out);
 
 	/*
@@ -535,7 +572,7 @@ static void check_non_tip(void)
 	 */
 	if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))
 		;		/* error */
-	else if (check_unreachable(&want_obj))
+	else if (check_unreachable(NULL, &want_obj))
 		return;
 
 	/* Pick one of them (we know there at least is one) */
-- 
2.7.0.377.g4cd97dd