Merge branch 'np/pack'
authorJunio C Hamano <junkio@cox.net>
Mon, 23 Oct 2006 05:51:42 +0000 (22:51 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 23 Oct 2006 05:51:42 +0000 (22:51 -0700)
* np/pack:
add the capability for index-pack to read from a stream
index-pack: compare only the first 20-bytes of the key.
git-repack: repo.usedeltabaseoffset
pack-objects: document --delta-base-offset option
allow delta data reuse even if base object is a preferred base
zap a debug remnant
let the GIT native protocol use offsets to delta base when possible
make pack data reuse compatible with both delta types
make git-pack-objects able to create deltas with offset to base
teach git-index-pack about deltas with offset to base
teach git-unpack-objects about deltas with offset to base
introduce delta objects with offset to base

1  2 
Documentation/config.txt
Documentation/git-pack-objects.txt
Documentation/git-repack.txt
cache.h
git-repack.sh
pack.h
sha1_file.c
diff --combined Documentation/config.txt
index 84e38911eeecd3f4e5f195ada61cb9bc831a4627,88e0bf00c2bd1dd1d57aa3ac91dfb004c4e2a4b3..05d657444fc31768f9bf9909324b708ac6fcfd46
@@@ -202,12 -202,6 +202,12 @@@ http.lowSpeedLimit, http.lowSpeedTime:
        Can be overridden by the 'GIT_HTTP_LOW_SPEED_LIMIT' and
        'GIT_HTTP_LOW_SPEED_TIME' environment variables.
  
 +http.noEPSV::
 +      A boolean which disables using of EPSV ftp command by curl.
 +      This can helpful with some "poor" ftp servers which doesn't
 +      support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
 +      environment variable. Default is false (curl will use EPSV).
 +
  i18n.commitEncoding::
        Character encoding the commit messages are stored in; git itself
        does not care per se, but this information is necessary e.g. when
@@@ -230,6 -224,10 +230,10 @@@ pull.octopus:
  pull.twohead::
        The default merge strategy to use when pulling a single branch.
  
+ repack.usedeltabaseoffset::
+       Allow gitlink:git-repack[1] to create packs that uses
+       delta-base offset.  Defaults to false.
  show.difftree::
        The default gitlink:git-diff-tree[1] arguments to be used
        for gitlink:git-show[1].
index f52e8fa8bfb6cd1b121eab7e8594368f5b1c6513,57887097108a6df5525eba3edd634fa61ceb8fd9..a1e55054bd544848605eec8bfaa2cc29ac94f3d0
@@@ -9,7 -9,7 +9,7 @@@ git-pack-objects - Create a packed arch
  SYNOPSIS
  --------
  [verse]
- 'git-pack-objects' [-q] [--no-reuse-delta] [--non-empty]
+ 'git-pack-objects' [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty]
        [--local] [--incremental] [--window=N] [--depth=N]
        [--revs [--unpacked | --all]*] [--stdout | base-name] < object-list
  
@@@ -71,11 -71,11 +71,11 @@@ base-name:
  --all::
        This implies `--revs`.  In addition to the list of
        revision arguments read from the standard input, pretend
 -      as if all refs under `$GIT_DIR/refs` are specifed to be
 +      as if all refs under `$GIT_DIR/refs` are specified to be
        included.
  
 ---window and --depth::
 -      These two options affects how the objects contained in
 +--window=[N], --depth=[N]::
 +      These two options affect how the objects contained in
        the pack are stored using delta compression.  The
        objects are first internally sorted by type, size and
        optionally names and compared against the other objects
@@@ -84,7 -84,6 +84,7 @@@
        it too deep affects the performance on the unpacker
        side, because delta data needs to be applied that many
        times to get to the necessary object.
 +      The default value for both --window and --depth is 10.
  
  --incremental::
        This flag causes an object already in a pack ignored
        This flag tells the command not to reuse existing deltas
        but compute them from scratch.
  
+ --delta-base-offset::
+       A packed archive can express base object of a delta as
+       either 20-byte object name or as an offset in the
+       stream, but older version of git does not understand the
+       latter.  By default, git-pack-objects only uses the
+       former format for better compatibility.  This option
+       allows the command to use the latter format for
+       compactness.  Depending on the average delta chain
+       length, this option typically shrinks the resulting
+       packfile by 3-5 per-cent.
  
  Author
  ------
index d2eaa0995ded915f89d436e4501c5ae667822642,4e6631a27fd10344984026949f990e36cb45e6bf..0fa47e3b018815ffa9de23725737fe2b599eaead
@@@ -57,16 -57,29 +57,30 @@@ OPTION
          `git update-server-info`.
  
  --window=[N], --depth=[N]::
 -      These two options affects how the objects contained in the pack are
 +      These two options affect how the objects contained in the pack are
        stored using delta compression. The objects are first internally
        sorted by type, size and optionally names and compared against the
        other objects within `--window` to see if using delta compression saves
        space. `--depth` limits the maximum delta depth; making it too deep
        affects the performance on the unpacker side, because delta data needs
        to be applied that many times to get to the necessary object.
 +      The default value for both --window and --depth is 10.
  
  
+ Configuration
+ -------------
+ When configuration variable `repack.UseDeltaBaseOffset` is set
+ for the repository, the command passes `--delta-base-offset`
+ option to `git-pack-objects`; this typically results in slightly
+ smaller packs, but the generated packs are incompatible with
+ versions of git older than (and including) v1.4.3; do not set
+ the variable in a repository that older version of git needs to
+ be able to read (this includes repositories from which packs can
+ be copied out over http or rsync, and people who obtained packs
+ that way can try to use older git with it).
  Author
  ------
  Written by Linus Torvalds <torvalds@osdl.org>
diff --combined cache.h
index c35470107dde85b2179333b354aff0be1eed6df2,3c5415e77da74a7fa8b930928509e9f26f6bb984..d0a1657292f5b47b7e345a87877d9b8894a80860
+++ b/cache.h
@@@ -245,8 -245,13 +245,8 @@@ char *enter_repo(char *path, int strict
  extern int sha1_object_info(const unsigned char *, char *, unsigned long *);
  extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size);
  extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
 +extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1);
  extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 -extern char *write_sha1_file_prepare(void *buf,
 -                                   unsigned long len,
 -                                   const char *type,
 -                                   unsigned char *sha1,
 -                                   unsigned char *hdr,
 -                                   int *hdrlen);
  
  extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
  
@@@ -269,8 -274,9 +269,9 @@@ enum object_type 
        OBJ_TREE = 2,
        OBJ_BLOB = 3,
        OBJ_TAG = 4,
-       /* 5/6 for future expansion */
-       OBJ_DELTA = 7,
+       /* 5 for future expansion */
+       OBJ_OFS_DELTA = 6,
+       OBJ_REF_DELTA = 7,
        OBJ_BAD,
  };
  
diff --combined git-repack.sh
index f2c9071d1109e014832f0efd8a1fd67dca44c8af,2a214891a8d8ddbf11dcce2d655d3cab0614e064..17e24526c279467891389295a55f8f257ca0d01b
@@@ -3,8 -3,7 +3,8 @@@
  # Copyright (c) 2005 Linus Torvalds
  #
  
- USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
+ USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]'
 +SUBDIRECTORY_OK='Yes'
  . git-sh-setup
  
  no_update_info= all_into_one= remove_redundant=
@@@ -25,6 -24,15 +25,15 @@@ d
        shift
  done
  
+ # Later we will default repack.UseDeltaBaseOffset to true
+ default_dbo=false
+ case "`git repo-config --bool repack.usedeltabaseoffset ||
+        echo $default_dbo`" in
+ true)
+       extra="$extra --delta-base-offset" ;;
+ esac
  PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
  PACKTMP="$GIT_DIR/.tmp-$$-pack"
  rm -f "$PACKTMP"-*
@@@ -33,10 -41,12 +42,10 @@@ trap 'rm -f "$PACKTMP"-*' 0 1 2 3 1
  # There will be more repacking strategies to come...
  case ",$all_into_one," in
  ,,)
 -      rev_list='--unpacked'
 -      pack_objects='--incremental'
 +      args='--unpacked --incremental'
        ;;
  ,t,)
 -      rev_list=
 -      pack_objects=
 +      args=
  
        # Redundancy check in all-into-one case is trivial.
        existing=`test -d "$PACKDIR" && cd "$PACKDIR" && \
        ;;
  esac
  
 -pack_objects="$pack_objects $local $quiet $no_reuse_delta$extra"
 -name=$( { git-rev-list --objects --all $rev_list ||
 -        echo "git-rev-list died with exit code $?"
 -      } |
 -      git-pack-objects --non-empty $pack_objects "$PACKTMP") ||
 +args="$args $local $quiet $no_reuse_delta$extra"
 +name=$(git-pack-objects --non-empty --all $args </dev/null "$PACKTMP") ||
        exit 1
  if [ -z "$name" ]; then
        echo Nothing new to pack.
diff --combined pack.h
index eb07b033ae54941d4bd00ee6bc30c7d50fd039b0,4c9bddd4f20946bdc1f94d983617e2176135dcc9..4814800f2806a245a675ea9832f894dc95b27b89
--- 1/pack.h
--- 2/pack.h
+++ b/pack.h
@@@ -7,7 -7,7 +7,7 @@@
   * Packed object header
   */
  #define PACK_SIGNATURE 0x5041434b     /* "PACK" */
 -#define PACK_VERSION 3
 +#define PACK_VERSION 2
  #define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
  struct pack_header {
        unsigned int hdr_signature;
@@@ -16,7 -16,4 +16,4 @@@
  };
  
  extern int verify_pack(struct packed_git *, int);
- extern int check_reuse_pack_delta(struct packed_git *, unsigned long,
-                                 unsigned char *, unsigned long *,
-                                 enum object_type *);
  #endif
diff --combined sha1_file.c
index 47e2a29abd6d2ae067d72c0f73bcf4e6afe0a938,18c2f881128f3d6e1756f07f64a0aedb9c755b84..e89d24c01595aa8ea6c6306928639b40f3313a50
@@@ -671,8 -671,14 +671,8 @@@ static void reprepare_packed_git(void
  
  int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
  {
 -      char header[100];
        unsigned char real_sha1[20];
 -      SHA_CTX c;
 -
 -      SHA1_Init(&c);
 -      SHA1_Update(&c, header, 1+sprintf(header, "%s %lu", type, size));
 -      SHA1_Update(&c, map, size);
 -      SHA1_Final(real_sha1, &c);
 +      hash_sha1_file(map, size, type, real_sha1);
        return hashcmp(sha1, real_sha1) ? -1 : 0;
  }
  
@@@ -877,26 -883,61 +877,61 @@@ void * unpack_sha1_file(void *map, unsi
        return unpack_sha1_rest(&stream, hdr, *size);
  }
  
+ static unsigned long get_delta_base(struct packed_git *p,
+                                   unsigned long offset,
+                                   enum object_type kind,
+                                   unsigned long delta_obj_offset,
+                                   unsigned long *base_obj_offset)
+ {
+       unsigned char *base_info = (unsigned char *) p->pack_base + offset;
+       unsigned long base_offset;
+       /* there must be at least 20 bytes left regardless of delta type */
+       if (p->pack_size <= offset + 20)
+               die("truncated pack file");
+       if (kind == OBJ_OFS_DELTA) {
+               unsigned used = 0;
+               unsigned char c = base_info[used++];
+               base_offset = c & 127;
+               while (c & 128) {
+                       base_offset += 1;
+                       if (!base_offset || base_offset & ~(~0UL >> 7))
+                               die("offset value overflow for delta base object");
+                       c = base_info[used++];
+                       base_offset = (base_offset << 7) + (c & 127);
+               }
+               base_offset = delta_obj_offset - base_offset;
+               if (base_offset >= delta_obj_offset)
+                       die("delta base offset out of bound");
+               offset += used;
+       } else if (kind == OBJ_REF_DELTA) {
+               /* The base entry _must_ be in the same pack */
+               base_offset = find_pack_entry_one(base_info, p);
+               if (!base_offset)
+                       die("failed to find delta-pack base object %s",
+                               sha1_to_hex(base_info));
+               offset += 20;
+       } else
+               die("I am totally screwed");
+       *base_obj_offset = base_offset;
+       return offset;
+ }
  /* forward declaration for a mutually recursive function */
  static int packed_object_info(struct packed_git *p, unsigned long offset,
                              char *type, unsigned long *sizep);
  
  static int packed_delta_info(struct packed_git *p,
                             unsigned long offset,
+                            enum object_type kind,
+                            unsigned long obj_offset,
                             char *type,
                             unsigned long *sizep)
  {
        unsigned long base_offset;
-       unsigned char *base_sha1 = (unsigned char *) p->pack_base + offset;
  
-       if (p->pack_size < offset + 20)
-               die("truncated pack file");
-       /* The base entry _must_ be in the same pack */
-       base_offset = find_pack_entry_one(base_sha1, p);
-       if (!base_offset)
-               die("failed to find delta-pack base object %s",
-                   sha1_to_hex(base_sha1));
-       offset += 20;
+       offset = get_delta_base(p, offset, kind, obj_offset, &base_offset);
  
        /* We choose to only get the type of the base object and
         * ignore potentially corrupt pack file that expects the delta
  
        if (sizep) {
                const unsigned char *data;
 -              unsigned char delta_head[64];
 +              unsigned char delta_head[20];
                unsigned long result_size;
                z_stream stream;
                int st;
@@@ -959,25 -1000,6 +994,6 @@@ static unsigned long unpack_object_head
        return offset + used;
  }
  
- 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;
-       hashcpy(base, (unsigned char *) p->pack_base + ptr);
-       status = 0;
-  done:
-       unuse_packed_git(p);
-       return status;
- }
  void packed_object_info_detail(struct packed_git *p,
                               unsigned long offset,
                               char *type,
                               unsigned int *delta_chain_length,
                               unsigned char *base_sha1)
  {
-       unsigned long val;
+       unsigned long obj_offset, val;
        unsigned char *next_sha1;
        enum object_type kind;
  
        *delta_chain_length = 0;
+       obj_offset = offset;
        offset = unpack_object_header(p, offset, &kind, size);
  
        for (;;) {
                        strcpy(type, type_names[kind]);
                        *store_size = 0; /* notyet */
                        return;
-               case OBJ_DELTA:
+               case OBJ_OFS_DELTA:
+                       get_delta_base(p, offset, kind, obj_offset, &offset);
+                       if (*delta_chain_length == 0) {
+                               /* TODO: find base_sha1 as pointed by offset */
+                       }
+                       break;
+               case OBJ_REF_DELTA:
                        if (p->pack_size <= offset + 20)
                                die("pack file %s records an incomplete delta base",
                                    p->pack_name);
                        offset = find_pack_entry_one(next_sha1, p);
                        break;
                }
+               obj_offset = offset;
                offset = unpack_object_header(p, offset, &kind, &val);
                (*delta_chain_length)++;
        }
  static int packed_object_info(struct packed_git *p, unsigned long offset,
                              char *type, unsigned long *sizep)
  {
-       unsigned long size;
+       unsigned long size, obj_offset = offset;
        enum object_type kind;
  
        offset = unpack_object_header(p, offset, &kind, &size);
  
-       if (kind == OBJ_DELTA)
-               return packed_delta_info(p, offset, type, sizep);
        switch (kind) {
+       case OBJ_OFS_DELTA:
+       case OBJ_REF_DELTA:
+               return packed_delta_info(p, offset, kind, obj_offset, type, sizep);
        case OBJ_COMMIT:
        case OBJ_TREE:
        case OBJ_BLOB:
@@@ -1077,23 -1107,15 +1101,15 @@@ static void *unpack_compressed_entry(st
  static void *unpack_delta_entry(struct packed_git *p,
                                unsigned long offset,
                                unsigned long delta_size,
+                               enum object_type kind,
+                               unsigned long obj_offset,
                                char *type,
                                unsigned long *sizep)
  {
        void *delta_data, *result, *base;
        unsigned long result_size, base_size, base_offset;
-       unsigned char *base_sha1;
-       if (p->pack_size < offset + 20)
-               die("truncated pack file");
-       /* The base entry _must_ be in the same pack */
-       base_sha1 = (unsigned char*)p->pack_base + offset;
-       base_offset = find_pack_entry_one(base_sha1, p);
-       if (!base_offset)
-               die("failed to find delta-pack base object %s",
-                   sha1_to_hex(base_sha1));
-       offset += 20;
  
+       offset = get_delta_base(p, offset, kind, obj_offset, &base_offset);
        base = unpack_entry_gently(p, base_offset, type, &base_size);
        if (!base)
                die("failed to read delta base object at %lu from %s",
@@@ -1130,13 -1152,14 +1146,14 @@@ static void *unpack_entry(struct pack_e
  void *unpack_entry_gently(struct packed_git *p, unsigned long offset,
                          char *type, unsigned long *sizep)
  {
-       unsigned long size;
+       unsigned long size, obj_offset = offset;
        enum object_type kind;
  
        offset = unpack_object_header(p, offset, &kind, &size);
        switch (kind) {
-       case OBJ_DELTA:
-               return unpack_delta_entry(p, offset, size, type, sizep);
+       case OBJ_OFS_DELTA:
+       case OBJ_REF_DELTA:
+               return unpack_delta_entry(p, offset, size, kind, obj_offset, type, sizep);
        case OBJ_COMMIT:
        case OBJ_TREE:
        case OBJ_BLOB:
@@@ -1341,9 -1364,12 +1358,9 @@@ void *read_object_with_reference(const 
        }
  }
  
 -char *write_sha1_file_prepare(void *buf,
 -                            unsigned long len,
 -                            const char *type,
 -                            unsigned char *sha1,
 -                            unsigned char *hdr,
 -                            int *hdrlen)
 +static void write_sha1_file_prepare(void *buf, unsigned long len,
 +                                    const char *type, unsigned char *sha1,
 +                                    unsigned char *hdr, int *hdrlen)
  {
        SHA_CTX c;
  
        SHA1_Update(&c, hdr, *hdrlen);
        SHA1_Update(&c, buf, len);
        SHA1_Final(sha1, &c);
 -
 -      return sha1_file_name(sha1);
  }
  
  /*
@@@ -1490,15 -1518,6 +1507,15 @@@ static void setup_object_header(z_strea
        stream->avail_out -= hdr;
  }
  
 +int hash_sha1_file(void *buf, unsigned long len, const char *type,
 +                   unsigned char *sha1)
 +{
 +      unsigned char hdr[50];
 +      int hdrlen;
 +      write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
 +      return 0;
 +}
 +
  int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
  {
        int size;
        /* Normally if we have it in the pack then we do not bother writing
         * it out into .git/objects/??/?{38} file.
         */
 -      filename = write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
 +      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))
@@@ -1783,6 -1801,8 +1800,6 @@@ int index_pipe(unsigned char *sha1, in
        unsigned long size = 4096;
        char *buf = xmalloc(size);
        int ret;
 -      unsigned char hdr[50];
 -      int hdrlen;
  
        if (read_pipe(fd, &buf, &size)) {
                free(buf);
                type = blob_type;
        if (write_object)
                ret = write_sha1_file(buf, size, type, sha1);
 -      else {
 -              write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
 -              ret = 0;
 -      }
 +      else
 +              ret = hash_sha1_file(buf, size, type, sha1);
        free(buf);
        return ret;
  }
@@@ -1804,6 -1826,8 +1821,6 @@@ int index_fd(unsigned char *sha1, int f
        unsigned long size = st->st_size;
        void *buf;
        int ret;
 -      unsigned char hdr[50];
 -      int hdrlen;
  
        buf = "";
        if (size)
                type = blob_type;
        if (write_object)
                ret = write_sha1_file(buf, size, type, sha1);
 -      else {
 -              write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
 -              ret = 0;
 -      }
 +      else
 +              ret = hash_sha1_file(buf, size, type, sha1);
        if (size)
                munmap(buf, size);
        return ret;
@@@ -1846,9 -1872,12 +1863,9 @@@ int index_path(unsigned char *sha1, con
                        return error("readlink(\"%s\"): %s", path,
                                     errstr);
                }
 -              if (!write_object) {
 -                      unsigned char hdr[50];
 -                      int hdrlen;
 -                      write_sha1_file_prepare(target, st->st_size, blob_type,
 -                                              sha1, hdr, &hdrlen);
 -              } else if (write_sha1_file(target, st->st_size, blob_type, sha1))
 +              if (!write_object)
 +                      hash_sha1_file(target, st->st_size, blob_type, sha1);
 +              else if (write_sha1_file(target, st->st_size, blob_type, sha1))
                        return error("%s: failed to insert into database",
                                     path);
                free(target);