The signature is: {'M', 'I', 'D', 'X'}
1-byte version number:
- Git only writes or recognizes version 1.
+ Git only writes version 2, but recognizes versions 1 and 2.
1-byte Object Id Version
We infer the length of object IDs (OIDs) from this value:
strings. There is no extra padding between the filenames,
and they are listed in lexicographic order. The chunk itself
is padded at the end with between 0 and 3 NUL bytes to make the
- chunk size a multiple of 4 bytes.
+ chunk size a multiple of 4 bytes. Version 1 MIDXs are required to
+ list their packs in lexicographic order, but version 2 MIDXs may
+ list their packs in any arbitrary order.
Bitmapped Packfiles (ID: {'B', 'T', 'M', 'P'})
Stores a table of two 4-byte unsigned integers in network order.
static size_t write_midx_header(const struct git_hash_algo *hash_algo,
struct hashfile *f, unsigned char num_chunks,
- uint32_t num_packs)
+ uint32_t num_packs, int version)
{
+ if (version != MIDX_VERSION_V1 && version != MIDX_VERSION_V2)
+ BUG("unexpected MIDX version: %d", version);
+
hashwrite_be32(f, MIDX_SIGNATURE);
- hashwrite_u8(f, MIDX_VERSION);
+ hashwrite_u8(f, version);
hashwrite_u8(f, oid_version(hash_algo));
hashwrite_u8(f, num_chunks);
hashwrite_u8(f, 0); /* unused */
uint32_t preferred_pack_idx;
+ int version; /* must be MIDX_VERSION_V1 or _V2 */
+
int incremental;
uint32_t num_multi_pack_indexes_before;
if (ctx->info[i].expired)
continue;
- if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0)
+ if (ctx->version == MIDX_VERSION_V1 &&
+ i && strcmp(ctx->info[i].pack_name,
+ ctx->info[i - 1].pack_name) <= 0)
BUG("incorrect pack-file order: %s before %s",
ctx->info[i - 1].pack_name,
ctx->info[i].pack_name);
if (!midx_checksum_valid(midx))
goto out;
+ /*
+ * If the version differs, we need to update.
+ */
+ if (midx->version != ctx->version)
+ goto out;
+
/*
* Ignore incremental updates for now. The assumption is that any
* incremental update would be either empty (in which case we will bail
struct tempfile *incr;
struct write_midx_context ctx = {
.preferred_pack_idx = NO_PREFERRED_PACK,
+ .version = MIDX_VERSION_V2,
};
struct multi_pack_index *midx_to_free = NULL;
int bitmapped_packs_concat_len = 0;
ctx.repo = r;
ctx.source = opts->source;
+ repo_config_get_int(ctx.repo, "midx.version", &ctx.version);
+ if (ctx.version != MIDX_VERSION_V1 && ctx.version != MIDX_VERSION_V2)
+ die(_("unknown MIDX version: %d"), ctx.version);
+
ctx.incremental = !!(opts->flags & MIDX_WRITE_INCREMENTAL);
if (ctx.incremental)
}
write_midx_header(r->hash_algo, f, get_num_chunks(cf),
- ctx.nr - dropped_packs);
+ ctx.nr - dropped_packs, ctx.version);
write_chunkfile(cf, &ctx);
finalize_hashfile(f, midx_hash, FSYNC_COMPONENT_PACK_METADATA,
m->signature, MIDX_SIGNATURE);
m->version = m->data[MIDX_BYTE_FILE_VERSION];
- if (m->version != MIDX_VERSION)
+ if (m->version != MIDX_VERSION_V1 && m->version != MIDX_VERSION_V2)
die(_("multi-pack-index version %d not recognized"),
m->version);
die(_("multi-pack-index pack-name chunk is too short"));
cur_pack_name = end + 1;
- if (i && strcmp(m->pack_names[i], m->pack_names[i - 1]) <= 0)
+ if (m->version == MIDX_VERSION_V1 &&
+ i && strcmp(m->pack_names[i], m->pack_names[i - 1]) <= 0)
die(_("multi-pack-index pack names out of order: '%s' before '%s'"),
m->pack_names[i - 1],
m->pack_names[i]);
}
FREE_AND_NULL(m->packs);
FREE_AND_NULL(m->pack_names);
+ FREE_AND_NULL(m->pack_names_sorted);
free(m);
}
return strcmp(idx_or_pack_name, idx_name);
}
+
+static int midx_pack_names_cmp(const void *a, const void *b, void *m_)
+{
+ struct multi_pack_index *m = m_;
+ return strcmp(m->pack_names[*(const size_t *)a],
+ m->pack_names[*(const size_t *)b]);
+}
+
static int midx_contains_pack_1(struct multi_pack_index *m,
const char *idx_or_pack_name)
{
uint32_t first = 0, last = m->num_packs;
+ if (m->version == MIDX_VERSION_V2 && !m->pack_names_sorted) {
+ uint32_t i;
+
+ ALLOC_ARRAY(m->pack_names_sorted, m->num_packs);
+
+ for (i = 0; i < m->num_packs; i++)
+ m->pack_names_sorted[i] = i;
+
+ QSORT_S(m->pack_names_sorted, m->num_packs, midx_pack_names_cmp,
+ m);
+ }
+
while (first < last) {
uint32_t mid = first + (last - first) / 2;
const char *current;
int cmp;
- current = m->pack_names[mid];
+ if (m->pack_names_sorted)
+ current = m->pack_names[m->pack_names_sorted[mid]];
+ else
+ current = m->pack_names[mid];
cmp = cmp_idx_or_pack_name(idx_or_pack_name, current);
if (!cmp)
return 1;
struct odb_source;
#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
-#define MIDX_VERSION 1
+#define MIDX_VERSION_V1 1
+#define MIDX_VERSION_V2 2
#define MIDX_BYTE_FILE_VERSION 4
#define MIDX_BYTE_HASH_VERSION 5
#define MIDX_BYTE_NUM_CHUNKS 6
uint32_t num_packs_in_base;
const char **pack_names;
+ size_t *pack_names_sorted;
struct packed_git **packs;
};
EXTRA_CHUNKS="$5"
{
cat <<-EOF &&
- header: 4d494458 1 $HASH_LEN $NUM_CHUNKS $NUM_PACKS
+ header: 4d494458 2 $HASH_LEN $NUM_CHUNKS $NUM_PACKS
chunks: pack-names oid-fanout oid-lookup object-offsets$EXTRA_CHUNKS
num_objects: $NUM_OBJECTS
packs:
"improper chunk offset(s)"
'
-test_expect_success 'verify packnames out of order' '
- corrupt_midx_and_verify $MIDX_BYTE_PACKNAME_ORDER "z" $objdir \
- "pack names out of order"
-'
-
test_expect_success 'verify missing pack' '
corrupt_midx_and_verify $MIDX_BYTE_PACKNAME_ORDER "a" $objdir \
"failed to load pack"
$objdir "incorrect checksum"
'
+test_expect_success 'setup for v1-specific fsck tests' '
+ git -c midx.version=1 multi-pack-index write
+'
+
+test_expect_success 'verify packnames out of order (v1)' '
+ corrupt_midx_and_verify $MIDX_BYTE_PACKNAME_ORDER "z" $objdir \
+ "pack names out of order"
+'
+
test_expect_success 'repack progress off for redirected stderr' '
GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir repack 2>err &&
test_line_count = 0 err