Commit Diff


commit - 3e6e13b22e6744ac9ba40feadac972d441b7e6c5
commit + 28a5e8a2773338dd831d61aeafa5d279417be5b6
blob - 325788b21bd4d9bdeadb462054ea0181f3832eb0
blob + d980f9e5fcf16e77de392818150df0d36e6e87ce
--- libexec/got-read-pack/got-read-pack.c
+++ libexec/got-read-pack/got-read-pack.c
@@ -1385,8 +1385,55 @@ done:
 
 	return err;
 }
+
 
 static const struct got_error *
+resolve_tag(struct got_object **obj, struct got_object_id *id,
+    struct got_packidx *packidx, struct got_pack *pack,
+    struct got_object_cache *objcache)
+{
+	const struct got_error *err;
+	struct got_object *tagged_obj;
+	struct got_tag_object *tag;
+	uint8_t *buf;
+	size_t len;
+	int idx;
+
+	err = got_packfile_extract_object_to_mem(&buf, &len, *obj, pack);
+	if (err)
+		return err;
+
+	(*obj)->size = len;
+	err = got_object_parse_tag(&tag, buf, len, id->algo);
+	if (err)
+		goto done;
+
+	idx = got_packidx_get_object_idx(packidx, &tag->id);
+	if (idx == -1) {
+		got_object_close(*obj);
+		*obj = NULL;
+		return NULL;
+	}
+
+	tagged_obj = got_object_cache_get(objcache, &tag->id);
+	if (tagged_obj) {
+		tagged_obj->refcnt++;
+	} else {
+		err = open_object(&tagged_obj, pack, packidx,
+		    idx, &tag->id, objcache);
+		if (err)
+			goto done;
+	}
+
+	got_object_close(*obj);
+	*obj = tagged_obj;
+done:
+	got_object_tag_close(tag);
+	free(buf);
+	return err;
+}
+
+static const struct got_error *
 enumeration_request(struct imsg *imsg, struct imsgbuf *ibuf,
     struct got_pack *pack, struct got_packidx *packidx,
     struct got_object_cache *objcache)
@@ -1461,29 +1508,25 @@ enumeration_request(struct imsg *imsg, struct imsgbuf 
 		if (err)
 			goto done;
 		if (obj->type == GOT_OBJ_TYPE_TAG) {
-			struct got_tag_object *tag;
-			uint8_t *buf;
-			size_t len;
-			err = got_packfile_extract_object_to_mem(&buf,
-			    &len, obj, pack);
-			if (err)
-				goto done;
-			obj->size = len;
-			err = got_object_parse_tag(&tag, buf, len,
-			    qid->id.algo);
-			if (err) {
-				free(buf);
-				goto done;
+			while (obj->type == GOT_OBJ_TYPE_TAG) {
+				err = resolve_tag(&obj, &qid->id, packidx,
+				    pack, objcache);
+				if (err)
+					goto done;
+				if (obj == NULL)
+					break;
 			}
-			idx = got_packidx_get_object_idx(packidx, &tag->id);
-			if (idx == -1) {
+			if (obj == NULL) {
 				have_all_entries = 0;
 				break;
 			}
+			if (obj->type != GOT_OBJ_TYPE_COMMIT) {
+				got_object_qid_free(qid);
+				qid = NULL;
+				continue;
+			}
 			err = open_commit(&commit, pack, packidx, idx,
-			    &tag->id, objcache);
-			got_object_tag_close(tag);
-			free(buf);
+			    &obj->id, objcache);
 			if (err)
 				goto done;
 		} else if (obj->type == GOT_OBJ_TYPE_COMMIT) {
blob - f2acf22971de6d69864669240411b42ba43f58bc
blob + 6b8fdb74f13c5157983c8ba15522d812f4865a83
--- regress/cmdline/pack.sh
+++ regress/cmdline/pack.sh
@@ -674,7 +674,43 @@ test_pack_bad_ref() {
 	fi
 	test_done "$testroot" "$ret"
 }
+
+test_pack_tagged_tag() {
+	local testroot=`test_init pack_tagged_tag`
+
+	got tag -r $testroot/repo -m 1.0 1.0 >/dev/null
+
+	git -C $testroot/repo tag -a -m "tagging a tag" 1.0-tag 1.0 \
+		2>$testroot/stderr
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		echo -n "git tag failed unexpectedly:" >&2
+		cat $testroot/stderr >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
 
+	gotadmin pack -r $testroot/repo -a > $testroot/stdout
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		echo "gotadmin pack failed unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	# try again, triggering the pack enumeration logic in got-read-pack
+	# such that it runs into a tag of a tag
+	gotadmin pack -a -r $testroot/repo -x 1.0-tag > $testroot/stdout
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		echo "gotadmin pack failed unexpectedly" >&2
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	test_done "$testroot" "$ret"
+}
+
 test_parseargs "$@"
 run_test test_pack_all_loose_objects
 run_test test_pack_exclude
@@ -684,3 +720,4 @@ run_test test_pack_ambiguous_arg
 run_test test_pack_loose_only
 run_test test_pack_all_objects
 run_test test_pack_bad_ref
+run_test test_pack_tagged_tag