diff options
Diffstat (limited to 'sha1_name.c')
-rw-r--r-- | sha1_name.c | 469 |
1 files changed, 302 insertions, 167 deletions
diff --git a/sha1_name.c b/sha1_name.c index ca7ddd6f2c..e9ffe685d5 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -7,15 +7,20 @@ #include "refs.h" #include "remote.h" #include "dir.h" +#include "sha1-array.h" static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *); -typedef int (*disambiguate_hint_fn)(const unsigned char *, void *); +typedef int (*disambiguate_hint_fn)(const struct object_id *, void *); struct disambiguate_state { + int len; /* length of prefix in hex chars */ + char hex_pfx[GIT_MAX_HEXSZ + 1]; + struct object_id bin_pfx; + disambiguate_hint_fn fn; void *cb_data; - unsigned char candidate[20]; + struct object_id candidate; unsigned candidate_exists:1; unsigned candidate_checked:1; unsigned candidate_ok:1; @@ -24,7 +29,7 @@ struct disambiguate_state { unsigned always_call_fn:1; }; -static void update_candidates(struct disambiguate_state *ds, const unsigned char *current) +static void update_candidates(struct disambiguate_state *ds, const struct object_id *current) { if (ds->always_call_fn) { ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0; @@ -32,10 +37,10 @@ static void update_candidates(struct disambiguate_state *ds, const unsigned char } if (!ds->candidate_exists) { /* this is the first candidate */ - hashcpy(ds->candidate, current); + oidcpy(&ds->candidate, current); ds->candidate_exists = 1; return; - } else if (!hashcmp(ds->candidate, current)) { + } else if (!oidcmp(&ds->candidate, current)) { /* the same as what we already have seen */ return; } @@ -47,14 +52,14 @@ static void update_candidates(struct disambiguate_state *ds, const unsigned char } if (!ds->candidate_checked) { - ds->candidate_ok = ds->fn(ds->candidate, ds->cb_data); + ds->candidate_ok = ds->fn(&ds->candidate, ds->cb_data); ds->disambiguate_fn_used = 1; ds->candidate_checked = 1; } if (!ds->candidate_ok) { /* discard the candidate; we know it does not satisfy fn */ - hashcpy(ds->candidate, current); + oidcpy(&ds->candidate, current); ds->candidate_checked = 0; return; } @@ -72,10 +77,10 @@ static void update_candidates(struct disambiguate_state *ds, const unsigned char /* otherwise, current can be discarded and candidate is still good */ } -static void find_short_object_filename(int len, const char *hex_pfx, struct disambiguate_state *ds) +static void find_short_object_filename(struct disambiguate_state *ds) { struct alternate_object_database *alt; - char hex[40]; + char hex[GIT_MAX_HEXSZ]; static struct alternate_object_database *fakeent; if (!fakeent) { @@ -86,38 +91,31 @@ static void find_short_object_filename(int len, const char *hex_pfx, struct disa * alt->name/alt->base while iterating over the * object databases including our own. */ - const char *objdir = get_object_directory(); - size_t objdir_len = strlen(objdir); - fakeent = xmalloc(st_add3(sizeof(*fakeent), objdir_len, 43)); - memcpy(fakeent->base, objdir, objdir_len); - fakeent->name = fakeent->base + objdir_len + 1; - fakeent->name[-1] = '/'; + fakeent = alloc_alt_odb(get_object_directory()); } fakeent->next = alt_odb_list; - xsnprintf(hex, sizeof(hex), "%.2s", hex_pfx); + xsnprintf(hex, sizeof(hex), "%.2s", ds->hex_pfx); for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) { + struct strbuf *buf = alt_scratch_buf(alt); struct dirent *de; DIR *dir; - /* - * every alt_odb struct has 42 extra bytes after the base - * for exactly this purpose - */ - xsnprintf(alt->name, 42, "%.2s/", hex_pfx); - dir = opendir(alt->base); + + strbuf_addf(buf, "%.2s/", ds->hex_pfx); + dir = opendir(buf->buf); if (!dir) continue; while (!ds->ambiguous && (de = readdir(dir)) != NULL) { - unsigned char sha1[20]; + struct object_id oid; - if (strlen(de->d_name) != 38) + if (strlen(de->d_name) != GIT_SHA1_HEXSZ - 2) continue; - if (memcmp(de->d_name, hex_pfx + 2, len - 2)) + if (memcmp(de->d_name, ds->hex_pfx + 2, ds->len - 2)) continue; - memcpy(hex + 2, de->d_name, 38); - if (!get_sha1_hex(hex, sha1)) - update_candidates(ds, sha1); + memcpy(hex + 2, de->d_name, GIT_SHA1_HEXSZ - 2); + if (!get_oid_hex(hex, &oid)) + update_candidates(ds, &oid); } closedir(dir); } @@ -138,13 +136,11 @@ static int match_sha(unsigned len, const unsigned char *a, const unsigned char * return 1; } -static void unique_in_pack(int len, - const unsigned char *bin_pfx, - struct packed_git *p, +static void unique_in_pack(struct packed_git *p, struct disambiguate_state *ds) { uint32_t num, last, i, first = 0; - const unsigned char *current = NULL; + const struct object_id *current = NULL; open_pack_index(p); num = p->num_objects; @@ -155,7 +151,7 @@ static void unique_in_pack(int len, int cmp; current = nth_packed_object_sha1(p, mid); - cmp = hashcmp(bin_pfx, current); + cmp = hashcmp(ds->bin_pfx.hash, current); if (!cmp) { first = mid; break; @@ -173,21 +169,21 @@ static void unique_in_pack(int len, * 0, 1 or more objects that actually match(es). */ for (i = first; i < num && !ds->ambiguous; i++) { - current = nth_packed_object_sha1(p, i); - if (!match_sha(len, bin_pfx, current)) + struct object_id oid; + current = nth_packed_object_oid(&oid, p, i); + if (!match_sha(ds->len, ds->bin_pfx.hash, current->hash)) break; update_candidates(ds, current); } } -static void find_short_packed_object(int len, const unsigned char *bin_pfx, - struct disambiguate_state *ds) +static void find_short_packed_object(struct disambiguate_state *ds) { struct packed_git *p; prepare_packed_git(); for (p = packed_git; p && !ds->ambiguous; p = p->next) - unique_in_pack(len, bin_pfx, p, ds); + unique_in_pack(p, ds); } #define SHORT_NAME_NOT_FOUND (-1) @@ -218,77 +214,109 @@ static int finish_object_disambiguation(struct disambiguate_state *ds, * same repository! */ ds->candidate_ok = (!ds->disambiguate_fn_used || - ds->fn(ds->candidate, ds->cb_data)); + ds->fn(&ds->candidate, ds->cb_data)); if (!ds->candidate_ok) return SHORT_NAME_AMBIGUOUS; - hashcpy(sha1, ds->candidate); + hashcpy(sha1, ds->candidate.hash); return 0; } -static int disambiguate_commit_only(const unsigned char *sha1, void *cb_data_unused) +static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused) { - int kind = sha1_object_info(sha1, NULL); + int kind = sha1_object_info(oid->hash, NULL); return kind == OBJ_COMMIT; } -static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data_unused) +static int disambiguate_committish_only(const struct object_id *oid, void *cb_data_unused) { struct object *obj; int kind; - kind = sha1_object_info(sha1, NULL); + kind = sha1_object_info(oid->hash, NULL); if (kind == OBJ_COMMIT) return 1; if (kind != OBJ_TAG) return 0; /* We need to do this the hard way... */ - obj = deref_tag(parse_object(sha1), NULL, 0); + obj = deref_tag(parse_object(oid), NULL, 0); if (obj && obj->type == OBJ_COMMIT) return 1; return 0; } -static int disambiguate_tree_only(const unsigned char *sha1, void *cb_data_unused) +static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused) { - int kind = sha1_object_info(sha1, NULL); + int kind = sha1_object_info(oid->hash, NULL); return kind == OBJ_TREE; } -static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_unused) +static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_unused) { struct object *obj; int kind; - kind = sha1_object_info(sha1, NULL); + kind = sha1_object_info(oid->hash, NULL); if (kind == OBJ_TREE || kind == OBJ_COMMIT) return 1; if (kind != OBJ_TAG) return 0; /* We need to do this the hard way... */ - obj = deref_tag(lookup_object(sha1), NULL, 0); + obj = deref_tag(parse_object(oid), NULL, 0); if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT)) return 1; return 0; } -static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unused) +static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused) { - int kind = sha1_object_info(sha1, NULL); + int kind = sha1_object_info(oid->hash, NULL); return kind == OBJ_BLOB; } -static int prepare_prefixes(const char *name, int len, - unsigned char *bin_pfx, - char *hex_pfx) +static disambiguate_hint_fn default_disambiguate_hint; + +int set_disambiguate_hint_config(const char *var, const char *value) +{ + static const struct { + const char *name; + disambiguate_hint_fn fn; + } hints[] = { + { "none", NULL }, + { "commit", disambiguate_commit_only }, + { "committish", disambiguate_committish_only }, + { "tree", disambiguate_tree_only }, + { "treeish", disambiguate_treeish_only }, + { "blob", disambiguate_blob_only } + }; + int i; + + if (!value) + return config_error_nonbool(var); + + for (i = 0; i < ARRAY_SIZE(hints); i++) { + if (!strcasecmp(value, hints[i].name)) { + default_disambiguate_hint = hints[i].fn; + return 0; + } + } + + return error("unknown hint type for '%s': %s", var, value); +} + +static int init_object_disambiguation(const char *name, int len, + struct disambiguate_state *ds) { int i; - hashclr(bin_pfx); - memset(hex_pfx, 'x', 40); + if (len < MINIMUM_ABBREV || len > GIT_SHA1_HEXSZ) + return -1; + + memset(ds, 0, sizeof(*ds)); + for (i = 0; i < len ;i++) { unsigned char c = name[i]; unsigned char val; @@ -302,11 +330,48 @@ static int prepare_prefixes(const char *name, int len, } else return -1; - hex_pfx[i] = c; + ds->hex_pfx[i] = c; if (!(i & 1)) val <<= 4; - bin_pfx[i >> 1] |= val; + ds->bin_pfx.hash[i >> 1] |= val; } + + ds->len = len; + ds->hex_pfx[len] = '\0'; + prepare_alt_odb(); + return 0; +} + +static int show_ambiguous_object(const struct object_id *oid, void *data) +{ + const struct disambiguate_state *ds = data; + struct strbuf desc = STRBUF_INIT; + int type; + + + if (ds->fn && !ds->fn(oid, ds->cb_data)) + return 0; + + type = sha1_object_info(oid->hash, NULL); + if (type == OBJ_COMMIT) { + struct commit *commit = lookup_commit(oid); + if (commit) { + struct pretty_print_context pp = {0}; + pp.date_mode.type = DATE_SHORT; + format_commit_message(commit, " %ad - %s", &desc, &pp); + } + } else if (type == OBJ_TAG) { + struct tag *tag = lookup_tag(oid); + if (!parse_tag(tag) && tag->tag) + strbuf_addf(&desc, " %s", tag->tag); + } + + advise(" %s %s%s", + find_unique_abbrev(oid->hash, DEFAULT_ABBREV), + typename(type) ? typename(type) : "unknown type", + desc.buf); + + strbuf_release(&desc); return 0; } @@ -314,19 +379,15 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1, unsigned flags) { int status; - char hex_pfx[40]; - unsigned char bin_pfx[20]; struct disambiguate_state ds; int quietly = !!(flags & GET_SHA1_QUIETLY); - if (len < MINIMUM_ABBREV || len > 40) - return -1; - if (prepare_prefixes(name, len, bin_pfx, hex_pfx) < 0) + if (init_object_disambiguation(name, len, &ds) < 0) return -1; - prepare_alt_odb(); + if (HAS_MULTI_BITS(flags & GET_SHA1_DISAMBIGUATORS)) + die("BUG: multiple get_short_sha1 disambiguator flags"); - memset(&ds, 0, sizeof(ds)); if (flags & GET_SHA1_COMMIT) ds.fn = disambiguate_commit_only; else if (flags & GET_SHA1_COMMITTISH) @@ -337,44 +398,98 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1, ds.fn = disambiguate_treeish_only; else if (flags & GET_SHA1_BLOB) ds.fn = disambiguate_blob_only; + else + ds.fn = default_disambiguate_hint; - find_short_object_filename(len, hex_pfx, &ds); - find_short_packed_object(len, bin_pfx, &ds); + find_short_object_filename(&ds); + find_short_packed_object(&ds); status = finish_object_disambiguation(&ds, sha1); - if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) - return error("short SHA1 %.*s is ambiguous.", len, hex_pfx); + if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) { + error(_("short SHA1 %s is ambiguous"), ds.hex_pfx); + + /* + * We may still have ambiguity if we simply saw a series of + * candidates that did not satisfy our hint function. In + * that case, we still want to show them, so disable the hint + * function entirely. + */ + if (!ds.ambiguous) + ds.fn = NULL; + + advise(_("The candidates are:")); + for_each_abbrev(ds.hex_pfx, show_ambiguous_object, &ds); + } + return status; } +static int collect_ambiguous(const struct object_id *oid, void *data) +{ + oid_array_append(data, oid); + return 0; +} + int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data) { - char hex_pfx[40]; - unsigned char bin_pfx[20]; + struct oid_array collect = OID_ARRAY_INIT; struct disambiguate_state ds; - int len = strlen(prefix); + int ret; - if (len < MINIMUM_ABBREV || len > 40) + if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0) return -1; - if (prepare_prefixes(prefix, len, bin_pfx, hex_pfx) < 0) - return -1; - - prepare_alt_odb(); - memset(&ds, 0, sizeof(ds)); ds.always_call_fn = 1; - ds.cb_data = cb_data; - ds.fn = fn; + ds.fn = collect_ambiguous; + ds.cb_data = &collect; + find_short_object_filename(&ds); + find_short_packed_object(&ds); + + ret = oid_array_for_each_unique(&collect, fn, cb_data); + oid_array_clear(&collect); + return ret; +} - find_short_object_filename(len, hex_pfx, &ds); - find_short_packed_object(len, bin_pfx, &ds); - return ds.ambiguous; +/* + * Return the slot of the most-significant bit set in "val". There are various + * ways to do this quickly with fls() or __builtin_clzl(), but speed is + * probably not a big deal here. + */ +static unsigned msb(unsigned long val) +{ + unsigned r = 0; + while (val >>= 1) + r++; + return r; } int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len) { int status, exists; + if (len < 0) { + unsigned long count = approximate_object_count(); + /* + * Add one because the MSB only tells us the highest bit set, + * not including the value of all the _other_ bits (so "15" + * is only one off of 2^4, but the MSB is the 3rd bit. + */ + len = msb(count) + 1; + /* + * We now know we have on the order of 2^len objects, which + * expects a collision at 2^(len/2). But we also care about hex + * chars, not bits, and there are 4 bits per hex. So all + * together we need to divide by 2; but we also want to round + * odd numbers up, hence adding one before dividing. + */ + len = (len + 1) / 2; + /* + * For very small repos, we stick with our regular fallback. + */ + if (len < FALLBACK_DEFAULT_ABBREV) + len = FALLBACK_DEFAULT_ABBREV; + } + sha1_to_hex_r(hex, sha1); if (len == 40 || !len) return 40; @@ -395,7 +510,10 @@ int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len) const char *find_unique_abbrev(const unsigned char *sha1, int len) { - static char hex[GIT_SHA1_HEXSZ + 1]; + static int bufno; + static char hexbuffer[4][GIT_MAX_HEXSZ + 1]; + char *hex = hexbuffer[bufno]; + bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer); find_unique_abbrev_r(hex, sha1, len); return hex; } @@ -433,7 +551,7 @@ static inline int at_mark(const char *string, int len, for (i = 0; i < nr; i++) { int suffix_len = strlen(suffix[i]); if (suffix_len <= len - && !memcmp(string, suffix[i], suffix_len)) + && !strncasecmp(string, suffix[i], suffix_len)) return suffix_len; } return 0; @@ -542,8 +660,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1, if (reflog_len) { int nth, i; - unsigned long at_time; - unsigned long co_time; + timestamp_t at_time; + timestamp_t co_time; int co_tz, co_cnt; /* Is it asking for N-th entry, or approxidate? */ @@ -604,14 +722,14 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1, static int get_parent(const char *name, int len, unsigned char *result, int idx) { - unsigned char sha1[20]; - int ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH); + struct object_id oid; + int ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH); struct commit *commit; struct commit_list *p; if (ret) return ret; - commit = lookup_commit_reference(sha1); + commit = lookup_commit_reference(&oid); if (parse_commit(commit)) return -1; if (!idx) { @@ -632,14 +750,14 @@ static int get_parent(const char *name, int len, static int get_nth_ancestor(const char *name, int len, unsigned char *result, int generation) { - unsigned char sha1[20]; + struct object_id oid; struct commit *commit; int ret; - ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH); + ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH); if (ret) return ret; - commit = lookup_commit_reference(sha1); + commit = lookup_commit_reference(&oid); if (!commit) return -1; @@ -658,7 +776,7 @@ struct object *peel_to_type(const char *name, int namelen, if (name && !namelen) namelen = strlen(name); while (1) { - if (!o || (!o->parsed && !parse_object(o->oid.hash))) + if (!o || (!o->parsed && !parse_object(&o->oid))) return NULL; if (expected_type == OBJ_ANY || o->type == expected_type) return o; @@ -677,12 +795,12 @@ struct object *peel_to_type(const char *name, int namelen, } } -static int peel_onion(const char *name, int len, unsigned char *sha1) +static int peel_onion(const char *name, int len, unsigned char *sha1, + unsigned lookup_flags) { - unsigned char outer[20]; + struct object_id outer; const char *sp; unsigned int expected_type = 0; - unsigned lookup_flags = 0; struct object *o; /* @@ -722,20 +840,21 @@ static int peel_onion(const char *name, int len, unsigned char *sha1) else return -1; + lookup_flags &= ~GET_SHA1_DISAMBIGUATORS; if (expected_type == OBJ_COMMIT) - lookup_flags = GET_SHA1_COMMITTISH; + lookup_flags |= GET_SHA1_COMMITTISH; else if (expected_type == OBJ_TREE) - lookup_flags = GET_SHA1_TREEISH; + lookup_flags |= GET_SHA1_TREEISH; - if (get_sha1_1(name, sp - name - 2, outer, lookup_flags)) + if (get_sha1_1(name, sp - name - 2, outer.hash, lookup_flags)) return -1; - o = parse_object(outer); + o = parse_object(&outer); if (!o) return -1; if (!expected_type) { o = deref_tag(o, name, sp - name - 2); - if (!o || (!o->parsed && !parse_object(o->oid.hash))) + if (!o || (!o->parsed && !parse_object(&o->oid))) return -1; hashcpy(sha1, o->oid.hash); return 0; @@ -826,7 +945,7 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned l return get_nth_ancestor(name, len1, sha1, num); } - ret = peel_onion(name, len, sha1); + ret = peel_onion(name, len, sha1, lookup_flags); if (!ret) return 0; @@ -862,7 +981,7 @@ static int handle_one_ref(const char *path, const struct object_id *oid, int flag, void *cb_data) { struct commit_list **list = cb_data; - struct object *object = parse_object(oid->hash); + struct object *object = parse_object(oid); if (!object) return 0; if (object->type == OBJ_TAG) { @@ -908,7 +1027,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1, int matches; commit = pop_most_recent_commit(&list, ONELINE_SEEN); - if (!parse_object(commit->object.oid.hash)) + if (!parse_object(&commit->object.oid)) continue; buf = get_commit_buffer(commit, NULL); p = strstr(buf, "\n\n"); @@ -934,8 +1053,8 @@ struct grab_nth_branch_switch_cbdata { struct strbuf buf; }; -static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, - const char *email, unsigned long timestamp, int tz, +static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct grab_nth_branch_switch_cbdata *cb = cb_data; @@ -995,35 +1114,35 @@ static int interpret_nth_prior_checkout(const char *name, int namelen, return retval; } -int get_sha1_mb(const char *name, unsigned char *sha1) +int get_oid_mb(const char *name, struct object_id *oid) { struct commit *one, *two; struct commit_list *mbs; - unsigned char sha1_tmp[20]; + struct object_id oid_tmp; const char *dots; int st; dots = strstr(name, "..."); if (!dots) - return get_sha1(name, sha1); + return get_oid(name, oid); if (dots == name) - st = get_sha1("HEAD", sha1_tmp); + st = get_oid("HEAD", &oid_tmp); else { struct strbuf sb; strbuf_init(&sb, dots - name); strbuf_add(&sb, name, dots - name); - st = get_sha1_committish(sb.buf, sha1_tmp); + st = get_sha1_committish(sb.buf, oid_tmp.hash); strbuf_release(&sb); } if (st) return st; - one = lookup_commit_reference_gently(sha1_tmp, 0); + one = lookup_commit_reference_gently(&oid_tmp, 0); if (!one) return -1; - if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", sha1_tmp)) + if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", oid_tmp.hash)) return -1; - two = lookup_commit_reference_gently(sha1_tmp, 0); + two = lookup_commit_reference_gently(&oid_tmp, 0); if (!two) return -1; mbs = get_merge_bases(one, two); @@ -1031,7 +1150,7 @@ int get_sha1_mb(const char *name, unsigned char *sha1) st = -1; else { st = 0; - hashcpy(sha1, mbs->item->object.oid.hash); + oidcpy(oid, &mbs->item->object.oid); } free_commit_list(mbs); return st; @@ -1059,7 +1178,8 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str return 1; } -static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf) +static int reinterpret(const char *name, int namelen, int len, + struct strbuf *buf, unsigned allowed) { /* we have extra data, which might need further processing */ struct strbuf tmp = STRBUF_INIT; @@ -1067,7 +1187,7 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu int ret; strbuf_add(buf, name + len, namelen - len); - ret = interpret_branch_name(buf->buf, buf->len, &tmp); + ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed); /* that data was not interpreted, remove our cruft */ if (ret < 0) { strbuf_setlen(buf, used); @@ -1088,11 +1208,27 @@ static void set_shortened_ref(struct strbuf *buf, const char *ref) free(s); } +static int branch_interpret_allowed(const char *refname, unsigned allowed) +{ + if (!allowed) + return 1; + + if ((allowed & INTERPRET_BRANCH_LOCAL) && + starts_with(refname, "refs/heads/")) + return 1; + if ((allowed & INTERPRET_BRANCH_REMOTE) && + starts_with(refname, "refs/remotes/")) + return 1; + + return 0; +} + static int interpret_branch_mark(const char *name, int namelen, int at, struct strbuf *buf, int (*get_mark)(const char *, int), const char *(*get_data)(struct branch *, - struct strbuf *)) + struct strbuf *), + unsigned allowed) { int len; struct branch *branch; @@ -1117,64 +1253,55 @@ static int interpret_branch_mark(const char *name, int namelen, if (!value) die("%s", err.buf); + if (!branch_interpret_allowed(value, allowed)) + return -1; + set_shortened_ref(buf, value); return len + at; } -/* - * This reads short-hand syntax that not only evaluates to a commit - * object name, but also can act as if the end user spelled the name - * of the branch from the command line. - * - * - "@{-N}" finds the name of the Nth previous branch we were on, and - * places the name of the branch in the given buf and returns the - * number of characters parsed if successful. - * - * - "<branch>@{upstream}" finds the name of the other ref that - * <branch> is configured to merge with (missing <branch> defaults - * to the current branch), and places the name of the branch in the - * given buf and returns the number of characters parsed if - * successful. - * - * If the input is not of the accepted format, it returns a negative - * number to signal an error. - * - * If the input was ok but there are not N branch switches in the - * reflog, it returns 0. - */ -int interpret_branch_name(const char *name, int namelen, struct strbuf *buf) +int interpret_branch_name(const char *name, int namelen, struct strbuf *buf, + unsigned allowed) { char *at; const char *start; - int len = interpret_nth_prior_checkout(name, namelen, buf); + int len; if (!namelen) namelen = strlen(name); - if (!len) { - return len; /* syntax Ok, not enough switches */ - } else if (len > 0) { - if (len == namelen) - return len; /* consumed all */ - else - return reinterpret(name, namelen, len, buf); + if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) { + len = interpret_nth_prior_checkout(name, namelen, buf); + if (!len) { + return len; /* syntax Ok, not enough switches */ + } else if (len > 0) { + if (len == namelen) + return len; /* consumed all */ + else + return reinterpret(name, namelen, len, buf, allowed); + } } for (start = name; (at = memchr(start, '@', namelen - (start - name))); start = at + 1) { - len = interpret_empty_at(name, namelen, at - name, buf); - if (len > 0) - return reinterpret(name, namelen, len, buf); + if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) { + len = interpret_empty_at(name, namelen, at - name, buf); + if (len > 0) + return reinterpret(name, namelen, len, buf, + allowed); + } len = interpret_branch_mark(name, namelen, at - name, buf, - upstream_mark, branch_get_upstream); + upstream_mark, branch_get_upstream, + allowed); if (len > 0) return len; len = interpret_branch_mark(name, namelen, at - name, buf, - push_mark, branch_get_push); + push_mark, branch_get_push, + allowed); if (len > 0) return len; } @@ -1182,22 +1309,19 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf) return -1; } -int strbuf_branchname(struct strbuf *sb, const char *name) +void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed) { int len = strlen(name); - int used = interpret_branch_name(name, len, sb); + int used = interpret_branch_name(name, len, sb, allowed); - if (used == len) - return 0; if (used < 0) used = 0; strbuf_add(sb, name + used, len - used); - return len; } int strbuf_check_branch_ref(struct strbuf *sb, const char *name) { - strbuf_branchname(sb, name); + strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL); if (name[0] == '-') return -1; strbuf_splice(sb, 0, 0, "refs/heads/", 11); @@ -1382,8 +1506,12 @@ static int get_sha1_with_context_1(const char *name, const char *cp; int only_to_die = flags & GET_SHA1_ONLY_TO_DIE; + if (only_to_die) + flags |= GET_SHA1_QUIETLY; + memset(oc, 0, sizeof(*oc)); oc->mode = S_IFINVALID; + strbuf_init(&oc->symlink_path, 0); ret = get_sha1_1(name, namelen, sha1, flags); if (!ret) return ret; @@ -1422,7 +1550,8 @@ static int get_sha1_with_context_1(const char *name, namelen = strlen(cp); } - strlcpy(oc->path, cp, sizeof(oc->path)); + if (flags & GET_SHA1_RECORD_PATH) + oc->path = xstrdup(cp); if (!active_cache) read_cache(); @@ -1435,7 +1564,7 @@ static int get_sha1_with_context_1(const char *name, memcmp(ce->name, cp, namelen)) break; if (ce_stage(ce) == stage) { - hashcpy(sha1, ce->sha1); + hashcpy(sha1, ce->oid.hash); oc->mode = ce->ce_mode; free(new_path); return 0; @@ -1458,7 +1587,12 @@ static int get_sha1_with_context_1(const char *name, if (*cp == ':') { unsigned char tree_sha1[20]; int len = cp - name; - if (!get_sha1_1(name, len, tree_sha1, GET_SHA1_TREEISH)) { + unsigned sub_flags = flags; + + sub_flags &= ~GET_SHA1_DISAMBIGUATORS; + sub_flags |= GET_SHA1_TREEISH; + + if (!get_sha1_1(name, len, tree_sha1, sub_flags)) { const char *filename = cp+1; char *new_filename = NULL; @@ -1480,7 +1614,8 @@ static int get_sha1_with_context_1(const char *name, } } hashcpy(oc->tree, tree_sha1); - strlcpy(oc->path, filename, sizeof(oc->path)); + if (flags & GET_SHA1_RECORD_PATH) + oc->path = xstrdup(filename); free(new_filename); return ret; @@ -1506,9 +1641,9 @@ void maybe_die_on_misspelt_object_name(const char *name, const char *prefix) get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc); } -int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc) +int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc) { if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE) die("BUG: incompatible flags for get_sha1_with_context"); - return get_sha1_with_context_1(str, flags, NULL, sha1, orc); + return get_sha1_with_context_1(str, flags, NULL, sha1, oc); } |