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