pack-object: tolerate broken packs that have duplicated objects
When --reuse-delta is in effect (which is the default), and an existing pack in the repository has the same object registered twice (e.g. one copy in a non-delta format and the other copy in a delta against some other object), an attempt to repack the repository can result in a cyclic delta dependency, causing write_one() function to infinitely recurse into itself. Detect such a case and break the loopy dependency by writing out an object that is involved in such a loop in the non-delta format. Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
6320526415
commit
f63c79dbc8
@ -404,25 +404,56 @@ static unsigned long write_object(struct sha1file *f,
|
|||||||
return hdrlen + datalen;
|
return hdrlen + datalen;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write_one(struct sha1file *f,
|
enum write_one_status {
|
||||||
struct object_entry *e,
|
WRITE_ONE_SKIP = -1, /* already written */
|
||||||
off_t *offset)
|
WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */
|
||||||
|
WRITE_ONE_WRITTEN = 1, /* normal */
|
||||||
|
WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum write_one_status write_one(struct sha1file *f,
|
||||||
|
struct object_entry *e,
|
||||||
|
off_t *offset)
|
||||||
{
|
{
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
|
int recursing;
|
||||||
|
|
||||||
/* offset is non zero if object is written already. */
|
/*
|
||||||
if (e->idx.offset || e->preferred_base)
|
* we set offset to 1 (which is an impossible value) to mark
|
||||||
return -1;
|
* the fact that this object is involved in "write its base
|
||||||
|
* first before writing a deltified object" recursion.
|
||||||
|
*/
|
||||||
|
recursing = (e->idx.offset == 1);
|
||||||
|
if (recursing) {
|
||||||
|
warning("recursive delta detected for object %s",
|
||||||
|
sha1_to_hex(e->idx.sha1));
|
||||||
|
return WRITE_ONE_RECURSIVE;
|
||||||
|
} else if (e->idx.offset || e->preferred_base) {
|
||||||
|
/* offset is non zero if object is written already. */
|
||||||
|
return WRITE_ONE_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
/* if we are deltified, write out base object first. */
|
/* if we are deltified, write out base object first. */
|
||||||
if (e->delta && !write_one(f, e->delta, offset))
|
if (e->delta) {
|
||||||
return 0;
|
e->idx.offset = 1; /* now recurse */
|
||||||
|
switch (write_one(f, e->delta, offset)) {
|
||||||
|
case WRITE_ONE_RECURSIVE:
|
||||||
|
/* we cannot depend on this one */
|
||||||
|
e->delta = NULL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
case WRITE_ONE_BREAK:
|
||||||
|
e->idx.offset = recursing;
|
||||||
|
return WRITE_ONE_BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
e->idx.offset = *offset;
|
e->idx.offset = *offset;
|
||||||
size = write_object(f, e, *offset);
|
size = write_object(f, e, *offset);
|
||||||
if (!size) {
|
if (!size) {
|
||||||
e->idx.offset = 0;
|
e->idx.offset = recursing;
|
||||||
return 0;
|
return WRITE_ONE_BREAK;
|
||||||
}
|
}
|
||||||
written_list[nr_written++] = &e->idx;
|
written_list[nr_written++] = &e->idx;
|
||||||
|
|
||||||
@ -430,7 +461,7 @@ static int write_one(struct sha1file *f,
|
|||||||
if (signed_add_overflows(*offset, size))
|
if (signed_add_overflows(*offset, size))
|
||||||
die("pack too large for current definition of off_t");
|
die("pack too large for current definition of off_t");
|
||||||
*offset += size;
|
*offset += size;
|
||||||
return 1;
|
return WRITE_ONE_WRITTEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_pack_file(void)
|
static void write_pack_file(void)
|
||||||
@ -468,7 +499,7 @@ static void write_pack_file(void)
|
|||||||
offset = sizeof(hdr);
|
offset = sizeof(hdr);
|
||||||
nr_written = 0;
|
nr_written = 0;
|
||||||
for (; i < nr_objects; i++) {
|
for (; i < nr_objects; i++) {
|
||||||
if (!write_one(f, objects + i, &offset))
|
if (write_one(f, objects + i, &offset) == WRITE_ONE_BREAK)
|
||||||
break;
|
break;
|
||||||
display_progress(progress_state, written);
|
display_progress(progress_state, written);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user