X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=remote.c;h=df91b2ff990f42d6b1bf115a3bdc99041c2dc8bb;hb=be6042cfa59358b733b6f9ba05bcbc317998d9fe;hp=500ca4d968753485ac38d44e238e93ab86372c1c;hpb=0f2890acd999e63892d0b5b401e70bdaefe3ba51;p=git.git diff --git a/remote.c b/remote.c index 500ca4d96..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) @@ -377,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) @@ -391,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; @@ -399,22 +562,22 @@ 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 struct ref *make_dst(const char *name, struct ref ***dst_tail) +static struct ref *make_linked_ref(const char *name, struct ref ***tail) { - struct ref *dst; + struct ref *ret; size_t len; len = strlen(name) + 1; - dst = xcalloc(1, sizeof(*dst) + len); - memcpy(dst->name, name, len); - link_dst_tail(dst, dst_tail); - return dst; + ret = alloc_ref(len); + memcpy(ret->name, name, len); + tail_link_ref(ret, tail); + return ret; } static int match_explicit(struct ref *src, struct ref *dst, @@ -462,7 +625,7 @@ static int match_explicit(struct ref *src, struct ref *dst, break; case 0: if (!memcmp(dst_value, "refs/", 5)) - matched_dst = make_dst(dst_value, dst_tail); + 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 " @@ -544,6 +707,13 @@ 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) { const char *dst_side = pat->dst ? pat->dst : pat->src; @@ -565,7 +735,7 @@ 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 */ - dst_peer = make_dst(dst_name, dst_tail); + dst_peer = make_linked_ref(dst_name, dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); } dst_peer->peer_ref = src; @@ -574,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; +}