diff options
Diffstat (limited to 'remote.c')
-rw-r--r-- | remote.c | 322 |
1 files changed, 222 insertions, 100 deletions
@@ -42,6 +42,7 @@ struct rewrites { static struct remote **remotes; static int remotes_alloc; static int remotes_nr; +static struct hashmap remotes_hash; static struct branch **branches; static int branches_alloc; @@ -49,6 +50,7 @@ static int branches_nr; static struct branch *current_branch; static const char *default_remote_name; +static const char *branch_pushremote_name; static const char *pushremote_name; static int explicit_default_remote_name; @@ -135,26 +137,51 @@ static void add_url_alias(struct remote *remote, const char *url) add_pushurl_alias(remote, url); } +struct remotes_hash_key { + const char *str; + int len; +}; + +static int remotes_hash_cmp(const struct remote *a, const struct remote *b, const struct remotes_hash_key *key) +{ + if (key) + return strncmp(a->name, key->str, key->len) || a->name[key->len]; + else + return strcmp(a->name, b->name); +} + +static inline void init_remotes_hash(void) +{ + if (!remotes_hash.cmpfn) + hashmap_init(&remotes_hash, (hashmap_cmp_fn)remotes_hash_cmp, 0); +} + static struct remote *make_remote(const char *name, int len) { - struct remote *ret; - int i; + struct remote *ret, *replaced; + struct remotes_hash_key lookup; + struct hashmap_entry lookup_entry; - for (i = 0; i < remotes_nr; i++) { - if (len ? (!strncmp(name, remotes[i]->name, len) && - !remotes[i]->name[len]) : - !strcmp(name, remotes[i]->name)) - return remotes[i]; - } + if (!len) + len = strlen(name); + + init_remotes_hash(); + lookup.str = name; + lookup.len = len; + hashmap_entry_init(&lookup_entry, memhash(name, len)); + + if ((ret = hashmap_get(&remotes_hash, &lookup_entry, &lookup)) != NULL) + return ret; ret = xcalloc(1, sizeof(struct remote)); ret->prune = -1; /* unspecified */ ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc); remotes[remotes_nr++] = ret; - if (len) - ret->name = xstrndup(name, len); - else - ret->name = xstrdup(name); + ret->name = xstrndup(name, len); + + hashmap_entry_init(ret, lookup_entry.hash); + replaced = hashmap_put(&remotes_hash, ret); + assert(replaced == NULL); /* no previous entry overwritten */ return ret; } @@ -169,7 +196,6 @@ static struct branch *make_branch(const char *name, int len) { struct branch *ret; int i; - char *refname; for (i = 0; i < branches_nr; i++) { if (len ? (!strncmp(name, branches[i]->name, len) && @@ -185,10 +211,7 @@ static struct branch *make_branch(const char *name, int len) ret->name = xstrndup(name, len); else ret->name = xstrdup(name); - refname = xmalloc(strlen(name) + strlen("refs/heads/") + 1); - strcpy(refname, "refs/heads/"); - strcpy(refname + strlen("refs/heads/"), ret->name); - ret->refname = refname; + ret->refname = xstrfmt("refs/heads/%s", ret->name); return ret; } @@ -352,7 +375,7 @@ static int handle_config(const char *key, const char *value, void *cb) } } else if (!strcmp(subkey, ".pushremote")) { if (branch == current_branch) - if (git_config_string(&pushremote_name, key, value)) + if (git_config_string(&branch_pushremote_name, key, value)) return -1; } else if (!strcmp(subkey, ".merge")) { if (!value) @@ -485,13 +508,16 @@ static void read_config(void) return; default_remote_name = "origin"; current_branch = NULL; - head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag); + head_ref = resolve_ref_unsafe("HEAD", 0, sha1, &flag); if (head_ref && (flag & REF_ISSYMREF) && - starts_with(head_ref, "refs/heads/")) { - current_branch = - make_branch(head_ref + strlen("refs/heads/"), 0); + skip_prefix(head_ref, "refs/heads/", &head_ref)) { + current_branch = make_branch(head_ref, 0); } git_config(handle_config, NULL); + if (branch_pushremote_name) { + free((char *)pushremote_name); + pushremote_name = branch_pushremote_name; + } alias_all_urls(); } @@ -518,7 +544,7 @@ static void free_refspecs(struct refspec *refspec, int nr_refspec) static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) { int i; - struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec); + struct refspec *rs = xcalloc(nr_refspec, sizeof(*rs)); for (i = 0; i < nr_refspec; i++) { size_t llen; @@ -717,13 +743,16 @@ struct remote *pushremote_get(const char *name) int remote_is_configured(const char *name) { - int i; + struct remotes_hash_key lookup; + struct hashmap_entry lookup_entry; read_config(); - for (i = 0; i < remotes_nr; i++) - if (!strcmp(name, remotes[i]->name)) - return 1; - return 0; + init_remotes_hash(); + lookup.str = name; + lookup.len = strlen(name); + hashmap_entry_init(&lookup_entry, memhash(name, lookup.len)); + + return hashmap_get(&remotes_hash, &lookup_entry, &lookup) != NULL; } int for_each_remote(each_remote_fn fn, void *priv) @@ -833,25 +862,44 @@ static int match_name_with_pattern(const char *key, const char *name, ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen && !memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen); if (ret && value) { + struct strbuf sb = STRBUF_INIT; const char *vstar = strchr(value, '*'); - size_t vlen; - size_t vsuffixlen; if (!vstar) die("Value '%s' of pattern has no '*'", value); - vlen = vstar - value; - vsuffixlen = strlen(vstar + 1); - *result = xmalloc(vlen + vsuffixlen + - strlen(name) - - klen - ksuffixlen + 1); - strncpy(*result, value, vlen); - strncpy(*result + vlen, - name + klen, namelen - klen - ksuffixlen); - strcpy(*result + vlen + namelen - klen - ksuffixlen, - vstar + 1); + strbuf_add(&sb, value, vstar - value); + strbuf_add(&sb, name + klen, namelen - klen - ksuffixlen); + strbuf_addstr(&sb, vstar + 1); + *result = strbuf_detach(&sb, NULL); } return ret; } +static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct refspec *query, struct string_list *results) +{ + int i; + int find_src = !query->src; + + if (find_src && !query->dst) + error("query_refspecs_multiple: need either src or dst"); + + for (i = 0; i < ref_count; i++) { + struct refspec *refspec = &refs[i]; + const char *key = find_src ? refspec->dst : refspec->src; + const char *value = find_src ? refspec->src : refspec->dst; + const char *needle = find_src ? query->dst : query->src; + char **result = find_src ? &query->src : &query->dst; + + if (!refspec->dst) + continue; + if (refspec->pattern) { + if (match_name_with_pattern(key, needle, value, result)) + string_list_append_nodup(results, *result); + } else if (!strcmp(needle, key)) { + string_list_append(results, value); + } + } +} + int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) { int i; @@ -927,8 +975,8 @@ struct ref *copy_ref(const struct ref *ref) cpy = xmalloc(sizeof(struct ref) + len + 1); memcpy(cpy, ref, sizeof(struct ref) + len + 1); cpy->next = NULL; - cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL; - cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL; + cpy->symref = xstrdup_or_null(ref->symref); + cpy->remote_status = xstrdup_or_null(ref->remote_status); cpy->peer_ref = copy_ref(ref->peer_ref); return cpy; } @@ -1031,11 +1079,13 @@ int count_refspec_match(const char *pattern, } } if (!matched) { - *matched_ref = matched_weak; + if (matched_ref) + *matched_ref = matched_weak; return weak_match; } else { - *matched_ref = matched; + if (matched_ref) + *matched_ref = matched; return match; } } @@ -1055,18 +1105,25 @@ static struct ref *alloc_delete_ref(void) return ref; } -static struct ref *try_explicit_object_name(const char *name) +static int try_explicit_object_name(const char *name, + struct ref **match) { unsigned char sha1[20]; - struct ref *ref; - if (!*name) - return alloc_delete_ref(); + if (!*name) { + if (match) + *match = alloc_delete_ref(); + return 0; + } + if (get_sha1(name, sha1)) - return NULL; - ref = alloc_ref(name); - hashcpy(ref->new_sha1, sha1); - return ref; + return -1; + + if (match) { + *match = alloc_ref(name); + hashcpy((*match)->new_sha1, sha1); + } + return 0; } static struct ref *make_linked_ref(const char *name, struct ref ***tail) @@ -1081,7 +1138,8 @@ static char *guess_ref(const char *name, struct ref *peer) struct strbuf buf = STRBUF_INIT; unsigned char sha1[20]; - const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL); + const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING, + sha1, NULL); if (!r) return NULL; @@ -1096,12 +1154,37 @@ static char *guess_ref(const char *name, struct ref *peer) return strbuf_detach(&buf, NULL); } +static int match_explicit_lhs(struct ref *src, + struct refspec *rs, + struct ref **match, + int *allocated_match) +{ + switch (count_refspec_match(rs->src, src, match)) { + case 1: + if (allocated_match) + *allocated_match = 0; + return 0; + 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. + */ + if (try_explicit_object_name(rs->src, match) < 0) + return error("src refspec %s does not match any.", rs->src); + if (allocated_match) + *allocated_match = 1; + return 0; + default: + return error("src refspec %s matches more than one.", rs->src); + } +} + static int match_explicit(struct ref *src, struct ref *dst, struct ref ***dst_tail, struct refspec *rs) { struct ref *matched_src, *matched_dst; - int copy_src; + int allocated_src; const char *dst_value = rs->dst; char *dst_guess; @@ -1110,29 +1193,16 @@ static int match_explicit(struct ref *src, struct ref *dst, return 0; matched_src = matched_dst = NULL; - switch (count_refspec_match(rs->src, src, &matched_src)) { - case 1: - copy_src = 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) - return error("src refspec %s does not match any.", rs->src); - copy_src = 0; - break; - default: - return error("src refspec %s matches more than one.", rs->src); - } + if (match_explicit_lhs(src, rs, &matched_src, &allocated_src) < 0) + return -1; if (!dst_value) { unsigned char sha1[20]; int flag; - dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag); + dst_value = resolve_ref_unsafe(matched_src->name, + RESOLVE_REF_READING, + sha1, &flag); if (!dst_value || ((flag & REF_ISSYMREF) && !starts_with(dst_value, "refs/heads/"))) @@ -1144,7 +1214,7 @@ static int match_explicit(struct ref *src, struct ref *dst, case 1: break; case 0: - if (!memcmp(dst_value, "refs/", 5)) + if (starts_with(dst_value, "refs/")) matched_dst = make_linked_ref(dst_value, dst_tail); else if (is_null_sha1(matched_src->new_sha1)) error("unable to delete '%s': remote ref does not exist", @@ -1171,7 +1241,9 @@ static int match_explicit(struct ref *src, struct ref *dst, return error("dst ref %s receives from more than one src.", matched_dst->name); else { - matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src; + matched_dst->peer_ref = allocated_src ? + matched_src : + copy_ref(matched_src); matched_dst->force = rs->force; } return 0; @@ -1284,7 +1356,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds } clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK); - sort_string_list(&dst_tag); + string_list_sort(&dst_tag); /* Collect tags they do not have. */ for (ref = src; ref; ref = ref->next) { @@ -1349,7 +1421,32 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref) for ( ; ref; ref = ref->next) string_list_append_nodup(ref_index, ref->name)->util = ref; - sort_string_list(ref_index); + string_list_sort(ref_index); +} + +/* + * Given only the set of local refs, sanity-check the set of push + * refspecs. We can't catch all errors that match_push_refs would, + * but we can catch some errors early before even talking to the + * remote side. + */ +int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names) +{ + struct refspec *refspec = parse_push_refspec(nr_refspec, refspec_names); + int ret = 0; + int i; + + for (i = 0; i < nr_refspec; i++) { + struct refspec *rs = refspec + i; + + if (rs->pattern || rs->matching) + continue; + + ret |= match_explicit_lhs(src, rs, NULL, NULL); + } + + free_refspec(nr_refspec, refspec); + return ret; } /* @@ -1404,7 +1501,7 @@ int match_push_refs(struct ref *src, struct ref **dst, /* * Remote doesn't have it, and we have no * explicit pattern, and we don't have - * --all nor --mirror. + * --all or --mirror. */ goto free_name; @@ -1534,6 +1631,27 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, } } +static void set_merge(struct branch *ret) +{ + char *ref; + unsigned char sha1[20]; + int i; + + ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge)); + 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]); + if (!remote_find_tracking(ret->remote, ret->merge[i]) || + strcmp(ret->remote_name, ".")) + continue; + if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]), + sha1, &ref) == 1) + ret->merge[i]->dst = ref; + else + ret->merge[i]->dst = xstrdup(ret->merge_name[i]); + } +} + struct branch *branch_get(const char *name) { struct branch *ret; @@ -1545,17 +1663,8 @@ struct branch *branch_get(const char *name) 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(ret->merge_nr, sizeof(*ret->merge)); - 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]); - if (remote_find_tracking(ret->remote, ret->merge[i]) - && !strcmp(ret->remote_name, ".")) - ret->merge[i]->dst = xstrdup(ret->merge_name[i]); - } - } + if (ret->merge_nr) + set_merge(ret); } return ret; } @@ -1579,7 +1688,7 @@ static int ignore_symref_update(const char *refname) unsigned char sha1[20]; int flag; - if (!resolve_ref_unsafe(refname, sha1, 0, &flag)) + if (!resolve_ref_unsafe(refname, 0, sha1, &flag)) return 0; /* non-existing refs are OK */ return (flag & REF_ISSYMREF); } @@ -1821,7 +1930,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs) init_revisions(&revs, NULL); setup_revisions(rev_argc, rev_argv, &revs, NULL); - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); /* ... and count the commits on each side. */ *num_ours = 0; @@ -1848,7 +1958,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs) int format_tracking_info(struct branch *branch, struct strbuf *sb) { int ours, theirs; - const char *base; + char *base; int upstream_is_gone = 0; switch (stat_tracking_info(branch, &ours, &theirs)) { @@ -1864,8 +1974,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) break; } - base = branch->merge[0]->dst; - base = shorten_unambiguous_ref(base, 0); + base = shorten_unambiguous_ref(branch->merge[0]->dst, 0); if (upstream_is_gone) { strbuf_addf(sb, _("Your branch is based on '%s', but the upstream is gone.\n"), @@ -1911,6 +2020,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) strbuf_addf(sb, _(" (use \"git pull\" to merge the remote branch into yours)\n")); } + free(base); return 1; } @@ -1992,25 +2102,37 @@ static int get_stale_heads_cb(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct stale_heads_info *info = cb_data; + struct string_list matches = STRING_LIST_INIT_DUP; struct refspec query; + int i, stale = 1; memset(&query, 0, sizeof(struct refspec)); query.dst = (char *)refname; - if (query_refspecs(info->refs, info->ref_count, &query)) - return 0; /* No matches */ + query_refspecs_multiple(info->refs, info->ref_count, &query, &matches); + if (matches.nr == 0) + goto clean_exit; /* No matches */ /* * If we did find a suitable refspec and it's not a symref and * it's not in the list of refs that currently exist in that - * remote we consider it to be stale. + * remote, we consider it to be stale. In order to deal with + * overlapping refspecs, we need to go over all of the + * matching refs. */ - if (!((flags & REF_ISSYMREF) || - string_list_has_string(info->ref_names, query.src))) { + if (flags & REF_ISSYMREF) + goto clean_exit; + + for (i = 0; stale && i < matches.nr; i++) + if (string_list_has_string(info->ref_names, matches.items[i].string)) + stale = 0; + + if (stale) { struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail); hashcpy(ref->new_sha1, sha1); } - free(query.src); +clean_exit: + string_list_clear(&matches, 0); return 0; } @@ -2025,7 +2147,7 @@ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fet info.ref_count = ref_count; for (ref = fetch_map; ref; ref = ref->next) string_list_append(&ref_names, ref->name); - sort_string_list(&ref_names); + string_list_sort(&ref_names); for_each_ref(get_stale_heads_cb, &info); string_list_clear(&ref_names, 0); return stale_refs; |