sha1_object_info_extended: provide delta base sha1s

A caller of sha1_object_info_extended technically has enough information to determine the base sha1 from the results of the call. It knows the pack, offset, and delta type of the object, which is sufficient to find the base. However, the functions to do so are not publicly available, and the code itself is intimate enough with the pack details that it should be abstracted away. We could add a public helper to allow callers to query the delta base separately, but it is simpler and slightly more efficient to optionally grab it along with the rest of the object_info data. For cases where the object is not stored as a delta, we write the null sha1 into the query field. A careful caller could check "oi.whence == OI_PACKED && oi.u.packed.is_delta" before looking at the base sha1, but using the null sha1 provides a simple alternative (and gives a better sanity check for a non-careful caller than simply returning random bytes). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Dec 21, 2013 at 09:24 UTC 5d642e75069334944fcc795a80cf04749dd12857
2 files changed +54
cache.h
+1
@@ -1074,6 +1074,7 @@ struct object_info {
1074 enum object_type *typep;
1075 unsigned long *sizep;
1076 unsigned long *disk_sizep;
1077 + unsigned char *delta_base_sha1;
1078
1079 /* Response */
1080 enum {
sha1_file.c
+53
@@ -1667,6 +1667,38 @@ static off_t get_delta_base(struct packed_git *p,
1667 return base_offset;
1668 }
1669
1670 +/*
1671 + * Like get_delta_base above, but we return the sha1 instead of the pack
1672 + * offset. This means it is cheaper for REF deltas (we do not have to do
1673 + * the final object lookup), but more expensive for OFS deltas (we
1674 + * have to load the revidx to convert the offset back into a sha1).
1675 + */
1676 +static const unsigned char *get_delta_base_sha1(struct packed_git *p,
1677 + struct pack_window **w_curs,
1678 + off_t curpos,
1679 + enum object_type type,
1680 + off_t delta_obj_offset)
1681 +{
1682 + if (type == OBJ_REF_DELTA) {
1683 + unsigned char *base = use_pack(p, w_curs, curpos, NULL);
1684 + return base;
1685 + } else if (type == OBJ_OFS_DELTA) {
1686 + struct revindex_entry *revidx;
1687 + off_t base_offset = get_delta_base(p, w_curs, &curpos,
1688 + type, delta_obj_offset);
1689 +
1690 + if (!base_offset)
1691 + return NULL;
1692 +
1693 + revidx = find_pack_revindex(p, base_offset);
1694 + if (!revidx)
1695 + return NULL;
1696 +
1697 + return nth_packed_object_sha1(p, revidx->nr);
1698 + } else
1699 + return NULL;
1700 +}
1701 +
1702 int unpack_object_header(struct packed_git *p,
1703 struct pack_window **w_curs,
1704 off_t *curpos,
@@ -1824,6 +1856,22 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
1856 }
1857 }
1858
1859 + if (oi->delta_base_sha1) {
1860 + if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
1861 + const unsigned char *base;
1862 +
1863 + base = get_delta_base_sha1(p, &w_curs, curpos,
1864 + type, obj_offset);
1865 + if (!base) {
1866 + type = OBJ_BAD;
1867 + goto out;
1868 + }
1869 +
1870 + hashcpy(oi->delta_base_sha1, base);
1871 + } else
1872 + hashclr(oi->delta_base_sha1);
1873 + }
1874 +
1875 out:
1876 unuse_pack(&w_curs);
1877 return type;
@@ -2407,6 +2455,9 @@ static int sha1_loose_object_info(const unsigned char *sha1,
2455 git_zstream stream;
2456 char hdr[32];
2457
2458 + if (oi->delta_base_sha1)
2459 + hashclr(oi->delta_base_sha1);
2460 +
2461 /*
2462 * If we don't care about type or size, then we don't
2463 * need to look inside the object at all. Note that we
@@ -2457,6 +2508,8 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
2508 *(oi->sizep) = co->size;
2509 if (oi->disk_sizep)
2510 *(oi->disk_sizep) = 0;
2511 + if (oi->delta_base_sha1)
2512 + hashclr(oi->delta_base_sha1);
2513 oi->whence = OI_CACHED;
2514 return 0;
2515 }