2
#include "repack.h"
3
#include "hash.h"
4
#include "hex.h"
5
+#include "lockfile.h"
6
+#include "midx.h"
7
#include "odb.h"
8
#include "oidset.h"
9
#include "pack-bitmap.h"
10
+#include "path.h"
11
#include "refs.h"
12
#include "run-command.h"
13
#include "tempfile.h"
14
+#include "trace2.h"
15
16
struct midx_snapshot_ref_data {
17
struct repository *repo;
297
}
298
299
static int repack_fill_midx_stdin_packs(struct child_process *cmd,
296
- struct string_list *include)
300
+ struct string_list *include,
301
+ struct string_list *out)
302
{
303
+ struct strbuf in_buf = STRBUF_INIT;
304
+ struct strbuf out_buf = STRBUF_INIT;
305
struct string_list_item *item;
299
- FILE *in;
306
int ret;
307
302
- cmd->in = -1;
303
-
308
strvec_push(&cmd->args, "--stdin-packs");
309
306
- ret = start_command(cmd);
307
- if (ret)
308
- return ret;
309
-
310
- in = xfdopen(cmd->in, "w");
310
for_each_string_list_item(item, include)
312
- fprintf(in, "%s\n", item->string);
313
- fclose(in);
311
+ strbuf_addf(&in_buf, "%s\n", item->string);
312
+
313
+ ret = pipe_command(cmd, in_buf.buf, in_buf.len,
314
+ out ? &out_buf : NULL, 0, NULL, 0);
315
+
316
+ if (out)
317
+ string_list_split_f(out, out_buf.buf, "\n", -1,
318
+ STRING_LIST_SPLIT_NONEMPTY);
319
+
320
+ strbuf_release(&in_buf);
321
+ strbuf_release(&out_buf);
322
315
- return finish_command(cmd);
323
+ return ret;
324
}
325
326
static int write_midx_included_packs(struct repack_write_midx_opts *opts)
377
strvec_pushf(&cmd.args, "--refs-snapshot=%s",
378
opts->refs_snapshot);
379
372
- ret = repack_fill_midx_stdin_packs(&cmd, &include);
380
+ ret = repack_fill_midx_stdin_packs(&cmd, &include, NULL);
381
done:
382
if (!ret && opts->write_bitmaps)
383
remove_redundant_bitmaps(&include, opts->packdir);
387
return ret;
388
}
389
390
+struct midx_compaction_step {
391
+ union {
392
+ struct multi_pack_index *copy;
393
+ struct string_list write;
394
+ struct {
395
+ struct multi_pack_index *from;
396
+ struct multi_pack_index *to;
397
+ } compact;
398
+ } u;
399
+
400
+ uint32_t objects_nr;
401
+ char *csum;
402
+
403
+ enum {
404
+ MIDX_COMPACTION_STEP_UNKNOWN,
405
+ MIDX_COMPACTION_STEP_COPY,
406
+ MIDX_COMPACTION_STEP_WRITE,
407
+ MIDX_COMPACTION_STEP_COMPACT,
408
+ } type;
409
+};
410
+
411
+static const char *midx_compaction_step_base(const struct midx_compaction_step *step)
412
+{
413
+ switch (step->type) {
414
+ case MIDX_COMPACTION_STEP_UNKNOWN:
415
+ BUG("cannot use UNKNOWN step as a base");
416
+ case MIDX_COMPACTION_STEP_COPY:
417
+ return midx_get_checksum_hex(step->u.copy);
418
+ case MIDX_COMPACTION_STEP_WRITE:
419
+ BUG("cannot use WRITE step as a base");
420
+ case MIDX_COMPACTION_STEP_COMPACT:
421
+ return midx_get_checksum_hex(step->u.compact.to);
422
+ default:
423
+ BUG("unhandled midx compaction step type %d", step->type);
424
+ }
425
+}
426
+
427
+static int midx_compaction_step_exec_copy(struct midx_compaction_step *step)
428
+{
429
+ step->csum = xstrdup(midx_get_checksum_hex(step->u.copy));
430
+ return 0;
431
+}
432
+
433
+static int midx_compaction_step_exec_write(struct midx_compaction_step *step,
434
+ struct repack_write_midx_opts *opts,
435
+ const char *base)
436
+{
437
+ struct child_process cmd = CHILD_PROCESS_INIT;
438
+ struct string_list hash = STRING_LIST_INIT_DUP;
439
+ struct string_list_item *item;
440
+ const char *preferred_pack = NULL;
441
+ int ret = 0;
442
+
443
+ if (!step->u.write.nr) {
444
+ ret = error(_("no packs to write MIDX during compaction"));
445
+ goto out;
446
+ }
447
+
448
+ for_each_string_list_item(item, &step->u.write) {
449
+ if (item->util)
450
+ preferred_pack = item->string;
451
+ }
452
+
453
+ repack_prepare_midx_command(&cmd, opts, "write");
454
+ strvec_pushl(&cmd.args, "--incremental", "--no-write-chain-file", NULL);
455
+ strvec_pushf(&cmd.args, "--base=%s", base ? base : "none");
456
+
457
+ if (preferred_pack) {
458
+ struct strbuf buf = STRBUF_INIT;
459
+
460
+ strbuf_addstr(&buf, preferred_pack);
461
+ strbuf_strip_suffix(&buf, ".idx");
462
+ strbuf_addstr(&buf, ".pack");
463
+
464
+ strvec_pushf(&cmd.args, "--preferred-pack=%s", buf.buf);
465
+
466
+ strbuf_release(&buf);
467
+ }
468
+
469
+ ret = repack_fill_midx_stdin_packs(&cmd, &step->u.write, &hash);
470
+ if (hash.nr != 1) {
471
+ ret = error(_("expected exactly one line during MIDX write, "
472
+ "got: %"PRIuMAX),
473
+ (uintmax_t)hash.nr);
474
+ goto out;
475
+ }
476
+
477
+ step->csum = xstrdup(hash.items[0].string);
478
+
479
+out:
480
+ string_list_clear(&hash, 0);
481
+
482
+ return ret;
483
+}
484
+
485
+static int midx_compaction_step_exec_compact(struct midx_compaction_step *step,
486
+ struct repack_write_midx_opts *opts)
487
+{
488
+ struct child_process cmd = CHILD_PROCESS_INIT;
489
+ struct strbuf buf = STRBUF_INIT;
490
+ FILE *out = NULL;
491
+ int ret;
492
+
493
+ repack_prepare_midx_command(&cmd, opts, "compact");
494
+ strvec_pushl(&cmd.args, "--incremental", "--no-write-chain-file",
495
+ midx_get_checksum_hex(step->u.compact.from),
496
+ midx_get_checksum_hex(step->u.compact.to), NULL);
497
+
498
+ cmd.out = -1;
499
+
500
+ ret = start_command(&cmd);
501
+ if (ret)
502
+ goto out;
503
+
504
+ out = xfdopen(cmd.out, "r");
505
+ while (strbuf_getline_lf(&buf, out) != EOF) {
506
+ if (step->csum) {
507
+ ret = error(_("unexpected MIDX output: '%s'"), buf.buf);
508
+ fclose(out);
509
+ out = NULL;
510
+ finish_command(&cmd);
511
+ goto out;
512
+ }
513
+ step->csum = strbuf_detach(&buf, NULL);
514
+ }
515
+
516
+ ret = finish_command(&cmd);
517
+
518
+out:
519
+ if (out)
520
+ fclose(out);
521
+ strbuf_release(&buf);
522
+
523
+ return ret;
524
+}
525
+
526
+static int midx_compaction_step_exec(struct midx_compaction_step *step,
527
+ struct repack_write_midx_opts *opts,
528
+ const char *base)
529
+{
530
+ switch (step->type) {
531
+ case MIDX_COMPACTION_STEP_UNKNOWN:
532
+ BUG("cannot execute UNKNOWN midx compaction step");
533
+ case MIDX_COMPACTION_STEP_COPY:
534
+ return midx_compaction_step_exec_copy(step);
535
+ case MIDX_COMPACTION_STEP_WRITE:
536
+ return midx_compaction_step_exec_write(step, opts, base);
537
+ case MIDX_COMPACTION_STEP_COMPACT:
538
+ return midx_compaction_step_exec_compact(step, opts);
539
+ default:
540
+ BUG("unhandled midx compaction step type %d", step->type);
541
+ }
542
+}
543
+
544
+static void midx_compaction_step_release(struct midx_compaction_step *step)
545
+{
546
+ if (step->type == MIDX_COMPACTION_STEP_WRITE)
547
+ string_list_clear(&step->u.write, 0);
548
+ free(step->csum);
549
+}
550
+
551
+static int repack_make_midx_compaction_plan(struct repack_write_midx_opts *opts,
552
+ struct midx_compaction_step **steps_p,
553
+ size_t *steps_nr_p)
554
+{
555
+ struct multi_pack_index *m;
556
+ struct midx_compaction_step *steps = NULL;
557
+ struct midx_compaction_step step = { 0 };
558
+ struct strbuf buf = STRBUF_INIT;
559
+ size_t steps_nr = 0, steps_alloc = 0;
560
+ uint32_t i;
561
+ int ret = 0;
562
+
563
+ trace2_region_enter("repack", "make_midx_compaction_plan",
564
+ opts->existing->repo);
565
+
566
+ odb_reprepare(opts->existing->repo->objects);
567
+ m = get_multi_pack_index(opts->existing->source);
568
+
569
+ for (i = 0; m && i < m->num_packs + m->num_packs_in_base; i++) {
570
+ if (prepare_midx_pack(m, i)) {
571
+ ret = error(_("could not load pack %"PRIu32" from MIDX"),
572
+ i);
573
+ goto out;
574
+ }
575
+ }
576
+
577
+ trace2_region_enter("repack", "steps:write", opts->existing->repo);
578
+
579
+ /*
580
+ * The first MIDX in the resulting chain is always going to be
581
+ * new.
582
+ *
583
+ * At a minimum, it will include all of the newly written packs.
584
+ * If there is an existing MIDX whose tip layer contains packs
585
+ * that were repacked, it will also include any of its packs
586
+ * which were *not* rolled up as part of the geometric repack
587
+ * (if any), and the previous tip will be replaced.
588
+ *
589
+ * It may grow to include the packs from zero or more MIDXs from
590
+ * the old chain, beginning either at the old tip (if the MIDX
591
+ * was *not* rewritten) or the old tip's base MIDX layer
592
+ * (otherwise).
593
+ */
594
+ step.type = MIDX_COMPACTION_STEP_WRITE;
595
+ string_list_init_dup(&step.u.write);
596
+
597
+ for (i = 0; i < opts->names->nr; i++) {
598
+ strbuf_reset(&buf);
599
+ strbuf_addf(&buf, "pack-%s.idx", opts->names->items[i].string);
600
+ string_list_append(&step.u.write, buf.buf);
601
+
602
+ trace2_data_string("repack", opts->existing->repo,
603
+ "include:fresh",
604
+ step.u.write.items[step.u.write.nr - 1].string);
605
+ }
606
+ for (i = 0; i < opts->geometry->split; i++) {
607
+ struct packed_git *p = opts->geometry->pack[i];
608
+ if (unsigned_add_overflows(step.objects_nr, p->num_objects)) {
609
+ ret = error(_("too many objects in MIDX compaction step"));
610
+ goto out;
611
+ }
612
+
613
+ step.objects_nr += p->num_objects;
614
+ }
615
+ trace2_data_intmax("repack", opts->existing->repo,
616
+ "include:fresh:objects_nr",
617
+ (uintmax_t)step.objects_nr);
618
+
619
+ /*
620
+ * Now handle any existing packs which were *not* rewritten.
621
+ *
622
+ * The list of packs in opts->geometry only contains MIDX'd
623
+ * packs from the newest layer when that layer has more than
624
+ * 'repack.midxNewLayerThreshold' number of packs.
625
+ *
626
+ * If the MIDX tip was rewritten (that is, one or more of those
627
+ * packs appear below the split line), then add all packs above
628
+ * the split line to the new layer, as the old one is no longer
629
+ * usable.
630
+ *
631
+ * If the MIDX tip was not rewritten (that is, all MIDX'd packs
632
+ * from the youngest layer appear below the split line, or were
633
+ * not included in the geometric repack at all because there
634
+ * were too few of them), ignore them since we'll retain the
635
+ * existing layer as-is.
636
+ */
637
+ for (i = opts->geometry->split; i < opts->geometry->pack_nr; i++) {
638
+ struct packed_git *p = opts->geometry->pack[i];
639
+ struct string_list_item *item;
640
+
641
+ strbuf_reset(&buf);
642
+ strbuf_addstr(&buf, pack_basename(p));
643
+ strbuf_strip_suffix(&buf, ".pack");
644
+ strbuf_addstr(&buf, ".idx");
645
+
646
+ if (p->multi_pack_index &&
647
+ !opts->geometry->midx_tip_rewritten) {
648
+ trace2_data_string("repack", opts->existing->repo,
649
+ "exclude:unmodified", buf.buf);
650
+ continue;
651
+ }
652
+
653
+ trace2_data_string("repack", opts->existing->repo,
654
+ "include:unmodified", buf.buf);
655
+ trace2_data_string("repack", opts->existing->repo,
656
+ "include:unmodified:midx",
657
+ p->multi_pack_index ? "true" : "false");
658
+
659
+ item = string_list_append(&step.u.write, buf.buf);
660
+ if (p->multi_pack_index || i == opts->geometry->pack_nr - 1)
661
+ item->util = (void *)1; /* mark as preferred */
662
+
663
+ if (unsigned_add_overflows(step.objects_nr, p->num_objects)) {
664
+ ret = error(_("too many objects in MIDX compaction step"));
665
+ goto out;
666
+ }
667
+
668
+ step.objects_nr += p->num_objects;
669
+ }
670
+ trace2_data_intmax("repack", opts->existing->repo,
671
+ "include:unmodified:objects_nr",
672
+ (uintmax_t)step.objects_nr);
673
+
674
+ /*
675
+ * If the MIDX tip was rewritten, then we no longer consider it
676
+ * a candidate for compaction, since it will not exist in the
677
+ * MIDX chain being built.
678
+ */
679
+ if (opts->geometry->midx_tip_rewritten)
680
+ m = m->base_midx;
681
+
682
+ trace2_data_string("repack", opts->existing->repo, "midx:rewrote-tip",
683
+ opts->geometry->midx_tip_rewritten ? "true" : "false");
684
+
685
+ trace2_region_enter("repack", "compact", opts->existing->repo);
686
+
687
+ /*
688
+ * Compact additional MIDX layers into this proposed one until
689
+ * the merging condition is violated.
690
+ */
691
+ while (m) {
692
+ uint32_t preferred_pack_idx;
693
+
694
+ trace2_data_string("repack", opts->existing->repo,
695
+ "candidate", midx_get_checksum_hex(m));
696
+
697
+ if (step.objects_nr < m->num_objects / opts->midx_split_factor) {
698
+ /*
699
+ * Stop compacting MIDX layer as soon as the
700
+ * merged size is less than half the size of the
701
+ * next layer in the chain.
702
+ */
703
+ trace2_data_string("repack", opts->existing->repo,
704
+ "compact", "violated");
705
+ trace2_data_intmax("repack", opts->existing->repo,
706
+ "objects_nr",
707
+ (uintmax_t)step.objects_nr);
708
+ trace2_data_intmax("repack", opts->existing->repo,
709
+ "next_objects_nr",
710
+ (uintmax_t)m->num_objects);
711
+ trace2_data_intmax("repack", opts->existing->repo,
712
+ "split_factor",
713
+ (uintmax_t)opts->midx_split_factor);
714
+
715
+ break;
716
+ }
717
+
718
+ if (midx_preferred_pack(m, &preferred_pack_idx) < 0) {
719
+ ret = error(_("could not find preferred pack for MIDX "
720
+ "%s"), midx_get_checksum_hex(m));
721
+ goto out;
722
+ }
723
+
724
+ for (i = 0; i < m->num_packs; i++) {
725
+ struct string_list_item *item;
726
+ uint32_t pack_int_id = i + m->num_packs_in_base;
727
+ struct packed_git *p = nth_midxed_pack(m, pack_int_id);
728
+
729
+ strbuf_reset(&buf);
730
+ strbuf_addstr(&buf, pack_basename(p));
731
+ strbuf_strip_suffix(&buf, ".pack");
732
+ strbuf_addstr(&buf, ".idx");
733
+
734
+ trace2_data_string("repack", opts->existing->repo,
735
+ "midx:pack", buf.buf);
736
+
737
+ item = string_list_append(&step.u.write, buf.buf);
738
+ if (pack_int_id == preferred_pack_idx)
739
+ item->util = (void *)1; /* mark as preferred */
740
+ }
741
+
742
+ if (unsigned_add_overflows(step.objects_nr, m->num_objects)) {
743
+ ret = error(_("too many objects in MIDX compaction step"));
744
+ goto out;
745
+ }
746
+ step.objects_nr += m->num_objects;
747
+
748
+ m = m->base_midx;
749
+ }
750
+
751
+ if (step.u.write.nr > 0) {
752
+ /*
753
+ * As long as there is at least one new pack to write
754
+ * (and thus the MIDX is non-empty), add it to the plan.
755
+ */
756
+ ALLOC_GROW(steps, steps_nr + 1, steps_alloc);
757
+ steps[steps_nr++] = step;
758
+ }
759
+
760
+ trace2_data_intmax("repack", opts->existing->repo,
761
+ "step:objects_nr", (uintmax_t)step.objects_nr);
762
+ trace2_data_intmax("repack", opts->existing->repo,
763
+ "step:packs_nr", (uintmax_t)step.u.write.nr);
764
+
765
+ trace2_region_leave("repack", "compact", opts->existing->repo);
766
+ trace2_region_leave("repack", "steps:write", opts->existing->repo);
767
+
768
+ trace2_region_enter("repack", "steps:rest", opts->existing->repo);
769
+
770
+ /*
771
+ * Then start over, repeat, and either compact or keep as-is
772
+ * each MIDX layer until we have exhausted the chain.
773
+ *
774
+ * Finally, evaluate the remainder of the chain (if any) and
775
+ * either compact a sequence of adjacent layers, or keep
776
+ * individual layers as-is according to the same merging
777
+ * condition as above.
778
+ */
779
+ while (m) {
780
+ struct multi_pack_index *next = m;
781
+
782
+ ALLOC_GROW(steps, steps_nr + 1, steps_alloc);
783
+
784
+ memset(&step, 0, sizeof(step));
785
+ step.type = MIDX_COMPACTION_STEP_UNKNOWN;
786
+
787
+ trace2_region_enter("repack", "step", opts->existing->repo);
788
+
789
+ trace2_data_string("repack", opts->existing->repo,
790
+ "from", midx_get_checksum_hex(m));
791
+
792
+ while (next) {
793
+ uint32_t proposed_objects_nr;
794
+ if (unsigned_add_overflows(step.objects_nr, next->num_objects)) {
795
+ ret = error(_("too many objects in MIDX compaction step"));
796
+ trace2_region_leave("repack", "step", opts->existing->repo);
797
+ goto out;
798
+ }
799
+
800
+ proposed_objects_nr = step.objects_nr + next->num_objects;
801
+
802
+ trace2_data_string("repack", opts->existing->repo,
803
+ "proposed",
804
+ midx_get_checksum_hex(next));
805
+ trace2_data_intmax("repack", opts->existing->repo,
806
+ "proposed:objects_nr",
807
+ (uintmax_t)next->num_objects);
808
+
809
+ if (!next->base_midx) {
810
+ /*
811
+ * If we are at the end of the MIDX
812
+ * chain, there is nothing to compact,
813
+ * so mark it and stop.
814
+ */
815
+ step.objects_nr = proposed_objects_nr;
816
+ break;
817
+ }
818
+
819
+ if (proposed_objects_nr < next->base_midx->num_objects / opts->midx_split_factor) {
820
+ /*
821
+ * If there is a MIDX following this
822
+ * one, but our accumulated size is less
823
+ * than half of its size, compacting
824
+ * them would violate the merging
825
+ * condition, so stop here.
826
+ */
827
+
828
+ trace2_data_string("repack", opts->existing->repo,
829
+ "compact:violated:at",
830
+ midx_get_checksum_hex(next->base_midx));
831
+ trace2_data_intmax("repack", opts->existing->repo,
832
+ "compact:violated:at:objects_nr",
833
+ (uintmax_t)next->base_midx->num_objects);
834
+ break;
835
+ }
836
+
837
+ /*
838
+ * Otherwise, it is OK to compact the next layer
839
+ * into this one. Do so, and then continue
840
+ * through the remainder of the chain.
841
+ */
842
+ step.objects_nr = proposed_objects_nr;
843
+ trace2_data_intmax("repack", opts->existing->repo,
844
+ "step:objects_nr",
845
+ (uintmax_t)step.objects_nr);
846
+ next = next->base_midx;
847
+ }
848
+
849
+ if (m == next) {
850
+ step.type = MIDX_COMPACTION_STEP_COPY;
851
+ step.u.copy = m;
852
+
853
+ trace2_data_string("repack", opts->existing->repo,
854
+ "type", "copy");
855
+ } else {
856
+ step.type = MIDX_COMPACTION_STEP_COMPACT;
857
+ step.u.compact.from = next;
858
+ step.u.compact.to = m;
859
+
860
+ trace2_data_string("repack", opts->existing->repo,
861
+ "to", midx_get_checksum_hex(m));
862
+ trace2_data_string("repack", opts->existing->repo,
863
+ "type", "compact");
864
+ }
865
+
866
+ m = next->base_midx;
867
+ steps[steps_nr++] = step;
868
+ trace2_region_leave("repack", "step", opts->existing->repo);
869
+ }
870
+
871
+ trace2_region_leave("repack", "steps:rest", opts->existing->repo);
872
+
873
+out:
874
+ *steps_p = steps;
875
+ *steps_nr_p = steps_nr;
876
+
877
+ strbuf_release(&buf);
878
+
879
+ trace2_region_leave("repack", "make_midx_compaction_plan",
880
+ opts->existing->repo);
881
+
882
+ return ret;
883
+}
884
+
885
+static int write_midx_incremental(struct repack_write_midx_opts *opts)
886
+{
887
+ struct midx_compaction_step *steps = NULL;
888
+ struct strbuf lock_name = STRBUF_INIT;
889
+ struct lock_file lf;
890
+ size_t steps_nr = 0;
891
+ size_t i;
892
+ int ret = 0;
893
+
894
+ get_midx_chain_filename(opts->existing->source, &lock_name);
895
+ if (safe_create_leading_directories(opts->existing->repo,
896
+ lock_name.buf))
897
+ die_errno(_("unable to create leading directories of %s"),
898
+ lock_name.buf);
899
+ hold_lock_file_for_update(&lf, lock_name.buf, LOCK_DIE_ON_ERROR);
900
+
901
+ if (!fdopen_lock_file(&lf, "w")) {
902
+ ret = error_errno(_("unable to open multi-pack-index chain file"));
903
+ goto done;
904
+ }
905
+
906
+ if (repack_make_midx_compaction_plan(opts, &steps, &steps_nr) < 0) {
907
+ ret = error(_("unable to generate compaction plan"));
908
+ goto done;
909
+ }
910
+
911
+ for (i = 0; i < steps_nr; i++) {
912
+ struct midx_compaction_step *step = &steps[i];
913
+ char *base = NULL;
914
+
915
+ if (i + 1 < steps_nr)
916
+ base = xstrdup(midx_compaction_step_base(&steps[i + 1]));
917
+
918
+ if (midx_compaction_step_exec(step, opts, base) < 0) {
919
+ ret = error(_("unable to execute compaction step %"PRIuMAX),
920
+ (uintmax_t)i);
921
+ free(base);
922
+ goto done;
923
+ }
924
+
925
+ free(base);
926
+ }
927
+
928
+ i = steps_nr;
929
+ while (i--) {
930
+ struct midx_compaction_step *step = &steps[i];
931
+ if (!step->csum)
932
+ BUG("missing result for compaction step %"PRIuMAX,
933
+ (uintmax_t)i);
934
+ fprintf(get_lock_file_fp(&lf), "%s\n", step->csum);
935
+ }
936
+
937
+ commit_lock_file(&lf);
938
+
939
+done:
940
+ strbuf_release(&lock_name);
941
+ for (i = 0; i < steps_nr; i++)
942
+ midx_compaction_step_release(&steps[i]);
943
+ free(steps);
944
+ return ret;
945
+}
946
+
947
int repack_write_midx(struct repack_write_midx_opts *opts)
948
{
949
switch (opts->mode) {
951
BUG("write_midx mode is NONE?");
952
case REPACK_WRITE_MIDX_DEFAULT:
953
return write_midx_included_packs(opts);
954
+ case REPACK_WRITE_MIDX_INCREMENTAL:
955
+ return write_midx_incremental(opts);
956
default:
957
BUG("unhandled write_midx mode: %d", opts->mode);
958
}