Change fetch_pack() and friends to take string_list arguments
[gitweb.git] / builtin / fetch-pack.c
index 10db15b18403f01b9ee5db27b37d1e0cdbb2abb1..df81995eab3b6e289cff3ebbca64fb493a62dcb9 100644 (file)
@@ -10,6 +10,7 @@
 #include "remote.h"
 #include "run-command.h"
 #include "transport.h"
+#include "version.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
@@ -18,6 +19,7 @@ static int prefer_ofs_delta = 1;
 static int no_done;
 static int fetch_fsck_objects = -1;
 static int transfer_fsck_objects = -1;
+static int agent_supported;
 static struct fetch_pack_args args = {
        /* .uploadpack = */ "git-upload-pack",
 };
@@ -327,6 +329,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                        if (args.no_progress)   strbuf_addstr(&c, " no-progress");
                        if (args.include_tag)   strbuf_addstr(&c, " include-tag");
                        if (prefer_ofs_delta)   strbuf_addstr(&c, " ofs-delta");
+                       if (agent_supported)    strbuf_addf(&c, " agent=%s",
+                                                           git_user_agent_sanitized());
                        packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
                        strbuf_release(&c);
                } else
@@ -521,25 +525,27 @@ static void mark_recent_complete_commits(unsigned long cutoff)
        }
 }
 
-static void filter_refs(struct ref **refs, int nr_match, char **match)
+static void filter_refs(struct ref **refs, struct string_list *sought)
 {
        struct ref **return_refs;
        struct ref *newlist = NULL;
        struct ref **newtail = &newlist;
        struct ref *ref, *next;
        struct ref *fastarray[32];
+       int sought_pos;
 
-       if (nr_match && !args.fetch_all) {
-               if (ARRAY_SIZE(fastarray) < nr_match)
-                       return_refs = xcalloc(nr_match, sizeof(struct ref *));
+       if (sought->nr && !args.fetch_all) {
+               if (ARRAY_SIZE(fastarray) < sought->nr)
+                       return_refs = xcalloc(sought->nr, sizeof(struct ref *));
                else {
                        return_refs = fastarray;
-                       memset(return_refs, 0, sizeof(struct ref *) * nr_match);
+                       memset(return_refs, 0, sizeof(struct ref *) * sought->nr);
                }
        }
        else
                return_refs = NULL;
 
+       sought_pos = 0;
        for (ref = *refs; ref; ref = next) {
                next = ref->next;
                if (!memcmp(ref->name, "refs/", 5) &&
@@ -553,15 +559,20 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
                        continue;
                }
                else {
-                       int i;
-                       for (i = 0; i < nr_match; i++) {
-                               if (!strcmp(ref->name, match[i])) {
-                                       match[i][0] = '\0';
-                                       return_refs[i] = ref;
+                       int cmp = -1;
+                       while (sought_pos < sought->nr) {
+                               cmp = strcmp(ref->name, sought->items[sought_pos].string);
+                               if (cmp < 0) /* definitely do not have it */
+                                       break;
+                               else if (cmp == 0) { /* definitely have it */
+                                       sought->items[sought_pos].string[0] = '\0';
+                                       return_refs[sought_pos] = ref;
                                        break;
                                }
+                               else /* might have it; keep looking */
+                                       sought_pos++;
                        }
-                       if (i < nr_match)
+                       if (!cmp)
                                continue; /* we will link it later */
                }
                free(ref);
@@ -569,7 +580,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
 
        if (!args.fetch_all) {
                int i;
-               for (i = 0; i < nr_match; i++) {
+               for (i = 0; i < sought->nr; i++) {
                        ref = return_refs[i];
                        if (ref) {
                                *newtail = ref;
@@ -588,7 +599,7 @@ static void mark_alternate_complete(const struct ref *ref, void *unused)
        mark_complete(NULL, ref->old_sha1, 0, NULL);
 }
 
-static int everything_local(struct ref **refs, int nr_match, char **match)
+static int everything_local(struct ref **refs, struct string_list *sought)
 {
        struct ref *ref;
        int retval;
@@ -639,7 +650,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
                }
        }
 
-       filter_refs(refs, nr_match, match);
+       filter_refs(refs, sought);
 
        for (retval = 1, ref = *refs; ref ; ref = ref->next) {
                const unsigned char *remote = ref->old_sha1;
@@ -770,12 +781,15 @@ static int get_pack(int xd[2], char **pack_lockfile)
 
 static struct ref *do_fetch_pack(int fd[2],
                const struct ref *orig_ref,
-               int nr_match,
-               char **match,
+               struct string_list *sought,
                char **pack_lockfile)
 {
        struct ref *ref = copy_ref_list(orig_ref);
        unsigned char sha1[20];
+       const char *agent_feature;
+       int agent_len;
+
+       sort_ref_list(&ref, ref_compare_name);
 
        if (is_repository_shallow() && !server_supports("shallow"))
                die("Server does not support shallow clients");
@@ -805,12 +819,26 @@ static struct ref *do_fetch_pack(int fd[2],
                        fprintf(stderr, "Server supports side-band\n");
                use_sideband = 1;
        }
+       if (!server_supports("thin-pack"))
+               args.use_thin_pack = 0;
+       if (!server_supports("no-progress"))
+               args.no_progress = 0;
+       if (!server_supports("include-tag"))
+               args.include_tag = 0;
        if (server_supports("ofs-delta")) {
                if (args.verbose)
                        fprintf(stderr, "Server supports ofs-delta\n");
        } else
                prefer_ofs_delta = 0;
-       if (everything_local(&ref, nr_match, match)) {
+
+       if ((agent_feature = server_feature_value("agent", &agent_len))) {
+               agent_supported = 1;
+               if (args.verbose && agent_len)
+                       fprintf(stderr, "Server version is %.*s\n",
+                               agent_len, agent_feature);
+       }
+
+       if (everything_local(&ref, sought)) {
                packet_flush(fd[1]);
                goto all_done;
        }
@@ -830,25 +858,16 @@ static struct ref *do_fetch_pack(int fd[2],
        return ref;
 }
 
-static int remove_duplicates(int nr_heads, char **heads)
+static int remove_duplicates(struct string_list *sought)
 {
        int src, dst;
 
-       for (src = dst = 0; src < nr_heads; src++) {
-               /* If heads[src] is different from any of
-                * heads[0..dst], push it in.
-                */
-               int i;
-               for (i = 0; i < dst; i++) {
-                       if (!strcmp(heads[i], heads[src]))
-                               break;
-               }
-               if (i < dst)
-                       continue;
-               if (src != dst)
-                       heads[dst] = heads[src];
-               dst++;
-       }
+       if (!sought->nr)
+               return 0;
+
+       for (src = dst = 1; src < sought->nr; src++)
+               if (strcmp(sought->items[src].string, sought->items[dst-1].string))
+                       sought->items[dst++] = sought->items[src];
        return dst;
 }
 
@@ -899,9 +918,10 @@ static void fetch_pack_setup(void)
 
 int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 {
-       int i, ret, nr_heads;
+       int i, ret;
        struct ref *ref = NULL;
-       char *dest = NULL, **heads;
+       const char *dest = NULL;
+       struct string_list sought = STRING_LIST_INIT_DUP;
        int fd[2];
        char *pack_lockfile = NULL;
        char **pack_lockfile_ptr = NULL;
@@ -909,84 +929,78 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 
        packet_trace_identity("fetch-pack");
 
-       nr_heads = 0;
-       heads = NULL;
-       for (i = 1; i < argc; i++) {
+       for (i = 1; i < argc && *argv[i] == '-'; i++) {
                const char *arg = argv[i];
 
-               if (*arg == '-') {
-                       if (!prefixcmp(arg, "--upload-pack=")) {
-                               args.uploadpack = arg + 14;
-                               continue;
-                       }
-                       if (!prefixcmp(arg, "--exec=")) {
-                               args.uploadpack = arg + 7;
-                               continue;
-                       }
-                       if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
-                               args.quiet = 1;
-                               continue;
-                       }
-                       if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
-                               args.lock_pack = args.keep_pack;
-                               args.keep_pack = 1;
-                               continue;
-                       }
-                       if (!strcmp("--thin", arg)) {
-                               args.use_thin_pack = 1;
-                               continue;
-                       }
-                       if (!strcmp("--include-tag", arg)) {
-                               args.include_tag = 1;
-                               continue;
-                       }
-                       if (!strcmp("--all", arg)) {
-                               args.fetch_all = 1;
-                               continue;
-                       }
-                       if (!strcmp("--stdin", arg)) {
-                               args.stdin_refs = 1;
-                               continue;
-                       }
-                       if (!strcmp("-v", arg)) {
-                               args.verbose = 1;
-                               continue;
-                       }
-                       if (!prefixcmp(arg, "--depth=")) {
-                               args.depth = strtol(arg + 8, NULL, 0);
-                               continue;
-                       }
-                       if (!strcmp("--no-progress", arg)) {
-                               args.no_progress = 1;
-                               continue;
-                       }
-                       if (!strcmp("--stateless-rpc", arg)) {
-                               args.stateless_rpc = 1;
-                               continue;
-                       }
-                       if (!strcmp("--lock-pack", arg)) {
-                               args.lock_pack = 1;
-                               pack_lockfile_ptr = &pack_lockfile;
-                               continue;
-                       }
-                       usage(fetch_pack_usage);
+               if (!prefixcmp(arg, "--upload-pack=")) {
+                       args.uploadpack = arg + 14;
+                       continue;
+               }
+               if (!prefixcmp(arg, "--exec=")) {
+                       args.uploadpack = arg + 7;
+                       continue;
+               }
+               if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
+                       args.quiet = 1;
+                       continue;
+               }
+               if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
+                       args.lock_pack = args.keep_pack;
+                       args.keep_pack = 1;
+                       continue;
+               }
+               if (!strcmp("--thin", arg)) {
+                       args.use_thin_pack = 1;
+                       continue;
+               }
+               if (!strcmp("--include-tag", arg)) {
+                       args.include_tag = 1;
+                       continue;
+               }
+               if (!strcmp("--all", arg)) {
+                       args.fetch_all = 1;
+                       continue;
+               }
+               if (!strcmp("--stdin", arg)) {
+                       args.stdin_refs = 1;
+                       continue;
+               }
+               if (!strcmp("-v", arg)) {
+                       args.verbose = 1;
+                       continue;
+               }
+               if (!prefixcmp(arg, "--depth=")) {
+                       args.depth = strtol(arg + 8, NULL, 0);
+                       continue;
                }
-               dest = (char *)arg;
-               heads = (char **)(argv + i + 1);
-               nr_heads = argc - i - 1;
-               break;
+               if (!strcmp("--no-progress", arg)) {
+                       args.no_progress = 1;
+                       continue;
+               }
+               if (!strcmp("--stateless-rpc", arg)) {
+                       args.stateless_rpc = 1;
+                       continue;
+               }
+               if (!strcmp("--lock-pack", arg)) {
+                       args.lock_pack = 1;
+                       pack_lockfile_ptr = &pack_lockfile;
+                       continue;
+               }
+               usage(fetch_pack_usage);
        }
-       if (!dest)
+
+       if (i < argc)
+               dest = argv[i++];
+       else
                usage(fetch_pack_usage);
 
+       /*
+        * Copy refs from cmdline to growable list, then append any
+        * refs from the standard input:
+        */
+       for (; i < argc; i++)
+               string_list_append(&sought, xstrdup(argv[i]));
        if (args.stdin_refs) {
-               /*
-                * Copy refs from cmdline to new growable list, then
-                * append the refs from the standard input.
-                */
-               int alloc_heads = nr_heads;
-               int size = nr_heads * sizeof(*heads);
-               heads = memcpy(xmalloc(size), heads, size);
                if (args.stateless_rpc) {
                        /* in stateless RPC mode we use pkt-line to read
                         * from stdin, until we get a flush packet
@@ -998,17 +1012,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                                        break;
                                if (line[n-1] == '\n')
                                        n--;
-                               ALLOC_GROW(heads, nr_heads + 1, alloc_heads);
-                               heads[nr_heads++] = xmemdupz(line, n);
+                               string_list_append(&sought, xmemdupz(line, n));
                        }
                }
                else {
                        /* read from stdin one ref per line, until EOF */
                        struct strbuf line = STRBUF_INIT;
-                       while (strbuf_getline(&line, stdin, '\n') != EOF) {
-                               ALLOC_GROW(heads, nr_heads + 1, alloc_heads);
-                               heads[nr_heads++] = strbuf_detach(&line, NULL);
-                       }
+                       while (strbuf_getline(&line, stdin, '\n') != EOF)
+                               string_list_append(&sought, strbuf_detach(&line, NULL));
                        strbuf_release(&line);
                }
        }
@@ -1018,14 +1029,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                fd[0] = 0;
                fd[1] = 1;
        } else {
-               conn = git_connect(fd, (char *)dest, args.uploadpack,
+               conn = git_connect(fd, dest, args.uploadpack,
                                   args.verbose ? CONNECT_VERBOSE : 0);
        }
 
        get_remote_heads(fd[0], &ref, 0, NULL);
 
        ref = fetch_pack(&args, fd, conn, ref, dest,
-               nr_heads, heads, pack_lockfile_ptr);
+                        &sought, pack_lockfile_ptr);
        if (pack_lockfile) {
                printf("lock %s\n", pack_lockfile);
                fflush(stdout);
@@ -1036,17 +1047,19 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                ref = NULL;
        ret = !ref;
 
-       if (!ret && nr_heads) {
+       if (!ret && sought.nr) {
                /* If the heads to pull were given, we should have
                 * consumed all of them by matching the remote.
                 * Otherwise, 'git fetch remote no-such-ref' would
                 * silently succeed without issuing an error.
                 */
-               for (i = 0; i < nr_heads; i++)
-                       if (heads[i] && heads[i][0]) {
-                               error("no such remote ref %s", heads[i]);
+               for (i = 0; i < sought.nr; i++) {
+                       char *s = sought.items[i].string;
+                       if (s && s[0]) {
+                               error("no such remote ref %s", s);
                                ret = 1;
                        }
+               }
        }
        while (ref) {
                printf("%s %s\n",
@@ -1060,10 +1073,9 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 struct ref *fetch_pack(struct fetch_pack_args *my_args,
                       int fd[], struct child_process *conn,
                       const struct ref *ref,
-               const char *dest,
-               int nr_heads,
-               char **heads,
-               char **pack_lockfile)
+                      const char *dest,
+                      struct string_list *sought,
+                      char **pack_lockfile)
 {
        struct stat st;
        struct ref *ref_cpy;
@@ -1076,13 +1088,16 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                        st.st_mtime = 0;
        }
 
-       if (heads && nr_heads)
-               nr_heads = remove_duplicates(nr_heads, heads);
+       if (sought->nr) {
+               sort_string_list(sought);
+               remove_duplicates(sought);
+       }
+
        if (!ref) {
                packet_flush(fd[1]);
                die("no matching remote head");
        }
-       ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
+       ref_cpy = do_fetch_pack(fd, ref, sought, pack_lockfile);
 
        if (args.depth > 0) {
                struct cache_time mtime;