X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=remote.c;h=df91b2ff990f42d6b1bf115a3bdc99041c2dc8bb;hb=be6042cfa59358b733b6f9ba05bcbc317998d9fe;hp=33c8e5055b8139fd248cd7b9250fa6e53f829ffa;hpb=e58db03bbee4af96e60b6ac88b84c1c11b8b037e;p=git.git diff --git a/remote.c b/remote.c index 33c8e5055..df91b2ff9 100644 --- a/remote.c +++ b/remote.c @@ -5,6 +5,12 @@ static struct remote **remotes; static int allocated_remotes; +static struct branch **branches; +static int allocated_branches; + +static struct branch *current_branch; +static const char *default_remote_name; + #define BUF_SIZE (2048) static char buffer[BUF_SIZE]; @@ -67,6 +73,54 @@ static struct remote *make_remote(const char *name, int len) return remotes[empty]; } +static void add_merge(struct branch *branch, const char *name) +{ + int nr = branch->merge_nr + 1; + branch->merge_name = + xrealloc(branch->merge_name, nr * sizeof(char *)); + branch->merge_name[nr-1] = name; + branch->merge_nr = nr; +} + +static struct branch *make_branch(const char *name, int len) +{ + int i, empty = -1; + char *refname; + + for (i = 0; i < allocated_branches; i++) { + if (!branches[i]) { + if (empty < 0) + empty = i; + } else { + if (len ? (!strncmp(name, branches[i]->name, len) && + !branches[i]->name[len]) : + !strcmp(name, branches[i]->name)) + return branches[i]; + } + } + + if (empty < 0) { + empty = allocated_branches; + allocated_branches += allocated_branches ? allocated_branches : 1; + branches = xrealloc(branches, + sizeof(*branches) * allocated_branches); + memset(branches + empty, 0, + (allocated_branches - empty) * sizeof(*branches)); + } + branches[empty] = xcalloc(1, sizeof(struct branch)); + if (len) + branches[empty]->name = xstrndup(name, len); + else + branches[empty]->name = xstrdup(name); + refname = malloc(strlen(name) + strlen("refs/heads/") + 1); + strcpy(refname, "refs/heads/"); + strcpy(refname + strlen("refs/heads/"), + branches[empty]->name); + branches[empty]->refname = refname; + + return branches[empty]; +} + static void read_remotes_file(struct remote *remote) { FILE *f = fopen(git_path("remotes/%s", remote->name), "r"); @@ -116,6 +170,8 @@ static void read_remotes_file(struct remote *remote) static void read_branches_file(struct remote *remote) { const char *slash = strchr(remote->name, '/'); + char *frag; + char *branch; int n = slash ? slash - remote->name : 1000; FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r"); char *s, *p; @@ -141,23 +197,41 @@ static void read_branches_file(struct remote *remote) strcpy(p, s); if (slash) strcat(p, slash); + frag = strchr(p, '#'); + if (frag) { + *(frag++) = '\0'; + branch = xmalloc(strlen(frag) + 12); + strcpy(branch, "refs/heads/"); + strcat(branch, frag); + } else { + branch = "refs/heads/master"; + } add_uri(remote, p); + add_fetch_refspec(remote, branch); + remote->fetch_tags = 1; /* always auto-follow */ } -static char *default_remote_name = NULL; -static const char *current_branch = NULL; -static int current_branch_len = 0; - static int handle_config(const char *key, const char *value) { const char *name; const char *subkey; struct remote *remote; - if (!prefixcmp(key, "branch.") && current_branch && - !strncmp(key + 7, current_branch, current_branch_len) && - !strcmp(key + 7 + current_branch_len, ".remote")) { - free(default_remote_name); - default_remote_name = xstrdup(value); + struct branch *branch; + if (!prefixcmp(key, "branch.")) { + name = key + 7; + subkey = strrchr(name, '.'); + branch = make_branch(name, subkey - name); + if (!subkey) + return 0; + if (!value) + return 0; + if (!strcmp(subkey, ".remote")) { + branch->remote_name = xstrdup(value); + if (branch == current_branch) + default_remote_name = branch->remote_name; + } else if (!strcmp(subkey, ".merge")) + add_merge(branch, xstrdup(value)); + return 0; } if (prefixcmp(key, "remote.")) return 0; @@ -196,6 +270,14 @@ static int handle_config(const char *key, const char *value) remote->receivepack = xstrdup(value); else error("more than one receivepack given, using the first"); + } else if (!strcmp(subkey, ".uploadpack")) { + if (!remote->uploadpack) + remote->uploadpack = xstrdup(value); + else + error("more than one uploadpack given, using the first"); + } else if (!strcmp(subkey, ".tagopt")) { + if (!strcmp(value, "--no-tags")) + remote->fetch_tags = -1; } return 0; } @@ -212,13 +294,13 @@ static void read_config(void) head_ref = resolve_ref("HEAD", sha1, 0, &flag); if (head_ref && (flag & REF_ISSYMREF) && !prefixcmp(head_ref, "refs/heads/")) { - current_branch = head_ref + strlen("refs/heads/"); - current_branch_len = strlen(current_branch); + current_branch = + make_branch(head_ref + strlen("refs/heads/"), 0); } git_config(handle_config); } -static struct refspec *parse_ref_spec(int nr_refspec, const char **refspec) +struct refspec *parse_ref_spec(int nr_refspec, const char **refspec) { int i; struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec); @@ -274,11 +356,34 @@ struct remote *remote_get(const char *name) add_uri(ret, name); if (!ret->uri) return NULL; + if (!strcmp(name, ".")) { + // we always fetch "refs/*:refs/*", which is trivial + add_fetch_refspec(ret, "refs/*:refs/*"); + } 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; @@ -289,37 +394,94 @@ int remote_has_uri(struct remote *remote, const char *uri) return 0; } +/* + * Returns true if, under the matching rules for fetching, name is the + * same as the given full name. + */ +static int ref_matches_abbrev(const char *name, const char *full) +{ + if (!prefixcmp(name, "refs/") || !strcmp(name, "HEAD")) + return !strcmp(name, full); + if (prefixcmp(full, "refs/")) + return 0; + if (!prefixcmp(name, "heads/") || + !prefixcmp(name, "tags/") || + !prefixcmp(name, "remotes/")) + return !strcmp(name, full + 5); + if (prefixcmp(full + 5, "heads/")) + return 0; + return !strcmp(full + 11, name); +} + 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(refspec->src, fetch->src)) { - refspec->dst = - xmalloc(strlen(fetch->dst) + - strlen(refspec->src) - - strlen(fetch->src) + 1); - strcpy(refspec->dst, fetch->dst); - strcpy(refspec->dst + strlen(fetch->dst), - refspec->src + strlen(fetch->src)); - refspec->force = fetch->force; - return 0; - } - } else { - if (!strcmp(refspec->src, fetch->src)) { - refspec->dst = xstrdup(fetch->dst); + 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; } } - refspec->dst = NULL; 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; +} + +static struct ref *copy_ref(struct ref *ref) +{ + struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1); + memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1); + ret->next = NULL; + 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) @@ -333,7 +495,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)) @@ -378,11 +539,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) @@ -392,7 +554,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; @@ -400,96 +562,102 @@ 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; + len = strlen(name) + 1; + ret = alloc_ref(len); + memcpy(ret->name, name, len); + tail_link_ref(ret, tail); + return ret; +} - if (rs[i].pattern) - continue; +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; - if (dst_value == NULL) - dst_value = rs[i].src; + const char *dst_value = rs->dst; - 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); + 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 == NULL) + 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; } @@ -513,6 +681,11 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, 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) { @@ -534,15 +707,23 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, 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) { - dst_name = xmalloc(strlen(pat->dst) + + 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, pat->dst); + strcpy(dst_name, dst_side); strcat(dst_name, src->name + strlen(pat->src)); } else - dst_name = strdup(src->name); + 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. */ @@ -554,11 +735,8 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, goto free_name; if (!dst_peer) { /* Create a new one and link it */ - int len = strlen(dst_name) + 1; - dst_peer = xcalloc(1, sizeof(*dst_peer) + len); - memcpy(dst_peer->name, dst_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; free_name: @@ -566,3 +744,153 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, } return 0; } + +struct branch *branch_get(const char *name) +{ + struct branch *ret; + + read_config(); + if (!name || !*name || !strcmp(name, "HEAD")) + ret = current_branch; + else + ret = make_branch(name, 0); + if (ret && ret->remote_name) { + ret->remote = remote_get(ret->remote_name); + if (ret->merge_nr) { + int i; + ret->merge = xcalloc(sizeof(*ret->merge), + ret->merge_nr); + for (i = 0; i < ret->merge_nr; i++) { + ret->merge[i] = xcalloc(1, sizeof(**ret->merge)); + ret->merge[i]->src = xstrdup(ret->merge_name[i]); + remote_find_tracking(ret->remote, + ret->merge[i]); + } + } + } + return ret; +} + +int branch_has_merge_config(struct branch *branch) +{ + return branch && !!branch->merge; +} + +int branch_merges(struct branch *branch, const char *refname) +{ + int i; + if (!branch) + return 0; + for (i = 0; i < branch->merge_nr; i++) { + if (ref_matches_abbrev(branch->merge[i]->src, refname)) + return 1; + } + return 0; +} + +static struct ref *get_expanded_map(struct ref *remote_refs, + const struct refspec *refspec) +{ + struct ref *ref; + struct ref *ret = NULL; + struct ref **tail = &ret; + + int remote_prefix_len = strlen(refspec->src); + int local_prefix_len = strlen(refspec->dst); + + for (ref = remote_refs; ref; ref = ref->next) { + if (strchr(ref->name, '^')) + continue; /* a dereference item */ + if (!prefixcmp(ref->name, refspec->src)) { + char *match; + struct ref *cpy = copy_ref(ref); + match = ref->name + remote_prefix_len; + + cpy->peer_ref = alloc_ref(local_prefix_len + + strlen(match) + 1); + sprintf(cpy->peer_ref->name, "%s%s", + refspec->dst, match); + if (refspec->force) + cpy->peer_ref->force = 1; + *tail = cpy; + tail = &cpy->next; + } + } + + return ret; +} + +static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name) +{ + struct ref *ref; + for (ref = refs; ref; ref = ref->next) { + if (ref_matches_abbrev(name, ref->name)) + return ref; + } + return NULL; +} + +struct ref *get_remote_ref(struct ref *remote_refs, const char *name) +{ + struct ref *ref = find_ref_by_name_abbrev(remote_refs, name); + + if (!ref) + die("Couldn't find remote ref %s\n", name); + + return copy_ref(ref); +} + +static struct ref *get_local_ref(const char *name) +{ + struct ref *ret; + if (!name) + return NULL; + + if (!prefixcmp(name, "refs/")) { + ret = alloc_ref(strlen(name) + 1); + strcpy(ret->name, name); + return ret; + } + + if (!prefixcmp(name, "heads/") || + !prefixcmp(name, "tags/") || + !prefixcmp(name, "remotes/")) { + ret = alloc_ref(strlen(name) + 6); + sprintf(ret->name, "refs/%s", name); + return ret; + } + + ret = alloc_ref(strlen(name) + 12); + sprintf(ret->name, "refs/heads/%s", name); + return ret; +} + +int get_fetch_map(struct ref *remote_refs, + const struct refspec *refspec, + struct ref ***tail) +{ + struct ref *ref_map, *rm; + + if (refspec->pattern) { + ref_map = get_expanded_map(remote_refs, refspec); + } else { + ref_map = get_remote_ref(remote_refs, + refspec->src[0] ? + refspec->src : "HEAD"); + + ref_map->peer_ref = get_local_ref(refspec->dst); + + if (refspec->force) + ref_map->peer_ref->force = 1; + } + + for (rm = ref_map; rm; rm = rm->next) { + if (rm->peer_ref && check_ref_format(rm->peer_ref->name + 5)) + die("* refusing to create funny ref '%s' locally", + rm->peer_ref->name); + } + + tail_link_ref(ref_map, tail); + + return 0; +}