git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3 5/8] repack: add `--filter=<filter-spec>` option

From
Taylor Blau <me@ttaylorr.com>
Date
Jul 25, 2023, 23:04 UTC
Message-ID
<ZMBU+SALVQthOgC7@nand.local>
In-Reply-To
<20230724085909.3831831-6-christian.couder@gmail.com>
On Mon, Jul 24, 2023 at 10:59:06AM +0200, Christian Couder wrote:
Show 12 quoted lines
> This feature is implemented by running `git pack-objects` twice in a
> row. The first command is run with `--filter=<filter-spec>`, using the
> specified filter. It packs objects while omitting the objects specified
> by the filter. Then another `git pack-objects` command is launched using
> `--stdin-packs`. We pass it all the previously existing packs into its
> stdin, so that it will pack all the objects in the previously existing
> packs. But we also pass into its stdin, the pack created by the previous
> `git pack-objects --filter=<filter-spec>` command as well as the kept
> packs, all prefixed with '^', so that the objects in these packs will be
> omitted from the resulting pack. The result is that only the objects
> filtered out by the first `git pack-objects` command are in the pack
> resulting from the second `git pack-objects` command.

Very nice. I appreciate you taking my suggestion here; I'm hopeful that it simplified things and resulted in fewer new lines of code.

Show 85 quoted lines
> Signed-off-by: John Cai <johncai86@gmail.com>
> Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
> ---
>  Documentation/git-repack.txt | 12 +++++++
>  builtin/repack.c             | 67 ++++++++++++++++++++++++++++++++++++
>  t/t7700-repack.sh            | 24 +++++++++++++
>  3 files changed, 103 insertions(+)
>
> diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
> index 4017157949..6d5bec7716 100644
> --- a/Documentation/git-repack.txt
> +++ b/Documentation/git-repack.txt
> @@ -143,6 +143,18 @@ depth is 4095.
>  	a larger and slower repository; see the discussion in
>  	`pack.packSizeLimit`.
>
> +--filter=<filter-spec>::
> +	Remove objects matching the filter specification from the
> +	resulting packfile and put them into a separate packfile. Note
> +	that objects used in the working directory are not filtered
> +	out. So for the split to fully work, it's best to perform it
> +	in a bare repo and to use the `-a` and `-d` options along with
> +	this option.  Also `--no-write-bitmap-index` (or the
> +	`repack.writebitmaps` config option set to `false`) should be
> +	used otherwise writing bitmap index will fail, as it supposes
> +	a single packfile containing all the objects. See
> +	linkgit:git-rev-list[1] for valid `<filter-spec>` forms.
> +
>  -b::
>  --write-bitmap-index::
>  	Write a reachability bitmap index as part of the repack. This
> diff --git a/builtin/repack.c b/builtin/repack.c
> index 21e3b89f27..2c81b7738e 100644
> --- a/builtin/repack.c
> +++ b/builtin/repack.c
> @@ -53,6 +53,7 @@ struct pack_objects_args {
>  	const char *depth;
>  	const char *threads;
>  	const char *max_pack_size;
> +	const char *filter;
>  	int no_reuse_delta;
>  	int no_reuse_object;
>  	int quiet;
> @@ -166,6 +167,8 @@ static void prepare_pack_objects(struct child_process *cmd,
>  		strvec_pushf(&cmd->args, "--threads=%s", args->threads);
>  	if (args->max_pack_size)
>  		strvec_pushf(&cmd->args, "--max-pack-size=%s", args->max_pack_size);
> +	if (args->filter)
> +		strvec_pushf(&cmd->args, "--filter=%s", args->filter);
>  	if (args->no_reuse_delta)
>  		strvec_pushf(&cmd->args, "--no-reuse-delta");
>  	if (args->no_reuse_object)
> @@ -726,6 +729,57 @@ static int finish_pack_objects_cmd(struct child_process *cmd,
>  	return finish_command(cmd);
>  }
>
> +static int write_filtered_pack(const struct pack_objects_args *args,
> +			       const char *destination,
> +			       const char *pack_prefix,
> +			       struct string_list *names,
> +			       struct string_list *existing_packs,
> +			       struct string_list *existing_kept_packs)
> +{
> +	struct child_process cmd = CHILD_PROCESS_INIT;
> +	struct string_list_item *item;
> +	FILE *in;
> +	int ret;
> +	const char *scratch;
> +	int local = skip_prefix(destination, packdir, &scratch);
> +
> +	/* We need to copy 'args' to modify it */
> +	struct pack_objects_args new_args = *args;
> +
> +	/* No need to filter again */
> +	new_args.filter = NULL;
> +
> +	prepare_pack_objects(&cmd, &new_args, destination);
> +
> +	strvec_push(&cmd.args, "--stdin-packs");
> +
> +	cmd.in = -1;
> +
> +	ret = start_command(&cmd);
> +	if (ret)
> +		return ret;
Show 9 quoted lines
> +	/*
> +	 * names has a confusing double use: it both provides the list
> +	 * of just-written new packs, and accepts the name of the
> +	 * filtered pack we are writing.
> +	 *
> +	 * By the time it is read here, it contains only the pack(s)
> +	 * that were just written, which is exactly the set of packs we
> +	 * want to consider kept.
> +	 */

I think that this comment partially comes from the cruft pack code, where we use the `names` string list both to reference existing packs at the start of the repack, and to keep track of the pack we just wrote (to exclude its contents from the cruft pack).

But I think we only write into "names" via finish_pack_objects_cmd() to record the name of the pack we just wrote containing objects which didn't meet the filter's conditions.

So I think that leaving this comment in is OK, but TBH I was on the fence when I wrote that back in f9825d1cf75 (builtin/repack.c: support generating a cruft pack, 2022-05-20), so I would just as soon drop it.

Show 5 quoted lines
> +	in = xfdopen(cmd.in, "w");
> +	for_each_string_list_item(item, names)
> +		fprintf(in, "^%s-%s.pack\n", pack_prefix, item->string);
> +	for_each_string_list_item(item, existing_packs)
> +		fprintf(in, "%s.pack\n", item->string);
> +	for_each_string_list_item(item, existing_kept_packs)
> +		fprintf(in, "^%s.pack\n", item->string);

I think we may only want to do this if `honor_pack_keep` is zero. Otherwise we'd avoid packing objects that appear in kept packs, even if the caller told us to include objects found in kept packs.

Show 14 quoted lines
> +	fclose(in);
> +
> +	return finish_pack_objects_cmd(&cmd, names, local);
> +}
> +
>  static int write_cruft_pack(const struct pack_objects_args *args,
>  			    const char *destination,
>  			    const char *pack_prefix,
> @@ -858,6 +912,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
>  				N_("limits the maximum number of threads")),
>  		OPT_STRING(0, "max-pack-size", &po_args.max_pack_size, N_("bytes"),
>  				N_("maximum size of each packfile")),
> +		OPT_STRING(0, "filter", &po_args.filter, N_("args"),
> +				N_("object filtering")),

I suppose we're storing the filter as a string here because we're just going to pass it down to pack-objects directly. That part makes sense, but I think we are producing subtly inconsistent behavior when specifying multiple --filter options.

IIRC, passing --filter more than once down to pack-objects produces a filter whose objects match all of the individually specified sub-filters. But IIUC, using OPT_STRING here means that later `--filter`'s override earlier ones.

So I think at minimum we'd want to store the filter arguments in a strvec. But I would probably just as soon parse them into a bona-fide list_objects_filter_options struct, and then reconstruct the arguments to pack-objects based on that.

Show 11 quoted lines
> diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
> index 27b66807cd..0a2c73bca7 100755
> --- a/t/t7700-repack.sh
> +++ b/t/t7700-repack.sh
> @@ -327,6 +327,30 @@ test_expect_success 'auto-bitmaps do not complain if unavailable' '
>  	test_must_be_empty actual
>  '
>
> +test_expect_success 'repacking with a filter works' '
> +	git -C bare.git repack -a -d &&
> +	test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&

Huh! I never knew about the test_stdout_line_count function, I thought that we always just had test_line_count. Neat!

> +	git -C bare.git -c repack.writebitmaps=false repack -a -d --filter=blob:none &&
> +	test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack &&
> +	commit_pack=$(test-tool -C bare.git find-pack HEAD) &&
> +	test -n "$commit_pack" &&

I wonder if the test-tool itself should exit with a non-zero code if it can't find the given object in any pack. It would at least allow us to drop the "test -n $foo" after every invocation of the test-helper in this test.

Arguably callers may want to ensure that an object doesn't exist in any pack, and this would be inconvenient for them, since they'd have to write something like:

    test_must_fail test-tool find-pack $obj
but I think a more direct test like
    test_must_fail git cat-file -t $obj
would do just as well.
Show 8 quoted lines
> +	blob_pack=$(test-tool -C bare.git find-pack HEAD:file1) &&
> +	test -n "$blob_pack" &&
> +	test "$commit_pack" != "$blob_pack" &&
> +	tree_pack=$(test-tool -C bare.git find-pack HEAD^{tree}) &&
> +	test "$tree_pack" = "$commit_pack" &&
> +	blob_pack2=$(test-tool -C bare.git find-pack HEAD:file2) &&
> +	test "$blob_pack2" = "$blob_pack"
> +'

This all looks good, but I think there are a couple of more things that we'd want to test for here:

  - That the list of all objects appears the same before and after all
    of the repacking. I think that this is tested implicitly already in
    your test, but having it written down explicitly would harden this
    against regressions that cause us to inadvertently delete an object
    we shouldn't have.
    (FWIW, I think this would be limited to running something like "git
    cat-file --batch-check='%(objectname)' --batch-all-objects" before
    and after all of the repacking, and ensuring that the two test_cmp
    without failure).
  - Another thing that I don't think we're testing here is that objects
    that *don't* match the filter don't appear in one of the filtered
    packs. I think we'd probably want to assert on the exact contents of
    the pack by dumping the list of objects into a file like "expect",
    and then dumping the actual set of objects with "git show-index
    <$idx | cut -d' ' -f2" or something.

Another thought from the OPT_STRING business above is that we probably want to test this with non-trivial filter arguments. There are probably a handful of interesting cases here, like passing `--no-filter`, passing `--filter` multiple times, passing invalid values for `--filter`, etc.

> +test_expect_success '--filter fails with --write-bitmap-index' '
> +	test_must_fail git -C bare.git repack -a -d --write-bitmap-index \
> +		--filter=blob:none &&

Do we want to ensure that we get the exit code corresponding with showing the usage text? I could go either way, but I do think that we should grep through the output on stderr to ensure that we get the appropriate error message.

> +	git -C bare.git repack -a -d --no-write-bitmap-index \
> +		--filter=blob:none

I don't think that this test is adding anything that the above "repacking with a filter works" test isn't covering already.

Thanks, Taylor

Previous: Christian CouderNext: Christian Couder
Message 87 of 161 in “Repack objects into separate packfiles based on a filter”
  1. 0/9 Repack objects into separate packfiles based on a filterChristian Couder, Jun 14, 2023
  2. 1/9 pack-objects: allow `--filter` without `--stdout`Christian Couder, Jun 14, 2023
  3. Taylor BlauJun 21, 2023
  4. Christian CouderJul 5, 2023
  5. 2/9 pack-objects: add `--print-filtered` to print omitted objectsChristian Couder, Jun 14, 2023
  6. Junio C HamanoJun 15, 2023
  7. Taylor BlauJun 21, 2023
  8. Christian CouderJun 21, 2023
  9. Taylor BlauJun 21, 2023
  10. 3/9 t/helper: add 'find-pack' test-toolChristian Couder, Jun 14, 2023
  11. Junio C HamanoJun 15, 2023
  12. Christian CouderJun 21, 2023
  13. Taylor BlauJun 21, 2023
  14. 5/9 repack: refactor finishing pack-objects commandChristian Couder, Jun 14, 2023
  15. Junio C HamanoJun 16, 2023
  16. Taylor BlauJun 21, 2023
  17. Christian CouderJun 21, 2023
  18. Taylor BlauJun 21, 2023
  19. 4/9 repack: refactor piping an oid to a commandChristian Couder, Jun 14, 2023
  20. Junio C HamanoJun 15, 2023
  21. Taylor BlauJun 21, 2023
  22. Christian CouderJun 21, 2023
  23. 6/9 repack: add `--filter=<filter-spec>` optionChristian Couder, Jun 14, 2023
  24. Junio C HamanoJun 16, 2023
  25. Taylor BlauJun 21, 2023
  26. Christian CouderJun 21, 2023
  27. Taylor BlauJun 22, 2023
  28. Christian CouderJun 21, 2023
  29. Junio C HamanoJun 21, 2023
  30. Christian CouderJun 22, 2023
  31. Junio C HamanoJun 22, 2023
  32. Taylor BlauJun 21, 2023
  33. Christian CouderJul 5, 2023
  34. 8/9 repack: implement `--filter-to` for storing filtered out objectsChristian Couder, Jun 14, 2023
  35. Junio C HamanoJun 16, 2023
  36. Taylor BlauJun 21, 2023
  37. Christian CouderJun 21, 2023
  38. Taylor BlauJun 21, 2023
  39. Junio C HamanoJun 21, 2023
  40. Christian CouderJul 5, 2023
  41. 7/9 gc: add `gc.repackFilter` config optionChristian Couder, Jun 14, 2023
  42. 9/9 gc: add `gc.repackFilterTo` config optionChristian Couder, Jun 14, 2023
  43. Junio C HamanoJun 16, 2023
  44. Junio C HamanoJun 14, 2023
  45. Junio C HamanoJun 16, 2023
  46. 0/8 Repack objects into separate packfiles based on a filterChristian Couder, Jul 5, 2023
  47. 1/8 pack-objects: allow `--filter` without `--stdout`Christian Couder, Jul 5, 2023
  48. 2/8 t/helper: add 'find-pack' test-toolChristian Couder, Jul 5, 2023
  49. 3/8 repack: refactor finishing pack-objects commandChristian Couder, Jul 5, 2023
  50. 4/8 repack: refactor finding pack prefixChristian Couder, Jul 5, 2023
  51. 5/8 repack: add `--filter=<filter-spec>` optionChristian Couder, Jul 5, 2023
  52. Junio C HamanoJul 5, 2023
  53. Christian CouderJul 24, 2023
  54. Junio C HamanoJul 24, 2023
  55. Christian CouderJul 25, 2023
  56. Junio C HamanoJul 25, 2023
  57. Junio C HamanoJul 25, 2023
  58. Christian CouderAug 8, 2023
  59. Taylor BlauAug 9, 2023
  60. Junio C HamanoAug 9, 2023
  61. Junio C HamanoAug 9, 2023
  62. Jeff KingAug 10, 2023
  63. Junio C HamanoJul 5, 2023
  64. Christian CouderJul 24, 2023
  65. 6/8 gc: add `gc.repackFilter` config optionChristian Couder, Jul 5, 2023
  66. 8/8 gc: add `gc.repackFilterTo` config optionChristian Couder, Jul 5, 2023
  67. 7/8 repack: implement `--filter-to` for storing filtered out objectsChristian Couder, Jul 5, 2023
  68. Junio C HamanoJul 5, 2023
  69. Christian CouderJul 24, 2023
  70. Junio C HamanoJul 24, 2023
  71. Robert CoupJul 25, 2023
  72. Junio C HamanoJul 25, 2023
  73. Christian CouderJul 25, 2023
  74. 0/8 Repack objects into separate packfiles based on a filterChristian Couder, Jul 24, 2023
  75. 1/8 pack-objects: allow `--filter` without `--stdout`Christian Couder, Jul 24, 2023
  76. Taylor BlauJul 25, 2023
  77. Junio C HamanoJul 25, 2023
  78. 3/8 repack: refactor finishing pack-objects commandChristian Couder, Jul 24, 2023
  79. Taylor BlauJul 25, 2023
  80. 4/8 repack: refactor finding pack prefixChristian Couder, Jul 24, 2023
  81. Taylor BlauJul 25, 2023
  82. Christian CouderAug 8, 2023
  83. 2/8 t/helper: add 'find-pack' test-toolChristian Couder, Jul 24, 2023
  84. Taylor BlauJul 25, 2023
  85. Christian CouderAug 8, 2023
  86. 5/8 repack: add `--filter=<filter-spec>` optionChristian Couder, Jul 24, 2023
  87. Taylor BlauJul 25, 2023
  88. Christian CouderAug 8, 2023
  89. Taylor BlauAug 9, 2023
  90. 6/8 gc: add `gc.repackFilter` config optionChristian Couder, Jul 24, 2023
  91. Taylor BlauJul 25, 2023
  92. Christian CouderAug 8, 2023
  93. Taylor BlauAug 9, 2023
  94. 8/8 gc: add `gc.repackFilterTo` config optionChristian Couder, Jul 24, 2023
  95. 7/8 repack: implement `--filter-to` for storing filtered out objectsChristian Couder, Jul 24, 2023
  96. Taylor BlauJul 25, 2023
  97. 0/8 Repack objects into separate packfiles based on a filterChristian Couder, Aug 8, 2023
  98. 7/8 repack: implement `--filter-to` for storing filtered out objectsChristian Couder, Aug 8, 2023
  99. 5/8 repack: add `--filter=<filter-spec>` optionChristian Couder, Aug 8, 2023
  100. Taylor BlauAug 9, 2023
  101. 4/8 repack: refactor finding pack prefixChristian Couder, Aug 8, 2023
  102. Taylor BlauAug 9, 2023
  103. 3/8 repack: refactor finishing pack-objects commandChristian Couder, Aug 8, 2023
  104. 6/8 gc: add `gc.repackFilter` config optionChristian Couder, Aug 8, 2023
  105. 8/8 gc: add `gc.repackFilterTo` config optionChristian Couder, Aug 8, 2023
  106. 1/8 pack-objects: allow `--filter` without `--stdout`Christian Couder, Aug 8, 2023
  107. 2/8 t/helper: add 'find-pack' test-toolChristian Couder, Aug 8, 2023
  108. Taylor BlauAug 9, 2023
  109. Taylor BlauAug 9, 2023
  110. Junio C HamanoAug 9, 2023
  111. Christian CouderAug 12, 2023
  112. 0/8 Repack objects into separate packfiles based on a filterChristian Couder, Aug 12, 2023
  113. 1/8 pack-objects: allow `--filter` without `--stdout`Christian Couder, Aug 12, 2023
  114. 2/8 t/helper: add 'find-pack' test-toolChristian Couder, Aug 12, 2023
  115. 3/8 repack: refactor finishing pack-objects commandChristian Couder, Aug 12, 2023
  116. 4/8 repack: refactor finding pack prefixChristian Couder, Aug 12, 2023
  117. 5/8 repack: add `--filter=<filter-spec>` optionChristian Couder, Aug 12, 2023
  118. 6/8 gc: add `gc.repackFilter` config optionChristian Couder, Aug 12, 2023
  119. 7/8 repack: implement `--filter-to` for storing filtered out objectsChristian Couder, Aug 12, 2023
  120. 8/8 gc: add `gc.repackFilterTo` config optionChristian Couder, Aug 12, 2023
  121. Junio C HamanoAug 15, 2023
  122. Taylor BlauAug 15, 2023
  123. Junio C HamanoAug 15, 2023
  124. Taylor BlauAug 15, 2023
  125. Junio C HamanoAug 15, 2023
  126. Taylor BlauAug 16, 2023
  127. Junio C HamanoAug 16, 2023
  128. Christian CouderSep 11, 2023
  129. 0/9 Repack objects into separate packfiles based on a filterChristian Couder, Sep 11, 2023
  130. 4/9 repack: refactor finding pack prefixChristian Couder, Sep 11, 2023
  131. 5/9 pack-bitmap-write: rebuild using new bitmap when remappingChristian Couder, Sep 11, 2023
  132. 2/9 t/helper: add 'find-pack' test-toolChristian Couder, Sep 11, 2023
  133. 6/9 repack: add `--filter=<filter-spec>` optionChristian Couder, Sep 11, 2023
  134. 1/9 pack-objects: allow `--filter` without `--stdout`Christian Couder, Sep 11, 2023
  135. 8/9 repack: implement `--filter-to` for storing filtered out objectsChristian Couder, Sep 11, 2023
  136. 7/9 gc: add `gc.repackFilter` config optionChristian Couder, Sep 11, 2023
  137. 3/9 repack: refactor finishing pack-objects commandChristian Couder, Sep 11, 2023
  138. 9/9 gc: add `gc.repackFilterTo` config optionChristian Couder, Sep 11, 2023
  139. 0/9 Repack objects into separate packfiles based on a filterChristian Couder, Sep 25, 2023
  140. 1/9 pack-objects: allow `--filter` without `--stdout`Christian Couder, Sep 25, 2023
  141. 2/9 t/helper: add 'find-pack' test-toolChristian Couder, Sep 25, 2023
  142. 3/9 repack: refactor finishing pack-objects commandChristian Couder, Sep 25, 2023
  143. 4/9 repack: refactor finding pack prefixChristian Couder, Sep 25, 2023
  144. 5/9 pack-bitmap-write: rebuild using new bitmap when remappingChristian Couder, Sep 25, 2023
  145. 7/9 gc: add `gc.repackFilter` config optionChristian Couder, Sep 25, 2023
  146. 6/9 repack: add `--filter=<filter-spec>` optionChristian Couder, Sep 25, 2023
  147. 8/9 repack: implement `--filter-to` for storing filtered out objectsChristian Couder, Sep 25, 2023
  148. 9/9 gc: add `gc.repackFilterTo` config optionChristian Couder, Sep 25, 2023
  149. Junio C HamanoSep 25, 2023
  150. Taylor BlauSep 25, 2023
  151. 0/9 Repack objects into separate packfiles based on a filterChristian Couder, Oct 2, 2023
  152. 1/9 pack-objects: allow `--filter` without `--stdout`Christian Couder, Oct 2, 2023
  153. 2/9 t/helper: add 'find-pack' test-toolChristian Couder, Oct 2, 2023
  154. 3/9 repack: refactor finishing pack-objects commandChristian Couder, Oct 2, 2023
  155. 4/9 repack: refactor finding pack prefixChristian Couder, Oct 2, 2023
  156. 5/9 pack-bitmap-write: rebuild using new bitmap when remappingChristian Couder, Oct 2, 2023
  157. 7/9 gc: add `gc.repackFilter` config optionChristian Couder, Oct 2, 2023
  158. 9/9 gc: add `gc.repackFilterTo` config optionChristian Couder, Oct 2, 2023
  159. 8/9 repack: implement `--filter-to` for storing filtered out objectsChristian Couder, Oct 2, 2023
  160. 6/9 repack: add `--filter=<filter-spec>` optionChristian Couder, Oct 2, 2023
  161. Taylor BlauOct 2, 2023

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.