sha1_name: minimize OID comparisons during disambiguation
[gitweb.git] / sha1_name.c
index 733815f1dc0ee8594d62968f51bf463b4ab2d8c4..05a635911be3c6b27cb3c572500483c47cee9b56 100644 (file)
@@ -153,7 +153,9 @@ static void unique_in_pack(struct packed_git *p,
        uint32_t num, last, 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) {
@@ -478,6 +480,7 @@ struct min_abbrev_data {
        unsigned int init_len;
        unsigned int cur_len;
        char *hex;
+       const unsigned char *hash;
 };
 
 static inline char get_hex_char_from_oid(const struct object_id *oid,
@@ -505,6 +508,67 @@ static int extend_abbrev_len(const struct object_id *oid, void *cb_data)
        return 0;
 }
 
+static void find_abbrev_len_for_pack(struct packed_git *p,
+                                    struct min_abbrev_data *mad)
+{
+       int match = 0;
+       uint32_t num, last, first = 0;
+       struct object_id oid;
+
+       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(mad->hash, current);
+               if (!cmp) {
+                       match = 1;
+                       first = mid;
+                       break;
+               }
+               if (cmp > 0) {
+                       first = mid + 1;
+                       continue;
+               }
+               last = mid;
+       }
+
+       /*
+        * 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 three objects
+        * nearby for the abbreviation length.
+        */
+       mad->init_len = 0;
+       if (!match) {
+               nth_packed_object_oid(&oid, p, first);
+               extend_abbrev_len(&oid, mad);
+       } else if (first < num - 1) {
+               nth_packed_object_oid(&oid, p, first + 1);
+               extend_abbrev_len(&oid, mad);
+       }
+       if (first > 0) {
+               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)
+{
+       struct packed_git *p;
+
+       prepare_packed_git();
+       for (p = packed_git; p; p = p->next)
+               find_abbrev_len_for_pack(p, mad);
+}
+
 int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
 {
        struct disambiguate_state ds;
@@ -536,19 +600,21 @@ int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
        if (len == GIT_SHA1_HEXSZ || !len)
                return GIT_SHA1_HEXSZ;
 
-       if (init_object_disambiguation(hex, len, &ds) < 0)
-               return -1;
-
        mad.init_len = len;
        mad.cur_len = len;
        mad.hex = hex;
+       mad.hash = sha1;
+
+       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);
-       find_short_packed_object(&ds);
        (void)finish_object_disambiguation(&ds, &oid_ret);
 
        hex[mad.cur_len] = 0;