update-index: add fsmonitor support to update-index
[gitweb.git] / packfile.c
index f251e38783c12e6cf4d40790d3779e117a844e52..f86fa051c9e67def994b8b487ad34f200967d5e9 100644 (file)
@@ -7,6 +7,7 @@
 #include "delta.h"
 #include "list.h"
 #include "streaming.h"
+#include "sha1-lookup.h"
 
 char *odb_pack_name(struct strbuf *buf,
                    const unsigned char *sha1,
@@ -509,7 +510,7 @@ static int open_packed_git_1(struct packed_git *p)
        return 0;
 }
 
-int open_packed_git(struct packed_git *p)
+static int open_packed_git(struct packed_git *p)
 {
        if (!open_packed_git_1(p))
                return 0;
@@ -1700,3 +1701,196 @@ off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
                                   ntohl(*((uint32_t *)(index + 4)));
        }
 }
+
+off_t find_pack_entry_one(const unsigned char *sha1,
+                                 struct packed_git *p)
+{
+       const uint32_t *level1_ofs = p->index_data;
+       const unsigned char *index = p->index_data;
+       unsigned hi, lo, stride;
+       static int debug_lookup = -1;
+
+       if (debug_lookup < 0)
+               debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
+
+       if (!index) {
+               if (open_pack_index(p))
+                       return 0;
+               level1_ofs = p->index_data;
+               index = p->index_data;
+       }
+       if (p->index_version > 1) {
+               level1_ofs += 2;
+               index += 8;
+       }
+       index += 4 * 256;
+       hi = ntohl(level1_ofs[*sha1]);
+       lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
+       if (p->index_version > 1) {
+               stride = 20;
+       } else {
+               stride = 24;
+               index += 4;
+       }
+
+       if (debug_lookup)
+               printf("%02x%02x%02x... lo %u hi %u nr %"PRIu32"\n",
+                      sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
+
+       while (lo < hi) {
+               unsigned mi = (lo + hi) / 2;
+               int cmp = hashcmp(index + mi * stride, sha1);
+
+               if (debug_lookup)
+                       printf("lo %u hi %u rg %u mi %u\n",
+                              lo, hi, hi - lo, mi);
+               if (!cmp)
+                       return nth_packed_object_offset(p, mi);
+               if (cmp > 0)
+                       hi = mi;
+               else
+                       lo = mi+1;
+       }
+       return 0;
+}
+
+int is_pack_valid(struct packed_git *p)
+{
+       /* An already open pack is known to be valid. */
+       if (p->pack_fd != -1)
+               return 1;
+
+       /* If the pack has one window completely covering the
+        * file size, the pack is known to be valid even if
+        * the descriptor is not currently open.
+        */
+       if (p->windows) {
+               struct pack_window *w = p->windows;
+
+               if (!w->offset && w->len == p->pack_size)
+                       return 1;
+       }
+
+       /* Force the pack to open to prove its valid. */
+       return !open_packed_git(p);
+}
+
+struct packed_git *find_sha1_pack(const unsigned char *sha1,
+                                 struct packed_git *packs)
+{
+       struct packed_git *p;
+
+       for (p = packs; p; p = p->next) {
+               if (find_pack_entry_one(sha1, p))
+                       return p;
+       }
+       return NULL;
+
+}
+
+static int fill_pack_entry(const unsigned char *sha1,
+                          struct pack_entry *e,
+                          struct packed_git *p)
+{
+       off_t offset;
+
+       if (p->num_bad_objects) {
+               unsigned i;
+               for (i = 0; i < p->num_bad_objects; i++)
+                       if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
+                               return 0;
+       }
+
+       offset = find_pack_entry_one(sha1, p);
+       if (!offset)
+               return 0;
+
+       /*
+        * We are about to tell the caller where they can locate the
+        * requested object.  We better make sure the packfile is
+        * still here and can be accessed before supplying that
+        * answer, as it may have been deleted since the index was
+        * loaded!
+        */
+       if (!is_pack_valid(p))
+               return 0;
+       e->offset = offset;
+       e->p = p;
+       hashcpy(e->sha1, sha1);
+       return 1;
+}
+
+/*
+ * Iff a pack file contains the object named by sha1, return true and
+ * store its location to e.
+ */
+int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
+{
+       struct mru_entry *p;
+
+       prepare_packed_git();
+       if (!packed_git)
+               return 0;
+
+       for (p = packed_git_mru->head; p; p = p->next) {
+               if (fill_pack_entry(sha1, e, p->item)) {
+                       mru_mark(packed_git_mru, p);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+int has_sha1_pack(const unsigned char *sha1)
+{
+       struct pack_entry e;
+       return find_pack_entry(sha1, &e);
+}
+
+int has_pack_index(const unsigned char *sha1)
+{
+       struct stat st;
+       if (stat(sha1_pack_index_name(sha1), &st))
+               return 0;
+       return 1;
+}
+
+static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data)
+{
+       uint32_t i;
+       int r = 0;
+
+       for (i = 0; i < p->num_objects; i++) {
+               struct object_id oid;
+
+               if (!nth_packed_object_oid(&oid, p, i))
+                       return error("unable to get sha1 of object %u in %s",
+                                    i, p->pack_name);
+
+               r = cb(&oid, p, i, data);
+               if (r)
+                       break;
+       }
+       return r;
+}
+
+int for_each_packed_object(each_packed_object_fn cb, void *data, unsigned flags)
+{
+       struct packed_git *p;
+       int r = 0;
+       int pack_errors = 0;
+
+       prepare_packed_git();
+       for (p = packed_git; p; p = p->next) {
+               if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
+                       continue;
+               if (open_pack_index(p)) {
+                       pack_errors = 1;
+                       continue;
+               }
+               r = for_each_object_in_pack(p, cb, data);
+               if (r)
+                       break;
+       }
+       return r ? r : pack_errors;
+}