summaryrefslogtreecommitdiff
path: root/sha1_name.c
diff options
context:
space:
mode:
Diffstat (limited to 'sha1_name.c')
-rw-r--r--sha1_name.c370
1 files changed, 250 insertions, 120 deletions
diff --git a/sha1_name.c b/sha1_name.c
index ca7ddd6f2c..cda9e49b12 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 *);
struct disambiguate_state {
+ int len; /* length of prefix in hex chars */
+ char hex_pfx[GIT_SHA1_HEXSZ + 1];
+ unsigned char bin_pfx[GIT_SHA1_RAWSZ];
+
disambiguate_hint_fn fn;
void *cb_data;
- unsigned char candidate[20];
+ unsigned char candidate[GIT_SHA1_RAWSZ];
unsigned candidate_exists:1;
unsigned candidate_checked:1;
unsigned candidate_ok:1;
@@ -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_SHA1_HEXSZ];
static struct alternate_object_database *fakeent;
if (!fakeent) {
@@ -86,25 +91,18 @@ 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;
@@ -113,7 +111,7 @@ static void find_short_object_filename(int len, const char *hex_pfx, struct disa
if (strlen(de->d_name) != 38)
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))
@@ -138,9 +136,7 @@ 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;
@@ -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, current);
if (!cmp) {
first = mid;
break;
@@ -174,20 +170,19 @@ static void unique_in_pack(int len,
*/
for (i = first; i < num && !ds->ambiguous; i++) {
current = nth_packed_object_sha1(p, i);
- if (!match_sha(len, bin_pfx, current))
+ if (!match_sha(ds->len, ds->bin_pfx, current))
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)
@@ -269,7 +264,7 @@ static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_un
return 0;
/* We need to do this the hard way... */
- obj = deref_tag(lookup_object(sha1), NULL, 0);
+ obj = deref_tag(parse_object(sha1), NULL, 0);
if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
return 1;
return 0;
@@ -281,14 +276,46 @@ static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unuse
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;
- hashclr(bin_pfx);
- memset(hex_pfx, 'x', 40);
+ 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;
+
+ 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 +329,47 @@ 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[i >> 1] |= val;
+ }
+
+ ds->len = len;
+ ds->hex_pfx[len] = '\0';
+ prepare_alt_odb();
+ return 0;
+}
+
+static int show_ambiguous_object(const unsigned char *sha1, void *data)
+{
+ const struct disambiguate_state *ds = data;
+ struct strbuf desc = STRBUF_INIT;
+ int type;
+
+ if (ds->fn && !ds->fn(sha1, ds->cb_data))
+ return 0;
+
+ type = sha1_object_info(sha1, NULL);
+ if (type == OBJ_COMMIT) {
+ struct commit *commit = lookup_commit(sha1);
+ 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(sha1);
+ if (!parse_tag(tag) && tag->tag)
+ strbuf_addf(&desc, " %s", tag->tag);
}
+
+ advise(" %s %s%s",
+ find_unique_abbrev(sha1, DEFAULT_ABBREV),
+ typename(type) ? typename(type) : "unknown type",
+ desc.buf);
+
+ strbuf_release(&desc);
return 0;
}
@@ -314,19 +377,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 +396,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 unsigned char *sha1, void *data)
+{
+ sha1_array_append(data, sha1);
+ 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 sha1_array collect = SHA1_ARRAY_INIT;
struct disambiguate_state ds;
- int len = strlen(prefix);
+ int ret;
- if (len < MINIMUM_ABBREV || len > 40)
- return -1;
- if (prepare_prefixes(prefix, len, bin_pfx, hex_pfx) < 0)
+ if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 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 = sha1_array_for_each_unique(&collect, fn, cb_data);
+ sha1_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 +508,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_SHA1_HEXSZ + 1];
+ char *hex = hexbuffer[bufno];
+ bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
find_unique_abbrev_r(hex, sha1, len);
return hex;
}
@@ -677,12 +793,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];
const char *sp;
unsigned int expected_type = 0;
- unsigned lookup_flags = 0;
struct object *o;
/*
@@ -722,10 +838,11 @@ 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))
return -1;
@@ -826,7 +943,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;
@@ -934,7 +1051,7 @@ struct grab_nth_branch_switch_cbdata {
struct strbuf buf;
};
-static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
+static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid,
const char *email, unsigned long timestamp, int tz,
const char *message, void *cb_data)
{
@@ -995,35 +1112,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.hash, 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.hash, 0);
if (!two)
return -1;
mbs = get_merge_bases(one, two);
@@ -1031,7 +1148,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 +1176,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 +1185,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 +1206,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 +1251,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 +1307,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,6 +1504,9 @@ 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;
ret = get_sha1_1(name, namelen, sha1, flags);
@@ -1435,7 +1560,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 +1583,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;