document --pretty=tformat: option
[gitweb.git] / sha1_file.c
index 1ddb96bb82b60f4da8489664d13f398d88bf5a15..f311c79e50bce4cf39cd1c2c51f63974517a2f5c 100644 (file)
@@ -14,6 +14,8 @@
 #include "tag.h"
 #include "tree.h"
 #include "refs.h"
+#include "pack-revindex.h"
+#include "sha1-lookup.h"
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -174,21 +176,23 @@ char *sha1_file_name(const unsigned char *sha1)
        return base;
 }
 
-char *sha1_pack_name(const unsigned char *sha1)
+static char *sha1_get_pack_name(const unsigned char *sha1,
+                               char **name, char **base, const char *which)
 {
        static const char hex[] = "0123456789abcdef";
-       static char *name, *base, *buf;
+       char *buf;
        int i;
 
-       if (!base) {
+       if (!*base) {
                const char *sha1_file_directory = get_object_directory();
                int len = strlen(sha1_file_directory);
-               base = xmalloc(len + 60);
-               sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.pack", sha1_file_directory);
-               name = base + len + 11;
+               *base = xmalloc(len + 60);
+               sprintf(*base, "%s/pack/pack-1234567890123456789012345678901234567890.%s",
+                       sha1_file_directory, which);
+               *name = *base + len + 11;
        }
 
-       buf = name;
+       buf = *name;
 
        for (i = 0; i < 20; i++) {
                unsigned int val = *sha1++;
@@ -196,32 +200,21 @@ char *sha1_pack_name(const unsigned char *sha1)
                *buf++ = hex[val & 0xf];
        }
 
-       return base;
+       return *base;
 }
 
-char *sha1_pack_index_name(const unsigned char *sha1)
+char *sha1_pack_name(const unsigned char *sha1)
 {
-       static const char hex[] = "0123456789abcdef";
-       static char *name, *base, *buf;
-       int i;
-
-       if (!base) {
-               const char *sha1_file_directory = get_object_directory();
-               int len = strlen(sha1_file_directory);
-               base = xmalloc(len + 60);
-               sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.idx", sha1_file_directory);
-               name = base + len + 11;
-       }
+       static char *name, *base;
 
-       buf = name;
+       return sha1_get_pack_name(sha1, &name, &base, "pack");
+}
 
-       for (i = 0; i < 20; i++) {
-               unsigned int val = *sha1++;
-               *buf++ = hex[val >> 4];
-               *buf++ = hex[val & 0xf];
-       }
+char *sha1_pack_index_name(const unsigned char *sha1)
+{
+       static char *name, *base;
 
-       return base;
+       return sha1_get_pack_name(sha1, &name, &base, "idx");
 }
 
 struct alternate_object_database *alt_odb_list;
@@ -378,6 +371,18 @@ static void read_info_alternates(const char * relative_base, int depth)
        munmap(map, mapsz);
 }
 
+void add_to_alternates_file(const char *reference)
+{
+       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+       int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), 1);
+       char *alt = mkpath("%s/objects\n", reference);
+       write_or_die(fd, alt, strlen(alt));
+       if (commit_lock_file(lock))
+               die("could not close alternates file");
+       if (alt_odb_tail)
+               link_alt_odb_entries(alt, alt + strlen(alt), '\n', NULL, 0);
+}
+
 void prepare_alt_odb(void)
 {
        const char *alt;
@@ -1367,11 +1372,15 @@ const char *packed_object_info_detail(struct packed_git *p,
        unsigned long dummy;
        unsigned char *next_sha1;
        enum object_type type;
+       struct revindex_entry *revidx;
 
        *delta_chain_length = 0;
        curpos = obj_offset;
        type = unpack_object_header(p, &w_curs, &curpos, size);
 
+       revidx = find_pack_revindex(p, obj_offset);
+       *store_size = revidx[1].offset - obj_offset;
+
        for (;;) {
                switch (type) {
                default:
@@ -1381,14 +1390,13 @@ const char *packed_object_info_detail(struct packed_git *p,
                case OBJ_TREE:
                case OBJ_BLOB:
                case OBJ_TAG:
-                       *store_size = 0; /* notyet */
                        unuse_pack(&w_curs);
                        return typename(type);
                case OBJ_OFS_DELTA:
                        obj_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
                        if (*delta_chain_length == 0) {
-                               /* TODO: find base_sha1 as pointed by curpos */
-                               hashclr(base_sha1);
+                               revidx = find_pack_revindex(p, obj_offset);
+                               hashcpy(base_sha1, nth_packed_object_sha1(p, revidx->nr));
                        }
                        break;
                case OBJ_REF_DELTA:
@@ -1671,7 +1679,12 @@ off_t find_pack_entry_one(const unsigned char *sha1,
 {
        const uint32_t *level1_ofs = p->index_data;
        const unsigned char *index = p->index_data;
-       unsigned hi, lo;
+       unsigned hi, lo, stride;
+       static int use_lookup = -1;
+       static int debug_lookup = -1;
+
+       if (debug_lookup < 0)
+               debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
 
        if (!index) {
                if (open_pack_index(p))
@@ -1686,11 +1699,34 @@ off_t find_pack_entry_one(const unsigned char *sha1,
        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 %u\n",
+                      sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
+
+       if (use_lookup < 0)
+               use_lookup = !!getenv("GIT_USE_LOOKUP");
+       if (use_lookup) {
+               int pos = sha1_entry_pos(index, stride, 0,
+                                        lo, hi, p->num_objects, sha1);
+               if (pos < 0)
+                       return 0;
+               return nth_packed_object_offset(p, pos);
+       }
 
        do {
                unsigned mi = (lo + hi) / 2;
-               unsigned x = (p->index_version > 1) ? (mi * 20) : (mi * 24 + 4);
-               int cmp = hashcmp(index + x, sha1);
+               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)
@@ -2069,26 +2105,25 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type,
        return 0;
 }
 
-int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
+/* Finalize a file on disk, and close it. */
+static void close_sha1_file(int fd)
+{
+       /* For safe-mode, we could fsync_or_die(fd, "sha1 file") here */
+       fchmod(fd, 0444);
+       if (close(fd) != 0)
+               die("unable to write sha1 file");
+}
+
+static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
+                             void *buf, unsigned long len, time_t mtime)
 {
-       int size, ret;
+       int fd, size, ret;
        unsigned char *compressed;
        z_stream stream;
-       unsigned char sha1[20];
        char *filename;
        static char tmpfile[PATH_MAX];
-       char hdr[32];
-       int fd, hdrlen;
 
-       /* Normally if we have it in the pack then we do not bother writing
-        * it out into .git/objects/??/?{38} file.
-        */
-       write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
        filename = sha1_file_name(sha1);
-       if (returnsha1)
-               hashcpy(returnsha1, sha1);
-       if (has_sha1_file(sha1))
-               return 0;
        fd = open(filename, O_RDONLY);
        if (fd >= 0) {
                /*
@@ -2144,14 +2179,56 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
 
        if (write_buffer(fd, compressed, size) < 0)
                die("unable to write sha1 file");
-       fchmod(fd, 0444);
-       if (close(fd))
-               die("unable to write sha1 file");
+       close_sha1_file(fd);
        free(compressed);
 
+       if (mtime) {
+               struct utimbuf utb;
+               utb.actime = mtime;
+               utb.modtime = mtime;
+               if (utime(tmpfile, &utb) < 0)
+                       warning("failed utime() on %s: %s",
+                               tmpfile, strerror(errno));
+       }
+
        return move_temp_to_file(tmpfile, filename);
 }
 
+int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
+{
+       unsigned char sha1[20];
+       char hdr[32];
+       int hdrlen;
+
+       /* Normally if we have it in the pack then we do not bother writing
+        * it out into .git/objects/??/?{38} file.
+        */
+       write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
+       if (returnsha1)
+               hashcpy(returnsha1, sha1);
+       if (has_sha1_file(sha1))
+               return 0;
+       return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
+}
+
+int force_object_loose(const unsigned char *sha1, time_t mtime)
+{
+       struct stat st;
+       void *buf;
+       unsigned long len;
+       enum object_type type;
+       char hdr[32];
+       int hdrlen;
+
+       if (find_sha1_file(sha1, &st))
+               return 0;
+       buf = read_packed_sha1(sha1, &type, &len);
+       if (!buf)
+               return error("cannot read sha1_file for %s", sha1_to_hex(sha1));
+       hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
+       return write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
+}
+
 /*
  * We need to unpack and recompress the object for writing
  * it out to a different file.
@@ -2280,9 +2357,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
        } while (1);
        inflateEnd(&stream);
 
-       fchmod(local, 0444);
-       if (close(local) != 0)
-               die("unable to write sha1 file");
+       close_sha1_file(local);
        SHA1_Final(real_sha1, &c);
        if (ret != Z_STREAM_END) {
                unlink(tmpfile);
@@ -2433,16 +2508,10 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
 
 int read_pack_header(int fd, struct pack_header *header)
 {
-       char *c = (char*)header;
-       ssize_t remaining = sizeof(struct pack_header);
-       do {
-               ssize_t r = xread(fd, c, remaining);
-               if (r <= 0)
-                       /* "eof before pack header was fully read" */
-                       return PH_ERROR_EOF;
-               remaining -= r;
-               c += r;
-       } while (remaining > 0);
+       if (read_in_full(fd, header, sizeof(*header)) < sizeof(*header))
+               /* "eof before pack header was fully read" */
+               return PH_ERROR_EOF;
+
        if (header->hdr_signature != htonl(PACK_SIGNATURE))
                /* "protocol error (pack signature mismatch detected)" */
                return PH_ERROR_PACK_SIGNATURE;