diff options
Diffstat (limited to 'object-name.c')
| -rw-r--r-- | object-name.c | 329 |
1 files changed, 210 insertions, 119 deletions
diff --git a/object-name.c b/object-name.c index 3a2ef5d680..945d5bdef2 100644 --- a/object-name.c +++ b/object-name.c @@ -1,3 +1,6 @@ +#define USE_THE_REPOSITORY_VARIABLE +#define DISABLE_SIGN_COMPARE_WARNINGS + #include "git-compat-util.h" #include "object-name.h" #include "advice.h" @@ -18,13 +21,16 @@ #include "pretty.h" #include "object-store-ll.h" #include "read-cache-ll.h" +#include "repo-settings.h" #include "repository.h" #include "setup.h" #include "midx.h" #include "commit-reach.h" #include "date.h" +#include "object-file-convert.h" -static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *); +static int get_oid_oneline(struct repository *r, const char *, struct object_id *, + const struct commit_list *); typedef int (*disambiguate_hint_fn)(struct repository *, const struct object_id *, void *); @@ -47,6 +53,7 @@ struct disambiguate_state { static void update_candidates(struct disambiguate_state *ds, const struct object_id *current) { + /* The hash algorithm of current has already been filtered */ if (ds->always_call_fn) { ds->ambiguous = ds->fn(ds->repo, current, ds->cb_data) ? 1 : 0; return; @@ -130,26 +137,32 @@ static int match_hash(unsigned len, const unsigned char *a, const unsigned char static void unique_in_midx(struct multi_pack_index *m, struct disambiguate_state *ds) { - uint32_t num, i, first = 0; - const struct object_id *current = NULL; - num = m->num_objects; + for (; m; m = m->base_midx) { + uint32_t num, i, first = 0; + const struct object_id *current = NULL; + int len = ds->len > ds->repo->hash_algo->hexsz ? + ds->repo->hash_algo->hexsz : ds->len; - if (!num) - return; + if (!m->num_objects) + continue; - bsearch_midx(&ds->bin_pfx, m, &first); + num = m->num_objects + m->num_objects_in_base; - /* - * At this point, "first" is the location of the lowest object - * with an object name that could match "bin_pfx". See if we have - * 0, 1 or more objects that actually match(es). - */ - for (i = first; i < num && !ds->ambiguous; i++) { - struct object_id oid; - current = nth_midxed_object_oid(&oid, m, i); - if (!match_hash(ds->len, ds->bin_pfx.hash, current->hash)) - break; - update_candidates(ds, current); + bsearch_one_midx(&ds->bin_pfx, m, &first); + + /* + * At this point, "first" is the location of the lowest + * object with an object name that could match + * "bin_pfx". See if we have 0, 1 or more objects that + * actually match(es). + */ + for (i = first; i < num && !ds->ambiguous; i++) { + struct object_id oid; + current = nth_midxed_object_oid(&oid, m, i); + if (!match_hash(len, ds->bin_pfx.hash, current->hash)) + break; + update_candidates(ds, current); + } } } @@ -157,6 +170,8 @@ static void unique_in_pack(struct packed_git *p, struct disambiguate_state *ds) { uint32_t num, i, first = 0; + int len = ds->len > ds->repo->hash_algo->hexsz ? + ds->repo->hash_algo->hexsz : ds->len; if (p->multi_pack_index) return; @@ -175,7 +190,7 @@ static void unique_in_pack(struct packed_git *p, for (i = first; i < num && !ds->ambiguous; i++) { struct object_id oid; nth_packed_object_id(&oid, p, i); - if (!match_hash(ds->len, ds->bin_pfx.hash, oid.hash)) + if (!match_hash(len, ds->bin_pfx.hash, oid.hash)) break; update_candidates(ds, &oid); } @@ -186,6 +201,10 @@ static void find_short_packed_object(struct disambiguate_state *ds) struct multi_pack_index *m; struct packed_git *p; + /* Skip, unless oids from the storage hash algorithm are wanted */ + if (ds->bin_pfx.algo && (&hash_algos[ds->bin_pfx.algo] != ds->repo->hash_algo)) + return; + for (m = get_multi_pack_index(ds->repo); m && !ds->ambiguous; m = m->next) unique_in_midx(m, ds); @@ -324,11 +343,12 @@ int set_disambiguate_hint_config(const char *var, const char *value) static int init_object_disambiguation(struct repository *r, const char *name, int len, + const struct git_hash_algo *algo, struct disambiguate_state *ds) { int i; - if (len < MINIMUM_ABBREV || len > the_hash_algo->hexsz) + if (len < MINIMUM_ABBREV || len > GIT_MAX_HEXSZ) return -1; memset(ds, 0, sizeof(*ds)); @@ -355,6 +375,7 @@ static int init_object_disambiguation(struct repository *r, ds->len = len; ds->hex_pfx[len] = '\0'; ds->repo = r; + ds->bin_pfx.algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN; prepare_alt_odb(r); return 0; } @@ -489,9 +510,10 @@ static int repo_collect_ambiguous(struct repository *r UNUSED, return collect_ambiguous(oid, data); } -static int sort_ambiguous(const void *a, const void *b, void *ctx) +static int sort_ambiguous(const void *va, const void *vb, void *ctx) { struct repository *sort_ambiguous_repo = ctx; + const struct object_id *a = va, *b = vb; int a_type = oid_object_info(sort_ambiguous_repo, a, NULL); int b_type = oid_object_info(sort_ambiguous_repo, b, NULL); int a_type_sort; @@ -501,8 +523,12 @@ static int sort_ambiguous(const void *a, const void *b, void *ctx) * Sorts by hash within the same object type, just as * oid_array_for_each_unique() would do. */ - if (a_type == b_type) - return oidcmp(a, b); + if (a_type == b_type) { + if (a->algo == b->algo) + return oidcmp(a, b); + else + return a->algo > b->algo ? 1 : -1; + } /* * Between object types show tags, then commits, and finally @@ -531,8 +557,12 @@ static enum get_oid_result get_short_oid(struct repository *r, int status; struct disambiguate_state ds; int quietly = !!(flags & GET_OID_QUIETLY); + const struct git_hash_algo *algo = r->hash_algo; - if (init_object_disambiguation(r, name, len, &ds) < 0) + if (flags & GET_OID_HASH_ANY) + algo = NULL; + + if (init_object_disambiguation(r, name, len, algo, &ds) < 0) return -1; if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS)) @@ -586,7 +616,7 @@ static enum get_oid_result get_short_oid(struct repository *r, if (!ds.ambiguous) ds.fn = NULL; - repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect); + repo_for_each_abbrev(r, ds.hex_pfx, algo, collect_ambiguous, &collect); sort_ambiguous_oid_array(r, &collect); if (oid_array_for_each(&collect, show_ambiguous_object, &out)) @@ -608,13 +638,14 @@ static enum get_oid_result get_short_oid(struct repository *r, } int repo_for_each_abbrev(struct repository *r, const char *prefix, + const struct git_hash_algo *algo, each_abbrev_fn fn, void *cb_data) { struct oid_array collect = OID_ARRAY_INIT; struct disambiguate_state ds; int ret; - if (init_object_disambiguation(r, prefix, strlen(prefix), &ds) < 0) + if (init_object_disambiguation(r, prefix, strlen(prefix), algo, &ds) < 0) return -1; ds.always_call_fn = 1; @@ -684,37 +715,40 @@ static int repo_extend_abbrev_len(struct repository *r UNUSED, static void find_abbrev_len_for_midx(struct multi_pack_index *m, struct min_abbrev_data *mad) { - int match = 0; - uint32_t num, first = 0; - struct object_id oid; - const struct object_id *mad_oid; + for (; m; m = m->base_midx) { + int match = 0; + uint32_t num, first = 0; + struct object_id oid; + const struct object_id *mad_oid; - if (!m->num_objects) - return; + if (!m->num_objects) + continue; - num = m->num_objects; - mad_oid = mad->oid; - match = bsearch_midx(mad_oid, m, &first); + num = m->num_objects + m->num_objects_in_base; + mad_oid = mad->oid; + match = bsearch_one_midx(mad_oid, m, &first); - /* - * first is now the position in the packfile where we would insert - * mad->hash if it does not exist (or the position of mad->hash if - * it does exist). Hence, we consider a maximum of two objects - * nearby for the abbreviation length. - */ - mad->init_len = 0; - if (!match) { - if (nth_midxed_object_oid(&oid, m, first)) - extend_abbrev_len(&oid, mad); - } else if (first < num - 1) { - if (nth_midxed_object_oid(&oid, m, first + 1)) - extend_abbrev_len(&oid, mad); - } - if (first > 0) { - if (nth_midxed_object_oid(&oid, m, first - 1)) - extend_abbrev_len(&oid, mad); + /* + * first is now the position in the packfile where we + * would insert mad->hash if it does not exist (or the + * position of mad->hash if it does exist). Hence, we + * consider a maximum of two objects nearby for the + * abbreviation length. + */ + mad->init_len = 0; + if (!match) { + if (nth_midxed_object_oid(&oid, m, first)) + extend_abbrev_len(&oid, mad); + } else if (first < num - 1) { + if (nth_midxed_object_oid(&oid, m, first + 1)) + extend_abbrev_len(&oid, mad); + } + if (first > 0) { + if (nth_midxed_object_oid(&oid, m, first - 1)) + extend_abbrev_len(&oid, mad); + } + mad->init_len = mad->cur_len; } - mad->init_len = mad->cur_len; } static void find_abbrev_len_for_pack(struct packed_git *p, @@ -785,10 +819,12 @@ void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid, int repo_find_unique_abbrev_r(struct repository *r, char *hex, const struct object_id *oid, int len) { + const struct git_hash_algo *algo = + oid->algo ? &hash_algos[oid->algo] : r->hash_algo; struct disambiguate_state ds; struct min_abbrev_data mad; struct object_id oid_ret; - const unsigned hexsz = r->hash_algo->hexsz; + const unsigned hexsz = algo->hexsz; if (len < 0) { unsigned long count = repo_approximate_object_count(r); @@ -813,7 +849,7 @@ int repo_find_unique_abbrev_r(struct repository *r, char *hex, } oid_to_hex_r(hex, oid); - if (len == hexsz || !len) + if (len >= hexsz || !len) return hexsz; mad.repo = r; @@ -824,7 +860,7 @@ int repo_find_unique_abbrev_r(struct repository *r, char *hex, find_abbrev_len_packed(&mad); - if (init_object_disambiguation(r, hex, mad.cur_len, &ds) < 0) + if (init_object_disambiguation(r, hex, mad.cur_len, algo, &ds) < 0) return -1; ds.fn = repo_extend_abbrev_len; @@ -917,7 +953,7 @@ static int get_oid_basic(struct repository *r, const char *str, int len, "\n" "where \"$br\" is somehow empty and a 40-hex ref is created. Please\n" "examine these refs and maybe delete them. Turn this message off by\n" - "running \"git config advice.objectNameWarning false\""); + "running \"git config set advice.objectNameWarning false\""); struct object_id tmp_oid; char *real_ref = NULL; int refs_found = 0; @@ -925,7 +961,7 @@ static int get_oid_basic(struct repository *r, const char *str, int len, int fatal = !(flags & GET_OID_QUIETLY); if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) { - if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) { + if (repo_settings_get_warn_ambiguous_refs(r) && warn_on_object_refname_ambiguity) { refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref, 0); if (refs_found > 0) { warning(warn_msg, len, str); @@ -986,7 +1022,7 @@ static int get_oid_basic(struct repository *r, const char *str, int len, if (!refs_found) return -1; - if (warn_ambiguous_refs && !(flags & GET_OID_QUIETLY) && + if (repo_settings_get_warn_ambiguous_refs(r) && !(flags & GET_OID_QUIETLY) && (refs_found > 1 || !get_short_oid(r, str, len, &tmp_oid, GET_OID_QUIETLY))) warning(warn_msg, len, str); @@ -1034,6 +1070,15 @@ static int get_oid_basic(struct repository *r, const char *str, int len, len, str, show_date(co_time, co_tz, DATE_MODE(RFC2822))); } + } else if (nth == co_cnt && !is_null_oid(oid)) { + /* + * We were asked for the Nth reflog (counting + * from 0), but there were only N entries. + * read_ref_at() will have returned "1" to tell + * us it did not find an entry, but it did + * still fill in the oid with the "old" value, + * which we can use. + */ } else { if (flags & GET_OID_QUIETLY) { exit(128); @@ -1219,12 +1264,66 @@ static int peel_onion(struct repository *r, const char *name, int len, prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1)); commit_list_insert((struct commit *)o, &list); ret = get_oid_oneline(r, prefix, oid, list); + + free_commit_list(list); free(prefix); return ret; } return 0; } +/* + * Documentation/revisions.txt says: + * '<describeOutput>', e.g. 'v1.7.4.2-679-g3bee7fb':: + * Output from `git describe`; i.e. a closest tag, optionally + * followed by a dash and a number of commits, followed by a dash, a + * 'g', and an abbreviated object name. + * + * which means that the stuff before '-g${HASH}' needs to be a valid + * refname, a dash, and a non-negative integer. This function verifies + * that. + * + * In particular, we do not want to treat + * branchname:path/to/file/named/i-gaffed + * as a request for commit affed. + * + * More generally, we should probably not treat + * 'refs/heads/./../.../ ~^:/?*[////\\\&}/busted.lock-g050e0ef6ead' + * as a request for object 050e0ef6ead either. + * + * We are called with name[len] == '-' and name[len+1] == 'g', i.e. + * we are verifying ${REFNAME}-{INTEGER} part of the name. + */ +static int ref_and_count_parts_valid(const char *name, int len) +{ + struct strbuf sb; + const char *cp; + int flags = REFNAME_ALLOW_ONELEVEL; + int ret = 1; + + /* Ensure we have at least one digit */ + if (!isxdigit(name[len-1])) + return 0; + + /* Skip over digits backwards until we get to the dash */ + for (cp = name + len - 2; name < cp; cp--) { + if (*cp == '-') + break; + if (!isxdigit(*cp)) + return 0; + } + /* Ensure we found the leading dash */ + if (*cp != '-') + return 0; + + len = cp - name; + strbuf_init(&sb, len); + strbuf_add(&sb, name, len); + ret = !check_refname_format(sb.buf, flags); + strbuf_release(&sb); + return ret; +} + static int get_describe_name(struct repository *r, const char *name, int len, struct object_id *oid) @@ -1238,7 +1337,8 @@ static int get_describe_name(struct repository *r, /* We must be looking at g in "SOMETHING-g" * for it to be describe output. */ - if (ch == 'g' && cp[-1] == '-') { + if (ch == 'g' && cp[-1] == '-' && + ref_and_count_parts_valid(name, cp - 1 - name)) { cp++; len -= cp - name; return get_short_oid(r, @@ -1330,7 +1430,7 @@ struct handle_one_ref_cb { struct commit_list **list; }; -static int handle_one_ref(const char *path, const struct object_id *oid, +static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid, int flag UNUSED, void *cb_data) { @@ -1353,9 +1453,10 @@ static int handle_one_ref(const char *path, const struct object_id *oid, static int get_oid_oneline(struct repository *r, const char *prefix, struct object_id *oid, - struct commit_list *list) + const struct commit_list *list) { - struct commit_list *backup = NULL, *l; + struct commit_list *copy = NULL, **copy_tail = © + const struct commit_list *l; int found = 0; int negative = 0; regex_t regex; @@ -1376,14 +1477,14 @@ static int get_oid_oneline(struct repository *r, for (l = list; l; l = l->next) { l->item->object.flags |= ONELINE_SEEN; - commit_list_insert(l->item, &backup); + copy_tail = &commit_list_insert(l->item, copy_tail)->next; } - while (list) { + while (copy) { const char *p, *buf; struct commit *commit; int matches; - commit = pop_most_recent_commit(&list, ONELINE_SEEN); + commit = pop_most_recent_commit(©, ONELINE_SEEN); if (!parse_object(r, &commit->object.oid)) continue; buf = repo_get_commit_buffer(r, commit, NULL); @@ -1398,10 +1499,9 @@ static int get_oid_oneline(struct repository *r, } } regfree(®ex); - free_commit_list(list); - for (l = backup; l; l = l->next) + for (l = list; l; l = l->next) clear_commit_marks(l->item, ONELINE_SEEN); - free_commit_list(backup); + free_commit_list(copy); return found ? 0 : -1; } @@ -1479,7 +1579,7 @@ int repo_get_oid_mb(struct repository *r, struct object_id *oid) { struct commit *one, *two; - struct commit_list *mbs; + struct commit_list *mbs = NULL; struct object_id oid_tmp; const char *dots; int st; @@ -1507,7 +1607,10 @@ int repo_get_oid_mb(struct repository *r, two = lookup_commit_reference_gently(r, &oid_tmp, 0); if (!two) return -1; - mbs = repo_get_merge_bases(r, one, two); + if (repo_get_merge_bases(r, one, two, &mbs) < 0) { + free_commit_list(mbs); + return -1; + } if (!mbs || mbs->next) st = -1; else { @@ -1685,40 +1788,10 @@ int repo_interpret_branch_name(struct repository *r, return -1; } -void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed) -{ - int len = strlen(name); - struct interpret_branch_name_options options = { - .allowed = allowed - }; - int used = repo_interpret_branch_name(the_repository, name, len, sb, - &options); - - if (used < 0) - used = 0; - strbuf_add(sb, name + used, len - used); -} - -int strbuf_check_branch_ref(struct strbuf *sb, const char *name) +void object_context_release(struct object_context *ctx) { - if (startup_info->have_repository) - strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL); - else - strbuf_addstr(sb, name); - - /* - * This splice must be done even if we end up rejecting the - * name; builtin/branch.c::copy_or_rename_branch() still wants - * to see what the name expanded to so that "branch -m" can be - * used as a tool to correct earlier mistakes. - */ - strbuf_splice(sb, 0, 0, "refs/heads/", 11); - - if (*name == '-' || - !strcmp(sb->buf, "refs/heads/HEAD")) - return -1; - - return check_refname_format(sb->buf, 0); + free(ctx->path); + strbuf_release(&ctx->symlink_path); } /* @@ -1728,7 +1801,9 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name) int repo_get_oid(struct repository *r, const char *name, struct object_id *oid) { struct object_context unused; - return get_oid_with_context(r, name, 0, oid, &unused); + int ret = get_oid_with_context(r, name, 0, oid, &unused); + object_context_release(&unused); + return ret; } /* @@ -1766,8 +1841,10 @@ int repo_get_oid_committish(struct repository *r, struct object_id *oid) { struct object_context unused; - return get_oid_with_context(r, name, GET_OID_COMMITTISH, - oid, &unused); + int ret = get_oid_with_context(r, name, GET_OID_COMMITTISH, + oid, &unused); + object_context_release(&unused); + return ret; } int repo_get_oid_treeish(struct repository *r, @@ -1775,8 +1852,10 @@ int repo_get_oid_treeish(struct repository *r, struct object_id *oid) { struct object_context unused; - return get_oid_with_context(r, name, GET_OID_TREEISH, - oid, &unused); + int ret = get_oid_with_context(r, name, GET_OID_TREEISH, + oid, &unused); + object_context_release(&unused); + return ret; } int repo_get_oid_commit(struct repository *r, @@ -1784,8 +1863,10 @@ int repo_get_oid_commit(struct repository *r, struct object_id *oid) { struct object_context unused; - return get_oid_with_context(r, name, GET_OID_COMMIT, - oid, &unused); + int ret = get_oid_with_context(r, name, GET_OID_COMMIT, + oid, &unused); + object_context_release(&unused); + return ret; } int repo_get_oid_tree(struct repository *r, @@ -1793,8 +1874,10 @@ int repo_get_oid_tree(struct repository *r, struct object_id *oid) { struct object_context unused; - return get_oid_with_context(r, name, GET_OID_TREE, - oid, &unused); + int ret = get_oid_with_context(r, name, GET_OID_TREE, + oid, &unused); + object_context_release(&unused); + return ret; } int repo_get_oid_blob(struct repository *r, @@ -1802,8 +1885,10 @@ int repo_get_oid_blob(struct repository *r, struct object_id *oid) { struct object_context unused; - return get_oid_with_context(r, name, GET_OID_BLOB, - oid, &unused); + int ret = get_oid_with_context(r, name, GET_OID_BLOB, + oid, &unused); + object_context_release(&unused); + return ret; } /* Must be called only when object_name:filename doesn't exist. */ @@ -1969,7 +2054,10 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo, refs_for_each_ref(get_main_ref_store(repo), handle_one_ref, &cb); refs_head_ref(get_main_ref_store(repo), handle_one_ref, &cb); commit_list_sort_by_date(&list); - return get_oid_oneline(repo, name + 2, oid, list); + ret = get_oid_oneline(repo, name + 2, oid, list); + + free_commit_list(list); + return ret; } if (namelen < 3 || name[2] != ':' || @@ -2017,12 +2105,14 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo, return -1; } for (cp = name, bracket_depth = 0; *cp; cp++) { - if (*cp == '{') + if (strchr("@^", *cp) && cp[1] == '{') { + cp++; bracket_depth++; - else if (bracket_depth && *cp == '}') + } else if (bracket_depth && *cp == '}') { bracket_depth--; - else if (!bracket_depth && *cp == ':') + } else if (!bracket_depth && *cp == ':') { break; + } } if (*cp == ':') { struct object_id tree_oid; @@ -2081,6 +2171,7 @@ void maybe_die_on_misspelt_object_name(struct repository *r, struct object_id oid; get_oid_with_context_1(r, name, GET_OID_ONLY_TO_DIE | GET_OID_QUIETLY, prefix, &oid, &oc); + object_context_release(&oc); } enum get_oid_result get_oid_with_context(struct repository *repo, |
