Merge branch 'tb/repack-write-midx' into tb/fix-midx-rename-while-mapped

* tb/repack-write-midx:
  test-read-midx: fix leak of bitmap_index struct
  builtin/repack.c: pass `--refs-snapshot` when writing bitmaps
  builtin/repack.c: make largest pack preferred
  builtin/repack.c: support writing a MIDX while repacking
  builtin/repack.c: extract showing progress to a variable
  builtin/repack.c: rename variables that deal with non-kept packs
  builtin/repack.c: keep track of existing packs unconditionally
  midx: preliminary support for `--refs-snapshot`
  builtin/multi-pack-index.c: support `--stdin-packs` mode
  midx: expose `write_midx_file_only()` publicly
This commit is contained in:
Junio C Hamano 2021-10-15 13:07:37 -07:00
commit 823b4281ca
14 changed files with 724 additions and 57 deletions

View File

@ -45,6 +45,25 @@ write::
--[no-]bitmap:: --[no-]bitmap::
Control whether or not a multi-pack bitmap is written. Control whether or not a multi-pack bitmap is written.
--stdin-packs::
Write a multi-pack index containing only the set of
line-delimited pack index basenames provided over stdin.
--refs-snapshot=<path>::
With `--bitmap`, optionally specify a file which
contains a "refs snapshot" taken prior to repacking.
+
A reference snapshot is composed of line-delimited OIDs corresponding to
the reference tips, usually taken by `git repack` prior to generating a
new pack. A line may optionally start with a `+` character to indicate
that the reference which corresponds to that OID is "preferred" (see
linkgit:git-config[1]'s `pack.preferBitmapTips`.)
+
The file given at `<path>` is expected to be readable, and can contain
duplicates. (If a given OID is given more than once, it is marked as
preferred if at least one instance of it begins with the special `+`
marker).
-- --
verify:: verify::

View File

@ -9,7 +9,7 @@ git-repack - Pack unpacked objects in a repository
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>] 'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m] [--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>] [--write-midx]
DESCRIPTION DESCRIPTION
----------- -----------
@ -128,10 +128,11 @@ depth is 4095.
-b:: -b::
--write-bitmap-index:: --write-bitmap-index::
Write a reachability bitmap index as part of the repack. This Write a reachability bitmap index as part of the repack. This
only makes sense when used with `-a` or `-A`, as the bitmaps only makes sense when used with `-a`, `-A` or `-m`, as the bitmaps
must be able to refer to all reachable objects. This option must be able to refer to all reachable objects. This option
overrides the setting of `repack.writeBitmaps`. This option overrides the setting of `repack.writeBitmaps`. This option
has no effect if multiple packfiles are created. has no effect if multiple packfiles are created, unless writing a
MIDX (in which case a multi-pack bitmap is created).
--pack-kept-objects:: --pack-kept-objects::
Include objects in `.keep` files when repacking. Note that we Include objects in `.keep` files when repacking. Note that we
@ -189,6 +190,15 @@ this "roll-up", without respect to their reachability. This is subject
to change in the future. This option (implying a drastically different to change in the future. This option (implying a drastically different
repack mode) is not guaranteed to work with all other combinations of repack mode) is not guaranteed to work with all other combinations of
option to `git repack`. option to `git repack`.
+
When writing a multi-pack bitmap, `git repack` selects the largest resulting
pack as the preferred pack for object selection by the MIDX (see
linkgit:git-multi-pack-index[1]).
-m::
--write-midx::
Write a multi-pack index (see linkgit:git-multi-pack-index[1])
containing the non-redundant packs.
CONFIGURATION CONFIGURATION
------------- -------------

View File

@ -7,7 +7,8 @@
#include "object-store.h" #include "object-store.h"
#define BUILTIN_MIDX_WRITE_USAGE \ #define BUILTIN_MIDX_WRITE_USAGE \
N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]") N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
"[--refs-snapshot=<path>]")
#define BUILTIN_MIDX_VERIFY_USAGE \ #define BUILTIN_MIDX_VERIFY_USAGE \
N_("git multi-pack-index [<options>] verify") N_("git multi-pack-index [<options>] verify")
@ -45,8 +46,10 @@ static char const * const builtin_multi_pack_index_usage[] = {
static struct opts_multi_pack_index { static struct opts_multi_pack_index {
const char *object_dir; const char *object_dir;
const char *preferred_pack; const char *preferred_pack;
const char *refs_snapshot;
unsigned long batch_size; unsigned long batch_size;
unsigned flags; unsigned flags;
int stdin_packs;
} opts; } opts;
static struct option common_opts[] = { static struct option common_opts[] = {
@ -77,6 +80,16 @@ static int git_multi_pack_index_write_config(const char *var, const char *value,
return 0; return 0;
} }
static void read_packs_from_stdin(struct string_list *to)
{
struct strbuf buf = STRBUF_INIT;
while (strbuf_getline(&buf, stdin) != EOF)
string_list_append(to, buf.buf);
string_list_sort(to);
strbuf_release(&buf);
}
static int cmd_multi_pack_index_write(int argc, const char **argv) static int cmd_multi_pack_index_write(int argc, const char **argv)
{ {
struct option *options; struct option *options;
@ -88,6 +101,10 @@ static int cmd_multi_pack_index_write(int argc, const char **argv)
MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX), MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX),
OPT_BIT(0, "progress", &opts.flags, OPT_BIT(0, "progress", &opts.flags,
N_("force progress reporting"), MIDX_PROGRESS), N_("force progress reporting"), MIDX_PROGRESS),
OPT_BOOL(0, "stdin-packs", &opts.stdin_packs,
N_("write multi-pack index containing only given indexes")),
OPT_FILENAME(0, "refs-snapshot", &opts.refs_snapshot,
N_("refs snapshot for selecting bitmap commits")),
OPT_END(), OPT_END(),
}; };
@ -110,8 +127,23 @@ static int cmd_multi_pack_index_write(int argc, const char **argv)
FREE_AND_NULL(options); FREE_AND_NULL(options);
if (opts.stdin_packs) {
struct string_list packs = STRING_LIST_INIT_DUP;
int ret;
read_packs_from_stdin(&packs);
ret = write_midx_file_only(opts.object_dir, &packs,
opts.preferred_pack,
opts.refs_snapshot, opts.flags);
string_list_clear(&packs, 0);
return ret;
}
return write_midx_file(opts.object_dir, opts.preferred_pack, return write_midx_file(opts.object_dir, opts.preferred_pack,
opts.flags); opts.refs_snapshot, opts.flags);
} }
static int cmd_multi_pack_index_verify(int argc, const char **argv) static int cmd_multi_pack_index_verify(int argc, const char **argv)

View File

@ -15,6 +15,8 @@
#include "promisor-remote.h" #include "promisor-remote.h"
#include "shallow.h" #include "shallow.h"
#include "pack.h" #include "pack.h"
#include "pack-bitmap.h"
#include "refs.h"
static int delta_base_offset = 1; static int delta_base_offset = 1;
static int pack_kept_objects = -1; static int pack_kept_objects = -1;
@ -94,11 +96,13 @@ static void remove_pack_on_signal(int signo)
} }
/* /*
* Adds all packs hex strings to the fname list, which do not * Adds all packs hex strings to either fname_nonkept_list or
* have a corresponding .keep file. These packs are not to * fname_kept_list based on whether each pack has a corresponding
* be kept if we are going to pack everything into one file. * .keep file or not. Packs without a .keep file are not to be kept
* if we are going to pack everything into one file.
*/ */
static void get_non_kept_pack_filenames(struct string_list *fname_list, static void collect_pack_filenames(struct string_list *fname_nonkept_list,
struct string_list *fname_kept_list,
const struct string_list *extra_keep) const struct string_list *extra_keep)
{ {
DIR *dir; DIR *dir;
@ -112,21 +116,20 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list,
size_t len; size_t len;
int i; int i;
for (i = 0; i < extra_keep->nr; i++)
if (!fspathcmp(e->d_name, extra_keep->items[i].string))
break;
if (extra_keep->nr > 0 && i < extra_keep->nr)
continue;
if (!strip_suffix(e->d_name, ".pack", &len)) if (!strip_suffix(e->d_name, ".pack", &len))
continue; continue;
for (i = 0; i < extra_keep->nr; i++)
if (!fspathcmp(e->d_name, extra_keep->items[i].string))
break;
fname = xmemdupz(e->d_name, len); fname = xmemdupz(e->d_name, len);
if (!file_exists(mkpath("%s/%s.keep", packdir, fname))) if ((extra_keep->nr > 0 && i < extra_keep->nr) ||
string_list_append_nodup(fname_list, fname); (file_exists(mkpath("%s/%s.keep", packdir, fname))))
string_list_append_nodup(fname_kept_list, fname);
else else
free(fname); string_list_append_nodup(fname_nonkept_list, fname);
} }
closedir(dir); closedir(dir);
} }
@ -422,6 +425,25 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor)
geometry->split = split; geometry->split = split;
} }
static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry)
{
if (!geometry) {
/*
* No geometry means either an all-into-one repack (in which
* case there is only one pack left and it is the largest) or an
* incremental one.
*
* If repacking incrementally, then we could check the size of
* all packs to determine which should be preferred, but leave
* this for later.
*/
return NULL;
}
if (geometry->split == geometry->pack_nr)
return NULL;
return geometry->pack[geometry->pack_nr - 1];
}
static void clear_pack_geometry(struct pack_geometry *geometry) static void clear_pack_geometry(struct pack_geometry *geometry)
{ {
if (!geometry) if (!geometry)
@ -433,17 +455,162 @@ static void clear_pack_geometry(struct pack_geometry *geometry)
geometry->split = 0; geometry->split = 0;
} }
struct midx_snapshot_ref_data {
struct tempfile *f;
struct oidset seen;
int preferred;
};
static int midx_snapshot_ref_one(const char *refname,
const struct object_id *oid,
int flag, void *_data)
{
struct midx_snapshot_ref_data *data = _data;
struct object_id peeled;
if (!peel_iterated_oid(oid, &peeled))
oid = &peeled;
if (oidset_insert(&data->seen, oid))
return 0; /* already seen */
if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT)
return 0;
fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
oid_to_hex(oid));
return 0;
}
static void midx_snapshot_refs(struct tempfile *f)
{
struct midx_snapshot_ref_data data;
const struct string_list *preferred = bitmap_preferred_tips(the_repository);
data.f = f;
data.preferred = 0;
oidset_init(&data.seen, 0);
if (!fdopen_tempfile(f, "w"))
die(_("could not open tempfile %s for writing"),
get_tempfile_path(f));
if (preferred) {
struct string_list_item *item;
data.preferred = 1;
for_each_string_list_item(item, preferred)
for_each_ref_in(item->string, midx_snapshot_ref_one, &data);
data.preferred = 0;
}
for_each_ref(midx_snapshot_ref_one, &data);
if (close_tempfile_gently(f)) {
int save_errno = errno;
delete_tempfile(&f);
errno = save_errno;
die_errno(_("could not close refs snapshot tempfile"));
}
oidset_clear(&data.seen);
}
static void midx_included_packs(struct string_list *include,
struct string_list *existing_nonkept_packs,
struct string_list *existing_kept_packs,
struct string_list *names,
struct pack_geometry *geometry)
{
struct string_list_item *item;
for_each_string_list_item(item, existing_kept_packs)
string_list_insert(include, xstrfmt("%s.idx", item->string));
for_each_string_list_item(item, names)
string_list_insert(include, xstrfmt("pack-%s.idx", item->string));
if (geometry) {
struct strbuf buf = STRBUF_INIT;
uint32_t i;
for (i = geometry->split; i < geometry->pack_nr; i++) {
struct packed_git *p = geometry->pack[i];
strbuf_addstr(&buf, pack_basename(p));
strbuf_strip_suffix(&buf, ".pack");
strbuf_addstr(&buf, ".idx");
string_list_insert(include, strbuf_detach(&buf, NULL));
}
} else {
for_each_string_list_item(item, existing_nonkept_packs) {
if (item->util)
continue;
string_list_insert(include, xstrfmt("%s.idx", item->string));
}
}
}
static int write_midx_included_packs(struct string_list *include,
struct pack_geometry *geometry,
const char *refs_snapshot,
int show_progress, int write_bitmaps)
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item;
struct packed_git *largest = get_largest_active_pack(geometry);
FILE *in;
int ret;
if (!include->nr)
return 0;
cmd.in = -1;
cmd.git_cmd = 1;
strvec_push(&cmd.args, "multi-pack-index");
strvec_pushl(&cmd.args, "write", "--stdin-packs", NULL);
if (show_progress)
strvec_push(&cmd.args, "--progress");
else
strvec_push(&cmd.args, "--no-progress");
if (write_bitmaps)
strvec_push(&cmd.args, "--bitmap");
if (largest)
strvec_pushf(&cmd.args, "--preferred-pack=%s",
pack_basename(largest));
if (refs_snapshot)
strvec_pushf(&cmd.args, "--refs-snapshot=%s", refs_snapshot);
ret = start_command(&cmd);
if (ret)
return ret;
in = xfdopen(cmd.in, "w");
for_each_string_list_item(item, include)
fprintf(in, "%s\n", item->string);
fclose(in);
return finish_command(&cmd);
}
int cmd_repack(int argc, const char **argv, const char *prefix) int cmd_repack(int argc, const char **argv, const char *prefix)
{ {
struct child_process cmd = CHILD_PROCESS_INIT; struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item; struct string_list_item *item;
struct string_list names = STRING_LIST_INIT_DUP; struct string_list names = STRING_LIST_INIT_DUP;
struct string_list rollback = STRING_LIST_INIT_NODUP; struct string_list rollback = STRING_LIST_INIT_NODUP;
struct string_list existing_packs = STRING_LIST_INIT_DUP; struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP;
struct string_list existing_kept_packs = STRING_LIST_INIT_DUP;
struct pack_geometry *geometry = NULL; struct pack_geometry *geometry = NULL;
struct strbuf line = STRBUF_INIT; struct strbuf line = STRBUF_INIT;
struct tempfile *refs_snapshot = NULL;
int i, ext, ret; int i, ext, ret;
FILE *out; FILE *out;
int show_progress = isatty(2);
/* variables to be filled by option parsing */ /* variables to be filled by option parsing */
int pack_everything = 0; int pack_everything = 0;
@ -454,6 +621,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int no_update_server_info = 0; int no_update_server_info = 0;
struct pack_objects_args po_args = {NULL}; struct pack_objects_args po_args = {NULL};
int geometric_factor = 0; int geometric_factor = 0;
int write_midx = 0;
struct option builtin_repack_options[] = { struct option builtin_repack_options[] = {
OPT_BIT('a', NULL, &pack_everything, OPT_BIT('a', NULL, &pack_everything,
@ -496,6 +664,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
N_("do not repack this pack")), N_("do not repack this pack")),
OPT_INTEGER('g', "geometric", &geometric_factor, OPT_INTEGER('g', "geometric", &geometric_factor,
N_("find a geometric progression with factor <N>")), N_("find a geometric progression with factor <N>")),
OPT_BOOL('m', "write-midx", &write_midx,
N_("write a multi-pack index of the resulting packs")),
OPT_END() OPT_END()
}; };
@ -512,8 +682,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
die(_("--keep-unreachable and -A are incompatible")); die(_("--keep-unreachable and -A are incompatible"));
if (write_bitmaps < 0) { if (write_bitmaps < 0) {
if (!(pack_everything & ALL_INTO_ONE) || if (!write_midx &&
!is_bare_repository()) (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
write_bitmaps = 0; write_bitmaps = 0;
} else if (write_bitmaps && } else if (write_bitmaps &&
git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0) && git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0) &&
@ -523,9 +693,21 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (pack_kept_objects < 0) if (pack_kept_objects < 0)
pack_kept_objects = write_bitmaps > 0; pack_kept_objects = write_bitmaps > 0;
if (write_bitmaps && !(pack_everything & ALL_INTO_ONE)) if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
die(_(incremental_bitmap_conflict_error)); die(_(incremental_bitmap_conflict_error));
if (write_midx && write_bitmaps) {
struct strbuf path = STRBUF_INIT;
strbuf_addf(&path, "%s/%s_XXXXXX", get_object_directory(),
"bitmap-ref-tips");
refs_snapshot = xmks_tempfile(path.buf);
midx_snapshot_refs(refs_snapshot);
strbuf_release(&path);
}
if (geometric_factor) { if (geometric_factor) {
if (pack_everything) if (pack_everything)
die(_("--geometric is incompatible with -A, -a")); die(_("--geometric is incompatible with -A, -a"));
@ -565,19 +747,22 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
} }
if (has_promisor_remote()) if (has_promisor_remote())
strvec_push(&cmd.args, "--exclude-promisor-objects"); strvec_push(&cmd.args, "--exclude-promisor-objects");
if (!write_midx) {
if (write_bitmaps > 0) if (write_bitmaps > 0)
strvec_push(&cmd.args, "--write-bitmap-index"); strvec_push(&cmd.args, "--write-bitmap-index");
else if (write_bitmaps < 0) else if (write_bitmaps < 0)
strvec_push(&cmd.args, "--write-bitmap-index-quiet"); strvec_push(&cmd.args, "--write-bitmap-index-quiet");
}
if (use_delta_islands) if (use_delta_islands)
strvec_push(&cmd.args, "--delta-islands"); strvec_push(&cmd.args, "--delta-islands");
if (pack_everything & ALL_INTO_ONE) { collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs,
get_non_kept_pack_filenames(&existing_packs, &keep_pack_list); &keep_pack_list);
if (pack_everything & ALL_INTO_ONE) {
repack_promisor_objects(&po_args, &names); repack_promisor_objects(&po_args, &names);
if (existing_packs.nr && delete_redundant) { if (existing_nonkept_packs.nr && delete_redundant) {
for_each_string_list_item(item, &names) { for_each_string_list_item(item, &names) {
strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack", strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack",
packtmp_name, item->string); packtmp_name, item->string);
@ -677,19 +862,47 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
} }
/* End of pack replacement. */ /* End of pack replacement. */
reprepare_packed_git(the_repository); if (delete_redundant && pack_everything & ALL_INTO_ONE) {
if (delete_redundant) {
const int hexsz = the_hash_algo->hexsz; const int hexsz = the_hash_algo->hexsz;
int opts = 0;
string_list_sort(&names); string_list_sort(&names);
for_each_string_list_item(item, &existing_packs) { for_each_string_list_item(item, &existing_nonkept_packs) {
char *sha1; char *sha1;
size_t len = strlen(item->string); size_t len = strlen(item->string);
if (len < hexsz) if (len < hexsz)
continue; continue;
sha1 = item->string + len - hexsz; sha1 = item->string + len - hexsz;
if (!string_list_has_string(&names, sha1)) /*
* Mark this pack for deletion, which ensures that this
* pack won't be included in a MIDX (if `--write-midx`
* was given) and that we will actually delete this pack
* (if `-d` was given).
*/
item->util = (void*)(intptr_t)!string_list_has_string(&names, sha1);
}
}
if (write_midx) {
struct string_list include = STRING_LIST_INIT_NODUP;
midx_included_packs(&include, &existing_nonkept_packs,
&existing_kept_packs, &names, geometry);
ret = write_midx_included_packs(&include, geometry,
refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL,
show_progress, write_bitmaps > 0);
string_list_clear(&include, 0);
if (ret)
return ret;
}
reprepare_packed_git(the_repository);
if (delete_redundant) {
int opts = 0;
for_each_string_list_item(item, &existing_nonkept_packs) {
if (!item->util)
continue;
remove_redundant_pack(packdir, item->string); remove_redundant_pack(packdir, item->string);
} }
@ -711,7 +924,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
} }
strbuf_release(&buf); strbuf_release(&buf);
} }
if (!po_args.quiet && isatty(2)) if (!po_args.quiet && show_progress)
opts |= PRUNE_PACKED_VERBOSE; opts |= PRUNE_PACKED_VERBOSE;
prune_packed_objects(opts); prune_packed_objects(opts);
@ -730,12 +943,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
unsigned flags = 0; unsigned flags = 0;
if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP, 0)) if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP, 0))
flags |= MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX; flags |= MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX;
write_midx_file(get_object_directory(), NULL, flags); write_midx_file(get_object_directory(), NULL, NULL, flags);
} }
string_list_clear(&names, 0); string_list_clear(&names, 0);
string_list_clear(&rollback, 0); string_list_clear(&rollback, 0);
string_list_clear(&existing_packs, 0); string_list_clear(&existing_nonkept_packs, 0);
string_list_clear(&existing_kept_packs, 0);
clear_pack_geometry(geometry); clear_pack_geometry(geometry);
strbuf_release(&line); strbuf_release(&line);

98
midx.c
View File

@ -460,6 +460,8 @@ struct write_midx_context {
uint32_t num_large_offsets; uint32_t num_large_offsets;
int preferred_pack_idx; int preferred_pack_idx;
struct string_list *to_include;
}; };
static void add_pack_to_midx(const char *full_path, size_t full_path_len, static void add_pack_to_midx(const char *full_path, size_t full_path_len,
@ -469,8 +471,26 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len,
if (ends_with(file_name, ".idx")) { if (ends_with(file_name, ".idx")) {
display_progress(ctx->progress, ++ctx->pack_paths_checked); display_progress(ctx->progress, ++ctx->pack_paths_checked);
/*
* Note that at most one of ctx->m and ctx->to_include are set,
* so we are testing midx_contains_pack() and
* string_list_has_string() independently (guarded by the
* appropriate NULL checks).
*
* We could support passing to_include while reusing an existing
* MIDX, but don't currently since the reuse process drags
* forward all packs from an existing MIDX (without checking
* whether or not they appear in the to_include list).
*
* If we added support for that, these next two conditional
* should be performed independently (likely checking
* to_include before the existing MIDX).
*/
if (ctx->m && midx_contains_pack(ctx->m, file_name)) if (ctx->m && midx_contains_pack(ctx->m, file_name))
return; return;
else if (ctx->to_include &&
!string_list_has_string(ctx->to_include, file_name))
return;
ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc); ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
@ -948,7 +968,43 @@ static void bitmap_show_commit(struct commit *commit, void *_data)
data->commits[data->commits_nr++] = commit; data->commits[data->commits_nr++] = commit;
} }
static int read_refs_snapshot(const char *refs_snapshot,
struct rev_info *revs)
{
struct strbuf buf = STRBUF_INIT;
struct object_id oid;
FILE *f = xfopen(refs_snapshot, "r");
while (strbuf_getline(&buf, f) != EOF) {
struct object *object;
int preferred = 0;
char *hex = buf.buf;
const char *end = NULL;
if (buf.len && *buf.buf == '+') {
preferred = 1;
hex = &buf.buf[1];
}
if (parse_oid_hex(hex, &oid, &end) < 0)
die(_("could not parse line: %s"), buf.buf);
if (*end)
die(_("malformed line: %s"), buf.buf);
object = parse_object_or_die(&oid, NULL);
if (preferred)
object->flags |= NEEDS_BITMAP;
add_pending_object(revs, object, "");
}
fclose(f);
strbuf_release(&buf);
return 0;
}
static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p, static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p,
const char *refs_snapshot,
struct write_midx_context *ctx) struct write_midx_context *ctx)
{ {
struct rev_info revs; struct rev_info revs;
@ -957,8 +1013,12 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
cb.ctx = ctx; cb.ctx = ctx;
repo_init_revisions(the_repository, &revs, NULL); repo_init_revisions(the_repository, &revs, NULL);
if (refs_snapshot) {
read_refs_snapshot(refs_snapshot, &revs);
} else {
setup_revisions(0, NULL, &revs, NULL); setup_revisions(0, NULL, &revs, NULL);
for_each_ref(add_ref_to_pending, &revs); for_each_ref(add_ref_to_pending, &revs);
}
/* /*
* Skipping promisor objects here is intentional, since it only excludes * Skipping promisor objects here is intentional, since it only excludes
@ -987,6 +1047,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
static int write_midx_bitmap(char *midx_name, unsigned char *midx_hash, static int write_midx_bitmap(char *midx_name, unsigned char *midx_hash,
struct write_midx_context *ctx, struct write_midx_context *ctx,
const char *refs_snapshot,
unsigned flags) unsigned flags)
{ {
struct packing_data pdata; struct packing_data pdata;
@ -1002,7 +1063,7 @@ static int write_midx_bitmap(char *midx_name, unsigned char *midx_hash,
prepare_midx_packing_data(&pdata, ctx); prepare_midx_packing_data(&pdata, ctx);
commits = find_commits_for_midx_bitmap(&commits_nr, ctx); commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, ctx);
/* /*
* Build the MIDX-order index based on pdata.objects (which is already * Build the MIDX-order index based on pdata.objects (which is already
@ -1047,8 +1108,10 @@ cleanup:
} }
static int write_midx_internal(const char *object_dir, static int write_midx_internal(const char *object_dir,
struct string_list *packs_to_include,
struct string_list *packs_to_drop, struct string_list *packs_to_drop,
const char *preferred_pack_name, const char *preferred_pack_name,
const char *refs_snapshot,
unsigned flags) unsigned flags)
{ {
char *midx_name; char *midx_name;
@ -1071,12 +1134,19 @@ static int write_midx_internal(const char *object_dir,
die_errno(_("unable to create leading directories of %s"), die_errno(_("unable to create leading directories of %s"),
midx_name); midx_name);
if (!packs_to_include) {
/*
* Only reference an existing MIDX when not filtering which
* packs to include, since all packs and objects are copied
* blindly from an existing MIDX if one is present.
*/
for (cur = get_multi_pack_index(the_repository); cur; cur = cur->next) { for (cur = get_multi_pack_index(the_repository); cur; cur = cur->next) {
if (!strcmp(object_dir, cur->object_dir)) { if (!strcmp(object_dir, cur->object_dir)) {
ctx.m = cur; ctx.m = cur;
break; break;
} }
} }
}
if (ctx.m && !midx_checksum_valid(ctx.m)) { if (ctx.m && !midx_checksum_valid(ctx.m)) {
warning(_("ignoring existing multi-pack-index; checksum mismatch")); warning(_("ignoring existing multi-pack-index; checksum mismatch"));
@ -1125,10 +1195,13 @@ static int write_midx_internal(const char *object_dir,
else else
ctx.progress = NULL; ctx.progress = NULL;
ctx.to_include = packs_to_include;
for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx); for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx);
stop_progress(&ctx.progress); stop_progress(&ctx.progress);
if (ctx.m && ctx.nr == ctx.m->num_packs && !packs_to_drop) { if ((ctx.m && ctx.nr == ctx.m->num_packs) &&
!(packs_to_include || packs_to_drop)) {
struct bitmap_index *bitmap_git; struct bitmap_index *bitmap_git;
int bitmap_exists; int bitmap_exists;
int want_bitmap = flags & MIDX_WRITE_BITMAP; int want_bitmap = flags & MIDX_WRITE_BITMAP;
@ -1332,7 +1405,8 @@ static int write_midx_internal(const char *object_dir,
if (flags & MIDX_WRITE_REV_INDEX) if (flags & MIDX_WRITE_REV_INDEX)
write_midx_reverse_index(midx_name, midx_hash, &ctx); write_midx_reverse_index(midx_name, midx_hash, &ctx);
if (flags & MIDX_WRITE_BITMAP) { if (flags & MIDX_WRITE_BITMAP) {
if (write_midx_bitmap(midx_name, midx_hash, &ctx, flags) < 0) { if (write_midx_bitmap(midx_name, midx_hash, &ctx,
refs_snapshot, flags) < 0) {
error(_("could not write multi-pack bitmap")); error(_("could not write multi-pack bitmap"));
result = 1; result = 1;
goto cleanup; goto cleanup;
@ -1367,9 +1441,21 @@ cleanup:
int write_midx_file(const char *object_dir, int write_midx_file(const char *object_dir,
const char *preferred_pack_name, const char *preferred_pack_name,
const char *refs_snapshot,
unsigned flags) unsigned flags)
{ {
return write_midx_internal(object_dir, NULL, preferred_pack_name, flags); return write_midx_internal(object_dir, NULL, NULL, preferred_pack_name,
refs_snapshot, flags);
}
int write_midx_file_only(const char *object_dir,
struct string_list *packs_to_include,
const char *preferred_pack_name,
const char *refs_snapshot,
unsigned flags)
{
return write_midx_internal(object_dir, packs_to_include, NULL,
preferred_pack_name, refs_snapshot, flags);
} }
struct clear_midx_data { struct clear_midx_data {
@ -1649,7 +1735,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla
free(count); free(count);
if (packs_to_drop.nr) { if (packs_to_drop.nr) {
result = write_midx_internal(object_dir, &packs_to_drop, NULL, flags); result = write_midx_internal(object_dir, NULL, &packs_to_drop, NULL, NULL, flags);
m = NULL; m = NULL;
} }
@ -1840,7 +1926,7 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
goto cleanup; goto cleanup;
} }
result = write_midx_internal(object_dir, NULL, NULL, flags); result = write_midx_internal(object_dir, NULL, NULL, NULL, NULL, flags);
m = NULL; m = NULL;
cleanup: cleanup:

15
midx.h
View File

@ -2,6 +2,7 @@
#define MIDX_H #define MIDX_H
#include "repository.h" #include "repository.h"
#include "string-list.h"
struct object_id; struct object_id;
struct pack_entry; struct pack_entry;
@ -62,7 +63,19 @@ int fill_midx_entry(struct repository *r, const struct object_id *oid, struct pa
int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name); int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name);
int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local); int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local);
int write_midx_file(const char *object_dir, const char *preferred_pack_name, unsigned flags); /*
* Variant of write_midx_file which writes a MIDX containing only the packs
* specified in packs_to_include.
*/
int write_midx_file(const char *object_dir,
const char *preferred_pack_name,
const char *refs_snapshot,
unsigned flags);
int write_midx_file_only(const char *object_dir,
struct string_list *packs_to_include,
const char *preferred_pack_name,
const char *refs_snapshot,
unsigned flags);
void clear_midx_file(struct repository *r); void clear_midx_file(struct repository *r);
int verify_midx_file(struct repository *r, const char *object_dir, unsigned flags); int verify_midx_file(struct repository *r, const char *object_dir, unsigned flags);
int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags); int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags);

View File

@ -1418,7 +1418,7 @@ static int try_partial_reuse(struct packed_git *pack,
return 0; return 0;
} }
static uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git) uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git)
{ {
struct multi_pack_index *m = bitmap_git->midx; struct multi_pack_index *m = bitmap_git->midx;
if (!m) if (!m)

View File

@ -56,6 +56,7 @@ int test_bitmap_hashes(struct repository *r);
struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs, struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
struct list_objects_filter_options *filter, struct list_objects_filter_options *filter,
int filter_provided_objects); int filter_provided_objects);
uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git);
int reuse_partial_packfile_from_bitmap(struct bitmap_index *, int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
struct packed_git **packfile, struct packed_git **packfile,
uint32_t *entries, uint32_t *entries,

View File

@ -3,6 +3,7 @@
#include "midx.h" #include "midx.h"
#include "repository.h" #include "repository.h"
#include "object-store.h" #include "object-store.h"
#include "pack-bitmap.h"
static int read_midx_file(const char *object_dir, int show_objects) static int read_midx_file(const char *object_dir, int show_objects)
{ {
@ -72,14 +73,40 @@ static int read_midx_checksum(const char *object_dir)
return 0; return 0;
} }
static int read_midx_preferred_pack(const char *object_dir)
{
struct multi_pack_index *midx = NULL;
struct bitmap_index *bitmap = NULL;
setup_git_directory();
midx = load_multi_pack_index(object_dir, 1);
if (!midx)
return 1;
bitmap = prepare_bitmap_git(the_repository);
if (!bitmap)
return 1;
if (!bitmap_is_midx(bitmap)) {
free_bitmap_index(bitmap);
return 1;
}
printf("%s\n", midx->pack_names[midx_preferred_pack(bitmap)]);
free_bitmap_index(bitmap);
return 0;
}
int cmd__read_midx(int argc, const char **argv) int cmd__read_midx(int argc, const char **argv)
{ {
if (!(argc == 2 || argc == 3)) if (!(argc == 2 || argc == 3))
usage("read-midx [--show-objects|--checksum] <object-dir>"); usage("read-midx [--show-objects|--checksum|--preferred-pack] <object-dir>");
if (!strcmp(argv[1], "--show-objects")) if (!strcmp(argv[1], "--show-objects"))
return read_midx_file(argv[2], 1); return read_midx_file(argv[2], 1);
else if (!strcmp(argv[1], "--checksum")) else if (!strcmp(argv[1], "--checksum"))
return read_midx_checksum(argv[2]); return read_midx_checksum(argv[2]);
else if (!strcmp(argv[1], "--preferred-pack"))
return read_midx_preferred_pack(argv[2]);
return read_midx_file(argv[1], 0); return read_midx_file(argv[1], 0);
} }

8
t/lib-midx.sh Normal file
View File

@ -0,0 +1,8 @@
# test_midx_consistent <objdir>
test_midx_consistent () {
ls $1/pack/pack-*.idx | xargs -n 1 basename | sort >expect &&
test-tool read-midx $1 | grep ^pack-.*\.idx$ | sort >actual &&
test_cmp expect actual &&
git multi-pack-index --object-dir=$1 verify
}

View File

@ -168,6 +168,21 @@ test_expect_success 'write midx with two packs' '
compare_results_with_midx "two packs" compare_results_with_midx "two packs"
test_expect_success 'write midx with --stdin-packs' '
rm -fr $objdir/pack/multi-pack-index &&
idx="$(find $objdir/pack -name "test-2-*.idx")" &&
basename "$idx" >in &&
git multi-pack-index write --stdin-packs <in &&
test-tool read-midx $objdir | grep "\.idx$" >packs &&
test_cmp packs in
'
compare_results_with_midx "mixed mode (one pack + extra)"
test_expect_success 'write progress off for redirected stderr' ' test_expect_success 'write progress off for redirected stderr' '
git multi-pack-index --object-dir=$objdir write 2>err && git multi-pack-index --object-dir=$objdir write 2>err &&
test_line_count = 0 err test_line_count = 0 err

View File

@ -283,6 +283,88 @@ test_expect_success 'pack.preferBitmapTips' '
) )
' '
test_expect_success 'writing a bitmap with --refs-snapshot' '
git init repo &&
test_when_finished "rm -fr repo" &&
(
cd repo &&
test_commit one &&
test_commit two &&
git rev-parse one >snapshot &&
git repack -ad &&
# First, write a MIDX which see both refs/tags/one and
# refs/tags/two (causing both of those commits to receive
# bitmaps).
git multi-pack-index write --bitmap &&
test_path_is_file $midx &&
test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
test-tool bitmap list-commits | sort >bitmaps &&
grep "$(git rev-parse one)" bitmaps &&
grep "$(git rev-parse two)" bitmaps &&
rm -fr $midx-$(midx_checksum $objdir).bitmap &&
rm -fr $midx-$(midx_checksum $objdir).rev &&
rm -fr $midx &&
# Then again, but with a refs snapshot which only sees
# refs/tags/one.
git multi-pack-index write --bitmap --refs-snapshot=snapshot &&
test_path_is_file $midx &&
test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
test-tool bitmap list-commits | sort >bitmaps &&
grep "$(git rev-parse one)" bitmaps &&
! grep "$(git rev-parse two)" bitmaps
)
'
test_expect_success 'write a bitmap with --refs-snapshot (preferred tips)' '
git init repo &&
test_when_finished "rm -fr repo" &&
(
cd repo &&
test_commit_bulk --message="%s" 103 &&
git log --format="%H" >commits.raw &&
sort <commits.raw >commits &&
git log --format="create refs/tags/%s %H" HEAD >refs &&
git update-ref --stdin <refs &&
git multi-pack-index write --bitmap &&
test_path_is_file $midx &&
test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
test-tool bitmap list-commits | sort >bitmaps &&
comm -13 bitmaps commits >before &&
test_line_count = 1 before &&
(
grep -vf before commits.raw &&
# mark missing commits as preferred
sed "s/^/+/" before
) >snapshot &&
rm -fr $midx-$(midx_checksum $objdir).bitmap &&
rm -fr $midx-$(midx_checksum $objdir).rev &&
rm -fr $midx &&
git multi-pack-index write --bitmap --refs-snapshot=snapshot &&
test-tool bitmap list-commits | sort >bitmaps &&
comm -13 bitmaps commits >after &&
! test_cmp before after
)
'
test_expect_success 'hash-cache values are propagated from pack bitmaps' ' test_expect_success 'hash-cache values are propagated from pack bitmaps' '
rm -fr repo && rm -fr repo &&
git init repo && git init repo &&

View File

@ -3,6 +3,8 @@
test_description='git repack works correctly' test_description='git repack works correctly'
. ./test-lib.sh . ./test-lib.sh
. "${TEST_DIRECTORY}/lib-bitmap.sh"
. "${TEST_DIRECTORY}/lib-midx.sh"
commit_and_pack () { commit_and_pack () {
test_commit "$@" 1>&2 && test_commit "$@" 1>&2 &&
@ -234,4 +236,140 @@ test_expect_success 'auto-bitmaps do not complain if unavailable' '
test_must_be_empty actual test_must_be_empty actual
' '
objdir=.git/objects
midx=$objdir/pack/multi-pack-index
test_expect_success 'setup for --write-midx tests' '
git init midx &&
(
cd midx &&
git config core.multiPackIndex true &&
test_commit base
)
'
test_expect_success '--write-midx unchanged' '
(
cd midx &&
GIT_TEST_MULTI_PACK_INDEX=0 git repack &&
test_path_is_missing $midx &&
test_path_is_missing $midx-*.bitmap &&
GIT_TEST_MULTI_PACK_INDEX=0 git repack --write-midx &&
test_path_is_file $midx &&
test_path_is_missing $midx-*.bitmap &&
test_midx_consistent $objdir
)
'
test_expect_success '--write-midx with a new pack' '
(
cd midx &&
test_commit loose &&
GIT_TEST_MULTI_PACK_INDEX=0 git repack --write-midx &&
test_path_is_file $midx &&
test_path_is_missing $midx-*.bitmap &&
test_midx_consistent $objdir
)
'
test_expect_success '--write-midx with -b' '
(
cd midx &&
GIT_TEST_MULTI_PACK_INDEX=0 git repack -mb &&
test_path_is_file $midx &&
test_path_is_file $midx-*.bitmap &&
test_midx_consistent $objdir
)
'
test_expect_success '--write-midx with -d' '
(
cd midx &&
test_commit repack &&
GIT_TEST_MULTI_PACK_INDEX=0 git repack -Ad --write-midx &&
test_path_is_file $midx &&
test_path_is_missing $midx-*.bitmap &&
test_midx_consistent $objdir
)
'
test_expect_success 'cleans up MIDX when appropriate' '
(
cd midx &&
test_commit repack-2 &&
GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adb --write-midx &&
checksum=$(midx_checksum $objdir) &&
test_path_is_file $midx &&
test_path_is_file $midx-$checksum.bitmap &&
test_path_is_file $midx-$checksum.rev &&
test_commit repack-3 &&
GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adb --write-midx &&
test_path_is_file $midx &&
test_path_is_missing $midx-$checksum.bitmap &&
test_path_is_missing $midx-$checksum.rev &&
test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
test_path_is_file $midx-$(midx_checksum $objdir).rev &&
test_commit repack-4 &&
GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adb &&
find $objdir/pack -type f -name "multi-pack-index*" >files &&
test_must_be_empty files
)
'
test_expect_success '--write-midx with preferred bitmap tips' '
git init midx-preferred-tips &&
test_when_finished "rm -fr midx-preferred-tips" &&
(
cd midx-preferred-tips &&
test_commit_bulk --message="%s" 103 &&
git log --format="%H" >commits.raw &&
sort <commits.raw >commits &&
git log --format="create refs/tags/%s/%s %H" HEAD >refs &&
git update-ref --stdin <refs &&
git repack --write-midx --write-bitmap-index &&
test_path_is_file $midx &&
test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
test-tool bitmap list-commits | sort >bitmaps &&
comm -13 bitmaps commits >before &&
test_line_count = 1 before &&
rm -fr $midx-$(midx_checksum $objdir).bitmap &&
rm -fr $midx-$(midx_checksum $objdir).rev &&
rm -fr $midx &&
# instead of constructing the snapshot ourselves (c.f., the test
# "write a bitmap with --refs-snapshot (preferred tips)" in
# t5326), mark the missing commit as preferred by adding it to
# the pack.preferBitmapTips configuration.
git for-each-ref --format="%(refname:rstrip=1)" \
--points-at="$(cat before)" >missing &&
git config pack.preferBitmapTips "$(cat missing)" &&
git repack --write-midx --write-bitmap-index &&
test-tool bitmap list-commits | sort >bitmaps &&
comm -13 bitmaps commits >after &&
! test_cmp before after
)
'
test_done test_done

View File

@ -15,7 +15,7 @@ test_expect_success '--geometric with no packs' '
( (
cd geometric && cd geometric &&
git repack --geometric 2 >out && git repack --write-midx --geometric 2 >out &&
test_i18ngrep "Nothing new to pack" out test_i18ngrep "Nothing new to pack" out
) )
' '
@ -180,4 +180,26 @@ test_expect_success '--geometric ignores kept packs' '
) )
' '
test_expect_success '--geometric chooses largest MIDX preferred pack' '
git init geometric &&
test_when_finished "rm -fr geometric" &&
(
cd geometric &&
# These packs already form a geometric progression.
test_commit_bulk --start=1 1 && # 3 objects
test_commit_bulk --start=2 2 && # 6 objects
ls $objdir/pack/pack-*.idx >before &&
test_commit_bulk --start=4 4 && # 12 objects
ls $objdir/pack/pack-*.idx >after &&
git repack --geometric 2 -dbm &&
comm -3 before after | xargs -n 1 basename >expect &&
test-tool read-midx --preferred-pack $objdir >actual &&
test_cmp expect actual
)
'
test_done test_done