{"thread":{"id":"5634","subject":"[PATCH 6/6] make git-pack-objects able to create deltas with offset to base","startedAt":"2006-09-21T04:09:44Z","lastAt":"2006-09-23T01:25:04Z","messageCount":2,"participants":["Nicolas Pitre"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"27332","messageId":"Pine.LNX.4.64.0609210008360.2627@xanadu.home","threadId":"5634","inReplyTo":null,"subject":"[PATCH 6/6] make git-pack-objects able to create deltas with offset to base","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2006-09-21T04:09:44Z","receivedAt":"2006-09-21T04:09:44Z","isPatch":true,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"\nThis is enabled with --delta-base-offset only, and doesn't work with\npack data reuse yet.\n\nThe idea is to allow for the fetch protocol to use an extension flag\nto notify the remote end that --delta-base-offset can be used with\ngit-pack-objects. Eventually git-repack will always provide this flag.\n\nWith this, all delta base objects are now pushed before deltas that depend\non them.  This is a requirements for OBJ_OFS_DELTA.  This is not a\nrequirement for OBJ_REF_DELTA but always doing so makes the code simpler.\n\nSigned-off-by: Nicolas Pitre <nico@cam.org>\n\n---\n\nDelta data reuse is possible, of course, but that will come through a\nseparate patch.  I need to think about it some more in order to implement\nit as efficiently as possible.  The issue is to get back to the\ncorresponding object entry given an object offset in an existing pack.\n\nI guess I could use the pack offset to find the corresponding sha1 in the pack\nindex, and then use locate_object_entry_hash(), but that would require a pack\nindex sorted by offset to be efficient... which means yet more memory usage.\nWell, I need to look at that revindex more closely I guess...\n\ndiff --git a/builtin-pack-objects.c b/builtin-pack-objects.c\nindex c62734a..ec57536 100644\n--- a/builtin-pack-objects.c\n+++ b/builtin-pack-objects.c\n@@ -60,6 +60,8 @@ static int non_empty;\n static int no_reuse_delta;\n static int local;\n static int incremental;\n+static int allow_ofs_delta;\n+\n static struct object_entry **sorted_by_sha, **sorted_by_type;\n static struct object_entry *objects;\n static int nr_objects, nr_alloc, nr_result;\n@@ -334,9 +336,6 @@ static unsigned long write_object(struct\n \tenum object_type obj_type;\n \tint to_reuse = 0;\n \n-\tif (entry->preferred_base)\n-\t\treturn 0;\n-\n \tobj_type = entry->type;\n \tif (! entry->in_pack)\n \t\tto_reuse = 0;\t/* can't reuse what we don't have */\n@@ -380,18 +379,35 @@ static unsigned long write_object(struct\n \t\tif (entry->delta) {\n \t\t\tbuf = delta_against(buf, size, entry);\n \t\t\tsize = entry->delta_size;\n-\t\t\tobj_type = OBJ_REF_DELTA;\n+\t\t\tobj_type = (allow_ofs_delta && entry->delta->offset) ?\n+\t\t\t\tOBJ_OFS_DELTA : OBJ_REF_DELTA;\n \t\t}\n \t\t/*\n \t\t * The object header is a byte of 'type' followed by zero or\n-\t\t * more bytes of length.  For deltas, the 20 bytes of delta\n-\t\t * sha1 follows that.\n+\t\t * more bytes of length.\n \t\t */\n \t\thdrlen = encode_header(obj_type, size, header);\n \t\tsha1write(f, header, hdrlen);\n \n-\t\tif (entry->delta) {\n-\t\t\tsha1write(f, entry->delta, 20);\n+\t\tif (obj_type == OBJ_OFS_DELTA) {\n+\t\t\t/*\n+\t\t\t * Deltas with relative base contain an additional\n+\t\t\t * encoding of the relative offset for the delta\n+\t\t\t * base from this object's position in the pack.\n+\t\t\t */\n+\t\t\tunsigned long ofs = entry->offset - entry->delta->offset;\n+\t\t\tunsigned pos = sizeof(header) - 1;\n+\t\t\theader[pos] = ofs & 127;\n+\t\t\twhile (ofs >>= 7)\n+\t\t\t\theader[--pos] = 128 | (--ofs & 127);\n+\t\t\tsha1write(f, header + pos, sizeof(header) - pos);\n+\t\t\thdrlen += sizeof(header) - pos;\n+\t\t} else if (obj_type == OBJ_REF_DELTA) {\n+\t\t\t/*\n+\t\t\t * Deltas with a base reference contain\n+\t\t\t * an additional 20 bytes for the base sha1.\n+\t\t\t */\n+\t\t\tsha1write(f, entry->delta->sha1, 20);\n \t\t\thdrlen += 20;\n \t\t}\n \t\tdatalen = sha1write_compressed(f, buf, size);\n@@ -413,7 +429,7 @@ static unsigned long write_object(struct\n \t\t\treused_delta++;\n \t\treused++;\n \t}\n-\tif (obj_type == OBJ_REF_DELTA)\n+\tif (entry->delta)\n \t\twritten_delta++;\n \twritten++;\n \treturn hdrlen + datalen;\n@@ -423,17 +439,16 @@ static unsigned long write_one(struct sh\n \t\t\t       struct object_entry *e,\n \t\t\t       unsigned long offset)\n {\n-\tif (e->offset)\n+\tif (e->offset || e->preferred_base)\n \t\t/* offset starts from header size and cannot be zero\n \t\t * if it is written already.\n \t\t */\n \t\treturn offset;\n-\te->offset = offset;\n-\toffset += write_object(f, e);\n-\t/* if we are deltified, write out its base object. */\n+\t/* if we are deltified, write out its base object first. */\n \tif (e->delta)\n \t\toffset = write_one(f, e->delta, offset);\n-\treturn offset;\n+\te->offset = offset;\n+\treturn offset + write_object(f, e);\n }\n \n static void write_pack_file(void)\n@@ -1484,6 +1499,10 @@ int cmd_pack_objects(int argc, const cha\n \t\t\tno_reuse_delta = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(\"--delta-base-offset\", arg)) {\n+\t\t\tallow_ofs_delta = no_reuse_delta = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(\"--stdout\", arg)) {\n \t\t\tpack_to_stdout = 1;\n \t\t\tcontinue;\n"},{"id":"27439","messageId":"Pine.LNX.4.64.0609222059570.2627@xanadu.home","threadId":"5634","inReplyTo":"Pine.LNX.4.64.0609210008360.2627@xanadu.home","subject":"[PATCH 7/6] make pack data reuse compatible with both delta types","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2006-09-23T01:25:04Z","receivedAt":"2006-09-23T01:25:04Z","isPatch":true,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"This is the missing part to git-pack-objects allowing it to reuse delta\ndata to/from any of the two delta types.  It can reuse delta from any \ntype, and it outputs base offsets when --allow-delta-base-offset is\nprovided and the base is also included in the pack.  Otherwise it \noutputs base sha1 references just like it always did.\n\nSigned-off-by: Nicolas Pitre <nico@cam.org>\n\n---\n\nOn Thu, 21 Sep 2006, Nicolas Pitre wrote:\n\n> Delta data reuse is possible, of course, but that will come through a\n> separate patch.  I need to think about it some more in order to implement\n> it as efficiently as possible.  The issue is to get back to the\n> corresponding object entry given an object offset in an existing pack.\n\nThis was done with the addition of a sequence number in the revindex \nentry which is 4 additional bytes per object that has to live in memory.  \nIf ever this is considered too much memory usage then there is always \nthe possibility of using an array of pointers to pack index entries \ninstead, but then the runtime cost might be higher since the sorting and \nsearching will have to dereference those pointers to get to the offset \nand endian swap it every time.  I think that memory usage should not be \nan issue in most cases, and if it is then this 4 bytes per object is \nprobably not going to save much anyway.\n\nSo I think support for delta base offset is now complete.\n\ndiff --git a/builtin-pack-objects.c b/builtin-pack-objects.c\nindex 2212649..6db97b6 100644\n--- a/builtin-pack-objects.c\n+++ b/builtin-pack-objects.c\n@@ -29,6 +29,7 @@ struct object_entry {\n \tenum object_type type;\n \tenum object_type in_pack_type;\t/* could be delta */\n \tunsigned long delta_size;\t/* delta data size (uncompressed) */\n+#define in_pack_header_size delta_size\t/* only when reusing pack data */\n \tstruct object_entry *delta;\t/* delta base object */\n \tstruct packed_git *in_pack; \t/* already in pack */\n \tunsigned int in_pack_offset;\n@@ -86,17 +87,25 @@ static int object_ix_hashsz;\n  * Pack index for existing packs give us easy access to the offsets into\n  * corresponding pack file where each object's data starts, but the entries\n  * do not store the size of the compressed representation (uncompressed\n- * size is easily available by examining the pack entry header).  We build\n- * a hashtable of existing packs (pack_revindex), and keep reverse index\n- * here -- pack index file is sorted by object name mapping to offset; this\n- * pack_revindex[].revindex array is an ordered list of offsets, so if you\n- * know the offset of an object, next offset is where its packed\n- * representation ends.\n+ * size is easily available by examining the pack entry header).  It is\n+ * also rather expensive to find the sha1 for an object given its offset.\n+ *\n+ * We build a hashtable of existing packs (pack_revindex), and keep reverse\n+ * index here -- pack index file is sorted by object name mapping to offset;\n+ * this pack_revindex[].revindex array is a list of offset/index_nr pairs\n+ * ordered by offset, so if you know the offset of an object, next offset\n+ * is where its packed representation ends and the index_nr can be used to\n+ * get the object sha1 from the main index.\n  */\n+struct revindex_entry {\n+\tunsigned int offset;\n+\tunsigned int nr;\n+};\n struct pack_revindex {\n \tstruct packed_git *p;\n-\tunsigned long *revindex;\n-} *pack_revindex = NULL;\n+\tstruct revindex_entry *revindex;\n+};\n+static struct  pack_revindex *pack_revindex;\n static int pack_revindex_hashsz;\n \n /*\n@@ -143,14 +152,9 @@ static void prepare_pack_ix(void)\n \n static int cmp_offset(const void *a_, const void *b_)\n {\n-\tunsigned long a = *(unsigned long *) a_;\n-\tunsigned long b = *(unsigned long *) b_;\n-\tif (a < b)\n-\t\treturn -1;\n-\telse if (a == b)\n-\t\treturn 0;\n-\telse\n-\t\treturn 1;\n+\tconst struct revindex_entry *a = a_;\n+\tconst struct revindex_entry *b = b_;\n+\treturn (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;\n }\n \n /*\n@@ -163,25 +167,27 @@ static void prepare_pack_revindex(struct\n \tint i;\n \tvoid *index = p->index_base + 256;\n \n-\trix->revindex = xmalloc(sizeof(unsigned long) * (num_ent + 1));\n+\trix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));\n \tfor (i = 0; i < num_ent; i++) {\n \t\tunsigned int hl = *((unsigned int *)((char *) index + 24*i));\n-\t\trix->revindex[i] = ntohl(hl);\n+\t\trix->revindex[i].offset = ntohl(hl);\n+\t\trix->revindex[i].nr = i;\n \t}\n \t/* This knows the pack format -- the 20-byte trailer\n \t * follows immediately after the last object data.\n \t */\n-\trix->revindex[num_ent] = p->pack_size - 20;\n-\tqsort(rix->revindex, num_ent, sizeof(unsigned long), cmp_offset);\n+\trix->revindex[num_ent].offset = p->pack_size - 20;\n+\trix->revindex[num_ent].nr = -1;\n+\tqsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset);\n }\n \n-static unsigned long find_packed_object_size(struct packed_git *p,\n-\t\t\t\t\t     unsigned long ofs)\n+static struct revindex_entry * find_packed_object(struct packed_git *p,\n+\t\t\t\t\t\t  unsigned int ofs)\n {\n \tint num;\n \tint lo, hi;\n \tstruct pack_revindex *rix;\n-\tunsigned long *revindex;\n+\tstruct revindex_entry *revindex;\n \tnum = pack_revindex_ix(p);\n \tif (num < 0)\n \t\tdie(\"internal error: pack revindex uninitialized\");\n@@ -193,10 +199,10 @@ static unsigned long find_packed_object_\n \thi = num_packed_objects(p) + 1;\n \tdo {\n \t\tint mi = (lo + hi) / 2;\n-\t\tif (revindex[mi] == ofs) {\n-\t\t\treturn revindex[mi+1] - ofs;\n+\t\tif (revindex[mi].offset == ofs) {\n+\t\t\treturn revindex + mi;\n \t\t}\n-\t\telse if (ofs < revindex[mi])\n+\t\telse if (ofs < revindex[mi].offset)\n \t\t\thi = mi;\n \t\telse\n \t\t\tlo = mi + 1;\n@@ -204,6 +210,20 @@ static unsigned long find_packed_object_\n \tdie(\"internal error: pack revindex corrupt\");\n }\n \n+static unsigned long find_packed_object_size(struct packed_git *p,\n+\t\t\t\t\t     unsigned long ofs)\n+{\n+\tstruct revindex_entry *entry = find_packed_object(p, ofs);\n+\treturn entry[1].offset - ofs;\n+}\n+\n+static unsigned char *find_packed_object_name(struct packed_git *p,\n+\t\t\t\t\t      unsigned long ofs)\n+{\n+\tstruct revindex_entry *entry = find_packed_object(p, ofs);\n+\treturn (unsigned char *)(p->index_base + 256) + 24 * entry->nr + 4;\n+}\n+\n static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)\n {\n \tunsigned long othersize, delta_size;\n@@ -249,6 +269,10 @@ static int encode_header(enum object_typ\n \treturn n;\n }\n \n+/*\n+ * we are going to reuse the existing object data as is.  make\n+ * sure it is not corrupt.\n+ */\n static int check_inflate(unsigned char *data, unsigned long len, unsigned long expect)\n {\n \tz_stream stream;\n@@ -280,32 +304,6 @@ static int check_inflate(unsigned char *\n \treturn st;\n }\n \n-/*\n- * we are going to reuse the existing pack entry data.  make\n- * sure it is not corrupt.\n- */\n-static int revalidate_pack_entry(struct object_entry *entry, unsigned char *data, unsigned long len)\n-{\n-\tenum object_type type;\n-\tunsigned long size, used;\n-\n-\tif (pack_to_stdout)\n-\t\treturn 0;\n-\n-\t/* the caller has already called use_packed_git() for us,\n-\t * so it is safe to access the pack data from mmapped location.\n-\t * make sure the entry inflates correctly.\n-\t */\n-\tused = unpack_object_header_gently(data, len, &type, &size);\n-\tif (!used)\n-\t\treturn -1;\n-\tif (type == OBJ_REF_DELTA)\n-\t\tused += 20; /* skip base object name */\n-\tdata += used;\n-\tlen -= used;\n-\treturn check_inflate(data, len, entry->size);\n-}\n-\n static int revalidate_loose_object(struct object_entry *entry,\n \t\t\t\t   unsigned char *map,\n \t\t\t\t   unsigned long mapsize)\n@@ -339,7 +337,7 @@ static unsigned long write_object(struct\n \tobj_type = entry->type;\n \tif (! entry->in_pack)\n \t\tto_reuse = 0;\t/* can't reuse what we don't have */\n-\telse if (obj_type == OBJ_REF_DELTA)\n+\telse if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA)\n \t\tto_reuse = 1;\t/* check_object() decided it for us */\n \telse if (obj_type != entry->in_pack_type)\n \t\tto_reuse = 0;\t/* pack has delta which is unusable */\n@@ -415,18 +413,38 @@ static unsigned long write_object(struct\n \t}\n \telse {\n \t\tstruct packed_git *p = entry->in_pack;\n-\t\tuse_packed_git(p);\n \n-\t\tdatalen = find_packed_object_size(p, entry->in_pack_offset);\n-\t\tbuf = (char *) p->pack_base + entry->in_pack_offset;\n+\t\tif (entry->delta) {\n+\t\t\tobj_type = (allow_ofs_delta && entry->delta->offset) ?\n+\t\t\t\tOBJ_OFS_DELTA : OBJ_REF_DELTA;\n+\t\t\treused_delta++;\n+\t\t}\n+\t\thdrlen = encode_header(obj_type, entry->size, header);\n+\t\tsha1write(f, header, hdrlen);\n+\t\tif (obj_type == OBJ_OFS_DELTA) {\n+\t\t\tunsigned long ofs = entry->offset - entry->delta->offset;\n+\t\t\tunsigned pos = sizeof(header) - 1;\n+\t\t\theader[pos] = ofs & 127;\n+\t\t\twhile (ofs >>= 7)\n+\t\t\t\theader[--pos] = 128 | (--ofs & 127);\n+\t\t\tsha1write(f, header + pos, sizeof(header) - pos);\n+\t\t\thdrlen += sizeof(header) - pos;\n+\t\t} else if (obj_type == OBJ_REF_DELTA) {\n+\t\t\tsha1write(f, entry->delta->sha1, 20);\n+\t\t\thdrlen += 20;\n+\t\t}\n \n-\t\tif (revalidate_pack_entry(entry, buf, datalen))\n+\t\tuse_packed_git(p);\n+\t\tbuf = (char *) p->pack_base\n+\t\t\t+ entry->in_pack_offset\n+\t\t\t+ entry->in_pack_header_size;\n+\t\tdatalen = find_packed_object_size(p, entry->in_pack_offset)\n+\t\t\t\t- entry->in_pack_header_size;\n+//fprintf(stderr, \"reusing %d at %d header %d size %d\\n\", obj_type, entry->in_pack_offset, entry->in_pack_header_size, datalen);\n+\t\tif (!pack_to_stdout && check_inflate(buf, datalen, entry->size))\n \t\t\tdie(\"corrupt delta in pack %s\", sha1_to_hex(entry->sha1));\n \t\tsha1write(f, buf, datalen);\n \t\tunuse_packed_git(p);\n-\t\thdrlen = 0; /* not really */\n-\t\tif (obj_type == OBJ_REF_DELTA)\n-\t\t\treused_delta++;\n \t\treused++;\n \t}\n \tif (entry->delta)\n@@ -914,26 +932,64 @@ static void check_object(struct object_e\n \tchar type[20];\n \n \tif (entry->in_pack && !entry->preferred_base) {\n-\t\tunsigned char base[20];\n-\t\tunsigned long size;\n-\t\tstruct object_entry *base_entry;\n+\t\tstruct packed_git *p = entry->in_pack;\n+\t\tunsigned long left = p->pack_size - entry->in_pack_offset;\n+\t\tunsigned long size, used;\n+\t\tunsigned char *buf;\n+\t\tstruct object_entry *base_entry = NULL;\n+\n+\t\tuse_packed_git(p);\n+\t\tbuf = p->pack_base;\n+\t\tbuf += entry->in_pack_offset;\n \n \t\t/* We want in_pack_type even if we do not reuse delta.\n \t\t * There is no point not reusing non-delta representations.\n \t\t */\n-\t\tcheck_reuse_pack_delta(entry->in_pack,\n-\t\t\t\t       entry->in_pack_offset,\n-\t\t\t\t       base, &size,\n-\t\t\t\t       &entry->in_pack_type);\n+\t\tused = unpack_object_header_gently(buf, left,\n+\t\t\t\t\t\t   &entry->in_pack_type, &size);\n+\t\tif (!used || left - used <= 20)\n+\t\t\tdie(\"corrupt pack for %s\", sha1_to_hex(entry->sha1));\n \n \t\t/* Check if it is delta, and the base is also an object\n \t\t * we are going to pack.  If so we will reuse the existing\n \t\t * delta.\n \t\t */\n-\t\tif (!no_reuse_delta &&\n-\t\t    entry->in_pack_type == OBJ_REF_DELTA &&\n-\t\t    (base_entry = locate_object_entry(base)) &&\n-\t\t    (!base_entry->preferred_base)) {\n+\t\tif (!no_reuse_delta) {\n+\t\t\tunsigned char c, *base_name;\n+\t\t\tunsigned long ofs;\n+\t\t\t/* there is at least 20 bytes left in the pack */\n+\t\t\tswitch (entry->in_pack_type) {\n+\t\t\tcase OBJ_REF_DELTA:\n+\t\t\t\tbase_name = buf + used;\n+\t\t\t\tused += 20;\n+\t\t\t\tbreak;\n+\t\t\tcase OBJ_OFS_DELTA:\n+\t\t\t\tc = buf[used++];\n+\t\t\t\tofs = c & 127;\n+\t\t\t\twhile (c & 128) {\n+\t\t\t\t\tofs += 1;\n+\t\t\t\t\tif (!ofs || ofs & ~(~0UL >> 7))\n+\t\t\t\t\t\tdie(\"delta base offset overflow in pack for %s\",\n+\t\t\t\t\t\t    sha1_to_hex(entry->sha1));\n+\t\t\t\t\tc = buf[used++];\n+\t\t\t\t\tofs = (ofs << 7) + (c & 127);\n+\t\t\t\t}\n+\t\t\t\tif (ofs >= entry->in_pack_offset)\n+\t\t\t\t\tdie(\"delta base offset out of bound for %s\",\n+\t\t\t\t\t    sha1_to_hex(entry->sha1));\n+\t\t\t\tofs = entry->in_pack_offset - ofs;\n+\t\t\t\tbase_name = find_packed_object_name(p, ofs);\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\tbase_name = NULL;\n+\t\t\t}\n+\t\t\tif (base_name)\n+\t\t\t\tbase_entry = locate_object_entry(base_name);\n+\t\t}\n+\t\tunuse_packed_git(p);\n+\t\tentry->in_pack_header_size = used;\n+\n+\t\tif (base_entry && !base_entry->preferred_base) {\n \n \t\t\t/* Depth value does not matter - find_deltas()\n \t\t\t * will never consider reused delta as the\n@@ -942,9 +998,9 @@ static void check_object(struct object_e\n \t\t\t */\n \n \t\t\t/* uncompressed size of the delta data */\n-\t\t\tentry->size = entry->delta_size = size;\n+\t\t\tentry->size = size;\n \t\t\tentry->delta = base_entry;\n-\t\t\tentry->type = OBJ_REF_DELTA;\n+\t\t\tentry->type = entry->in_pack_type;\n \n \t\t\tentry->delta_sibling = base_entry->delta_child;\n \t\t\tbase_entry->delta_child = entry;\n@@ -1500,7 +1556,7 @@ int cmd_pack_objects(int argc, const cha\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(\"--delta-base-offset\", arg)) {\n-\t\t\tallow_ofs_delta = no_reuse_delta = 1;\n+\t\t\tallow_ofs_delta = 1;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(\"--stdout\", arg)) {\ndiff --git a/pack.h b/pack.h\nindex 05557da..4c9bddd 100644\n--- a/pack.h\n+++ b/pack.h\n@@ -16,7 +16,4 @@ struct pack_header {\n };\n \n extern int verify_pack(struct packed_git *, int);\n-extern int check_reuse_pack_delta(struct packed_git *, unsigned long,\n-\t\t\t\t  unsigned char *, unsigned long *,\n-\t\t\t\t  enum object_type *);\n #endif\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 66ebdde..e4bc4ae 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -1004,25 +1004,6 @@ static unsigned long unpack_object_heade\n \treturn offset + used;\n }\n \n-int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,\n-\t\t\t   unsigned char *base, unsigned long *sizep,\n-\t\t\t   enum object_type *kindp)\n-{\n-\tunsigned long ptr;\n-\tint status = -1;\n-\n-\tuse_packed_git(p);\n-\tptr = offset;\n-\tptr = unpack_object_header(p, ptr, kindp, sizep);\n-\tif (*kindp != OBJ_REF_DELTA)\n-\t\tgoto done;\n-\thashcpy(base, (unsigned char *) p->pack_base + ptr);\n-\tstatus = 0;\n- done:\n-\tunuse_packed_git(p);\n-\treturn status;\n-}\n-\n void packed_object_info_detail(struct packed_git *p,\n \t\t\t       unsigned long offset,\n \t\t\t       char *type,\n"}]}