Code

Merge branch 'wh/normalize-alt-odb-path'
authorJunio C Hamano <gitster@pobox.com>
Wed, 5 Oct 2011 19:36:22 +0000 (12:36 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 5 Oct 2011 19:36:22 +0000 (12:36 -0700)
* wh/normalize-alt-odb-path:
  sha1_file: normalize alt_odb path before comparing and storing

1  2 
sha1_file.c

diff --combined sha1_file.c
index e002056b85ce89c089dd09c1300461d60d81dffa,d880137ef448d36940c5dd25d4c7d6fef5371a78..34013014442e18bd02ae0ce33df34a89ec8d6171
@@@ -248,27 -248,30 +248,30 @@@ static int link_alt_odb_entry(const cha
        const char *objdir = get_object_directory();
        struct alternate_object_database *ent;
        struct alternate_object_database *alt;
-       /* 43 = 40-byte + 2 '/' + terminating NUL */
-       int pfxlen = len;
-       int entlen = pfxlen + 43;
-       int base_len = -1;
+       int pfxlen, entlen;
+       struct strbuf pathbuf = STRBUF_INIT;
  
        if (!is_absolute_path(entry) && relative_base) {
-               /* Relative alt-odb */
-               if (base_len < 0)
-                       base_len = strlen(relative_base) + 1;
-               entlen += base_len;
-               pfxlen += base_len;
+               strbuf_addstr(&pathbuf, real_path(relative_base));
+               strbuf_addch(&pathbuf, '/');
        }
-       ent = xmalloc(sizeof(*ent) + entlen);
+       strbuf_add(&pathbuf, entry, len);
  
-       if (!is_absolute_path(entry) && relative_base) {
-               memcpy(ent->base, relative_base, base_len - 1);
-               ent->base[base_len - 1] = '/';
-               memcpy(ent->base + base_len, entry, len);
-       }
-       else
-               memcpy(ent->base, entry, pfxlen);
+       normalize_path_copy(pathbuf.buf, pathbuf.buf);
+       pfxlen = strlen(pathbuf.buf);
+       /*
+        * The trailing slash after the directory name is given by
+        * this function at the end. Remove duplicates.
+        */
+       while (pfxlen && pathbuf.buf[pfxlen-1] == '/')
+               pfxlen -= 1;
+       entlen = pfxlen + 43; /* '/' + 2 hex + '/' + 38 hex + NUL */
+       ent = xmalloc(sizeof(*ent) + entlen);
+       memcpy(ent->base, pathbuf.buf, pfxlen);
+       strbuf_release(&pathbuf);
  
        ent->name = ent->base + pfxlen + 1;
        ent->base[pfxlen + 3] = '/';
@@@ -380,7 -383,7 +383,7 @@@ void add_to_alternates_file(const char 
  {
        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/objects\n", reference);
 +      char *alt = mkpath("%s\n", reference);
        write_or_die(fd, alt, strlen(alt));
        if (commit_lock_file(lock))
                die("could not close alternates file");
@@@ -834,7 -837,7 +837,7 @@@ static int in_window(struct pack_windo
  unsigned char *use_pack(struct packed_git *p,
                struct pack_window **w_cursor,
                off_t offset,
 -              unsigned int *left)
 +              unsigned long *left)
  {
        struct pack_window *win = *w_cursor;
  
@@@ -1186,7 -1189,7 +1189,7 @@@ static int open_sha1_file(const unsigne
        return -1;
  }
  
 -static void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
 +void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
  {
        void *map;
        int fd;
        return map;
  }
  
 -static int legacy_loose_object(unsigned char *map)
 +/*
 + * There used to be a second loose object header format which
 + * was meant to mimic the in-pack format, allowing for direct
 + * copy of the object data.  This format turned up not to be
 + * really worth it and we no longer write loose objects in that
 + * format.
 + */
 +static int experimental_loose_object(unsigned char *map)
  {
        unsigned int word;
  
        /*
 -       * Is it a zlib-compressed buffer? If so, the first byte
 -       * must be 0x78 (15-bit window size, deflated), and the
 -       * first 16-bit word is evenly divisible by 31
 +       * We must determine if the buffer contains the standard
 +       * zlib-deflated stream or the experimental format based
 +       * on the in-pack object format. Compare the header byte
 +       * for each format:
 +       *
 +       * RFC1950 zlib w/ deflate : 0www1000 : 0 <= www <= 7
 +       * Experimental pack-based : Stttssss : ttt = 1,2,3,4
 +       *
 +       * If bit 7 is clear and bits 0-3 equal 8, the buffer MUST be
 +       * in standard loose-object format, UNLESS it is a Git-pack
 +       * format object *exactly* 8 bytes in size when inflated.
 +       *
 +       * However, RFC1950 also specifies that the 1st 16-bit word
 +       * must be divisible by 31 - this checksum tells us our buffer
 +       * is in the standard format, giving a false positive only if
 +       * the 1st word of the Git-pack format object happens to be
 +       * divisible by 31, ie:
 +       *      ((byte0 * 256) + byte1) % 31 = 0
 +       *   =>        0ttt10000www1000 % 31 = 0
 +       *
 +       * As it happens, this case can only arise for www=3 & ttt=1
 +       * - ie, a Commit object, which would have to be 8 bytes in
 +       * size. As no Commit can be that small, we find that the
 +       * combination of these two criteria (bitmask & checksum)
 +       * can always correctly determine the buffer format.
         */
        word = (map[0] << 8) + map[1];
 -      if (map[0] == 0x78 && !(word % 31))
 -              return 1;
 -      else
 +      if ((map[0] & 0x8F) == 0x08 && !(word % 31))
                return 0;
 +      else
 +              return 1;
  }
  
  unsigned long unpack_object_header_buffer(const unsigned char *buf,
        return used;
  }
  
 -static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
 +int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
  {
        unsigned long size, used;
        static const char valid_loose_object_type[8] = {
        stream->next_out = buffer;
        stream->avail_out = bufsiz;
  
 -      if (legacy_loose_object(map)) {
 -              git_inflate_init(stream);
 -              return git_inflate(stream, 0);
 -      }
 -
 +      if (experimental_loose_object(map)) {
 +              /*
 +               * The old experimental format we no longer produce;
 +               * we can still read it.
 +               */
 +              used = unpack_object_header_buffer(map, mapsize, &type, &size);
 +              if (!used || !valid_loose_object_type[type])
 +                      return -1;
 +              map += used;
 +              mapsize -= used;
  
 -      /*
 -       * There used to be a second loose object header format which
 -       * was meant to mimic the in-pack format, allowing for direct
 -       * copy of the object data.  This format turned up not to be
 -       * really worth it and we don't write it any longer.  But we
 -       * can still read it.
 -       */
 -      used = unpack_object_header_buffer(map, mapsize, &type, &size);
 -      if (!used || !valid_loose_object_type[type])
 -              return -1;
 -      map += used;
 -      mapsize -= used;
 +              /* Set up the stream for the rest.. */
 +              stream->next_in = map;
 +              stream->avail_in = mapsize;
 +              git_inflate_init(stream);
  
 -      /* Set up the stream for the rest.. */
 -      stream->next_in = map;
 -      stream->avail_in = mapsize;
 +              /* And generate the fake traditional header */
 +              stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
 +                                               typename(type), size);
 +              return 0;
 +      }
        git_inflate_init(stream);
 -
 -      /* And generate the fake traditional header */
 -      stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
 -                                       typename(type), size);
 -      return 0;
 +      return git_inflate(stream, 0);
  }
  
 -static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
 +static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
  {
        int bytes = strlen(buffer) + 1;
        unsigned char *buf = xmallocz(size);
   * too permissive for what we want to check. So do an anal
   * object header parse by hand.
   */
 -static int parse_sha1_header(const char *hdr, unsigned long *sizep)
 +int parse_sha1_header(const char *hdr, unsigned long *sizep)
  {
        char type[10];
        int i;
  static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
  {
        int ret;
 -      z_stream stream;
 +      git_zstream stream;
        char hdr[8192];
  
        ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr));
@@@ -1431,7 -1410,7 +1434,7 @@@ unsigned long get_size_from_delta(struc
  {
        const unsigned char *data;
        unsigned char delta_head[20], *in;
 -      z_stream stream;
 +      git_zstream stream;
        int st;
  
        memset(&stream, 0, sizeof(stream));
@@@ -1505,7 -1484,7 +1508,7 @@@ static off_t get_delta_base(struct pack
  
  /* forward declaration for a mutually recursive function */
  static int packed_object_info(struct packed_git *p, off_t offset,
 -                            unsigned long *sizep);
 +                            unsigned long *sizep, int *rtype);
  
  static int packed_delta_info(struct packed_git *p,
                             struct pack_window **w_curs,
        base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
        if (!base_offset)
                return OBJ_BAD;
 -      type = packed_object_info(p, base_offset, NULL);
 +      type = packed_object_info(p, base_offset, NULL, NULL);
        if (type <= OBJ_NONE) {
                struct revindex_entry *revidx;
                const unsigned char *base_sha1;
        return type;
  }
  
 -static int unpack_object_header(struct packed_git *p,
 -                              struct pack_window **w_curs,
 -                              off_t *curpos,
 -                              unsigned long *sizep)
 +int unpack_object_header(struct packed_git *p,
 +                       struct pack_window **w_curs,
 +                       off_t *curpos,
 +                       unsigned long *sizep)
  {
        unsigned char *base;
 -      unsigned int left;
 +      unsigned long left;
        unsigned long used;
        enum object_type type;
  
        return type;
  }
  
 -const char *packed_object_info_detail(struct packed_git *p,
 -                                    off_t obj_offset,
 -                                    unsigned long *size,
 -                                    unsigned long *store_size,
 -                                    unsigned int *delta_chain_length,
 -                                    unsigned char *base_sha1)
 -{
 -      struct pack_window *w_curs = NULL;
 -      off_t curpos;
 -      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:
 -                      die("pack %s contains unknown object type %d",
 -                          p->pack_name, type);
 -              case OBJ_COMMIT:
 -              case OBJ_TREE:
 -              case OBJ_BLOB:
 -              case OBJ_TAG:
 -                      unuse_pack(&w_curs);
 -                      return typename(type);
 -              case OBJ_OFS_DELTA:
 -                      obj_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
 -                      if (!obj_offset)
 -                              die("pack %s contains bad delta base reference of type %s",
 -                                  p->pack_name, typename(type));
 -                      if (*delta_chain_length == 0) {
 -                              revidx = find_pack_revindex(p, obj_offset);
 -                              hashcpy(base_sha1, nth_packed_object_sha1(p, revidx->nr));
 -                      }
 -                      break;
 -              case OBJ_REF_DELTA:
 -                      next_sha1 = use_pack(p, &w_curs, curpos, NULL);
 -                      if (*delta_chain_length == 0)
 -                              hashcpy(base_sha1, next_sha1);
 -                      obj_offset = find_pack_entry_one(next_sha1, p);
 -                      break;
 -              }
 -              (*delta_chain_length)++;
 -              curpos = obj_offset;
 -              type = unpack_object_header(p, &w_curs, &curpos, &dummy);
 -      }
 -}
 -
  static int packed_object_info(struct packed_git *p, off_t obj_offset,
 -                            unsigned long *sizep)
 +                            unsigned long *sizep, int *rtype)
  {
        struct pack_window *w_curs = NULL;
        unsigned long size;
        enum object_type type;
  
        type = unpack_object_header(p, &w_curs, &curpos, &size);
 +      if (rtype)
 +              *rtype = type; /* representation type */
  
        switch (type) {
        case OBJ_OFS_DELTA:
@@@ -1613,7 -1645,7 +1616,7 @@@ static void *unpack_compressed_entry(st
                                    unsigned long size)
  {
        int st;
 -      z_stream stream;
 +      git_zstream stream;
        unsigned char *buffer, *in;
  
        buffer = xmallocz(size);
@@@ -1666,13 -1698,6 +1669,13 @@@ static unsigned long pack_entry_hash(st
        return hash % MAX_DELTA_CACHE;
  }
  
 +static int in_delta_base_cache(struct packed_git *p, off_t base_offset)
 +{
 +      unsigned long hash = pack_entry_hash(p, base_offset);
 +      struct delta_base_cache_entry *ent = delta_base_cache + hash;
 +      return (ent->data && ent->p == p && ent->base_offset == base_offset);
 +}
 +
  static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
        unsigned long *base_size, enum object_type *type, int keep_cache)
  {
@@@ -1817,24 -1842,6 +1820,24 @@@ static void *unpack_delta_entry(struct 
        return result;
  }
  
 +static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
 +{
 +      static FILE *log_file;
 +
 +      if (!log_file) {
 +              log_file = fopen(log_pack_access, "w");
 +              if (!log_file) {
 +                      error("cannot open pack access log '%s' for writing: %s",
 +                            log_pack_access, strerror(errno));
 +                      log_pack_access = NULL;
 +                      return;
 +              }
 +      }
 +      fprintf(log_file, "%s %"PRIuMAX"\n",
 +              p->pack_name, (uintmax_t)obj_offset);
 +      fflush(log_file);
 +}
 +
  int do_check_packed_object_crc;
  
  void *unpack_entry(struct packed_git *p, off_t obj_offset,
        off_t curpos = obj_offset;
        void *data;
  
 +      if (log_pack_access)
 +              write_pack_access_log(p, obj_offset);
 +
        if (do_check_packed_object_crc && p->index_version > 1) {
                struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
                unsigned long len = revidx[1].offset - obj_offset;
@@@ -2074,7 -2078,7 +2077,7 @@@ static int sha1_loose_object_info(cons
        int status;
        unsigned long mapsize, size;
        void *map;
 -      z_stream stream;
 +      git_zstream stream;
        char hdr[32];
  
        map = map_sha1_file(sha1, &mapsize);
        return status;
  }
  
 -int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 +/* returns enum object_type or negative */
 +int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
  {
        struct cached_object *co;
        struct pack_entry e;
 -      int status;
 +      int status, rtype;
  
        co = find_cached_object(sha1);
        if (co) {
 -              if (sizep)
 -                      *sizep = co->size;
 +              if (oi->sizep)
 +                      *(oi->sizep) = co->size;
 +              oi->whence = OI_CACHED;
                return co->type;
        }
  
        if (!find_pack_entry(sha1, &e)) {
                /* Most likely it's a loose object. */
 -              status = sha1_loose_object_info(sha1, sizep);
 -              if (status >= 0)
 +              status = sha1_loose_object_info(sha1, oi->sizep);
 +              if (status >= 0) {
 +                      oi->whence = OI_LOOSE;
                        return status;
 +              }
  
                /* Not a loose object; someone else may have just packed it. */
                reprepare_packed_git();
                        return status;
        }
  
 -      status = packed_object_info(e.p, e.offset, sizep);
 +      status = packed_object_info(e.p, e.offset, oi->sizep, &rtype);
        if (status < 0) {
                mark_bad_packed_object(e.p, sha1);
 -              status = sha1_object_info(sha1, sizep);
 +              status = sha1_object_info_extended(sha1, oi);
 +      } else if (in_delta_base_cache(e.p, e.offset)) {
 +              oi->whence = OI_DBCACHED;
 +      } else {
 +              oi->whence = OI_PACKED;
 +              oi->u.packed.offset = e.offset;
 +              oi->u.packed.pack = e.p;
 +              oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
 +                                       rtype == OBJ_OFS_DELTA);
        }
  
        return status;
  }
  
 +int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 +{
 +      struct object_info oi;
 +
 +      oi.sizep = sizep;
 +      return sha1_object_info_extended(sha1, &oi);
 +}
 +
  static void *read_packed_sha1(const unsigned char *sha1,
                              enum object_type *type, unsigned long *size)
  {
@@@ -2443,7 -2427,7 +2446,7 @@@ static int write_loose_object(const uns
  {
        int fd, ret;
        unsigned char compressed[4096];
 -      z_stream stream;
 +      git_zstream stream;
        git_SHA_CTX c;
        unsigned char parano_sha1[20];
        char *filename;
  
        /* Set it up */
        memset(&stream, 0, sizeof(stream));
 -      deflateInit(&stream, zlib_compression_level);
 +      git_deflate_init(&stream, zlib_compression_level);
        stream.next_out = compressed;
        stream.avail_out = sizeof(compressed);
        git_SHA1_Init(&c);
        /* First header.. */
        stream.next_in = (unsigned char *)hdr;
        stream.avail_in = hdrlen;
 -      while (deflate(&stream, 0) == Z_OK)
 -              /* nothing */;
 +      while (git_deflate(&stream, 0) == Z_OK)
 +              ; /* nothing */
        git_SHA1_Update(&c, hdr, hdrlen);
  
        /* Then the data itself.. */
        stream.avail_in = len;
        do {
                unsigned char *in0 = stream.next_in;
 -              ret = deflate(&stream, Z_FINISH);
 +              ret = git_deflate(&stream, Z_FINISH);
                git_SHA1_Update(&c, in0, stream.next_in - in0);
                if (write_buffer(fd, compressed, stream.next_out - compressed) < 0)
                        die("unable to write sha1 file");
  
        if (ret != Z_STREAM_END)
                die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
 -      ret = deflateEnd(&stream);
 +      ret = git_deflate_end_gently(&stream);
        if (ret != Z_OK)
                die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
        git_SHA1_Final(parano_sha1, &c);
@@@ -2723,7 -2707,7 +2726,7 @@@ static int index_stream(unsigned char *
        while (size) {
                char buf[10240];
                size_t sz = size < sizeof(buf) ? size : sizeof(buf);
 -              size_t actual;
 +              ssize_t actual;
  
                actual = read_in_full(fd, buf, sz);
                if (actual < 0)