X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=sidebyside;f=remote.c;h=9a88917aab32f32235d0ebaa1ffd0e2f2773a927;hb=acef41c9db33f2261e46e17cca098df3403dd745;hp=bc27a8e3df59915d13bdca357fbe76789caf646e;hpb=6b62816cb12e621c5952f088542bec6dfc7ec5d6;p=git.git diff --git a/remote.c b/remote.c index bc27a8e3d..9a88917aa 100644 --- a/remote.c +++ b/remote.c @@ -17,6 +17,15 @@ static void add_push_refspec(struct remote *remote, const char *ref) remote->push_refspec_nr = nr; } +static void add_fetch_refspec(struct remote *remote, const char *ref) +{ + int nr = remote->fetch_refspec_nr + 1; + remote->fetch_refspec = + xrealloc(remote->fetch_refspec, nr * sizeof(char *)); + remote->fetch_refspec[nr-1] = ref; + remote->fetch_refspec_nr = nr; +} + static void add_uri(struct remote *remote, const char *uri) { int nr = remote->uri_nr + 1; @@ -74,6 +83,9 @@ static void read_remotes_file(struct remote *remote) } else if (!prefixcmp(buffer, "Push:")) { value_list = 1; s = buffer + 5; + } else if (!prefixcmp(buffer, "Pull:")) { + value_list = 2; + s = buffer + 5; } else continue; @@ -93,6 +105,9 @@ static void read_remotes_file(struct remote *remote) case 1: add_push_refspec(remote, xstrdup(s)); break; + case 2: + add_fetch_refspec(remote, xstrdup(s)); + break; } } fclose(f); @@ -174,6 +189,8 @@ static int handle_config(const char *key, const char *value) add_uri(remote, xstrdup(value)); } else if (!strcmp(subkey, ".push")) { add_push_refspec(remote, xstrdup(value)); + } else if (!strcmp(subkey, ".fetch")) { + add_fetch_refspec(remote, xstrdup(value)); } else if (!strcmp(subkey, ".receivepack")) { if (!remote->receivepack) remote->receivepack = xstrdup(value); @@ -257,10 +274,101 @@ struct remote *remote_get(const char *name) add_uri(ret, name); if (!ret->uri) return NULL; + ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec); ret->push = parse_ref_spec(ret->push_refspec_nr, ret->push_refspec); return ret; } +int for_each_remote(each_remote_fn fn, void *priv) +{ + int i, result = 0; + read_config(); + for (i = 0; i < allocated_remotes && !result; i++) { + struct remote *r = remotes[i]; + if (!r) + continue; + if (!r->fetch) + r->fetch = parse_ref_spec(r->fetch_refspec_nr, + r->fetch_refspec); + if (!r->push) + r->push = parse_ref_spec(r->push_refspec_nr, + r->push_refspec); + result = fn(r, priv); + } + return result; +} + +int remote_has_uri(struct remote *remote, const char *uri) +{ + int i; + for (i = 0; i < remote->uri_nr; i++) { + if (!strcmp(remote->uri[i], uri)) + return 1; + } + return 0; +} + +int remote_find_tracking(struct remote *remote, struct refspec *refspec) +{ + int find_src = refspec->src == NULL; + char *needle, **result; + int i; + + if (find_src) { + if (refspec->dst == NULL) + return error("find_tracking: need either src or dst"); + needle = refspec->dst; + result = &refspec->src; + } else { + needle = refspec->src; + result = &refspec->dst; + } + + for (i = 0; i < remote->fetch_refspec_nr; i++) { + struct refspec *fetch = &remote->fetch[i]; + const char *key = find_src ? fetch->dst : fetch->src; + const char *value = find_src ? fetch->src : fetch->dst; + if (!fetch->dst) + continue; + if (fetch->pattern) { + if (!prefixcmp(needle, key)) { + *result = xmalloc(strlen(value) + + strlen(needle) - + strlen(key) + 1); + strcpy(*result, value); + strcpy(*result + strlen(value), + needle + strlen(key)); + refspec->force = fetch->force; + return 0; + } + } else if (!strcmp(needle, key)) { + *result = xstrdup(value); + refspec->force = fetch->force; + return 0; + } + } + return -1; +} + +struct ref *alloc_ref(unsigned namelen) +{ + struct ref *ret = xmalloc(sizeof(struct ref) + namelen); + memset(ret, 0, sizeof(struct ref) + namelen); + return ret; +} + +void free_refs(struct ref *ref) +{ + struct ref *next; + while (ref) { + next = ref->next; + if (ref->peer_ref) + free(ref->peer_ref); + free(ref); + ref = next; + } +} + static int count_refspec_match(const char *pattern, struct ref *refs, struct ref **matched_ref) @@ -274,7 +382,6 @@ static int count_refspec_match(const char *pattern, for (weak_match = match = 0; refs; refs = refs->next) { char *name = refs->name; int namelen = strlen(name); - int weak_match; if (namelen < patlen || memcmp(name + namelen - patlen, pattern, patlen)) @@ -319,11 +426,12 @@ static int count_refspec_match(const char *pattern, } } -static void link_dst_tail(struct ref *ref, struct ref ***tail) +static void tail_link_ref(struct ref *ref, struct ref ***tail) { **tail = ref; + while (ref->next) + ref = ref->next; *tail = &ref->next; - **tail = NULL; } static struct ref *try_explicit_object_name(const char *name) @@ -333,7 +441,7 @@ static struct ref *try_explicit_object_name(const char *name) int len; if (!*name) { - ref = xcalloc(1, sizeof(*ref) + 20); + ref = alloc_ref(20); strcpy(ref->name, "(delete)"); hashclr(ref->new_sha1); return ref; @@ -341,92 +449,105 @@ static struct ref *try_explicit_object_name(const char *name) if (get_sha1(name, sha1)) return NULL; len = strlen(name) + 1; - ref = xcalloc(1, sizeof(*ref) + len); + ref = alloc_ref(len); memcpy(ref->name, name, len); hashcpy(ref->new_sha1, sha1); return ref; } -static int match_explicit_refs(struct ref *src, struct ref *dst, - struct ref ***dst_tail, struct refspec *rs, - int rs_nr) +static struct ref *make_linked_ref(const char *name, struct ref ***tail) { - int i, errs; - for (i = errs = 0; i < rs_nr; i++) { - struct ref *matched_src, *matched_dst; + struct ref *ret; + size_t len; - const char *dst_value = rs[i].dst; - if (dst_value == NULL) - dst_value = rs[i].src; + len = strlen(name) + 1; + ret = alloc_ref(len); + memcpy(ret->name, name, len); + tail_link_ref(ret, tail); + return ret; +} - matched_src = matched_dst = NULL; - switch (count_refspec_match(rs[i].src, src, &matched_src)) { - case 1: - break; - case 0: - /* The source could be in the get_sha1() format - * not a reference name. :refs/other is a - * way to delete 'other' ref at the remote end. - */ - matched_src = try_explicit_object_name(rs[i].src); - if (matched_src) - break; - errs = 1; - error("src refspec %s does not match any.", - rs[i].src); - break; - default: - errs = 1; - error("src refspec %s matches more than one.", - rs[i].src); - break; - } - switch (count_refspec_match(dst_value, dst, &matched_dst)) { - case 1: - break; - case 0: - if (!memcmp(dst_value, "refs/", 5)) { - int len = strlen(dst_value) + 1; - matched_dst = xcalloc(1, sizeof(*dst) + len); - memcpy(matched_dst->name, dst_value, len); - link_dst_tail(matched_dst, dst_tail); - } - else if (!strcmp(rs[i].src, dst_value) && - matched_src) { - /* pushing "master:master" when - * remote does not have master yet. - */ - int len = strlen(matched_src->name) + 1; - matched_dst = xcalloc(1, sizeof(*dst) + len); - memcpy(matched_dst->name, matched_src->name, - len); - link_dst_tail(matched_dst, dst_tail); - } - else { - errs = 1; - error("dst refspec %s does not match any " - "existing ref on the remote and does " - "not start with refs/.", dst_value); - } - break; - default: - errs = 1; - error("dst refspec %s matches more than one.", - dst_value); +static int match_explicit(struct ref *src, struct ref *dst, + struct ref ***dst_tail, + struct refspec *rs, + int errs) +{ + struct ref *matched_src, *matched_dst; + + const char *dst_value = rs->dst; + + if (rs->pattern) + return errs; + + matched_src = matched_dst = NULL; + switch (count_refspec_match(rs->src, src, &matched_src)) { + case 1: + break; + case 0: + /* The source could be in the get_sha1() format + * not a reference name. :refs/other is a + * way to delete 'other' ref at the remote end. + */ + matched_src = try_explicit_object_name(rs->src); + if (matched_src) break; - } - if (errs) - continue; - if (matched_dst->peer_ref) { - errs = 1; - error("dst ref %s receives from more than one src.", - matched_dst->name); - } - else { - matched_dst->peer_ref = matched_src; - matched_dst->force = rs[i].force; - } + error("src refspec %s does not match any.", + rs->src); + break; + default: + matched_src = NULL; + error("src refspec %s matches more than one.", + rs->src); + break; + } + + if (!matched_src) + errs = 1; + + if (!dst_value) { + if (!matched_src) + return errs; + dst_value = matched_src->name; } + + switch (count_refspec_match(dst_value, dst, &matched_dst)) { + case 1: + break; + case 0: + if (!memcmp(dst_value, "refs/", 5)) + matched_dst = make_linked_ref(dst_value, dst_tail); + else + error("dst refspec %s does not match any " + "existing ref on the remote and does " + "not start with refs/.", dst_value); + break; + default: + matched_dst = NULL; + error("dst refspec %s matches more than one.", + dst_value); + break; + } + if (errs || matched_dst == NULL) + return 1; + if (matched_dst->peer_ref) { + errs = 1; + error("dst ref %s receives from more than one src.", + matched_dst->name); + } + else { + matched_dst->peer_ref = matched_src; + matched_dst->force = rs->force; + } + return errs; +} + +static int match_explicit_refs(struct ref *src, struct ref *dst, + struct ref ***dst_tail, struct refspec *rs, + int rs_nr) +{ + int i, errs; + for (i = errs = 0; i < rs_nr; i++) + errs |= match_explicit(src, dst, dst_tail, &rs[i], errs); return -errs; } @@ -438,32 +559,80 @@ static struct ref *find_ref_by_name(struct ref *list, const char *name) return NULL; } +static const struct refspec *check_pattern_match(const struct refspec *rs, + int rs_nr, + const struct ref *src) +{ + int i; + for (i = 0; i < rs_nr; i++) { + if (rs[i].pattern && !prefixcmp(src->name, rs[i].src)) + return rs + i; + } + return NULL; +} + +/* + * Note. This is used only by "push"; refspec matching rules for + * push and fetch are subtly different, so do not try to reuse it + * without thinking. + */ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, int nr_refspec, char **refspec, int all) { struct refspec *rs = parse_ref_spec(nr_refspec, (const char **) refspec); - if (nr_refspec) - return match_explicit_refs(src, dst, dst_tail, rs, nr_refspec); + if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec)) + return -1; /* pick the remainder */ for ( ; src; src = src->next) { struct ref *dst_peer; + const struct refspec *pat = NULL; + char *dst_name; if (src->peer_ref) continue; - dst_peer = find_ref_by_name(dst, src->name); - if ((dst_peer && dst_peer->peer_ref) || (!dst_peer && !all)) + if (nr_refspec) { + pat = check_pattern_match(rs, nr_refspec, src); + if (!pat) + continue; + } + else if (prefixcmp(src->name, "refs/heads/")) + /* + * "matching refs"; traditionally we pushed everything + * including refs outside refs/heads/ hierarchy, but + * that does not make much sense these days. + */ continue; + + if (pat) { + const char *dst_side = pat->dst ? pat->dst : pat->src; + dst_name = xmalloc(strlen(dst_side) + + strlen(src->name) - + strlen(pat->src) + 2); + strcpy(dst_name, dst_side); + strcat(dst_name, src->name + strlen(pat->src)); + } else + dst_name = xstrdup(src->name); + dst_peer = find_ref_by_name(dst, dst_name); + if (dst_peer && dst_peer->peer_ref) + /* We're already sending something to this ref. */ + goto free_name; + if (!dst_peer && !nr_refspec && !all) + /* Remote doesn't have it, and we have no + * explicit pattern, and we don't have + * --all. */ + goto free_name; if (!dst_peer) { /* Create a new one and link it */ - int len = strlen(src->name) + 1; - dst_peer = xcalloc(1, sizeof(*dst_peer) + len); - memcpy(dst_peer->name, src->name, len); + dst_peer = make_linked_ref(dst_name, dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); - link_dst_tail(dst_peer, dst_tail); } dst_peer->peer_ref = src; + if (pat) + dst_peer->force = pat->force; + free_name: + free(dst_name); } return 0; }