fetch: use struct ref to represent refs to be fetched

Even though "git fetch" has full infrastructure to parse refspecs to be fetched and match them against the list of refs to come up with the final list of refs to be fetched, the list of refs that are requested to be fetched were internally converted to a plain list of strings at the transport layer and then passed to the underlying fetch-pack driver. Stop this conversion and instead pass around an array of refs. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jan 29, 2013 at 14:02 UTC f2db854d24f32de7b03dde5a7d7134f5e31100b9
5 files changed +89 -53
builtin/fetch-pack.c
+32 -8
@@ -7,12 +7,31 @@ static const char fetch_pack_usage[] =
7 "[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
8 "[--no-progress] [-v] [<host>:]<directory> [<refs>...]";
9
10 +static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
11 + const char *name, int namelen)
12 +{
13 + struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1);
14 +
15 + memcpy(ref->name, name, namelen);
16 + ref->name[namelen] = '\0';
17 + (*nr)++;
18 + ALLOC_GROW(*sought, *nr, *alloc);
19 + (*sought)[*nr - 1] = ref;
20 +}
21 +
22 +static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
23 + const char *string)
24 +{
25 + add_sought_entry_mem(sought, nr, alloc, string, strlen(string));
26 +}
27 +
28 int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
29 {
30 int i, ret;
31 struct ref *ref = NULL;
32 const char *dest = NULL;
15 - struct string_list sought = STRING_LIST_INIT_DUP;
33 + struct ref **sought = NULL;
34 + int nr_sought = 0, alloc_sought = 0;
35 int fd[2];
36 char *pack_lockfile = NULL;
37 char **pack_lockfile_ptr = NULL;
@@ -94,7 +113,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
113 * refs from the standard input:
114 */
115 for (; i < argc; i++)
97 - string_list_append(&sought, xstrdup(argv[i]));
116 + add_sought_entry(&sought, &nr_sought, &alloc_sought, argv[i]);
117 if (args.stdin_refs) {
118 if (args.stateless_rpc) {
119 /* in stateless RPC mode we use pkt-line to read
@@ -107,14 +126,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
126 break;
127 if (line[n-1] == '\n')
128 n--;
110 - string_list_append(&sought, xmemdupz(line, n));
129 + add_sought_entry_mem(&sought, &nr_sought, &alloc_sought, line, n);
130 }
131 }
132 else {
133 /* read from stdin one ref per line, until EOF */
134 struct strbuf line = STRBUF_INIT;
135 while (strbuf_getline(&line, stdin, '\n') != EOF)
117 - string_list_append(&sought, strbuf_detach(&line, NULL));
136 + add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf);
137 strbuf_release(&line);
138 }
139 }
@@ -131,7 +150,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
150 get_remote_heads(fd[0], &ref, 0, NULL);
151
152 ref = fetch_pack(&args, fd, conn, ref, dest,
134 - &sought, pack_lockfile_ptr);
153 + sought, nr_sought, pack_lockfile_ptr);
154 if (pack_lockfile) {
155 printf("lock %s\n", pack_lockfile);
156 fflush(stdout);
@@ -141,7 +160,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
160 if (finish_connect(conn))
161 return 1;
162
144 - ret = !ref || sought.nr;
163 + ret = !ref;
164
165 /*
166 * If the heads to pull were given, we should have consumed
@@ -149,8 +168,13 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
168 * remote no-such-ref' would silently succeed without issuing
169 * an error.
170 */
152 - for (i = 0; i < sought.nr; i++)
153 - error("no such remote ref %s", sought.items[i].string);
171 + for (i = 0; i < nr_sought; i++) {
172 + if (!sought[i] || sought[i]->matched)
173 + continue;
174 + error("no such remote ref %s", sought[i]->name);
175 + ret = 1;
176 + }
177 +
178 while (ref) {
179 printf("%s %s\n",
180 sha1_to_hex(ref->old_sha1), ref->name);
cache.h
+2 -1
@@ -1013,7 +1013,8 @@ struct ref {
1013 nonfastforward:1,
1014 not_forwardable:1,
1015 update:1,
1016 - deletion:1;
1016 + deletion:1,
1017 + matched:1;
1018 enum {
1019 REF_STATUS_NONE = 0,
1020 REF_STATUS_OK,
fetch-pack.c
+48 -31
@@ -520,47 +520,37 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args,
520 }
521 }
522
523 -static int non_matching_ref(struct string_list_item *item, void *unused)
524 -{
525 - if (item->util) {
526 - item->util = NULL;
527 - return 0;
528 - }
529 - else
530 - return 1;
531 -}
532 -
523 static void filter_refs(struct fetch_pack_args *args,
534 - struct ref **refs, struct string_list *sought)
524 + struct ref **refs,
525 + struct ref **sought, int nr_sought)
526 {
527 struct ref *newlist = NULL;
528 struct ref **newtail = &newlist;
529 struct ref *ref, *next;
539 - int sought_pos;
530 + int i;
531
541 - sought_pos = 0;
532 + i = 0;
533 for (ref = *refs; ref; ref = next) {
534 int keep = 0;
535 next = ref->next;
536 +
537 if (!memcmp(ref->name, "refs/", 5) &&
538 check_refname_format(ref->name + 5, 0))
539 ; /* trash */
540 else {
549 - while (sought_pos < sought->nr) {
550 - int cmp = strcmp(ref->name, sought->items[sought_pos].string);
541 + while (i < nr_sought) {
542 + int cmp = strcmp(ref->name, sought[i]->name);
543 if (cmp < 0)
544 break; /* definitely do not have it */
545 else if (cmp == 0) {
546 keep = 1; /* definitely have it */
555 - sought->items[sought_pos++].util = "matched";
556 - break;
547 + sought[i]->matched = 1;
548 }
558 - else
559 - sought_pos++; /* might have it; keep looking */
549 + i++;
550 }
551 }
552
563 - if (! keep && args->fetch_all &&
553 + if (!keep && args->fetch_all &&
554 (!args->depth || prefixcmp(ref->name, "refs/tags/")))
555 keep = 1;
556
@@ -573,7 +563,6 @@ static void filter_refs(struct fetch_pack_args *args,
563 }
564 }
565
576 - filter_string_list(sought, 0, non_matching_ref, NULL);
566 *refs = newlist;
567 }
568
@@ -583,7 +572,8 @@ static void mark_alternate_complete(const struct ref *ref, void *unused)
572 }
573
574 static int everything_local(struct fetch_pack_args *args,
586 - struct ref **refs, struct string_list *sought)
575 + struct ref **refs,
576 + struct ref **sought, int nr_sought)
577 {
578 struct ref *ref;
579 int retval;
@@ -634,7 +624,7 @@ static int everything_local(struct fetch_pack_args *args,
624 }
625 }
626
637 - filter_refs(args, refs, sought);
627 + filter_refs(args, refs, sought, nr_sought);
628
629 for (retval = 1, ref = *refs; ref ; ref = ref->next) {
630 const unsigned char *remote = ref->old_sha1;
@@ -764,10 +754,17 @@ static int get_pack(struct fetch_pack_args *args,
754 return 0;
755 }
756
757 +static int cmp_ref_by_name(const void *a_, const void *b_)
758 +{
759 + const struct ref *a = *((const struct ref **)a_);
760 + const struct ref *b = *((const struct ref **)b_);
761 + return strcmp(a->name, b->name);
762 +}
763 +
764 static struct ref *do_fetch_pack(struct fetch_pack_args *args,
765 int fd[2],
766 const struct ref *orig_ref,
770 - struct string_list *sought,
767 + struct ref **sought, int nr_sought,
768 char **pack_lockfile)
769 {
770 struct ref *ref = copy_ref_list(orig_ref);
@@ -776,6 +773,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
773 int agent_len;
774
775 sort_ref_list(&ref, ref_compare_name);
776 + qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name);
777
778 if (is_repository_shallow() && !server_supports("shallow"))
779 die("Server does not support shallow clients");
@@ -824,7 +822,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
822 agent_len, agent_feature);
823 }
824
827 - if (everything_local(args, &ref, sought)) {
825 + if (everything_local(args, &ref, sought, nr_sought)) {
826 packet_flush(fd[1]);
827 goto all_done;
828 }
@@ -887,11 +885,32 @@ static void fetch_pack_setup(void)
885 did_setup = 1;
886 }
887
888 +static int remove_duplicates_in_refs(struct ref **ref, int nr)
889 +{
890 + struct string_list names = STRING_LIST_INIT_NODUP;
891 + int src, dst;
892 +
893 + for (src = dst = 0; src < nr; src++) {
894 + struct string_list_item *item;
895 + item = string_list_insert(&names, ref[src]->name);
896 + if (item->util)
897 + continue; /* already have it */
898 + item->util = ref[src];
899 + if (src != dst)
900 + ref[dst] = ref[src];
901 + dst++;
902 + }
903 + for (src = dst; src < nr; src++)
904 + ref[src] = NULL;
905 + string_list_clear(&names, 0);
906 + return dst;
907 +}
908 +
909 struct ref *fetch_pack(struct fetch_pack_args *args,
910 int fd[], struct child_process *conn,
911 const struct ref *ref,
912 const char *dest,
894 - struct string_list *sought,
913 + struct ref **sought, int nr_sought,
914 char **pack_lockfile)
915 {
916 struct stat st;
@@ -903,16 +922,14 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
922 st.st_mtime = 0;
923 }
924
906 - if (sought->nr) {
907 - sort_string_list(sought);
908 - string_list_remove_duplicates(sought, 0);
909 - }
925 + if (nr_sought)
926 + nr_sought = remove_duplicates_in_refs(sought, nr_sought);
927
928 if (!ref) {
929 packet_flush(fd[1]);
930 die("no matching remote head");
931 }
915 - ref_cpy = do_fetch_pack(args, fd, ref, sought, pack_lockfile);
932 + ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
933
934 if (args->depth > 0) {
935 static struct lock_file lock;
fetch-pack.h
+5 -6
@@ -20,17 +20,16 @@ struct fetch_pack_args {
20 };
21
22 /*
23 - * sought contains the full names of remote references that should be
24 - * updated from. On return, the names that were found on the remote
25 - * will have been removed from the list. The util members of the
26 - * string_list_items are used internally; they must be NULL on entry
27 - * (and will be NULL on exit).
23 + * sought represents remote references that should be updated from.
24 + * On return, the names that were found on the remote will have been
25 + * marked as such.
26 */
27 struct ref *fetch_pack(struct fetch_pack_args *args,
28 int fd[], struct child_process *conn,
29 const struct ref *ref,
30 const char *dest,
33 - struct string_list *sought,
31 + struct ref **sought,
32 + int nr_sought,
33 char **pack_lockfile);
34
35 #endif
transport.c
+2 -7
@@ -518,11 +518,9 @@ static int fetch_refs_via_pack(struct transport *transport,
518 int nr_heads, struct ref **to_fetch)
519 {
520 struct git_transport_data *data = transport->data;
521 - struct string_list sought = STRING_LIST_INIT_DUP;
521 const struct ref *refs;
522 char *dest = xstrdup(transport->url);
523 struct fetch_pack_args args;
525 - int i;
524 struct ref *refs_tmp = NULL;
525
526 memset(&args, 0, sizeof(args));
@@ -536,9 +534,6 @@ static int fetch_refs_via_pack(struct transport *transport,
534 args.no_progress = !transport->progress;
535 args.depth = data->options.depth;
536
539 - for (i = 0; i < nr_heads; i++)
540 - string_list_append(&sought, to_fetch[i]->name);
541 -
537 if (!data->got_remote_heads) {
538 connect_setup(transport, 0, 0);
539 get_remote_heads(data->fd[0], &refs_tmp, 0, NULL);
@@ -547,7 +542,8 @@ static int fetch_refs_via_pack(struct transport *transport,
542
543 refs = fetch_pack(&args, data->fd, data->conn,
544 refs_tmp ? refs_tmp : transport->remote_refs,
550 - dest, &sought, &transport->pack_lockfile);
545 + dest, to_fetch, nr_heads,
546 + &transport->pack_lockfile);
547 close(data->fd[0]);
548 close(data->fd[1]);
549 if (finish_connect(data->conn))
@@ -557,7 +553,6 @@ static int fetch_refs_via_pack(struct transport *transport,
553
554 free_refs(refs_tmp);
555
560 - string_list_clear(&sought, 0);
556 free(dest);
557 return (refs ? 0 : -1);
558 }