binary diff and apply: testsuite.
[gitweb.git] / sha1_file.c
index 1d799f793a936a696cea86deb36fa2bdb04c29f4..54648282592d85cea0b1d42e02f358c240991ce2 100644 (file)
@@ -9,6 +9,10 @@
 #include "cache.h"
 #include "delta.h"
 #include "pack.h"
+#include "blob.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree.h"
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -104,9 +108,10 @@ int safe_create_leading_directories(char *path)
 
 char * sha1_to_hex(const unsigned char *sha1)
 {
-       static char buffer[50];
+       static int bufno;
+       static char hexbuffer[4][50];
        static const char hex[] = "0123456789abcdef";
-       char *buf = buffer;
+       char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
        int i;
 
        for (i = 0; i < 20; i++) {
@@ -247,6 +252,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
                for ( ; cp < ep && *cp != sep; cp++)
                        ;
                if (last != cp) {
+                       struct stat st;
                        struct alternate_object_database *alt;
                        /* 43 = 40-byte + 2 '/' + terminating NUL */
                        int pfxlen = cp - last;
@@ -269,9 +275,19 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
                        }
                        else
                                memcpy(ent->base, last, pfxlen);
+
                        ent->name = ent->base + pfxlen + 1;
-                       ent->base[pfxlen] = ent->base[pfxlen + 3] = '/';
-                       ent->base[entlen-1] = 0;
+                       ent->base[pfxlen + 3] = '/';
+                       ent->base[pfxlen] = ent->base[entlen-1] = 0;
+
+                       /* Detect cases where alternate disappeared */
+                       if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
+                               error("object directory %s does not exist; "
+                                     "check .git/objects/info/alternates.",
+                                     ent->base);
+                               goto bad;
+                       }
+                       ent->base[pfxlen] = '/';
 
                        /* Prevent the common mistake of listing the same
                         * thing twice, or object directory itself.
@@ -552,7 +568,9 @@ static void prepare_packed_git_one(char *objdir, int local)
        len = strlen(path);
        dir = opendir(path);
        if (!dir) {
-               fprintf(stderr, "unable to open object pack directory: %s: %s\n", path, strerror(errno));
+               if (errno != ENOENT)
+                       error("unable to open object pack directory: %s: %s",
+                             path, strerror(errno));
                return;
        }
        path[len++] = '/';
@@ -791,10 +809,12 @@ static int packed_delta_info(unsigned char *base_sha1,
                 * the result size.
                 */
                data = delta_head;
-               get_delta_hdr_size(&data); /* ignore base size */
+
+               /* ignore base size */
+               get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
 
                /* Read the result size */
-               result_size = get_delta_hdr_size(&data);
+               result_size = get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
                *sizep = result_size;
        }
        return 0;
@@ -828,6 +848,25 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of
        return offset;
 }
 
+int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
+                          unsigned char *base, unsigned long *sizep,
+                          enum object_type *kindp)
+{
+       unsigned long ptr;
+       int status = -1;
+
+       use_packed_git(p);
+       ptr = offset;
+       ptr = unpack_object_header(p, ptr, kindp, sizep);
+       if (*kindp != OBJ_DELTA)
+               goto done;
+       memcpy(base, p->pack_base + ptr, 20);
+       status = 0;
+ done:
+       unuse_packed_git(p);
+       return status;
+}
+
 void packed_object_info_detail(struct pack_entry *e,
                               char *type,
                               unsigned long *size,
@@ -836,17 +875,19 @@ void packed_object_info_detail(struct pack_entry *e,
                               unsigned char *base_sha1)
 {
        struct packed_git *p = e->p;
-       unsigned long offset, left;
+       unsigned long offset;
        unsigned char *pack;
        enum object_type kind;
 
        offset = unpack_object_header(p, e->offset, &kind, size);
        pack = p->pack_base + offset;
-       left = p->pack_size - offset;
        if (kind != OBJ_DELTA)
                *delta_chain_length = 0;
        else {
                unsigned int chain_length = 0;
+               if (p->pack_size <= offset + 20)
+                       die("pack file %s records an incomplete delta base",
+                           p->pack_name);
                memcpy(base_sha1, pack, 20);
                do {
                        struct pack_entry base_ent;
@@ -862,16 +903,16 @@ void packed_object_info_detail(struct pack_entry *e,
        }
        switch (kind) {
        case OBJ_COMMIT:
-               strcpy(type, "commit");
+               strcpy(type, commit_type);
                break;
        case OBJ_TREE:
-               strcpy(type, "tree");
+               strcpy(type, tree_type);
                break;
        case OBJ_BLOB:
-               strcpy(type, "blob");
+               strcpy(type, blob_type);
                break;
        case OBJ_TAG:
-               strcpy(type, "tag");
+               strcpy(type, tag_type);
                break;
        default:
                die("corrupted pack file %s containing object of kind %d",
@@ -902,16 +943,16 @@ static int packed_object_info(struct pack_entry *entry,
                unuse_packed_git(p);
                return retval;
        case OBJ_COMMIT:
-               strcpy(type, "commit");
+               strcpy(type, commit_type);
                break;
        case OBJ_TREE:
-               strcpy(type, "tree");
+               strcpy(type, tree_type);
                break;
        case OBJ_BLOB:
-               strcpy(type, "blob");
+               strcpy(type, blob_type);
                break;
        case OBJ_TAG:
-               strcpy(type, "tag");
+               strcpy(type, tag_type);
                break;
        default:
                die("corrupted pack file %s containing object of kind %d",
@@ -941,6 +982,16 @@ static void *unpack_delta_entry(unsigned char *base_sha1,
 
        if (left < 20)
                die("truncated pack file");
+
+       /* The base entry _must_ be in the same pack */
+       if (!find_pack_entry_one(base_sha1, &base_ent, p))
+               die("failed to find delta-pack base object %s",
+                   sha1_to_hex(base_sha1));
+       base = unpack_entry_gently(&base_ent, type, &base_size);
+       if (!base)
+               die("failed to read delta-pack base object %s",
+                   sha1_to_hex(base_sha1));
+
        data = base_sha1 + 20;
        data_size = left - 20;
        delta_data = xmalloc(delta_size);
@@ -958,14 +1009,6 @@ static void *unpack_delta_entry(unsigned char *base_sha1,
        if ((st != Z_STREAM_END) || stream.total_out != delta_size)
                die("delta data unpack failed");
 
-       /* The base entry _must_ be in the same pack */
-       if (!find_pack_entry_one(base_sha1, &base_ent, p))
-               die("failed to find delta-pack base object %s",
-                   sha1_to_hex(base_sha1));
-       base = unpack_entry_gently(&base_ent, type, &base_size);
-       if (!base)
-               die("failed to read delta-pack base object %s",
-                   sha1_to_hex(base_sha1));
        result = patch_delta(base, base_size,
                             delta_data, delta_size,
                             &result_size);
@@ -1037,16 +1080,16 @@ void *unpack_entry_gently(struct pack_entry *entry,
                retval = unpack_delta_entry(pack, size, left, type, sizep, p);
                return retval;
        case OBJ_COMMIT:
-               strcpy(type, "commit");
+               strcpy(type, commit_type);
                break;
        case OBJ_TREE:
-               strcpy(type, "tree");
+               strcpy(type, tree_type);
                break;
        case OBJ_BLOB:
-               strcpy(type, "blob");
+               strcpy(type, blob_type);
                break;
        case OBJ_TAG:
-               strcpy(type, "tag");
+               strcpy(type, tag_type);
                break;
        default:
                return NULL;
@@ -1207,9 +1250,9 @@ void *read_object_with_reference(const unsigned char *sha1,
                        return buffer;
                }
                /* Handle references */
-               else if (!strcmp(type, "commit"))
+               else if (!strcmp(type, commit_type))
                        ref_type = "tree ";
-               else if (!strcmp(type, "tag"))
+               else if (!strcmp(type, tag_type))
                        ref_type = "object ";
                else {
                        free(buffer);
@@ -1481,7 +1524,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
 
        local = mkstemp(tmpfile);
        if (local < 0)
-               return error("Couldn't open %s for %s\n", tmpfile, sha1_to_hex(sha1));
+               return error("Couldn't open %s for %s",
+                            tmpfile, sha1_to_hex(sha1));
 
        memset(&stream, 0, sizeof(stream));
 
@@ -1529,7 +1573,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
        }
        if (memcmp(sha1, real_sha1, 20)) {
                unlink(tmpfile);
-               return error("File %s has bad hash\n", sha1_to_hex(sha1));
+               return error("File %s has bad hash", sha1_to_hex(sha1));
        }
 
        return move_temp_to_file(tmpfile, sha1_file_name(sha1));
@@ -1590,7 +1634,7 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
                return -1;
        }
        if (!type)
-               type = "blob";
+               type = blob_type;
        if (write_object)
                ret = write_sha1_file(buf, off, type, sha1);
        else {
@@ -1617,7 +1661,7 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
                return -1;
 
        if (!type)
-               type = "blob";
+               type = blob_type;
        if (write_object)
                ret = write_sha1_file(buf, size, type, sha1);
        else {
@@ -1655,9 +1699,9 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
                if (!write_object) {
                        unsigned char hdr[50];
                        int hdrlen;
-                       write_sha1_file_prepare(target, st->st_size, "blob",
+                       write_sha1_file_prepare(target, st->st_size, blob_type,
                                                sha1, hdr, &hdrlen);
-               } else if (write_sha1_file(target, st->st_size, "blob", sha1))
+               } else if (write_sha1_file(target, st->st_size, blob_type, sha1))
                        return error("%s: failed to insert into database",
                                     path);
                free(target);