unsigned candidate_ok:1;
unsigned disambiguate_fn_used:1;
unsigned ambiguous:1;
+ unsigned always_call_fn:1;
};
static void update_candidates(struct disambiguate_state *ds, const unsigned char *current)
{
+ if (ds->always_call_fn) {
+ ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
+ return;
+ }
if (!ds->candidate_exists) {
/* this is the first candidate */
hashcpy(ds->candidate, current);
return 0;
}
-static int get_short_sha1(const char *name, int len, unsigned char *sha1,
- unsigned flags)
+static int disambiguate_tree_only(const unsigned char *sha1, void *cb_data_unused)
{
- int i, status;
- char hex_pfx[40];
- unsigned char bin_pfx[20];
- struct disambiguate_state ds;
- int quietly = !!(flags & GET_SHA1_QUIETLY);
+ int kind = sha1_object_info(sha1, NULL);
+ return kind == OBJ_TREE;
+}
+
+static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_unused)
+{
+ struct object *obj;
+ int kind;
+
+ kind = sha1_object_info(sha1, 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);
+ 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)
+{
+ int kind = sha1_object_info(sha1, NULL);
+ return kind == OBJ_BLOB;
+}
+
+static int prepare_prefixes(const char *name, int len,
+ unsigned char *bin_pfx,
+ char *hex_pfx)
+{
+ int i;
- if (len < MINIMUM_ABBREV || len > 40)
- return -1;
hashclr(bin_pfx);
memset(hex_pfx, 'x', 40);
for (i = 0; i < len ;i++) {
val <<= 4;
bin_pfx[i >> 1] |= val;
}
+ return 0;
+}
+
+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)
+ return -1;
prepare_alt_odb();
ds.fn = disambiguate_commit_only;
else if (flags & GET_SHA1_COMMITTISH)
ds.fn = disambiguate_committish_only;
+ else if (flags & GET_SHA1_TREE)
+ ds.fn = disambiguate_tree_only;
+ else if (flags & GET_SHA1_TREEISH)
+ ds.fn = disambiguate_treeish_only;
+ else if (flags & GET_SHA1_BLOB)
+ ds.fn = disambiguate_blob_only;
find_short_object_filename(len, hex_pfx, &ds);
find_short_packed_object(len, bin_pfx, &ds);
return status;
}
+
+int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
+{
+ char hex_pfx[40];
+ unsigned char bin_pfx[20];
+ struct disambiguate_state ds;
+ int len = strlen(prefix);
+
+ if (len < MINIMUM_ABBREV || len > 40)
+ 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;
+
+ find_short_object_filename(len, hex_pfx, &ds);
+ find_short_packed_object(len, bin_pfx, &ds);
+ return ds.ambiguous;
+}
+
const char *find_unique_abbrev(const unsigned char *sha1, int len)
{
int status, exists;
struct strbuf sb;
strbuf_init(&sb, dots - name);
strbuf_add(&sb, name, dots - name);
- st = get_sha1(sb.buf, sha1_tmp);
+ st = get_sha1_committish(sb.buf, sha1_tmp);
strbuf_release(&sb);
}
if (st)
if (!one)
return -1;
- if (get_sha1(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
+ if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
return -1;
two = lookup_commit_reference_gently(sha1_tmp, 0);
if (!two)
len = cp + tmp_len - name;
cp = xstrndup(name, cp - name);
upstream = branch_get(*cp ? cp : NULL);
- if (!upstream
- || !upstream->merge
- || !upstream->merge[0]->dst)
- return error("No upstream branch found for '%s'", cp);
+ /*
+ * Upstream can be NULL only if cp refers to HEAD and HEAD
+ * points to something different than a branch.
+ */
+ if (!upstream)
+ return error(_("HEAD does not point to a branch"));
+ if (!upstream->merge || !upstream->merge[0]->dst) {
+ if (!ref_exists(upstream->refname))
+ return error(_("No such branch: '%s'"), cp);
+ if (!upstream->merge)
+ return error(_("No upstream configured for branch '%s'"),
+ upstream->name);
+ return error(
+ _("Upstream branch '%s' not stored as a remote-tracking branch"),
+ upstream->merge[0]->src);
+ }
free(cp);
cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
strbuf_reset(buf);
return get_sha1_with_context(name, 0, sha1, &unused);
}
+/*
+ * Many callers know that the user meant to name a committish by
+ * syntactical positions where the object name appears. Calling this
+ * function allows the machinery to disambiguate shorter-than-unique
+ * abbreviated object names between committish and others.
+ *
+ * Note that this does NOT error out when the named object is not a
+ * committish. It is merely to give a hint to the disambiguation
+ * machinery.
+ */
+int get_sha1_committish(const char *name, unsigned char *sha1)
+{
+ struct object_context unused;
+ return get_sha1_with_context(name, GET_SHA1_COMMITTISH,
+ sha1, &unused);
+}
+
+int get_sha1_treeish(const char *name, unsigned char *sha1)
+{
+ struct object_context unused;
+ return get_sha1_with_context(name, GET_SHA1_TREEISH,
+ sha1, &unused);
+}
+
+int get_sha1_commit(const char *name, unsigned char *sha1)
+{
+ struct object_context unused;
+ return get_sha1_with_context(name, GET_SHA1_COMMIT,
+ sha1, &unused);
+}
+
+int get_sha1_tree(const char *name, unsigned char *sha1)
+{
+ struct object_context unused;
+ return get_sha1_with_context(name, GET_SHA1_TREE,
+ sha1, &unused);
+}
+
+int get_sha1_blob(const char *name, unsigned char *sha1)
+{
+ struct object_context unused;
+ return get_sha1_with_context(name, GET_SHA1_BLOB,
+ sha1, &unused);
+}
+
/* Must be called only when object_name:filename doesn't exist. */
static void diagnose_invalid_sha1_path(const char *prefix,
const char *filename,
strncpy(object_name, name, cp-name);
object_name[cp-name] = '\0';
}
- if (!get_sha1_1(name, cp-name, tree_sha1, 0)) {
+ if (!get_sha1_1(name, cp-name, tree_sha1, GET_SHA1_TREEISH)) {
const char *filename = cp+1;
char *new_filename = NULL;
if (new_filename)
filename = new_filename;
ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
- if (only_to_die) {
+ if (ret && only_to_die) {
diagnose_invalid_sha1_path(prefix, filename,
tree_sha1, object_name);
free(object_name);