teach git-index-pack about deltas with offset to base
Signed-off-by: Nicolas Pitre <nico@cam.org> Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
parent
209c554ab4
commit
53dda6ff62
111
index-pack.c
111
index-pack.c
@ -18,10 +18,15 @@ struct object_entry
|
|||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
union delta_base {
|
||||||
|
unsigned char sha1[20];
|
||||||
|
unsigned long offset;
|
||||||
|
};
|
||||||
|
|
||||||
struct delta_entry
|
struct delta_entry
|
||||||
{
|
{
|
||||||
struct object_entry *obj;
|
struct object_entry *obj;
|
||||||
unsigned char base_sha1[20];
|
union delta_base base;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *pack_name;
|
static const char *pack_name;
|
||||||
@ -134,13 +139,13 @@ static void *unpack_entry_data(unsigned long offset,
|
|||||||
static void *unpack_raw_entry(unsigned long offset,
|
static void *unpack_raw_entry(unsigned long offset,
|
||||||
enum object_type *obj_type,
|
enum object_type *obj_type,
|
||||||
unsigned long *obj_size,
|
unsigned long *obj_size,
|
||||||
unsigned char *delta_base,
|
union delta_base *delta_base,
|
||||||
unsigned long *next_obj_offset)
|
unsigned long *next_obj_offset)
|
||||||
{
|
{
|
||||||
unsigned long pack_limit = pack_size - 20;
|
unsigned long pack_limit = pack_size - 20;
|
||||||
unsigned long pos = offset;
|
unsigned long pos = offset;
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
unsigned long size;
|
unsigned long size, base_offset;
|
||||||
unsigned shift;
|
unsigned shift;
|
||||||
enum object_type type;
|
enum object_type type;
|
||||||
void *data;
|
void *data;
|
||||||
@ -161,26 +166,43 @@ static void *unpack_raw_entry(unsigned long offset,
|
|||||||
case OBJ_REF_DELTA:
|
case OBJ_REF_DELTA:
|
||||||
if (pos + 20 >= pack_limit)
|
if (pos + 20 >= pack_limit)
|
||||||
bad_object(offset, "object extends past end of pack");
|
bad_object(offset, "object extends past end of pack");
|
||||||
hashcpy(delta_base, pack_base + pos);
|
hashcpy(delta_base->sha1, pack_base + pos);
|
||||||
pos += 20;
|
pos += 20;
|
||||||
/* fallthru */
|
break;
|
||||||
|
case OBJ_OFS_DELTA:
|
||||||
|
memset(delta_base, 0, sizeof(*delta_base));
|
||||||
|
c = pack_base[pos++];
|
||||||
|
base_offset = c & 127;
|
||||||
|
while (c & 128) {
|
||||||
|
base_offset += 1;
|
||||||
|
if (!base_offset || base_offset & ~(~0UL >> 7))
|
||||||
|
bad_object(offset, "offset value overflow for delta base object");
|
||||||
|
if (pos >= pack_limit)
|
||||||
|
bad_object(offset, "object extends past end of pack");
|
||||||
|
c = pack_base[pos++];
|
||||||
|
base_offset = (base_offset << 7) + (c & 127);
|
||||||
|
}
|
||||||
|
delta_base->offset = offset - base_offset;
|
||||||
|
if (delta_base->offset >= offset)
|
||||||
|
bad_object(offset, "delta base offset is out of bound");
|
||||||
|
break;
|
||||||
case OBJ_COMMIT:
|
case OBJ_COMMIT:
|
||||||
case OBJ_TREE:
|
case OBJ_TREE:
|
||||||
case OBJ_BLOB:
|
case OBJ_BLOB:
|
||||||
case OBJ_TAG:
|
case OBJ_TAG:
|
||||||
data = unpack_entry_data(offset, &pos, size);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
bad_object(offset, "bad object type %d", type);
|
bad_object(offset, "bad object type %d", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data = unpack_entry_data(offset, &pos, size);
|
||||||
*obj_type = type;
|
*obj_type = type;
|
||||||
*obj_size = size;
|
*obj_size = size;
|
||||||
*next_obj_offset = pos;
|
*next_obj_offset = pos;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_delta(const unsigned char *base_sha1)
|
static int find_delta(const union delta_base *base)
|
||||||
{
|
{
|
||||||
int first = 0, last = nr_deltas;
|
int first = 0, last = nr_deltas;
|
||||||
|
|
||||||
@ -189,7 +211,7 @@ static int find_delta(const unsigned char *base_sha1)
|
|||||||
struct delta_entry *delta = &deltas[next];
|
struct delta_entry *delta = &deltas[next];
|
||||||
int cmp;
|
int cmp;
|
||||||
|
|
||||||
cmp = hashcmp(base_sha1, delta->base_sha1);
|
cmp = memcmp(base, &delta->base, sizeof(*base));
|
||||||
if (!cmp)
|
if (!cmp)
|
||||||
return next;
|
return next;
|
||||||
if (cmp < 0) {
|
if (cmp < 0) {
|
||||||
@ -201,18 +223,18 @@ static int find_delta(const unsigned char *base_sha1)
|
|||||||
return -first-1;
|
return -first-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_deltas_based_on_sha1(const unsigned char *base_sha1,
|
static int find_delta_childs(const union delta_base *base,
|
||||||
int *first_index, int *last_index)
|
int *first_index, int *last_index)
|
||||||
{
|
{
|
||||||
int first = find_delta(base_sha1);
|
int first = find_delta(base);
|
||||||
int last = first;
|
int last = first;
|
||||||
int end = nr_deltas - 1;
|
int end = nr_deltas - 1;
|
||||||
|
|
||||||
if (first < 0)
|
if (first < 0)
|
||||||
return -1;
|
return -1;
|
||||||
while (first > 0 && !hashcmp(deltas[first - 1].base_sha1, base_sha1))
|
while (first > 0 && !memcmp(&deltas[first - 1].base, base, sizeof(*base)))
|
||||||
--first;
|
--first;
|
||||||
while (last < end && !hashcmp(deltas[last + 1].base_sha1, base_sha1))
|
while (last < end && !memcmp(&deltas[last + 1].base, base, sizeof(*base)))
|
||||||
++last;
|
++last;
|
||||||
*first_index = first;
|
*first_index = first;
|
||||||
*last_index = last;
|
*last_index = last;
|
||||||
@ -253,13 +275,13 @@ static void resolve_delta(struct delta_entry *delta, void *base_data,
|
|||||||
void *result;
|
void *result;
|
||||||
unsigned long result_size;
|
unsigned long result_size;
|
||||||
enum object_type delta_type;
|
enum object_type delta_type;
|
||||||
unsigned char base_sha1[20];
|
union delta_base delta_base;
|
||||||
unsigned long next_obj_offset;
|
unsigned long next_obj_offset;
|
||||||
int j, first, last;
|
int j, first, last;
|
||||||
|
|
||||||
obj->real_type = type;
|
obj->real_type = type;
|
||||||
delta_data = unpack_raw_entry(obj->offset, &delta_type,
|
delta_data = unpack_raw_entry(obj->offset, &delta_type,
|
||||||
&delta_size, base_sha1,
|
&delta_size, &delta_base,
|
||||||
&next_obj_offset);
|
&next_obj_offset);
|
||||||
result = patch_delta(base_data, base_size, delta_data, delta_size,
|
result = patch_delta(base_data, base_size, delta_data, delta_size,
|
||||||
&result_size);
|
&result_size);
|
||||||
@ -267,10 +289,22 @@ static void resolve_delta(struct delta_entry *delta, void *base_data,
|
|||||||
if (!result)
|
if (!result)
|
||||||
bad_object(obj->offset, "failed to apply delta");
|
bad_object(obj->offset, "failed to apply delta");
|
||||||
sha1_object(result, result_size, type, obj->sha1);
|
sha1_object(result, result_size, type, obj->sha1);
|
||||||
if (!find_deltas_based_on_sha1(obj->sha1, &first, &last)) {
|
|
||||||
|
hashcpy(delta_base.sha1, obj->sha1);
|
||||||
|
if (!find_delta_childs(&delta_base, &first, &last)) {
|
||||||
for (j = first; j <= last; j++)
|
for (j = first; j <= last; j++)
|
||||||
resolve_delta(&deltas[j], result, result_size, type);
|
if (deltas[j].obj->type == OBJ_REF_DELTA)
|
||||||
|
resolve_delta(&deltas[j], result, result_size, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(&delta_base, 0, sizeof(delta_base));
|
||||||
|
delta_base.offset = obj->offset;
|
||||||
|
if (!find_delta_childs(&delta_base, &first, &last)) {
|
||||||
|
for (j = first; j <= last; j++)
|
||||||
|
if (deltas[j].obj->type == OBJ_OFS_DELTA)
|
||||||
|
resolve_delta(&deltas[j], result, result_size, type);
|
||||||
|
}
|
||||||
|
|
||||||
free(result);
|
free(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,14 +312,14 @@ static int compare_delta_entry(const void *a, const void *b)
|
|||||||
{
|
{
|
||||||
const struct delta_entry *delta_a = a;
|
const struct delta_entry *delta_a = a;
|
||||||
const struct delta_entry *delta_b = b;
|
const struct delta_entry *delta_b = b;
|
||||||
return hashcmp(delta_a->base_sha1, delta_b->base_sha1);
|
return memcmp(&delta_a->base, &delta_b->base, sizeof(union delta_base));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_pack_objects(void)
|
static void parse_pack_objects(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
unsigned long offset = sizeof(struct pack_header);
|
unsigned long offset = sizeof(struct pack_header);
|
||||||
unsigned char base_sha1[20];
|
struct delta_entry *delta = deltas;
|
||||||
void *data;
|
void *data;
|
||||||
unsigned long data_size;
|
unsigned long data_size;
|
||||||
|
|
||||||
@ -299,12 +333,12 @@ static void parse_pack_objects(void)
|
|||||||
struct object_entry *obj = &objects[i];
|
struct object_entry *obj = &objects[i];
|
||||||
obj->offset = offset;
|
obj->offset = offset;
|
||||||
data = unpack_raw_entry(offset, &obj->type, &data_size,
|
data = unpack_raw_entry(offset, &obj->type, &data_size,
|
||||||
base_sha1, &offset);
|
&delta->base, &offset);
|
||||||
obj->real_type = obj->type;
|
obj->real_type = obj->type;
|
||||||
if (obj->type == OBJ_REF_DELTA) {
|
if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
|
||||||
struct delta_entry *delta = &deltas[nr_deltas++];
|
nr_deltas++;
|
||||||
delta->obj = obj;
|
delta->obj = obj;
|
||||||
hashcpy(delta->base_sha1, base_sha1);
|
delta++;
|
||||||
} else
|
} else
|
||||||
sha1_object(data, data_size, obj->type, obj->sha1);
|
sha1_object(data, data_size, obj->type, obj->sha1);
|
||||||
free(data);
|
free(data);
|
||||||
@ -312,7 +346,7 @@ static void parse_pack_objects(void)
|
|||||||
if (offset != pack_size - 20)
|
if (offset != pack_size - 20)
|
||||||
die("packfile '%s' has junk at the end", pack_name);
|
die("packfile '%s' has junk at the end", pack_name);
|
||||||
|
|
||||||
/* Sort deltas by base SHA1 for fast searching */
|
/* Sort deltas by base SHA1/offset for fast searching */
|
||||||
qsort(deltas, nr_deltas, sizeof(struct delta_entry),
|
qsort(deltas, nr_deltas, sizeof(struct delta_entry),
|
||||||
compare_delta_entry);
|
compare_delta_entry);
|
||||||
|
|
||||||
@ -326,22 +360,37 @@ static void parse_pack_objects(void)
|
|||||||
*/
|
*/
|
||||||
for (i = 0; i < nr_objects; i++) {
|
for (i = 0; i < nr_objects; i++) {
|
||||||
struct object_entry *obj = &objects[i];
|
struct object_entry *obj = &objects[i];
|
||||||
int j, first, last;
|
union delta_base base;
|
||||||
|
int j, ref, ref_first, ref_last, ofs, ofs_first, ofs_last;
|
||||||
|
|
||||||
if (obj->type == OBJ_REF_DELTA)
|
if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
|
||||||
continue;
|
continue;
|
||||||
if (find_deltas_based_on_sha1(obj->sha1, &first, &last))
|
hashcpy(base.sha1, obj->sha1);
|
||||||
|
ref = !find_delta_childs(&base, &ref_first, &ref_last);
|
||||||
|
memset(&base, 0, sizeof(base));
|
||||||
|
base.offset = obj->offset;
|
||||||
|
ofs = !find_delta_childs(&base, &ofs_first, &ofs_last);
|
||||||
|
if (!ref && !ofs)
|
||||||
continue;
|
continue;
|
||||||
data = unpack_raw_entry(obj->offset, &obj->type, &data_size,
|
data = unpack_raw_entry(obj->offset, &obj->type, &data_size,
|
||||||
base_sha1, &offset);
|
&base, &offset);
|
||||||
for (j = first; j <= last; j++)
|
if (ref)
|
||||||
resolve_delta(&deltas[j], data, data_size, obj->type);
|
for (j = ref_first; j <= ref_last; j++)
|
||||||
|
if (deltas[j].obj->type == OBJ_REF_DELTA)
|
||||||
|
resolve_delta(&deltas[j], data,
|
||||||
|
data_size, obj->type);
|
||||||
|
if (ofs)
|
||||||
|
for (j = ofs_first; j <= ofs_last; j++)
|
||||||
|
if (deltas[j].obj->type == OBJ_OFS_DELTA)
|
||||||
|
resolve_delta(&deltas[j], data,
|
||||||
|
data_size, obj->type);
|
||||||
free(data);
|
free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for unresolved deltas */
|
/* Check for unresolved deltas */
|
||||||
for (i = 0; i < nr_deltas; i++) {
|
for (i = 0; i < nr_deltas; i++) {
|
||||||
if (deltas[i].obj->real_type == OBJ_REF_DELTA)
|
if (deltas[i].obj->real_type == OBJ_REF_DELTA ||
|
||||||
|
deltas[i].obj->real_type == OBJ_OFS_DELTA)
|
||||||
die("packfile '%s' has unresolved deltas", pack_name);
|
die("packfile '%s' has unresolved deltas", pack_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user