Merge branch 'sg/t3020-typofix'
[gitweb.git] / sha1_file.c
index 0c70152c17fbf612bbdf0112bf12fe8f7a852761..08302f5857b8ad298809adf04a6bffc136f843a4 100644 (file)
@@ -404,13 +404,46 @@ void read_info_alternates(const char * relative_base, int depth)
 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"), LOCK_DIE_ON_ERROR);
-       char *alt = mkpath("%s\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, strlen(alt), '\n', NULL, 0);
+       char *alts = git_pathdup("objects/info/alternates");
+       FILE *in, *out;
+
+       hold_lock_file_for_update(lock, alts, LOCK_DIE_ON_ERROR);
+       out = fdopen_lock_file(lock, "w");
+       if (!out)
+               die_errno("unable to fdopen alternates lockfile");
+
+       in = fopen(alts, "r");
+       if (in) {
+               struct strbuf line = STRBUF_INIT;
+               int found = 0;
+
+               while (strbuf_getline(&line, in, '\n') != EOF) {
+                       if (!strcmp(reference, line.buf)) {
+                               found = 1;
+                               break;
+                       }
+                       fprintf_or_die(out, "%s\n", line.buf);
+               }
+
+               strbuf_release(&line);
+               fclose(in);
+
+               if (found) {
+                       rollback_lock_file(lock);
+                       lock = NULL;
+               }
+       }
+       else if (errno != ENOENT)
+               die_errno("unable to read alternates file");
+
+       if (lock) {
+               fprintf_or_die(out, "%s\n", reference);
+               if (commit_lock_file(lock))
+                       die_errno("unable to move new alternates file into place");
+               if (alt_odb_tail)
+                       link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
+       }
+       free(alts);
 }
 
 int foreach_alt_odb(alt_odb_fn fn, void *cb)
@@ -443,6 +476,7 @@ void prepare_alt_odb(void)
        read_info_alternates(get_object_directory(), 0);
 }
 
+/* Returns 1 if we have successfully freshened the file, 0 otherwise. */
 static int freshen_file(const char *fn)
 {
        struct utimbuf t;
@@ -450,11 +484,18 @@ static int freshen_file(const char *fn)
        return !utime(fn, &t);
 }
 
+/*
+ * All of the check_and_freshen functions return 1 if the file exists and was
+ * freshened (if freshening was requested), 0 otherwise. If they return
+ * 0, you should not assume that it is safe to skip a write of the object (it
+ * either does not exist on disk, or has a stale mtime and may be subject to
+ * pruning).
+ */
 static int check_and_freshen_file(const char *fn, int freshen)
 {
        if (access(fn, F_OK))
                return 0;
-       if (freshen && freshen_file(fn))
+       if (freshen && !freshen_file(fn))
                return 0;
        return 1;
 }
@@ -1453,7 +1494,10 @@ int git_open_noatime(const char *name)
        static int sha1_file_open_flag = O_NOATIME;
 
        for (;;) {
-               int fd = open(name, O_RDONLY | sha1_file_open_flag);
+               int fd;
+
+               errno = 0;
+               fd = open(name, O_RDONLY | sha1_file_open_flag);
                if (fd >= 0)
                        return fd;
 
@@ -1571,6 +1615,40 @@ int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long ma
        return git_inflate(stream, 0);
 }
 
+static int unpack_sha1_header_to_strbuf(git_zstream *stream, unsigned char *map,
+                                       unsigned long mapsize, void *buffer,
+                                       unsigned long bufsiz, struct strbuf *header)
+{
+       int status;
+
+       status = unpack_sha1_header(stream, map, mapsize, buffer, bufsiz);
+
+       /*
+        * Check if entire header is unpacked in the first iteration.
+        */
+       if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
+               return 0;
+
+       /*
+        * buffer[0..bufsiz] was not large enough.  Copy the partial
+        * result out to header, and then append the result of further
+        * reading the stream.
+        */
+       strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
+       stream->next_out = buffer;
+       stream->avail_out = bufsiz;
+
+       do {
+               status = git_inflate(stream, 0);
+               strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
+               if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
+                       return 0;
+               stream->next_out = buffer;
+               stream->avail_out = bufsiz;
+       } while (status != Z_STREAM_END);
+       return -1;
+}
+
 static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
 {
        int bytes = strlen(buffer) + 1;
@@ -1621,27 +1699,38 @@ static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long s
  * too permissive for what we want to check. So do an anal
  * object header parse by hand.
  */
-int parse_sha1_header(const char *hdr, unsigned long *sizep)
+static int parse_sha1_header_extended(const char *hdr, struct object_info *oi,
+                              unsigned int flags)
 {
-       char type[10];
-       int i;
+       const char *type_buf = hdr;
        unsigned long size;
+       int type, type_len = 0;
 
        /*
-        * The type can be at most ten bytes (including the
-        * terminating '\0' that we add), and is followed by
+        * The type can be of any size but is followed by
         * a space.
         */
-       i = 0;
        for (;;) {
                char c = *hdr++;
                if (c == ' ')
                        break;
-               type[i++] = c;
-               if (i >= sizeof(type))
-                       return -1;
+               type_len++;
        }
-       type[i] = 0;
+
+       type = type_from_string_gently(type_buf, type_len, 1);
+       if (oi->typename)
+               strbuf_add(oi->typename, type_buf, type_len);
+       /*
+        * Set type to 0 if its an unknown object and
+        * we're obtaining the type using '--allow-unkown-type'
+        * option.
+        */
+       if ((flags & LOOKUP_UNKNOWN_OBJECT) && (type < 0))
+               type = 0;
+       else if (type < 0)
+               die("invalid object type");
+       if (oi->typep)
+               *oi->typep = type;
 
        /*
         * The length must follow immediately, and be in canonical
@@ -1659,12 +1748,24 @@ int parse_sha1_header(const char *hdr, unsigned long *sizep)
                        size = size * 10 + c;
                }
        }
-       *sizep = size;
+
+       if (oi->sizep)
+               *oi->sizep = size;
 
        /*
         * The length must be followed by a zero byte
         */
-       return *hdr ? -1 : type_from_string(type);
+       return *hdr ? -1 : type;
+}
+
+int parse_sha1_header(const char *hdr, unsigned long *sizep)
+{
+       struct object_info oi;
+
+       oi.sizep = sizep;
+       oi.typename = NULL;
+       oi.typep = NULL;
+       return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);
 }
 
 static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
@@ -2529,13 +2630,15 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1,
 }
 
 static int sha1_loose_object_info(const unsigned char *sha1,
-                                 struct object_info *oi)
+                                 struct object_info *oi,
+                                 int flags)
 {
-       int status;
-       unsigned long mapsize, size;
+       int status = 0;
+       unsigned long mapsize;
        void *map;
        git_zstream stream;
        char hdr[32];
+       struct strbuf hdrbuf = STRBUF_INIT;
 
        if (oi->delta_base_sha1)
                hashclr(oi->delta_base_sha1);
@@ -2548,7 +2651,7 @@ static int sha1_loose_object_info(const unsigned char *sha1,
         * return value implicitly indicates whether the
         * object even exists.
         */
-       if (!oi->typep && !oi->sizep) {
+       if (!oi->typep && !oi->typename && !oi->sizep) {
                struct stat st;
                if (stat_sha1_file(sha1, &st) < 0)
                        return -1;
@@ -2562,17 +2665,26 @@ static int sha1_loose_object_info(const unsigned char *sha1,
                return -1;
        if (oi->disk_sizep)
                *oi->disk_sizep = mapsize;
-       if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
+       if ((flags & LOOKUP_UNKNOWN_OBJECT)) {
+               if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0)
+                       status = error("unable to unpack %s header with --allow-unknown-type",
+                                      sha1_to_hex(sha1));
+       } else if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
                status = error("unable to unpack %s header",
                               sha1_to_hex(sha1));
-       else if ((status = parse_sha1_header(hdr, &size)) < 0)
+       if (status < 0)
+               ; /* Do nothing */
+       else if (hdrbuf.len) {
+               if ((status = parse_sha1_header_extended(hdrbuf.buf, oi, flags)) < 0)
+                       status = error("unable to parse %s header with --allow-unknown-type",
+                                      sha1_to_hex(sha1));
+       } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0)
                status = error("unable to parse %s header", sha1_to_hex(sha1));
-       else if (oi->sizep)
-               *oi->sizep = size;
        git_inflate_end(&stream);
        munmap(map, mapsize);
-       if (oi->typep)
+       if (status && oi->typep)
                *oi->typep = status;
+       strbuf_release(&hdrbuf);
        return 0;
 }
 
@@ -2581,6 +2693,7 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
        struct cached_object *co;
        struct pack_entry e;
        int rtype;
+       enum object_type real_type;
        const unsigned char *real = lookup_replace_object_extended(sha1, flags);
 
        co = find_cached_object(real);
@@ -2593,13 +2706,15 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
                        *(oi->disk_sizep) = 0;
                if (oi->delta_base_sha1)
                        hashclr(oi->delta_base_sha1);
+               if (oi->typename)
+                       strbuf_addstr(oi->typename, typename(co->type));
                oi->whence = OI_CACHED;
                return 0;
        }
 
        if (!find_pack_entry(real, &e)) {
                /* Most likely it's a loose object. */
-               if (!sha1_loose_object_info(real, oi)) {
+               if (!sha1_loose_object_info(real, oi, flags)) {
                        oi->whence = OI_LOOSE;
                        return 0;
                }
@@ -2610,9 +2725,18 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
                        return -1;
        }
 
+       /*
+        * packed_object_info() does not follow the delta chain to
+        * find out the real type, unless it is given oi->typep.
+        */
+       if (oi->typename && !oi->typep)
+               oi->typep = &real_type;
+
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
                mark_bad_packed_object(e.p, real);
+               if (oi->typep == &real_type)
+                       oi->typep = NULL;
                return sha1_object_info_extended(real, oi, 0);
        } else if (in_delta_base_cache(e.p, e.offset)) {
                oi->whence = OI_DBCACHED;
@@ -2623,6 +2747,10 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
                oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
                                         rtype == OBJ_OFS_DELTA);
        }
+       if (oi->typename)
+               strbuf_addstr(oi->typename, typename(*oi->typep));
+       if (oi->typep == &real_type)
+               oi->typep = NULL;
 
        return 0;
 }
@@ -2816,11 +2944,8 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len,
 
 /*
  * Move the just written object into its final resting place.
- * NEEDSWORK: this should be renamed to finalize_temp_file() as
- * "moving" is only a part of what it does, when no patch between
- * master to pu changes the call sites of this function.
  */
-int move_temp_to_file(const char *tmpfile, const char *filename)
+int finalize_object_file(const char *tmpfile, const char *filename)
 {
        int ret = 0;
 
@@ -2993,7 +3118,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
                                tmp_file, strerror(errno));
        }
 
-       return move_temp_to_file(tmp_file, filename);
+       return finalize_object_file(tmp_file, filename);
 }
 
 static int freshen_loose_object(const unsigned char *sha1)
@@ -3491,14 +3616,19 @@ 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;
+       return r ? r : pack_errors;
 }