git.c: convert --list-* to --list-cmds=*
[gitweb.git] / sha1_name.c
index 603e667faa6b935ed5c44fc7d67188088bdfaa84..5b93bf8da36939376b506f96624f568875397969 100644 (file)
@@ -10,6 +10,8 @@
 #include "dir.h"
 #include "sha1-array.h"
 #include "packfile.h"
+#include "object-store.h"
+#include "repository.h"
 
 static int get_oid_oneline(const char *, struct object_id *, struct commit_list *);
 
@@ -104,7 +106,7 @@ static void find_short_object_filename(struct disambiguate_state *ds)
                 */
                fakeent = alloc_alt_odb(get_object_directory());
        }
-       fakeent->next = alt_odb_list;
+       fakeent->next = the_repository->objects->alt_odb_list;
 
        for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
                int pos;
@@ -150,29 +152,14 @@ static int match_sha(unsigned len, const unsigned char *a, const unsigned char *
 static void unique_in_pack(struct packed_git *p,
                           struct disambiguate_state *ds)
 {
-       uint32_t num, last, i, first = 0;
+       uint32_t num, i, first = 0;
        const struct object_id *current = NULL;
 
-       open_pack_index(p);
+       if (open_pack_index(p) || !p->num_objects)
+               return;
+
        num = p->num_objects;
-       last = num;
-       while (first < last) {
-               uint32_t mid = first + (last - first) / 2;
-               const unsigned char *current;
-               int cmp;
-
-               current = nth_packed_object_sha1(p, mid);
-               cmp = hashcmp(ds->bin_pfx.hash, current);
-               if (!cmp) {
-                       first = mid;
-                       break;
-               }
-               if (cmp > 0) {
-                       first = mid+1;
-                       continue;
-               }
-               last = mid;
-       }
+       bsearch_pack(&ds->bin_pfx, p, &first);
 
        /*
         * At this point, "first" is the location of the lowest object
@@ -192,8 +179,8 @@ 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)
+       for (p = get_packed_git(the_repository); p && !ds->ambiguous;
+            p = p->next)
                unique_in_pack(p, ds);
 }
 
@@ -236,7 +223,7 @@ static int finish_object_disambiguation(struct disambiguate_state *ds,
 
 static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused)
 {
-       int kind = sha1_object_info(oid->hash, NULL);
+       int kind = oid_object_info(oid, NULL);
        return kind == OBJ_COMMIT;
 }
 
@@ -245,7 +232,7 @@ static int disambiguate_committish_only(const struct object_id *oid, void *cb_da
        struct object *obj;
        int kind;
 
-       kind = sha1_object_info(oid->hash, NULL);
+       kind = oid_object_info(oid, NULL);
        if (kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
@@ -260,7 +247,7 @@ static int disambiguate_committish_only(const struct object_id *oid, void *cb_da
 
 static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused)
 {
-       int kind = sha1_object_info(oid->hash, NULL);
+       int kind = oid_object_info(oid, NULL);
        return kind == OBJ_TREE;
 }
 
@@ -269,7 +256,7 @@ static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_
        struct object *obj;
        int kind;
 
-       kind = sha1_object_info(oid->hash, NULL);
+       kind = oid_object_info(oid, NULL);
        if (kind == OBJ_TREE || kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
@@ -284,7 +271,7 @@ static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_
 
 static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused)
 {
-       int kind = sha1_object_info(oid->hash, NULL);
+       int kind = oid_object_info(oid, NULL);
        return kind == OBJ_BLOB;
 }
 
@@ -349,7 +336,7 @@ static int init_object_disambiguation(const char *name, int len,
 
        ds->len = len;
        ds->hex_pfx[len] = '\0';
-       prepare_alt_odb();
+       prepare_alt_odb(the_repository);
        return 0;
 }
 
@@ -363,7 +350,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)
        if (ds->fn && !ds->fn(oid, ds->cb_data))
                return 0;
 
-       type = sha1_object_info(oid->hash, NULL);
+       type = oid_object_info(oid, NULL);
        if (type == OBJ_COMMIT) {
                struct commit *commit = lookup_commit(oid);
                if (commit) {
@@ -378,8 +365,8 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)
        }
 
        advise("  %s %s%s",
-              find_unique_abbrev(oid->hash, DEFAULT_ABBREV),
-              typename(type) ? typename(type) : "unknown type",
+              find_unique_abbrev(oid, DEFAULT_ABBREV),
+              type_name(type) ? type_name(type) : "unknown type",
               desc.buf);
 
        strbuf_release(&desc);
@@ -474,10 +461,87 @@ static unsigned msb(unsigned long val)
        return r;
 }
 
-int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
+struct min_abbrev_data {
+       unsigned int init_len;
+       unsigned int cur_len;
+       char *hex;
+       const struct object_id *oid;
+};
+
+static inline char get_hex_char_from_oid(const struct object_id *oid,
+                                        unsigned int pos)
+{
+       static const char hex[] = "0123456789abcdef";
+
+       if ((pos & 1) == 0)
+               return hex[oid->hash[pos >> 1] >> 4];
+       else
+               return hex[oid->hash[pos >> 1] & 0xf];
+}
+
+static int extend_abbrev_len(const struct object_id *oid, void *cb_data)
+{
+       struct min_abbrev_data *mad = cb_data;
+
+       unsigned int i = mad->init_len;
+       while (mad->hex[i] && mad->hex[i] == get_hex_char_from_oid(oid, i))
+               i++;
+
+       if (i < GIT_MAX_RAWSZ && i >= mad->cur_len)
+               mad->cur_len = i + 1;
+
+       return 0;
+}
+
+static void find_abbrev_len_for_pack(struct packed_git *p,
+                                    struct min_abbrev_data *mad)
+{
+       int match = 0;
+       uint32_t num, first = 0;
+       struct object_id oid;
+       const struct object_id *mad_oid;
+
+       if (open_pack_index(p) || !p->num_objects)
+               return;
+
+       num = p->num_objects;
+       mad_oid = mad->oid;
+       match = bsearch_pack(mad_oid, p, &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_packed_object_oid(&oid, p, first))
+                       extend_abbrev_len(&oid, mad);
+       } else if (first < num - 1) {
+               if (nth_packed_object_oid(&oid, p, first + 1))
+                       extend_abbrev_len(&oid, mad);
+       }
+       if (first > 0) {
+               if (nth_packed_object_oid(&oid, p, first - 1))
+                       extend_abbrev_len(&oid, mad);
+       }
+       mad->init_len = mad->cur_len;
+}
+
+static void find_abbrev_len_packed(struct min_abbrev_data *mad)
 {
-       int status, exists;
+       struct packed_git *p;
+
+       for (p = get_packed_git(the_repository); p; p = p->next)
+               find_abbrev_len_for_pack(p, mad);
+}
 
+int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len)
+{
+       struct disambiguate_state ds;
+       struct min_abbrev_data mad;
+       struct object_id oid_ret;
        if (len < 0) {
                unsigned long count = approximate_object_count();
                /*
@@ -500,31 +564,38 @@ int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
                        len = FALLBACK_DEFAULT_ABBREV;
        }
 
-       sha1_to_hex_r(hex, sha1);
+       oid_to_hex_r(hex, oid);
        if (len == GIT_SHA1_HEXSZ || !len)
                return GIT_SHA1_HEXSZ;
-       exists = has_sha1_file(sha1);
-       while (len < GIT_SHA1_HEXSZ) {
-               struct object_id oid_ret;
-               status = get_short_oid(hex, len, &oid_ret, GET_OID_QUIETLY);
-               if (exists
-                   ? !status
-                   : status == SHORT_NAME_NOT_FOUND) {
-                       hex[len] = 0;
-                       return len;
-               }
-               len++;
-       }
-       return len;
+
+       mad.init_len = len;
+       mad.cur_len = len;
+       mad.hex = hex;
+       mad.oid = oid;
+
+       find_abbrev_len_packed(&mad);
+
+       if (init_object_disambiguation(hex, mad.cur_len, &ds) < 0)
+               return -1;
+
+       ds.fn = extend_abbrev_len;
+       ds.always_call_fn = 1;
+       ds.cb_data = (void *)&mad;
+
+       find_short_object_filename(&ds);
+       (void)finish_object_disambiguation(&ds, &oid_ret);
+
+       hex[mad.cur_len] = 0;
+       return mad.cur_len;
 }
 
-const char *find_unique_abbrev(const unsigned char *sha1, int len)
+const char *find_unique_abbrev(const struct object_id *oid, int len)
 {
        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);
+       find_unique_abbrev_r(hex, oid, len);
        return hex;
 }
 
@@ -603,7 +674,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
 
        if (len == GIT_SHA1_HEXSZ && !get_oid_hex(str, oid)) {
                if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
-                       refs_found = dwim_ref(str, len, tmp_oid.hash, &real_ref);
+                       refs_found = dwim_ref(str, len, &tmp_oid, &real_ref);
                        if (refs_found > 0) {
                                warning(warn_msg, len, str);
                                if (advice_object_name_warning)
@@ -654,11 +725,11 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
 
        if (!len && reflog_len)
                /* allow "@{...}" to mean the current branch reflog */
-               refs_found = dwim_ref("HEAD", 4, oid->hash, &real_ref);
+               refs_found = dwim_ref("HEAD", 4, oid, &real_ref);
        else if (reflog_len)
-               refs_found = dwim_log(str, len, oid->hash, &real_ref);
+               refs_found = dwim_log(str, len, oid, &real_ref);
        else
-               refs_found = dwim_ref(str, len, oid->hash, &real_ref);
+               refs_found = dwim_ref(str, len, oid, &real_ref);
 
        if (!refs_found)
                return -1;
@@ -697,7 +768,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
                                return -1;
                        }
                }
-               if (read_ref_at(real_ref, flags, at_time, nth, oid->hash, NULL,
+               if (read_ref_at(real_ref, flags, at_time, nth, oid, NULL,
                                &co_time, &co_tz, &co_cnt)) {
                        if (!len) {
                                if (starts_with(real_ref, "refs/heads/")) {
@@ -798,8 +869,8 @@ struct object *peel_to_type(const char *name, int namelen,
                        if (name)
                                error("%.*s: expected %s type, but the object "
                                      "dereferences to %s type",
-                                     namelen, name, typename(expected_type),
-                                     typename(o->type));
+                                     namelen, name, type_name(expected_type),
+                                     type_name(o->type));
                        return NULL;
                }
        }
@@ -1335,9 +1406,19 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
                strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
        else
                strbuf_addstr(sb, name);
-       if (name[0] == '-')
-               return -1;
+
+       /*
+        * 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);
 }
 
@@ -1416,8 +1497,7 @@ static void diagnose_invalid_oid_path(const char *prefix,
        if (is_missing_file_error(errno)) {
                char *fullname = xstrfmt("%s%s", prefix, filename);
 
-               if (!get_tree_entry(tree_oid->hash, fullname,
-                                   oid.hash, &mode)) {
+               if (!get_tree_entry(tree_oid, fullname, &oid, &mode)) {
                        die("Path '%s' exists, but not '%s'.\n"
                            "Did you mean '%.*s:%s' aka '%.*s:./%s'?",
                            fullname,
@@ -1609,8 +1689,8 @@ static int get_oid_with_context_1(const char *name,
                                        filename, oid->hash, &oc->symlink_path,
                                        &oc->mode);
                        } else {
-                               ret = get_tree_entry(tree_oid.hash, filename,
-                                                    oid->hash, &oc->mode);
+                               ret = get_tree_entry(&tree_oid, filename, oid,
+                                                    &oc->mode);
                                if (ret && only_to_die) {
                                        diagnose_invalid_oid_path(prefix,
                                                                   filename,