113
int incremental;
114
uint32_t num_multi_pack_indexes_before;
115
116
+ struct multi_pack_index *compact_from;
117
+ struct multi_pack_index *compact_to;
118
+ int compact;
119
+
120
struct string_list *to_include;
121
122
struct repository *repo;
126
static uint32_t midx_pack_perm(struct write_midx_context *ctx,
127
uint32_t orig_pack_int_id)
128
{
129
+ if (ctx->compact)
130
+ orig_pack_int_id -= ctx->compact_from->num_packs_in_base;
131
return ctx->pack_perm[orig_pack_int_id];
132
}
133
274
QSORT(fanout->entries, fanout->nr, midx_oid_compare);
275
}
276
271
-static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
272
- struct multi_pack_index *m,
273
- uint32_t cur_fanout,
274
- uint32_t preferred_pack)
277
+static void midx_fanout_add_midx_fanout_1(struct midx_fanout *fanout,
278
+ struct multi_pack_index *m,
279
+ uint32_t cur_fanout,
280
+ uint32_t preferred_pack)
281
{
282
uint32_t start = m->num_objects_in_base, end;
283
uint32_t cur_object;
284
279
- if (m->base_midx)
280
- midx_fanout_add_midx_fanout(fanout, m->base_midx, cur_fanout,
281
- preferred_pack);
282
-
285
if (cur_fanout)
286
start += ntohl(m->chunk_oid_fanout[cur_fanout - 1]);
287
end = m->num_objects_in_base + ntohl(m->chunk_oid_fanout[cur_fanout]);
305
}
306
}
307
308
+static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
309
+ struct multi_pack_index *m,
310
+ uint32_t cur_fanout,
311
+ uint32_t preferred_pack)
312
+{
313
+ if (m->base_midx)
314
+ midx_fanout_add_midx_fanout(fanout, m->base_midx, cur_fanout,
315
+ preferred_pack);
316
+ midx_fanout_add_midx_fanout_1(fanout, m, cur_fanout, preferred_pack);
317
+}
318
+
319
static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
320
struct pack_info *info,
321
uint32_t cur_pack,
365
cur_fanout);
366
}
367
368
+static void midx_fanout_add_compact(struct midx_fanout *fanout,
369
+ struct write_midx_context *ctx,
370
+ uint32_t cur_fanout)
371
+{
372
+ struct multi_pack_index *m = ctx->compact_to;
373
+
374
+ ASSERT(ctx->compact);
375
+
376
+ while (m && m != ctx->compact_from->base_midx) {
377
+ midx_fanout_add_midx_fanout_1(fanout, m, cur_fanout,
378
+ NO_PREFERRED_PACK);
379
+ m = m->base_midx;
380
+ }
381
+}
382
+
383
/*
384
* It is possible to artificially get into a state where there are many
385
* duplicate copies of objects. That can create high memory pressure if
398
size_t alloc_objects, total_objects = 0;
399
struct midx_fanout fanout = { 0 };
400
401
+ if (ctx->compact)
402
+ ASSERT(!start_pack);
403
+
404
for (cur_pack = start_pack; cur_pack < ctx->nr; cur_pack++)
405
total_objects = st_add(total_objects,
406
ctx->info[cur_pack].p->num_objects);
419
for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
420
fanout.nr = 0;
421
391
- midx_fanout_add(&fanout, ctx, start_pack, cur_fanout);
422
+ if (ctx->compact)
423
+ midx_fanout_add_compact(&fanout, ctx, cur_fanout);
424
+ else
425
+ midx_fanout_add(&fanout, ctx, start_pack, cur_fanout);
426
midx_fanout_sort(&fanout);
427
428
/*
990
return 0;
991
}
992
993
+static uint32_t compactible_packs_between(const struct multi_pack_index *from,
994
+ const struct multi_pack_index *to)
995
+{
996
+ uint32_t nr;
997
+
998
+ ASSERT(from && to);
999
+
1000
+ if (unsigned_add_overflows(to->num_packs, to->num_packs_in_base))
1001
+ die(_("too many packs, unable to compact"));
1002
+
1003
+ nr = to->num_packs + to->num_packs_in_base;
1004
+ if (nr < from->num_packs_in_base)
1005
+ BUG("unexpected number of packs in base during compaction: "
1006
+ "%"PRIu32" < %"PRIu32, nr, from->num_packs_in_base);
1007
+
1008
+ return nr - from->num_packs_in_base;
1009
+}
1010
+
1011
+static int fill_packs_from_midx_range(struct write_midx_context *ctx,
1012
+ int bitmap_order)
1013
+{
1014
+ struct multi_pack_index *m = ctx->compact_to;
1015
+ uint32_t packs_nr;
1016
+
1017
+ ASSERT(ctx->compact && !ctx->nr);
1018
+ ASSERT(ctx->compact_from);
1019
+ ASSERT(ctx->compact_to);
1020
+
1021
+ packs_nr = compactible_packs_between(ctx->compact_from,
1022
+ ctx->compact_to);
1023
+
1024
+ ALLOC_GROW(ctx->info, packs_nr, ctx->alloc);
1025
+
1026
+ while (m != ctx->compact_from->base_midx) {
1027
+ uint32_t pack_int_id, preferred_pack_id;
1028
+ uint32_t i;
1029
+
1030
+ if (bitmap_order) {
1031
+ if (midx_preferred_pack(m, &preferred_pack_id) < 0)
1032
+ die(_("could not determine preferred pack"));
1033
+ } else {
1034
+ preferred_pack_id = m->num_packs_in_base;
1035
+ }
1036
+
1037
+ pack_int_id = m->num_packs_in_base - ctx->compact_from->num_packs_in_base;
1038
+
1039
+ if (fill_pack_from_midx(&ctx->info[pack_int_id++], m,
1040
+ preferred_pack_id) < 0)
1041
+ return -1;
1042
+
1043
+ for (i = m->num_packs_in_base;
1044
+ i < m->num_packs_in_base + m->num_packs; i++) {
1045
+ if (preferred_pack_id == i)
1046
+ continue;
1047
+
1048
+ if (fill_pack_from_midx(&ctx->info[pack_int_id++], m,
1049
+ i) < 0)
1050
+ return -1;
1051
+ }
1052
+
1053
+ ctx->nr += m->num_packs;
1054
+ m = m->base_midx;
1055
+ }
1056
+
1057
+ ASSERT(ctx->nr == packs_nr);
1058
+
1059
+ return 0;
1060
+}
1061
+
1062
static struct {
1063
const char *non_split;
1064
const char *split;
1178
if (ctx->incremental)
1179
goto out;
1180
1181
+ if (ctx->compact)
1182
+ goto out; /* Compaction always requires an update. */
1183
+
1184
/*
1185
* Otherwise, we need to verify that the packs covered by the existing
1186
* MIDX match the packs that we already have. The logic to do so is way
1226
return needed;
1227
}
1228
1229
+static int midx_hashcmp(const struct multi_pack_index *a,
1230
+ const struct multi_pack_index *b,
1231
+ const struct git_hash_algo *algop)
1232
+{
1233
+ return hashcmp(midx_get_checksum_hash(a), midx_get_checksum_hash(b),
1234
+ algop);
1235
+}
1236
+
1237
struct write_midx_opts {
1238
struct odb_source *source; /* non-optional */
1239
1240
struct string_list *packs_to_include;
1241
struct string_list *packs_to_drop;
1242
1243
+ struct multi_pack_index *compact_from;
1244
+ struct multi_pack_index *compact_to;
1245
+
1246
const char *preferred_pack_name;
1247
const char *refs_snapshot;
1248
unsigned flags;
1267
int dropped_packs = 0;
1268
int result = -1;
1269
const char **keep_hashes = NULL;
1270
+ size_t keep_hashes_nr = 0;
1271
struct chunkfile *cf;
1272
1273
trace2_region_enter("midx", "write_midx_internal", r);
1280
die(_("unknown MIDX version: %d"), ctx.version);
1281
1282
ctx.incremental = !!(opts->flags & MIDX_WRITE_INCREMENTAL);
1283
+ ctx.compact = !!(opts->flags & MIDX_WRITE_COMPACT);
1284
+
1285
+ if (ctx.compact) {
1286
+ if (ctx.version != MIDX_VERSION_V2)
1287
+ die(_("cannot perform MIDX compaction with v1 format"));
1288
+ if (!opts->compact_from)
1289
+ BUG("expected non-NULL 'from' MIDX during compaction");
1290
+ if (!opts->compact_to)
1291
+ BUG("expected non-NULL 'to' MIDX during compaction");
1292
+
1293
+ ctx.compact_from = opts->compact_from;
1294
+ ctx.compact_to = opts->compact_to;
1295
+ }
1296
1297
if (ctx.incremental)
1298
strbuf_addf(&midx_name,
1320
*/
1321
if (ctx.incremental)
1322
ctx.base_midx = m;
1192
- else if (!opts->packs_to_include)
1323
+ if (!opts->packs_to_include)
1324
ctx.m = m;
1325
}
1326
}
1327
1328
+ /*
1329
+ * If compacting MIDX layer(s) in the range [from, to], then the
1330
+ * compacted MIDX will share the same base MIDX as 'from'.
1331
+ */
1332
+ if (ctx.compact)
1333
+ ctx.base_midx = ctx.compact_from->base_midx;
1334
+
1335
ctx.nr = 0;
1336
ctx.alloc = ctx.m ? ctx.m->num_packs + ctx.m->num_packs_in_base : 16;
1337
ctx.info = NULL;
1348
ctx.num_multi_pack_indexes_before++;
1349
m = m->base_midx;
1350
}
1213
- } else if (ctx.m && fill_packs_from_midx(&ctx)) {
1351
+ } else if (ctx.m && !ctx.compact && fill_packs_from_midx(&ctx)) {
1352
goto cleanup;
1353
}
1354
1361
else
1362
ctx.progress = NULL;
1363
1226
- ctx.to_include = opts->packs_to_include;
1364
+ if (ctx.compact) {
1365
+ int bitmap_order = 0;
1366
+ if (opts->preferred_pack_name)
1367
+ bitmap_order |= 1;
1368
+ else if (opts->flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))
1369
+ bitmap_order |= 1;
1370
1228
- for_each_file_in_pack_dir(opts->source->path, add_pack_to_midx, &ctx);
1371
+ fill_packs_from_midx_range(&ctx, bitmap_order);
1372
+ } else {
1373
+ ctx.to_include = opts->packs_to_include;
1374
+ for_each_file_in_pack_dir(opts->source->path, add_pack_to_midx, &ctx);
1375
+ }
1376
stop_progress(&ctx.progress);
1377
1378
if (!opts->packs_to_drop) {
1501
ctx.large_offsets_needed = 1;
1502
}
1503
1357
- QSORT(ctx.info, ctx.nr, pack_info_compare);
1504
+ if (ctx.compact) {
1505
+ if (ctx.version != MIDX_VERSION_V2)
1506
+ BUG("performing MIDX compaction with v1 MIDX");
1507
+ } else {
1508
+ QSORT(ctx.info, ctx.nr, pack_info_compare);
1509
+ }
1510
1511
if (opts->packs_to_drop && opts->packs_to_drop->nr) {
1512
size_t drop_index = 0;
1513
int missing_drops = 0;
1514
1515
+ ASSERT(!ctx.compact);
1516
+
1517
for (size_t i = 0;
1518
i < ctx.nr && drop_index < opts->packs_to_drop->nr; i++) {
1519
int cmp = strcmp(ctx.info[i].pack_name,
1545
*/
1546
ALLOC_ARRAY(ctx.pack_perm, ctx.nr);
1547
for (size_t i = 0; i < ctx.nr; i++) {
1548
+ uint32_t from = ctx.info[i].orig_pack_int_id;
1549
+ uint32_t to;
1550
+
1551
if (ctx.info[i].expired) {
1552
+ to = PACK_EXPIRED;
1553
dropped_packs++;
1396
- ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED;
1554
} else {
1398
- ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs;
1555
+ to = i - dropped_packs;
1556
}
1557
+
1558
+ if (ctx.compact)
1559
+ from -= ctx.compact_from->num_packs_in_base;
1560
+
1561
+ ctx.pack_perm[from] = to;
1562
}
1563
1564
for (size_t i = 0; i < ctx.nr; i++) {
1704
if (ctx.num_multi_pack_indexes_before == UINT32_MAX)
1705
die(_("too many multi-pack-indexes"));
1706
1545
- CALLOC_ARRAY(keep_hashes, ctx.num_multi_pack_indexes_before + 1);
1707
+ if (ctx.compact) {
1708
+ struct multi_pack_index *m;
1709
+
1710
+ /*
1711
+ * Keep all MIDX layers excluding those in the range [from, to].
1712
+ */
1713
+ for (m = ctx.base_midx; m; m = m->base_midx)
1714
+ keep_hashes_nr++;
1715
+ for (m = ctx.m;
1716
+ m && midx_hashcmp(m, ctx.compact_to, r->hash_algo);
1717
+ m = m->base_midx)
1718
+ keep_hashes_nr++;
1719
+
1720
+ keep_hashes_nr++; /* include the compacted layer */
1721
+ } else {
1722
+ keep_hashes_nr = ctx.num_multi_pack_indexes_before + 1;
1723
+ }
1724
+ CALLOC_ARRAY(keep_hashes, keep_hashes_nr);
1725
1726
if (ctx.incremental) {
1727
FILE *chainf = fdopen_lock_file(&lk, "w");
1746
1747
strbuf_release(&final_midx_name);
1748
1570
- keep_hashes[ctx.num_multi_pack_indexes_before] =
1571
- xstrdup(hash_to_hex_algop(midx_hash, r->hash_algo));
1749
+ if (ctx.compact) {
1750
+ struct multi_pack_index *m;
1751
+ uint32_t num_layers_before_from = 0;
1752
+ uint32_t i;
1753
1573
- for (uint32_t i = 0; i < ctx.num_multi_pack_indexes_before; i++) {
1574
- uint32_t j = ctx.num_multi_pack_indexes_before - i - 1;
1754
+ for (m = ctx.base_midx; m; m = m->base_midx)
1755
+ num_layers_before_from++;
1756
1576
- keep_hashes[j] = xstrdup(midx_get_checksum_hex(m));
1577
- m = m->base_midx;
1757
+ m = ctx.base_midx;
1758
+ for (i = 0; i < num_layers_before_from; i++) {
1759
+ uint32_t j = num_layers_before_from - i - 1;
1760
+
1761
+ keep_hashes[j] = xstrdup(midx_get_checksum_hex(m));
1762
+ m = m->base_midx;
1763
+ }
1764
+
1765
+ keep_hashes[i] = xstrdup(hash_to_hex_algop(midx_hash,
1766
+ r->hash_algo));
1767
+
1768
+ i = 0;
1769
+ for (m = ctx.m;
1770
+ m && midx_hashcmp(m, ctx.compact_to, r->hash_algo);
1771
+ m = m->base_midx) {
1772
+ keep_hashes[keep_hashes_nr - i - 1] =
1773
+ xstrdup(midx_get_checksum_hex(m));
1774
+ i++;
1775
+ }
1776
+ } else {
1777
+ keep_hashes[ctx.num_multi_pack_indexes_before] =
1778
+ xstrdup(hash_to_hex_algop(midx_hash,
1779
+ r->hash_algo));
1780
+
1781
+ for (uint32_t i = 0; i < ctx.num_multi_pack_indexes_before; i++) {
1782
+ uint32_t j = ctx.num_multi_pack_indexes_before - i - 1;
1783
+
1784
+ keep_hashes[j] = xstrdup(midx_get_checksum_hex(m));
1785
+ m = m->base_midx;
1786
+ }
1787
}
1788
1580
- for (uint32_t i = 0; i <= ctx.num_multi_pack_indexes_before; i++)
1789
+ for (uint32_t i = 0; i < keep_hashes_nr; i++)
1790
fprintf(get_lock_file_fp(&lk), "%s\n", keep_hashes[i]);
1791
} else {
1792
keep_hashes[ctx.num_multi_pack_indexes_before] =
1799
if (commit_lock_file(&lk) < 0)
1800
die_errno(_("could not write multi-pack-index"));
1801
1593
- clear_midx_files(opts->source, keep_hashes,
1594
- ctx.num_multi_pack_indexes_before + 1,
1802
+ clear_midx_files(opts->source, keep_hashes, keep_hashes_nr,
1803
ctx.incremental);
1804
result = 0;
1805
1817
free(ctx.pack_perm);
1818
free(ctx.pack_order);
1819
if (keep_hashes) {
1612
- for (uint32_t i = 0; i <= ctx.num_multi_pack_indexes_before; i++)
1820
+ for (uint32_t i = 0; i < keep_hashes_nr; i++)
1821
free((char *)keep_hashes[i]);
1822
free(keep_hashes);
1823
}
1859
return write_midx_internal(&opts);
1860
}
1861
1862
+int write_midx_file_compact(struct odb_source *source,
1863
+ struct multi_pack_index *from,
1864
+ struct multi_pack_index *to,
1865
+ unsigned flags)
1866
+{
1867
+ struct write_midx_opts opts = {
1868
+ .source = source,
1869
+ .compact_from = from,
1870
+ .compact_to = to,
1871
+ .flags = flags | MIDX_WRITE_COMPACT,
1872
+ };
1873
+
1874
+ return write_midx_internal(&opts);
1875
+}
1876
+
1877
int expire_midx_packs(struct odb_source *source, unsigned flags)
1878
{
1879
uint32_t i, *count, result = 0;