Teach fetch to send refspecs to the underlying transport, and teach all
components used by the HTTP transport (remote-curl, transport-helper,
fetch-pack) to understand and propagate the names and SHA-1s of the refs
fetched.

Signed-off-by: Jonathan Tan <jonathanta...@google.com>
---
 builtin/clone.c                    |   4 +-
 builtin/fetch-pack.c               |   8 ++-
 builtin/fetch.c                    | 100 ++++++++++++++++++++++++++++++-------
 remote-curl.c                      |  91 ++++++++++++++++++++-------------
 t/t5552-upload-pack-ref-in-want.sh |   4 +-
 transport-helper.c                 |  74 +++++++++++++++++++++------
 transport.c                        |  10 ++--
 transport.h                        |  20 +++++---
 8 files changed, 229 insertions(+), 82 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 3191da391..765a3a3b6 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -1078,7 +1078,7 @@ int cmd_clone(int argc, const char **argv, const char 
*prefix)
                        }
 
                if (!is_local && !complete_refs_before_fetch) {
-                       transport_fetch_refs(transport, mapped_refs,
+                       transport_fetch_refs(transport, NULL, 0, mapped_refs,
                                             &new_remote_refs);
                        if (new_remote_refs) {
                                refs = new_remote_refs;
@@ -1124,7 +1124,7 @@ int cmd_clone(int argc, const char **argv, const char 
*prefix)
        if (is_local)
                clone_local(path, git_dir);
        else if (refs && complete_refs_before_fetch)
-               transport_fetch_refs(transport, mapped_refs, NULL);
+               transport_fetch_refs(transport, NULL, 0, mapped_refs, NULL);
 
        update_remote_refs(refs, mapped_refs, remote_head_points_at,
                           branch_top.buf, reflog_msg.buf, transport, 
!is_local);
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 24af3b7c5..ed1608c12 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -35,6 +35,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char 
*prefix)
        struct fetch_pack_args args;
        struct sha1_array shallow = SHA1_ARRAY_INIT;
        struct string_list deepen_not = STRING_LIST_INIT_DUP;
+       int always_print_refs = 0;
 
        packet_trace_identity("fetch-pack");
 
@@ -126,6 +127,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char 
*prefix)
                        args.update_shallow = 1;
                        continue;
                }
+               if (!strcmp("--always-print-refs", arg)) {
+                       always_print_refs = 1;
+                       continue;
+               }
                usage(fetch_pack_usage);
        }
        if (deepen_not.nr)
@@ -218,7 +223,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char 
*prefix)
                ret = 1;
        }
 
-       if (args.stateless_rpc)
+       if (args.stateless_rpc && !always_print_refs)
                goto cleanup;
 
        while (ref) {
@@ -226,6 +231,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char 
*prefix)
                       oid_to_hex(&ref->old_oid), ref->name);
                ref = ref->next;
        }
+       fflush(stdout);
 
 cleanup:
        close(fd[0]);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 19e3f40a0..87de00e49 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -302,10 +302,75 @@ static void find_non_local_tags(const struct ref *refs,
        string_list_clear(&remote_refs, 0);
 }
 
+static void get_effective_refspecs(struct refspec **e_rs, int *e_rs_nr,
+                                  const struct remote *remote,
+                                  const struct refspec *cli_rs, int cli_rs_nr,
+                                  int tags, int *autotags)
+{
+       static struct refspec head_refspec;
+
+       const struct refspec *base_rs;
+       int base_rs_nr;
+       struct branch *merge_branch = NULL;
+       int i;
+
+       struct refspec *rs;
+       int nr, alloc;
+
+       if (cli_rs_nr) {
+               base_rs = cli_rs;
+               base_rs_nr = cli_rs_nr;
+       } else if (refmap_array) {
+               die("--refmap option is only meaningful with command-line 
refspec(s).");
+       } else {
+               /* Use the defaults */
+               struct branch *branch = branch_get(NULL);
+               int has_merge = branch_has_merge_config(branch);
+               /* Note: has_merge implies non-NULL branch->remote_name */
+               if (has_merge && !strcmp(branch->remote_name, remote->name))
+                       /*
+                        * if the remote we're fetching from is the same
+                        * as given in branch.<name>.remote, we add the
+                        * ref given in branch.<name>.merge, too.
+                        */
+                       merge_branch = branch;
+               if (remote &&
+                   (remote->fetch_refspec_nr || merge_branch)) {
+                       base_rs = remote->fetch;
+                       base_rs_nr = remote->fetch_refspec_nr;
+               } else {
+                       head_refspec.src = "HEAD";
+                       base_rs = &head_refspec;
+                       base_rs_nr = 1;
+               }
+       }
+
+       for (i = 0; i < base_rs_nr; i++)
+               if (base_rs[i].dst && base_rs[i].dst[0]) {
+                       *autotags = 1;
+                       break;
+               }
+
+       alloc = base_rs_nr +
+               (merge_branch ? merge_branch->merge_nr : 0) +
+               (tags == TAGS_SET);
+       rs = xcalloc(alloc, sizeof(*rs));
+       memcpy(rs, base_rs, base_rs_nr * sizeof(*rs));
+       nr = base_rs_nr;
+       if (merge_branch)
+               for (i = 0; i < merge_branch->merge_nr; i++)
+                       rs[nr++].src = merge_branch->merge[i]->src;
+       if (tags == TAGS_SET)
+               rs[nr++] = *tag_refspec;
+
+       *e_rs = rs;
+       *e_rs_nr = nr;
+}
+
 static struct ref *get_ref_map(const struct remote *remote,
                               const struct ref *remote_refs,
                               struct refspec *refspecs, int refspec_count,
-                              int tags, int *autotags)
+                              int tags, int autotags)
 {
        int i;
        struct ref *rm;
@@ -321,11 +386,8 @@ static struct ref *get_ref_map(const struct remote *remote,
                struct refspec *fetch_refspec;
                int fetch_refspec_nr;
 
-               for (i = 0; i < refspec_count; i++) {
+               for (i = 0; i < refspec_count; i++)
                        get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
-                       if (refspecs[i].dst && refspecs[i].dst[0])
-                               *autotags = 1;
-               }
                /* Merge everything on the command line (but not --tags) */
                for (rm = ref_map; rm; rm = rm->next)
                        rm->fetch_head_status = FETCH_HEAD_MERGE;
@@ -372,9 +434,6 @@ static struct ref *get_ref_map(const struct remote *remote,
                     (has_merge && !strcmp(branch->remote_name, 
remote->name)))) {
                        for (i = 0; i < remote->fetch_refspec_nr; i++) {
                                get_fetch_map(remote_refs, &remote->fetch[i], 
&tail, 0);
-                               if (remote->fetch[i].dst &&
-                                   remote->fetch[i].dst[0])
-                                       *autotags = 1;
                                if (!i && !has_merge && ref_map &&
                                    !remote->fetch[0].pattern)
                                        ref_map->fetch_head_status = 
FETCH_HEAD_MERGE;
@@ -401,7 +460,7 @@ static struct ref *get_ref_map(const struct remote *remote,
        if (tags == TAGS_SET)
                /* also fetch all tags */
                get_fetch_map(remote_refs, tag_refspec, &tail, 0);
-       else if (tags == TAGS_DEFAULT && *autotags)
+       else if (tags == TAGS_DEFAULT && autotags)
                find_non_local_tags(remote_refs, &ref_map, &tail);
 
        /* Now append any refs to be updated opportunistically: */
@@ -911,13 +970,14 @@ static int quickfetch(struct ref *ref_map)
        return check_connected(iterate_ref_map, &rm, &opt);
 }
 
-static int fetch_refs(struct transport *transport, struct ref *ref_map,
-                     struct ref **updated_remote_refs)
+static int fetch_refs(struct transport *transport,
+                     struct refspec *refspecs, int refspec_nr,
+                     struct ref *ref_map, struct ref **updated_remote_refs)
 {
        int ret = quickfetch(ref_map);
        if (ret)
-               ret = transport_fetch_refs(transport, ref_map,
-                                          updated_remote_refs);
+               ret = transport_fetch_refs(transport, refspecs, refspec_nr,
+                                          ref_map, updated_remote_refs);
        if (ret)
                transport_unlock_pack(transport);
        return ret;
@@ -1068,7 +1128,7 @@ static void backfill_tags(struct transport *transport, 
struct ref *ref_map)
        transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
        transport_set_option(transport, TRANS_OPT_DEPTH, "0");
        transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
-       if (!fetch_refs(transport, ref_map, NULL))
+       if (!fetch_refs(transport, NULL, 0, ref_map, NULL))
                consume_refs(transport, ref_map);
 
        if (gsecondary) {
@@ -1083,6 +1143,10 @@ static int do_fetch(struct transport *transport,
        struct ref *ref_map;
        int autotags = (transport->remote->fetch_tags == 1);
        int retcode = 0;
+
+       struct refspec *e_rs;
+       int e_rs_nr;
+
        const struct ref *remote_refs;
        struct ref *new_remote_refs = NULL;
 
@@ -1103,9 +1167,11 @@ static int do_fetch(struct transport *transport,
                        goto cleanup;
        }
 
+       get_effective_refspecs(&e_rs, &e_rs_nr, transport->remote,
+                              refs, ref_count, tags, &autotags);
        remote_refs = transport_get_remote_refs(transport);
        ref_map = get_ref_map(transport->remote, remote_refs, refs, ref_count,
-                             tags, &autotags);
+                             tags, autotags);
        if (!update_head_ok)
                check_not_current_branch(ref_map);
 
@@ -1126,7 +1192,7 @@ static int do_fetch(struct transport *transport,
                                   transport->url);
                }
        }
-       if (fetch_refs(transport, ref_map, &new_remote_refs)) {
+       if (fetch_refs(transport, e_rs, e_rs_nr, ref_map, &new_remote_refs)) {
                free_refs(ref_map);
                retcode = 1;
                goto cleanup;
@@ -1134,7 +1200,7 @@ static int do_fetch(struct transport *transport,
        if (new_remote_refs) {
                free_refs(ref_map);
                ref_map = get_ref_map(transport->remote, new_remote_refs,
-                                     refs, ref_count, tags, &autotags);
+                                     refs, ref_count, tags, autotags);
                free_refs(new_remote_refs);
        }
 
diff --git a/remote-curl.c b/remote-curl.c
index 34a97e732..e78959d47 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -12,6 +12,7 @@
 #include "credential.h"
 #include "sha1-array.h"
 #include "send-pack.h"
+#include "refs.h"
 
 static struct remote *remote;
 /* always ends with a trailing slash */
@@ -31,7 +32,8 @@ struct options {
                thin : 1,
                /* One of the SEND_PACK_PUSH_CERT_* constants. */
                push_cert : 2,
-               deepen_relative : 1;
+               deepen_relative : 1,
+               echo_refs : 1;
 };
 static struct options options;
 static struct string_list cas_options = STRING_LIST_INIT_DUP;
@@ -139,6 +141,14 @@ static int set_option(const char *name, const char *value)
                else
                        return -1;
                return 0;
+       } else if (!strcmp(name, "echo-refs")) {
+               if (!strcmp(value, "true"))
+                       options.echo_refs = 1;
+               else if (!strcmp(value, "false"))
+                       options.echo_refs = 0;
+               else
+                       return -1;
+               return 0;
 
 #if LIBCURL_VERSION_NUM >= 0x070a08
        } else if (!strcmp(name, "family")) {
@@ -750,7 +760,7 @@ static int rpc_service(struct rpc_state *rpc, struct 
discovery *heads)
        return err;
 }
 
-static int fetch_dumb(int nr_heads, struct ref **to_fetch)
+static int fetch_dumb(int nr_heads, const struct ref **to_fetch)
 {
        struct walker *walker;
        char **targets;
@@ -775,11 +785,24 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
                free(targets[i]);
        free(targets);
 
+       if (options.echo_refs) {
+               struct strbuf sb = STRBUF_INIT;
+               for (i = 0; i < nr_heads; i++) {
+                       strbuf_reset(&sb);
+                       strbuf_addf(&sb,
+                                   "%s %s\n",
+                                   oid_to_hex(&to_fetch[i]->old_oid),
+                                   to_fetch[i]->name);
+                       write_or_die(1, sb.buf, sb.len);
+               }
+       }
+
        return ret ? error("fetch failed.") : 0;
 }
 
 static int fetch_git(struct discovery *heads,
-       int nr_heads, struct ref **to_fetch)
+       int nr_refspec, const struct refspec *refspecs,
+       int nr_heads, const struct ref **to_fetch)
 {
        struct rpc_state rpc;
        struct strbuf preamble = STRBUF_INIT;
@@ -811,10 +834,15 @@ static int fetch_git(struct discovery *heads,
                                 options.deepen_not.items[i].string);
        if (options.deepen_relative && options.depth)
                argv_array_push(&args, "--deepen-relative");
+       if (options.echo_refs)
+               argv_array_push(&args, "--always-print-refs");
        argv_array_push(&args, url.buf);
 
-       for (i = 0; i < nr_heads; i++) {
-               struct ref *ref = to_fetch[i];
+       if (refspecs) {
+               for (i = 0; i < nr_refspec; i++)
+                       packet_buf_write(&preamble, "%s\n", refspecs[i].src);
+       } else {
+               const struct ref *ref = to_fetch[i];
                if (!*ref->name)
                        die("cannot fetch by sha1 over smart http");
                packet_buf_write(&preamble, "%s %s\n",
@@ -837,46 +865,38 @@ static int fetch_git(struct discovery *heads,
        return err;
 }
 
-static int fetch(int nr_heads, struct ref **to_fetch)
+static int fetch(int nr_refspec, const struct refspec *refspecs)
 {
+       const struct ref **to_fetch;
+       int nr;
+       int ret, i;
        struct discovery *d = discover_refs("git-upload-pack", 0);
+       get_ref_array(&to_fetch, &nr, d->refs, refspecs, nr_refspec);
+
        if (d->proto_git)
-               return fetch_git(d, nr_heads, to_fetch);
+               ret = fetch_git(d, nr_refspec, refspecs, nr, to_fetch);
        else
-               return fetch_dumb(nr_heads, to_fetch);
+               ret = fetch_dumb(nr, to_fetch);
+
+       for (i = 0; i < nr; i++) {
+               free((void *) to_fetch[i]);
+       }
+       free(to_fetch);
+
+       return ret;
 }
 
 static void parse_fetch(struct strbuf *buf)
 {
-       struct ref **to_fetch = NULL;
-       struct ref *list_head = NULL;
-       struct ref **list = &list_head;
-       int alloc_heads = 0, nr_heads = 0;
+       struct refspec *to_fetch = NULL;
+       int alloc = 0, nr = 0;
 
        do {
                const char *p;
                if (skip_prefix(buf->buf, "fetch ", &p)) {
-                       const char *name;
-                       struct ref *ref;
-                       struct object_id old_oid;
-
-                       if (get_oid_hex(p, &old_oid))
-                               die("protocol error: expected sha/ref, got 
%s'", p);
-                       if (p[GIT_SHA1_HEXSZ] == ' ')
-                               name = p + GIT_SHA1_HEXSZ + 1;
-                       else if (!p[GIT_SHA1_HEXSZ])
-                               name = "";
-                       else
-                               die("protocol error: expected sha/ref, got 
%s'", p);
-
-                       ref = alloc_ref(name);
-                       oidcpy(&ref->old_oid, &old_oid);
-
-                       *list = ref;
-                       list = &ref->next;
-
-                       ALLOC_GROW(to_fetch, nr_heads + 1, alloc_heads);
-                       to_fetch[nr_heads++] = ref;
+                       nr++;
+                       ALLOC_GROW(to_fetch, nr, alloc);
+                       parse_ref_or_pattern(&to_fetch[nr - 1], p);
                }
                else
                        die("http transport does not support %s", buf->buf);
@@ -888,10 +908,8 @@ static void parse_fetch(struct strbuf *buf)
                        break;
        } while (1);
 
-       if (fetch(nr_heads, to_fetch))
+       if (fetch(nr, to_fetch))
                exit(128); /* error already reported */
-       free_refs(list_head);
-       free(to_fetch);
 
        printf("\n");
        fflush(stdout);
@@ -1084,6 +1102,7 @@ int cmd_main(int argc, const char **argv)
                        printf("option\n");
                        printf("push\n");
                        printf("check-connectivity\n");
+                       printf("echo-refs\n");
                        printf("\n");
                        fflush(stdout);
                } else {
diff --git a/t/t5552-upload-pack-ref-in-want.sh 
b/t/t5552-upload-pack-ref-in-want.sh
index 80cf2263a..26e785f3b 100755
--- a/t/t5552-upload-pack-ref-in-want.sh
+++ b/t/t5552-upload-pack-ref-in-want.sh
@@ -345,7 +345,7 @@ test_expect_success 'server is initially ahead - no ref in 
want' '
        grep "ERR upload-pack: not our ref" err
 '
 
-test_expect_failure 'server is initially ahead - ref in want' '
+test_expect_success 'server is initially ahead - ref in want' '
        git -C "$REPO" config uploadpack.advertiseRefInWant true &&
        rm -rf local &&
        cp -r "$LOCAL_PRISTINE" local &&
@@ -369,7 +369,7 @@ test_expect_success 'server is initially behind - no ref in 
want' '
        test_cmp expected actual
 '
 
-test_expect_failure 'server is initially behind - ref in want' '
+test_expect_success 'server is initially behind - ref in want' '
        git -C "$REPO" config uploadpack.advertiseRefInWant true &&
        rm -rf local &&
        cp -r "$LOCAL_PRISTINE" local &&
diff --git a/transport-helper.c b/transport-helper.c
index be0aa6d39..fcd9edcdc 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -28,7 +28,8 @@ struct helper_data {
                signed_tags : 1,
                check_connectivity : 1,
                no_disconnect_req : 1,
-               no_private_update : 1;
+               no_private_update : 1,
+               echo_refs : 1;
        char *export_marks;
        char *import_marks;
        /* These go from remote name (as in "list") to private name */
@@ -195,6 +196,8 @@ static struct child_process *get_helper(struct transport 
*transport)
                        data->import_marks = xstrdup(arg);
                } else if (starts_with(capname, "no-private-update")) {
                        data->no_private_update = 1;
+               } else if (!strcmp(capname, "echo-refs")) {
+                       data->echo_refs = 1;
                } else if (mandatory) {
                        die("Unknown mandatory capability %s. This remote "
                            "helper probably needs newer version of Git.",
@@ -383,27 +386,49 @@ static int release_helper(struct transport *transport)
        return res;
 }
 
+static struct ref *copy_ref_array(struct ref **array, int nr)
+{
+       struct ref *head = NULL, **tail = &head;
+       int i;
+       for (i = 0; i < nr; i++) {
+               *tail = copy_ref(array[i]);
+               tail = &(*tail)->next;
+       }
+       return head;
+}
+
 static int fetch_with_fetch(struct transport *transport,
-                           int nr_heads, const struct ref **to_fetch)
+                           int nr_refspec, const struct refspec *refspecs,
+                           int nr_heads, const struct ref **to_fetch,
+                           struct ref **fetched_refs)
 {
        struct helper_data *data = transport->data;
        int i;
        struct strbuf buf = STRBUF_INIT;
-
-       for (i = 0; i < nr_heads; i++) {
-               const struct ref *posn = to_fetch[i];
-               if (posn->status & REF_STATUS_UPTODATE)
-                       continue;
-
-               strbuf_addf(&buf, "fetch %s %s\n",
-                           oid_to_hex(&posn->old_oid),
-                           posn->symref ? posn->symref : posn->name);
+       int use_echo_refs = data->echo_refs && refspecs;
+       struct ref *fetched = NULL;
+
+       if (use_echo_refs) {
+               set_helper_option(transport, "echo-refs", "true");
+               for (i = 0; i < nr_refspec; i++)
+                       strbuf_addf(&buf, "fetch %s\n", refspecs[i].src);
+       } else {
+               for (i = 0; i < nr_heads; i++) {
+                       const struct ref *posn = to_fetch[i];
+                       if (posn->status & REF_STATUS_UPTODATE)
+                               continue;
+
+                       strbuf_addf(&buf, "fetch %s %s\n",
+                                   oid_to_hex(&posn->old_oid),
+                                   posn->symref ? posn->symref : posn->name);
+               }
        }
 
        strbuf_addch(&buf, '\n');
        sendline(data, &buf);
 
        while (1) {
+               struct object_id oid;
                if (recvline(data, &buf))
                        exit(128);
 
@@ -418,12 +443,29 @@ static int fetch_with_fetch(struct transport *transport,
                         
data->transport_options.check_self_contained_and_connected &&
                         !strcmp(buf.buf, "connectivity-ok"))
                        data->transport_options.self_contained_and_connected = 
1;
-               else if (!buf.len)
+               else if (use_echo_refs && !get_oid_hex(buf.buf, &oid)
+                        && buf.buf[GIT_SHA1_HEXSZ] == ' ') {
+                       struct ref *ref = alloc_ref(buf.buf + GIT_SHA1_HEXSZ + 
1);
+                       oidcpy(&ref->old_oid, &oid);
+                       ref->next = fetched;
+                       fetched = ref;
+               } else if (!buf.len)
                        break;
                else
                        warning("%s unexpectedly said: '%s'", data->name, 
buf.buf);
        }
        strbuf_release(&buf);
+
+       if (use_echo_refs) {
+               if (fetched_refs)
+                       *fetched_refs = fetched;
+       } else {
+               assert(fetched == NULL);
+               if (fetched_refs)
+                       *fetched_refs = copy_ref_array((struct ref **) to_fetch,
+                                                      nr_heads);
+       }
+
        return 0;
 }
 
@@ -657,6 +699,7 @@ static int connect_helper(struct transport *transport, 
const char *name,
 }
 
 static int fetch(struct transport *transport,
+                int nr_refspec, const struct refspec *refspecs,
                 int nr_heads, const struct ref **to_fetch,
                 struct ref **fetched_refs)
 {
@@ -665,8 +708,8 @@ static int fetch(struct transport *transport,
 
        if (process_connect(transport, 0)) {
                do_take_over(transport);
-               return transport->fetch(transport, nr_heads, to_fetch,
-                                       fetched_refs);
+               return transport->fetch(transport, nr_refspec, refspecs,
+                                       nr_heads, to_fetch, fetched_refs);
        }
 
        count = 0;
@@ -688,7 +731,8 @@ static int fetch(struct transport *transport,
                set_helper_option(transport, "update-shallow", "true");
 
        if (data->fetch)
-               return fetch_with_fetch(transport, nr_heads, to_fetch);
+               return fetch_with_fetch(transport, nr_refspec, refspecs,
+                                       nr_heads, to_fetch, fetched_refs);
 
        if (data->import)
                return fetch_with_import(transport, nr_heads, to_fetch, 
fetched_refs);
diff --git a/transport.c b/transport.c
index 85a4c5369..734c605b1 100644
--- a/transport.c
+++ b/transport.c
@@ -95,6 +95,7 @@ static struct ref *get_refs_from_bundle(struct transport 
*transport, int for_pus
 }
 
 static int fetch_refs_from_bundle(struct transport *transport,
+                              int nr_refspec, const struct refspec *refspecs,
                               int nr_heads, const struct ref **to_fetch,
                               struct ref **fetched_refs)
 {
@@ -203,6 +204,7 @@ static struct ref *get_refs_via_connect(struct transport 
*transport, int for_pus
 }
 
 static int fetch_refs_via_pack(struct transport *transport,
+                              int nr_refspec, const struct refspec *refspecs,
                               int nr_heads, const struct ref **to_fetch,
                               struct ref **fetched_refs)
 {
@@ -1096,8 +1098,9 @@ const struct ref *transport_get_remote_refs(struct 
transport *transport)
        return transport->remote_refs;
 }
 
-int transport_fetch_refs(struct transport *transport, struct ref *refs,
-                        struct ref **fetched_refs)
+int transport_fetch_refs(struct transport *transport,
+                        const struct refspec *refspecs, int refspec_nr,
+                        struct ref *refs, struct ref **fetched_refs)
 {
        int rc;
        int nr_heads = 0, nr_alloc = 0, nr_refs = 0;
@@ -1136,7 +1139,8 @@ int transport_fetch_refs(struct transport *transport, 
struct ref *refs,
                        heads[nr_heads++] = rm;
        }
 
-       rc = transport->fetch(transport, nr_heads, heads, fetched_refs);
+       rc = transport->fetch(transport, refspec_nr, refspecs,
+                             nr_heads, heads, fetched_refs);
        if (nop_head) {
                *nop_tail = *fetched_refs;
                *fetched_refs = nop_head;
diff --git a/transport.h b/transport.h
index 326ff9bd6..d7a007d21 100644
--- a/transport.h
+++ b/transport.h
@@ -82,13 +82,20 @@ struct transport {
         * Fetch the objects for the given refs. Note that this gets
         * an array, and should ignore the list structure.
         *
+        * The user may provide the array of refspecs used to generate the
+        * given refs. If provided, the transport should prefer the refspecs if
+        * possible (but may still use the refs for pre-fetch optimizations,
+        * for example).
+        *
         * The transport *may* provide, in fetched_refs, the list of refs that
-        * it fetched. If the transport knows anything about the fetched refs
-        * that the caller does not know (for example, shallow status or ref
-        * hashes), it should provide that list of refs and include that
-        * information in the list.
+        * it fetched, and must do so if it is different from the given refs.
+        * If the transport knows anything about the fetched refs that the
+        * caller does not know (for example, shallow status or ref hashes), it
+        * should provide that list of refs and include that information in the
+        * list.
         **/
        int (*fetch)(struct transport *transport,
+                    int refspec_nr, const struct refspec *refspecs,
                     int refs_nr, const struct ref **refs,
                     struct ref **fetched_refs);
 
@@ -234,8 +241,9 @@ int transport_push(struct transport *connection,
 
 const struct ref *transport_get_remote_refs(struct transport *transport);
 
-int transport_fetch_refs(struct transport *transport, struct ref *refs,
-                        struct ref **fetched_refs);
+int transport_fetch_refs(struct transport *transport,
+                        const struct refspec *refspecs, int refspec_nr,
+                        struct ref *refs, struct ref **fetched_refs);
 void transport_unlock_pack(struct transport *transport);
 int transport_disconnect(struct transport *transport);
 char *transport_anonymize_url(const char *url);
-- 
2.11.0.483.g087da7b7c-goog

Reply via email to