Skip to content

Commit 36628c5

Browse files
committed
Merge branch 'ps/fix-geom-repack-with-alternates'
Geometric repacking ("git repack --geometric=<n>") in a repository that borrows from an alternate object database had various corner case bugs, which have been corrected. * ps/fix-geom-repack-with-alternates: repack: disable writing bitmaps when doing a local repack repack: honor `-l` when calculating pack geometry t/helper: allow chmtime to print verbosely without modifying mtime pack-objects: extend test coverage of `--stdin-packs` with alternates pack-objects: fix error when same packfile is included and excluded pack-objects: fix error when packing same pack twice pack-objects: split out `--stdin-packs` tests into separate file repack: fix generating multi-pack-index with only non-local packs repack: fix trying to use preferred pack in alternates midx: fix segfault with no packs and invalid preferred pack
2 parents c4c9d55 + d85cd18 commit 36628c5

11 files changed

+504
-151
lines changed

builtin/pack-objects.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3360,16 +3360,16 @@ static void read_packs_list_from_stdin(void)
33603360
}
33613361

33623362
string_list_sort(&include_packs);
3363+
string_list_remove_duplicates(&include_packs, 0);
33633364
string_list_sort(&exclude_packs);
3365+
string_list_remove_duplicates(&exclude_packs, 0);
33643366

33653367
for (p = get_all_packs(the_repository); p; p = p->next) {
33663368
const char *pack_name = pack_basename(p);
33673369

3368-
item = string_list_lookup(&include_packs, pack_name);
3369-
if (!item)
3370-
item = string_list_lookup(&exclude_packs, pack_name);
3371-
3372-
if (item)
3370+
if ((item = string_list_lookup(&include_packs, pack_name)))
3371+
item->util = p;
3372+
if ((item = string_list_lookup(&exclude_packs, pack_name)))
33733373
item->util = p;
33743374
}
33753375

builtin/repack.c

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,8 @@ static int geometry_cmp(const void *va, const void *vb)
325325
}
326326

327327
static void init_pack_geometry(struct pack_geometry **geometry_p,
328-
struct string_list *existing_kept_packs)
328+
struct string_list *existing_kept_packs,
329+
const struct pack_objects_args *args)
329330
{
330331
struct packed_git *p;
331332
struct pack_geometry *geometry;
@@ -335,6 +336,14 @@ static void init_pack_geometry(struct pack_geometry **geometry_p,
335336
geometry = *geometry_p;
336337

337338
for (p = get_all_packs(the_repository); p; p = p->next) {
339+
if (args->local && !p->pack_local)
340+
/*
341+
* When asked to only repack local packfiles we skip
342+
* over any packfiles that are borrowed from alternate
343+
* object directories.
344+
*/
345+
continue;
346+
338347
if (!pack_kept_objects) {
339348
/*
340349
* Any pack that has its pack_keep bit set will appear
@@ -448,8 +457,10 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor)
448457
geometry->split = split;
449458
}
450459

451-
static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry)
460+
static struct packed_git *get_preferred_pack(struct pack_geometry *geometry)
452461
{
462+
uint32_t i;
463+
453464
if (!geometry) {
454465
/*
455466
* No geometry means either an all-into-one repack (in which
@@ -464,7 +475,21 @@ static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry
464475
}
465476
if (geometry->split == geometry->pack_nr)
466477
return NULL;
467-
return geometry->pack[geometry->pack_nr - 1];
478+
479+
/*
480+
* The preferred pack is the largest pack above the split line. In
481+
* other words, it is the largest pack that does not get rolled up in
482+
* the geometric repack.
483+
*/
484+
for (i = geometry->pack_nr; i > geometry->split; i--)
485+
/*
486+
* A pack that is not local would never be included in a
487+
* multi-pack index. We thus skip over any non-local packs.
488+
*/
489+
if (geometry->pack[i - 1]->pack_local)
490+
return geometry->pack[i - 1];
491+
492+
return NULL;
468493
}
469494

470495
static void clear_pack_geometry(struct pack_geometry *geometry)
@@ -558,6 +583,17 @@ static void midx_included_packs(struct string_list *include,
558583
for (i = geometry->split; i < geometry->pack_nr; i++) {
559584
struct packed_git *p = geometry->pack[i];
560585

586+
/*
587+
* The multi-pack index never refers to packfiles part
588+
* of an alternate object database, so we skip these.
589+
* While git-multi-pack-index(1) would silently ignore
590+
* them anyway, this allows us to skip executing the
591+
* command completely when we have only non-local
592+
* packfiles.
593+
*/
594+
if (!p->pack_local)
595+
continue;
596+
561597
strbuf_addstr(&buf, pack_basename(p));
562598
strbuf_strip_suffix(&buf, ".pack");
563599
strbuf_addstr(&buf, ".idx");
@@ -591,7 +627,7 @@ static int write_midx_included_packs(struct string_list *include,
591627
{
592628
struct child_process cmd = CHILD_PROCESS_INIT;
593629
struct string_list_item *item;
594-
struct packed_git *largest = get_largest_active_pack(geometry);
630+
struct packed_git *preferred = get_preferred_pack(geometry);
595631
FILE *in;
596632
int ret;
597633

@@ -612,9 +648,9 @@ static int write_midx_included_packs(struct string_list *include,
612648
if (write_bitmaps)
613649
strvec_push(&cmd.args, "--bitmap");
614650

615-
if (largest)
651+
if (preferred)
616652
strvec_pushf(&cmd.args, "--preferred-pack=%s",
617-
pack_basename(largest));
653+
pack_basename(preferred));
618654

619655
if (refs_snapshot)
620656
strvec_pushf(&cmd.args, "--refs-snapshot=%s", refs_snapshot);
@@ -853,6 +889,18 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
853889
if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
854890
die(_(incremental_bitmap_conflict_error));
855891

892+
if (write_bitmaps && po_args.local && has_alt_odb(the_repository)) {
893+
/*
894+
* When asked to do a local repack, but we have
895+
* packfiles that are inherited from an alternate, then
896+
* we cannot guarantee that the multi-pack-index would
897+
* have full coverage of all objects. We thus disable
898+
* writing bitmaps in that case.
899+
*/
900+
warning(_("disabling bitmap writing, as some objects are not being packed"));
901+
write_bitmaps = 0;
902+
}
903+
856904
if (write_midx && write_bitmaps) {
857905
struct strbuf path = STRBUF_INIT;
858906

@@ -875,7 +923,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
875923
if (geometric_factor) {
876924
if (pack_everything)
877925
die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a");
878-
init_pack_geometry(&geometry, &existing_kept_packs);
926+
init_pack_geometry(&geometry, &existing_kept_packs, &po_args);
879927
split_pack_geometry(geometry, geometric_factor);
880928
}
881929

midx.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,17 +1331,17 @@ static int write_midx_internal(const char *object_dir,
13311331
}
13321332

13331333
if (preferred_pack_name) {
1334-
int found = 0;
1334+
ctx.preferred_pack_idx = -1;
1335+
13351336
for (i = 0; i < ctx.nr; i++) {
13361337
if (!cmp_idx_or_pack_name(preferred_pack_name,
13371338
ctx.info[i].pack_name)) {
13381339
ctx.preferred_pack_idx = i;
1339-
found = 1;
13401340
break;
13411341
}
13421342
}
13431343

1344-
if (!found)
1344+
if (ctx.preferred_pack_idx == -1)
13451345
warning(_("unknown preferred pack: '%s'"),
13461346
preferred_pack_name);
13471347
} else if (ctx.nr &&

object-file.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,12 @@ void prepare_alt_odb(struct repository *r)
953953
r->objects->loaded_alternates = 1;
954954
}
955955

956+
int has_alt_odb(struct repository *r)
957+
{
958+
prepare_alt_odb(r);
959+
return !!r->objects->odb->next;
960+
}
961+
956962
/* Returns 1 if we have successfully freshened the file, 0 otherwise. */
957963
static int freshen_file(const char *fn)
958964
{

object-store.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
5656
struct object_directory *, 1, fspathhash, fspatheq)
5757

5858
void prepare_alt_odb(struct repository *r);
59+
int has_alt_odb(struct repository *r);
5960
char *compute_alternate_path(const char *path, struct strbuf *err);
6061
struct object_directory *find_odb(struct repository *r, const char *obj_dir);
6162
typedef int alt_odb_fn(struct object_directory *, void *);

t/helper/test-chmtime.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ int cmd__chmtime(int argc, const char **argv)
9494
if (timespec_arg(argv[i], &set_time, &set_eq)) {
9595
++i;
9696
} else {
97-
if (get == 0) {
97+
if (get == 0 && verbose == 0) {
9898
fprintf(stderr, "Not a base-10 integer: %s\n", argv[i] + 1);
9999
goto usage;
100100
}

t/t5300-pack-object.sh

Lines changed: 0 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -589,141 +589,6 @@ test_expect_success 'prefetch objects' '
589589
test_line_count = 1 donelines
590590
'
591591

592-
test_expect_success 'setup for --stdin-packs tests' '
593-
git init stdin-packs &&
594-
(
595-
cd stdin-packs &&
596-
597-
test_commit A &&
598-
test_commit B &&
599-
test_commit C &&
600-
601-
for id in A B C
602-
do
603-
git pack-objects .git/objects/pack/pack-$id \
604-
--incremental --revs <<-EOF || exit 1
605-
refs/tags/$id
606-
EOF
607-
done &&
608-
609-
ls -la .git/objects/pack
610-
)
611-
'
612-
613-
test_expect_success '--stdin-packs with excluded packs' '
614-
(
615-
cd stdin-packs &&
616-
617-
PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
618-
PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
619-
PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
620-
621-
git pack-objects test --stdin-packs <<-EOF &&
622-
$PACK_A
623-
^$PACK_B
624-
$PACK_C
625-
EOF
626-
627-
(
628-
git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
629-
git show-index <$(ls .git/objects/pack/pack-C-*.idx)
630-
) >expect.raw &&
631-
git show-index <$(ls test-*.idx) >actual.raw &&
632-
633-
cut -d" " -f2 <expect.raw | sort >expect &&
634-
cut -d" " -f2 <actual.raw | sort >actual &&
635-
test_cmp expect actual
636-
)
637-
'
638-
639-
test_expect_success '--stdin-packs is incompatible with --filter' '
640-
(
641-
cd stdin-packs &&
642-
test_must_fail git pack-objects --stdin-packs --stdout \
643-
--filter=blob:none </dev/null 2>err &&
644-
test_i18ngrep "cannot use --filter with --stdin-packs" err
645-
)
646-
'
647-
648-
test_expect_success '--stdin-packs is incompatible with --revs' '
649-
(
650-
cd stdin-packs &&
651-
test_must_fail git pack-objects --stdin-packs --revs out \
652-
</dev/null 2>err &&
653-
test_i18ngrep "cannot use internal rev list with --stdin-packs" err
654-
)
655-
'
656-
657-
test_expect_success '--stdin-packs with loose objects' '
658-
(
659-
cd stdin-packs &&
660-
661-
PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
662-
PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
663-
PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
664-
665-
test_commit D && # loose
666-
667-
git pack-objects test2 --stdin-packs --unpacked <<-EOF &&
668-
$PACK_A
669-
^$PACK_B
670-
$PACK_C
671-
EOF
672-
673-
(
674-
git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
675-
git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
676-
git rev-list --objects --no-object-names \
677-
refs/tags/C..refs/tags/D
678-
679-
) >expect.raw &&
680-
ls -la . &&
681-
git show-index <$(ls test2-*.idx) >actual.raw &&
682-
683-
cut -d" " -f2 <expect.raw | sort >expect &&
684-
cut -d" " -f2 <actual.raw | sort >actual &&
685-
test_cmp expect actual
686-
)
687-
'
688-
689-
test_expect_success '--stdin-packs with broken links' '
690-
(
691-
cd stdin-packs &&
692-
693-
# make an unreachable object with a bogus parent
694-
git cat-file -p HEAD >commit &&
695-
sed "s/$(git rev-parse HEAD^)/$(test_oid zero)/" <commit |
696-
git hash-object -w -t commit --stdin >in &&
697-
698-
git pack-objects .git/objects/pack/pack-D <in &&
699-
700-
PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
701-
PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
702-
PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
703-
PACK_D="$(basename .git/objects/pack/pack-D-*.pack)" &&
704-
705-
git pack-objects test3 --stdin-packs --unpacked <<-EOF &&
706-
$PACK_A
707-
^$PACK_B
708-
$PACK_C
709-
$PACK_D
710-
EOF
711-
712-
(
713-
git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
714-
git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
715-
git show-index <$(ls .git/objects/pack/pack-D-*.idx) &&
716-
git rev-list --objects --no-object-names \
717-
refs/tags/C..refs/tags/D
718-
) >expect.raw &&
719-
git show-index <$(ls test3-*.idx) >actual.raw &&
720-
721-
cut -d" " -f2 <expect.raw | sort >expect &&
722-
cut -d" " -f2 <actual.raw | sort >actual &&
723-
test_cmp expect actual
724-
)
725-
'
726-
727592
test_expect_success 'negative window clamps to 0' '
728593
git pack-objects --progress --window=-1 neg-window <obj-list 2>stderr &&
729594
check_deltas stderr = 0

t/t5319-multi-pack-index.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,18 @@ test_expect_success 'write midx with --stdin-packs' '
183183

184184
compare_results_with_midx "mixed mode (one pack + extra)"
185185

186+
test_expect_success 'write with no objects and preferred pack' '
187+
test_when_finished "rm -rf empty" &&
188+
git init empty &&
189+
test_must_fail git -C empty multi-pack-index write \
190+
--stdin-packs --preferred-pack=does-not-exist </dev/null 2>err &&
191+
cat >expect <<-EOF &&
192+
warning: unknown preferred pack: ${SQ}does-not-exist${SQ}
193+
error: no pack files to index.
194+
EOF
195+
test_cmp expect err
196+
'
197+
186198
test_expect_success 'write progress off for redirected stderr' '
187199
git multi-pack-index --object-dir=$objdir write 2>err &&
188200
test_line_count = 0 err

0 commit comments

Comments
 (0)