pretty: --format output should honor logOutputEncoding
[gitweb.git] / sha1_name.c
index c824bdd3eb5ba146b21e7c0046ec1414fa9a2971..3820f28ae757cce54a95014629ade4f7feb56efc 100644 (file)
@@ -20,10 +20,15 @@ struct disambiguate_state {
        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);
@@ -218,16 +223,66 @@ static int finish_object_disambiguation(struct disambiguate_state *ds,
        return 0;
 }
 
-static int get_short_sha1(const char *name, int len, unsigned char *sha1,
-                         int quietly)
+static int disambiguate_commit_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 kind = sha1_object_info(sha1, NULL);
+       return kind == OBJ_COMMIT;
+}
+
+static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data_unused)
+{
+       struct object *obj;
+       int kind;
+
+       kind = sha1_object_info(sha1, NULL);
+       if (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_COMMIT)
+               return 1;
+       return 0;
+}
+
+static int disambiguate_tree_only(const unsigned char *sha1, void *cb_data_unused)
+{
+       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++) {
@@ -248,10 +303,37 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
                        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();
 
        memset(&ds, 0, sizeof(ds));
+       if (flags & GET_SHA1_COMMIT)
+               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);
        status = finish_object_disambiguation(&ds, sha1);
@@ -261,6 +343,31 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
        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;
@@ -272,7 +379,7 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
                return hex;
        while (len < 40) {
                unsigned char sha1_ret[20];
-               status = get_short_sha1(hex, len, sha1_ret, 1);
+               status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
                if (exists
                    ? !status
                    : status == SHORT_NAME_NOT_FOUND) {
@@ -323,7 +430,7 @@ static inline int upstream_mark(const char *string, int len)
        return 0;
 }
 
-static int get_sha1_1(const char *name, int len, unsigned char *sha1);
+static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
 
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 {
@@ -360,7 +467,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                ret = interpret_branch_name(str+at, &buf);
                if (ret > 0) {
                        /* substitute this branch name and restart */
-                       return get_sha1_1(buf.buf, buf.len, sha1);
+                       return get_sha1_1(buf.buf, buf.len, sha1, 0);
                } else if (ret == 0) {
                        return -1;
                }
@@ -430,7 +537,7 @@ 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);
+       int ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
        struct commit *commit;
        struct commit_list *p;
 
@@ -463,7 +570,7 @@ static int get_nth_ancestor(const char *name, int len,
        struct commit *commit;
        int ret;
 
-       ret = get_sha1_1(name, len, sha1);
+       ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
        if (ret)
                return ret;
        commit = lookup_commit_reference(sha1);
@@ -487,7 +594,7 @@ struct object *peel_to_type(const char *name, int namelen,
        while (1) {
                if (!o || (!o->parsed && !parse_object(o->sha1)))
                        return NULL;
-               if (o->type == expected_type)
+               if (expected_type == OBJ_ANY || o->type == expected_type)
                        return o;
                if (o->type == OBJ_TAG)
                        o = ((struct tag*) o)->tagged;
@@ -509,6 +616,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
        unsigned char outer[20];
        const char *sp;
        unsigned int expected_type = 0;
+       unsigned lookup_flags = 0;
        struct object *o;
 
        /*
@@ -537,6 +645,8 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
                expected_type = OBJ_TREE;
        else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
                expected_type = OBJ_BLOB;
+       else if (!prefixcmp(sp, "object}"))
+               expected_type = OBJ_ANY;
        else if (sp[0] == '}')
                expected_type = OBJ_NONE;
        else if (sp[0] == '/')
@@ -544,7 +654,12 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
        else
                return -1;
 
-       if (get_sha1_1(name, sp - name - 2, outer))
+       if (expected_type == OBJ_COMMIT)
+               lookup_flags = GET_SHA1_COMMITTISH;
+       else if (expected_type == OBJ_TREE)
+               lookup_flags = GET_SHA1_TREEISH;
+
+       if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
                return -1;
 
        o = parse_object(outer);
@@ -593,6 +708,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
 static int get_describe_name(const char *name, int len, unsigned char *sha1)
 {
        const char *cp;
+       unsigned flags = GET_SHA1_QUIETLY | GET_SHA1_COMMIT;
 
        for (cp = name + len - 1; name + 2 <= cp; cp--) {
                char ch = *cp;
@@ -603,14 +719,14 @@ static int get_describe_name(const char *name, int len, unsigned char *sha1)
                        if (ch == 'g' && cp[-1] == '-') {
                                cp++;
                                len -= cp - name;
-                               return get_short_sha1(cp, len, sha1, 1);
+                               return get_short_sha1(cp, len, sha1, flags);
                        }
                }
        }
        return -1;
 }
 
-static int get_sha1_1(const char *name, int len, unsigned char *sha1)
+static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags)
 {
        int ret, has_suffix;
        const char *cp;
@@ -655,7 +771,7 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
        if (!ret)
                return 0;
 
-       return get_short_sha1(name, len, sha1, 0);
+       return get_short_sha1(name, len, sha1, lookup_flags);
 }
 
 /*
@@ -744,8 +860,8 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
 }
 
 struct grab_nth_branch_switch_cbdata {
-       long cnt, alloc;
-       struct strbuf *buf;
+       int remaining;
+       struct strbuf buf;
 };
 
 static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
@@ -755,7 +871,6 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
        struct grab_nth_branch_switch_cbdata *cb = cb_data;
        const char *match = NULL, *target = NULL;
        size_t len;
-       int nth;
 
        if (!prefixcmp(message, "checkout: moving from ")) {
                match = message + strlen("checkout: moving from ");
@@ -764,11 +879,12 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
 
        if (!match || !target)
                return 0;
-
-       len = target - match;
-       nth = cb->cnt++ % cb->alloc;
-       strbuf_reset(&cb->buf[nth]);
-       strbuf_add(&cb->buf[nth], match, len);
+       if (--(cb->remaining) == 0) {
+               len = target - match;
+               strbuf_reset(&cb->buf);
+               strbuf_add(&cb->buf, match, len);
+               return 1; /* we are done */
+       }
        return 0;
 }
 
@@ -779,7 +895,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
 static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
 {
        long nth;
-       int i, retval;
+       int retval;
        struct grab_nth_branch_switch_cbdata cb;
        const char *brace;
        char *num_end;
@@ -789,34 +905,22 @@ static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
        brace = strchr(name, '}');
        if (!brace)
                return -1;
-       nth = strtol(name+3, &num_end, 10);
+       nth = strtol(name + 3, &num_end, 10);
        if (num_end != brace)
                return -1;
        if (nth <= 0)
                return -1;
-       cb.alloc = nth;
-       cb.buf = xmalloc(nth * sizeof(struct strbuf));
-       for (i = 0; i < nth; i++)
-               strbuf_init(&cb.buf[i], 20);
-       cb.cnt = 0;
+       cb.remaining = nth;
+       strbuf_init(&cb.buf, 20);
+
        retval = 0;
-       for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb);
-       if (cb.cnt < nth) {
-               cb.cnt = 0;
-               for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+       if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) {
+               strbuf_reset(buf);
+               strbuf_add(buf, cb.buf.buf, cb.buf.len);
+               retval = brace - name + 1;
        }
-       if (cb.cnt < nth)
-               goto release_return;
-       i = cb.cnt % nth;
-       strbuf_reset(buf);
-       strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len);
-       retval = brace-name+1;
-
-release_return:
-       for (i = 0; i < nth; i++)
-               strbuf_release(&cb.buf[i]);
-       free(cb.buf);
 
+       strbuf_release(&cb.buf);
        return retval;
 }
 
@@ -837,7 +941,7 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
                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)
@@ -846,7 +950,7 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
        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)
@@ -924,10 +1028,22 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
        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);
@@ -961,14 +1077,60 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
 int get_sha1(const char *name, unsigned char *sha1)
 {
        struct object_context unused;
-       return get_sha1_with_context(name, sha1, &unused);
+       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,
                                       const unsigned char *tree_sha1,
-                                      const char *object_name)
+                                      const char *object_name,
+                                      int object_name_len)
 {
        struct stat st;
        unsigned char sha1[20];
@@ -978,8 +1140,8 @@ static void diagnose_invalid_sha1_path(const char *prefix,
                prefix = "";
 
        if (!lstat(filename, &st))
-               die("Path '%s' exists on disk, but not in '%s'.",
-                   filename, object_name);
+               die("Path '%s' exists on disk, but not in '%.*s'.",
+                   filename, object_name_len, object_name);
        if (errno == ENOENT || errno == ENOTDIR) {
                char *fullname = xmalloc(strlen(filename)
                                             + strlen(prefix) + 1);
@@ -989,16 +1151,16 @@ static void diagnose_invalid_sha1_path(const char *prefix,
                if (!get_tree_entry(tree_sha1, fullname,
                                    sha1, &mode)) {
                        die("Path '%s' exists, but not '%s'.\n"
-                           "Did you mean '%s:%s' aka '%s:./%s'?",
+                           "Did you mean '%.*s:%s' aka '%.*s:./%s'?",
                            fullname,
                            filename,
-                           object_name,
+                           object_name_len, object_name,
                            fullname,
-                           object_name,
+                           object_name_len, object_name,
                            filename);
                }
-               die("Path '%s' does not exist in '%s'",
-                   filename, object_name);
+               die("Path '%s' does not exist in '%.*s'",
+                   filename, object_name_len, object_name);
        }
 }
 
@@ -1077,20 +1239,24 @@ static char *resolve_relative_path(const char *rel)
                           rel);
 }
 
-static int get_sha1_with_context_1(const char *name, unsigned char *sha1,
-                                  struct object_context *oc,
-                                  int only_to_die, const char *prefix)
+static int get_sha1_with_context_1(const char *name,
+                                  unsigned flags,
+                                  const char *prefix,
+                                  unsigned char *sha1,
+                                  struct object_context *oc)
 {
        int ret, bracket_depth;
        int namelen = strlen(name);
        const char *cp;
+       int only_to_die = flags & GET_SHA1_ONLY_TO_DIE;
 
        memset(oc, 0, sizeof(*oc));
        oc->mode = S_IFINVALID;
-       ret = get_sha1_1(name, namelen, sha1);
+       ret = get_sha1_1(name, namelen, sha1, flags);
        if (!ret)
                return ret;
-       /* sha1:path --> object name of path in ent sha1
+       /*
+        * sha1:path --> object name of path in ent sha1
         * :path -> object name of absolute path in index
         * :./path -> object name of path relative to cwd in index
         * :[0-3]:path -> object name of path in index at stage
@@ -1159,13 +1325,8 @@ static int get_sha1_with_context_1(const char *name, unsigned char *sha1,
        }
        if (*cp == ':') {
                unsigned char tree_sha1[20];
-               char *object_name = NULL;
-               if (only_to_die) {
-                       object_name = xmalloc(cp-name+1);
-                       strncpy(object_name, name, cp-name);
-                       object_name[cp-name] = '\0';
-               }
-               if (!get_sha1_1(name, cp-name, tree_sha1)) {
+               int len = cp - name;
+               if (!get_sha1_1(name, len, tree_sha1, GET_SHA1_TREEISH)) {
                        const char *filename = cp+1;
                        char *new_filename = NULL;
 
@@ -1173,10 +1334,10 @@ static int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                        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);
+                                                          tree_sha1,
+                                                          name, len);
                        }
                        hashcpy(oc->tree, tree_sha1);
                        strncpy(oc->path, filename,
@@ -1187,7 +1348,7 @@ static int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                        return ret;
                } else {
                        if (only_to_die)
-                               die("Invalid object name '%s'.", object_name);
+                               die("Invalid object name '%.*s'.", len, name);
                }
        }
        return ret;
@@ -1204,10 +1365,10 @@ void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
 {
        struct object_context oc;
        unsigned char sha1[20];
-       get_sha1_with_context_1(name, sha1, &oc, 1, prefix);
+       get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc);
 }
 
-int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
+int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
 {
-       return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
+       return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
 }