[RFC 10/14] fetch-pack: support partial names and globs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Teach fetch-pack to support partial ref names and ref patterns as input.

This does not use "want-ref" yet - support for that will be added in a
future patch.

Signed-off-by: Jonathan Tan <jonathantanmy@xxxxxxxxxx>
---
 builtin/fetch-pack.c  | 40 ++++++++++++-------------------------
 remote.c              | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++
 remote.h              | 16 +++++++++++++++
 t/t5500-fetch-pack.sh | 38 +++++++++++++++++++++++++++++++++++
 4 files changed, 122 insertions(+), 27 deletions(-)

diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index a18fd0c44..5f14242ae 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -11,32 +11,12 @@ static const char fetch_pack_usage[] =
 "[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
 "[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]";
 
-static void add_sought_entry(const struct ref ***sought, int *nr, int *alloc,
+static void add_sought_entry(struct refspec **sought, int *nr, int *alloc,
 			     const char *name)
 {
-	struct ref *ref;
-	struct object_id oid;
-
-	if (!get_oid_hex(name, &oid)) {
-		if (name[GIT_SHA1_HEXSZ] == ' ') {
-			/* <sha1> <ref>, find refname */
-			name += GIT_SHA1_HEXSZ + 1;
-		} else if (name[GIT_SHA1_HEXSZ] == '\0') {
-			; /* <sha1>, leave sha1 as name */
-		} else {
-			/* <ref>, clear cruft from oid */
-			oidclr(&oid);
-		}
-	} else {
-		/* <ref>, clear cruft from get_oid_hex */
-		oidclr(&oid);
-	}
-
-	ref = alloc_ref(name);
-	oidcpy(&ref->old_oid, &oid);
 	(*nr)++;
 	ALLOC_GROW(*sought, *nr, *alloc);
-	(*sought)[*nr - 1] = ref;
+	parse_ref_or_pattern(&(*sought)[*nr - 1], name);
 }
 
 int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
@@ -44,8 +24,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 	int i, ret;
 	struct ref *ref = NULL;
 	const char *dest = NULL;
-	const struct ref **sought = NULL;
+	struct refspec *sought = NULL;
 	int nr_sought = 0, alloc_sought = 0;
+	const struct ref **sought_refs;
+	int nr_sought_refs;
 	int fd[2];
 	char *pack_lockfile = NULL;
 	char **pack_lockfile_ptr = NULL;
@@ -195,8 +177,9 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 			return args.diag_url ? 0 : 1;
 	}
 	get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL, &shallow);
+	get_ref_array(&sought_refs, &nr_sought_refs, ref, sought, nr_sought);
 
-	ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought,
+	ref = fetch_pack(&args, fd, conn, ref, dest, sought_refs, nr_sought_refs,
 			 &shallow, pack_lockfile_ptr);
 	if (pack_lockfile) {
 		printf("lock %s\n", pack_lockfile);
@@ -222,12 +205,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 	 */
 	for (i = 0; i < nr_sought; i++) {
 		struct ref *r;
-		for (r = ref; r; r = r->next)
-			if (!sought[i] || refname_match(sought[i]->name, r->name))
+		if (sought[i].pattern)
+			continue; /* patterns do not need to match anything */
+		for (r = ref; r; r = r->next) {
+			if (refname_match(sought[i].src, r->name))
 				break;
+		}
 		if (r)
 			continue;
-		error("no such remote ref %s", sought[i]->name);
+		error("no such remote ref %s", sought[i].src);
 		ret = 1;
 	}
 
diff --git a/remote.c b/remote.c
index 725a2d39a..08f3c910e 100644
--- a/remote.c
+++ b/remote.c
@@ -612,6 +612,39 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
 	die("Invalid refspec '%s'", refspec[i]);
 }
 
+void parse_ref_or_pattern(struct refspec *refspec, const char *str)
+{
+	struct object_id oid;
+	memset(refspec, 0, sizeof(*refspec));
+
+	if (!get_oid_hex(str, &oid)) {
+		if (str[GIT_SHA1_HEXSZ] == ' ') {
+			struct object_id oid2;
+			/* <sha1> <ref>, find refname */
+			refspec->src = xstrdup(str + GIT_SHA1_HEXSZ + 1);
+			if (!get_oid_hex(refspec->src, &oid2)
+			    && !oidcmp(&oid, &oid2))
+				/* The name is actually a SHA-1 */
+				refspec->exact_sha1 = 1;
+		} else if (str[GIT_SHA1_HEXSZ] == '\0') {
+			; /* <sha1>, leave sha1 as name */
+			refspec->src = xstrdup(str);
+			refspec->exact_sha1 = 1;
+		} else {
+			/* <ref> */
+			refspec->src = xstrdup(str);
+		}
+	} else {
+		/* <ref> */
+		refspec->src = xstrdup(str);
+	}
+
+	if (has_glob_specials(refspec->src)) {
+		refspec->pattern = 1;
+		refspec->dst = refspec->src;
+	}
+}
+
 int valid_fetch_refspec(const char *fetch_refspec_str)
 {
 	struct refspec *refspec;
@@ -1924,6 +1957,28 @@ int get_fetch_map(const struct ref *remote_refs,
 	return 0;
 }
 
+void get_ref_array(const struct ref ***refs, int *nr_ref,
+		   const struct ref *remote_refs,
+		   const struct refspec *refspecs, int nr_refspecs)
+{
+	struct ref *head = NULL, **tail = &head;
+	const struct ref **array = NULL;
+	int nr = 0, alloc = 0;
+
+	struct ref *r;
+	int i;
+
+	for (i = 0; i < nr_refspecs; i++)
+		get_fetch_map(remote_refs, &refspecs[i], &tail, 1);
+	for (r = head; r; r = r->next) {
+		nr++;
+		ALLOC_GROW(array, nr, alloc);
+		array[nr - 1] = r;
+	}
+	*refs = array;
+	*nr_ref = nr;
+}
+
 int resolve_remote_symref(struct ref *ref, struct ref *list)
 {
 	if (!ref->symref)
diff --git a/remote.h b/remote.h
index 2f7f23d47..daca1c65e 100644
--- a/remote.h
+++ b/remote.h
@@ -162,6 +162,13 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);
  */
 struct ref *ref_remove_duplicates(struct ref *ref_map);
 
+/*
+ * Parse the given ref or ref pattern. If a ref, write a refspec with that ref
+ * as src, and with an empty dst. If a ref pattern, write a glob refspec with
+ * that pattern as src and dst.
+ */
+void parse_ref_or_pattern(struct refspec *refspec, const char *str);
+
 int valid_fetch_refspec(const char *refspec);
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
 
@@ -192,6 +199,15 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
 int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec,
 		  struct ref ***tail, int missing_ok);
 
+/*
+ * Convenience function to generate an array of refs corresponding to the given
+ * refspecs. This is equivalent to repeatedly calling get_fetch_map and
+ * rearranging the returned refs as an array.
+ */
+void get_ref_array(const struct ref ***refs, int *nr_ref,
+		   const struct ref *remote_refs,
+		   const struct refspec *refspecs, int nr_refspecs);
+
 struct ref *get_remote_ref(const struct ref *remote_refs, const char *name);
 
 /*
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 505e1b4a7..cb1b7d949 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -547,6 +547,44 @@ test_expect_success 'fetch-pack can fetch a raw sha1' '
 	git fetch-pack hidden $(git -C hidden rev-parse refs/hidden/one)
 '
 
+test_expect_success 'fetch-pack can fetch refs using a partial name' '
+	git init server &&
+	(
+		cd server &&
+		test_commit 1 &&
+		test_commit 2 &&
+		git checkout -b one
+	) &&
+	rm -f trace &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git fetch-pack server one >actual &&
+	grep " want " trace &&
+	! grep " want-ref " trace &&
+
+	grep "$(printf "%s refs/heads/one" $(git -C server rev-parse --verify one))" actual
+'
+
+test_expect_success 'fetch-pack can fetch refs using a glob' '
+	rm -rf server &&
+	git init server &&
+	(
+		cd server &&
+		test_commit 1 &&
+		test_commit 2 &&
+		git checkout -b ona &&
+		git checkout -b onb &&
+		test_commit 3 &&
+		git checkout -b onc
+	) &&
+	rm -f trace &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git fetch-pack server "refs/heads/on*" >actual &&
+	grep " want " trace &&
+	! grep " want-ref " trace &&
+
+	grep "$(printf "%s refs/heads/ona" $(git -C server rev-parse --verify ona))" actual &&
+	grep "$(printf "%s refs/heads/onb" $(git -C server rev-parse --verify onb))" actual &&
+	grep "$(printf "%s refs/heads/onc" $(git -C server rev-parse --verify onc))" actual
+'
+
 check_prot_path () {
 	cat >expected <<-EOF &&
 	Diag: url=$1
-- 
2.11.0.483.g087da7b7c-goog




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]