From: "Qin ShiCheng via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Patrick Steinhardt <ps@pks.im>, Taylor Blau <ttaylorr@openai.com>,
Junio C Hamano <gitster@pobox.com>,
Justin Tobler <jltobler@gmail.com>, qeesung <qeesung@live.com>,
Qin ShiCheng <qeesung@live.com>
Subject: [PATCH v3 4/5] pack-objects: add --keep-pack-from-file
Date: Thu, 08 Oct 2026 09:52:20 +0000 [thread overview]
Message-ID: <148175cfa00fd835a296dd1ed04703c2a80b7e3d.1791453141.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2219.v3.git.1791453141.gitgitgadget@gmail.com>
From: Qin ShiCheng <qeesung@live.com>
"--keep-pack" names one pack per occurrence, and there is only so much
room on the command line: ARG_MAX is shared with the environment, and
on Windows the whole line is capped at 32,767 characters, which a few
hundred pack names fill. Past that the spawn fails before pack-objects
has started. fetch-pack grew "--stdin" in 078b895fef (fetch-pack: new
--stdin option to read refs from stdin, 2012-04-02) for the same
reason.
stdin is taken here: every mode repack drives pack-objects in already
uses it, for the revision list under "-a", object names for the
promisor pack, and pack lists for "--stdin-packs" and "--cruft". So
read the names from a file instead, one per line, skipping empty
lines. They go into the same list as the "--keep-pack" names and are
treated exactly alike: matched against local packs, ignored when they
match nothing, and kept open under "--stdin-packs=follow". A relative
path is resolved against the directory the user ran from, as
"--refs-snapshot" of "git multi-pack-index write" is.
The list now holds strings from two sources, so let it own its copies.
repack is about to use this to hand pack-objects its own snapshot of
the packs that have a ".keep" file.
Signed-off-by: Qin ShiCheng <qeesung@live.com>
---
Documentation/git-pack-objects.adoc | 8 +++++
builtin/pack-objects.c | 28 ++++++++++++++++++
t/t5331-pack-objects-stdin.sh | 46 +++++++++++++++++++++++++++++
3 files changed, 82 insertions(+)
diff --git a/Documentation/git-pack-objects.adoc b/Documentation/git-pack-objects.adoc
index 65cd00c152..938e27f69d 100644
--- a/Documentation/git-pack-objects.adoc
+++ b/Documentation/git-pack-objects.adoc
@@ -13,6 +13,7 @@ SYNOPSIS
[--no-reuse-delta] [--delta-base-offset] [--non-empty]
[--local] [--incremental] [--window=<n>] [--depth=<n>]
[--revs [--unpacked | --all]] [--keep-pack=<pack-name>]
+ [--keep-pack-from-file=<file>]
[--cruft] [--cruft-expiration=<time>]
[--stdout [--filter=<filter-spec>] | <base-name>]
[--shallow] [--keep-true-parents] [--[no-]sparse]
@@ -193,6 +194,13 @@ depth is 4095.
leading directory (e.g. `pack-123.pack`). The option could be
specified multiple times to keep multiple packs.
+--keep-pack-from-file=<file>::
+ Read names of packs to keep from `<file>`, one per line, and
+ treat each of them as if it had been given with `--keep-pack`.
+ Empty lines are ignored. This is meant for callers such as
+ linkgit:git-repack[1] that may have to name more packs than fit
+ on a command line.
+
--incremental::
This flag causes an object already in a pack to be ignored
even if it would have otherwise been packed.
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 48faef2227..621b333d0b 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -194,6 +194,7 @@ static const char *const pack_usage[] = {
" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n"
" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n"
" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n"
+ " [--keep-pack-from-file=<file>]\n"
" [--cruft] [--cruft-expiration=<time>]\n"
" [--stdout [--filter=<filter-spec>] | <base-name>]\n"
" [--shallow] [--keep-true-parents] [--[no-]sparse]\n"
@@ -5002,6 +5003,26 @@ static void get_object_list(struct rev_info *revs, struct strvec *argv)
oid_array_clear(&recent_objects);
}
+/*
+ * Read pack names from the file, one per line, as if each of them had
+ * been given with "--keep-pack".
+ */
+static void read_keep_pack_list(struct string_list *names, const char *path)
+{
+ struct strbuf buf = STRBUF_INIT;
+ FILE *fp = xfopen(path, "r");
+
+ while (strbuf_getline(&buf, fp) != EOF) {
+ if (!buf.len)
+ continue;
+ string_list_append(names, buf.buf);
+ }
+ if (ferror(fp))
+ die_errno(_("could not read '%s'"), path);
+ fclose(fp);
+ strbuf_release(&buf);
+}
+
static void add_extra_kept_packs(struct string_list *names,
enum stdin_packs_mode stdin_packs)
{
@@ -5142,8 +5163,10 @@ int cmd_pack_objects(int argc,
int rev_list_index = 0;
enum stdin_packs_mode stdin_packs = STDIN_PACKS_MODE_NONE;
struct string_list keep_pack_list = {
+ .strdup_strings = 1,
.cmp = fspathcmp,
};
+ char *keep_pack_from_file = NULL;
struct list_objects_filter_options filter_options =
LIST_OBJECTS_FILTER_INIT;
struct repo_config_values *cfg = repo_config_values(the_repository);
@@ -5228,6 +5251,8 @@ int cmd_pack_objects(int argc,
N_("ignore packs that have companion .keep file")),
OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
N_("ignore this pack")),
+ OPT_FILENAME(0, "keep-pack-from-file", &keep_pack_from_file,
+ N_("ignore the packs named in <file>")),
OPT_INTEGER(0, "compression", &cfg->pack_compression_level,
N_("pack compression level")),
OPT_BOOL(0, "keep-true-parents", &grafts_keep_true_parents,
@@ -5455,6 +5480,8 @@ int cmd_pack_objects(int argc,
if (progress && all_progress_implied)
progress = 2;
+ if (keep_pack_from_file)
+ read_keep_pack_list(&keep_pack_list, keep_pack_from_file);
add_extra_kept_packs(&keep_pack_list, stdin_packs);
if (ignore_packed_keep_on_disk) {
struct packed_git *p;
@@ -5549,6 +5576,7 @@ cleanup:
clear_packing_data(&to_pack);
list_objects_filter_release(&filter_options);
string_list_clear(&keep_pack_list, 0);
+ free(keep_pack_from_file);
strvec_clear(&rp);
return 0;
diff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh
index 4e1fde1b08..d590aa4dad 100755
--- a/t/t5331-pack-objects-stdin.sh
+++ b/t/t5331-pack-objects-stdin.sh
@@ -561,4 +561,50 @@ test_expect_success '--stdin-packs with !-delimited pack without follow' '
)
'
+test_expect_success '--keep-pack-from-file names packs to keep' '
+ test_when_finished "rm -fr repo" &&
+
+ git init repo &&
+ (
+ cd repo &&
+ git config set maintenance.auto false &&
+
+ test_commit A &&
+ test_commit B &&
+ test_commit C &&
+
+ A="$(echo A | git pack-objects --revs $packdir/pack)" &&
+ B="$(echo A..B | git pack-objects --revs $packdir/pack)" &&
+ C="$(echo B..C | git pack-objects --revs $packdir/pack)" &&
+ git prune-packed &&
+
+ # Empty lines and names that match no pack are ignored,
+ # as they would be with --keep-pack.
+ cat >keep <<-EOF &&
+ pack-$A.pack
+
+ pack-$B.pack
+ pack-does-not-exist.pack
+ EOF
+
+ P=$(git pack-objects --all --keep-pack=pack-$A.pack \
+ --keep-pack=pack-$B.pack from-argv </dev/null) &&
+ packed_objects from-argv-$P.idx >expect &&
+
+ P=$(git pack-objects --all --keep-pack-from-file=keep \
+ from-file </dev/null) &&
+ packed_objects from-file-$P.idx >actual &&
+ test_cmp expect actual &&
+
+ objects_in_packs $C >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '--keep-pack-from-file with a missing file' '
+ test_must_fail git pack-objects --stdout \
+ --keep-pack-from-file=does-not-exist </dev/null 2>err &&
+ test_grep "could not open .does-not-exist. for reading" err
+'
+
test_done
--
gitgitgadget
next prev parent reply other threads:[~2026-10-08 9:52 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-14 11:31 [PATCH 0/6] repack: don't lose objects to a ".keep" that appears mid-run qeesung via GitGitGadget
2026-09-14 11:31 ` [PATCH 1/6] odb: don't remove a ".keep" we never installed Qin ShiCheng via GitGitGadget
2026-09-15 18:25 ` Justin Tobler
2026-09-16 6:01 ` Qin ShiCheng
2026-09-14 11:31 ` [PATCH 2/6] pack-objects: keep --keep-pack open when following Qin ShiCheng via GitGitGadget
2026-09-14 11:31 ` [PATCH 3/6] pack-objects: reset kept-pack cache for cruft walk Qin ShiCheng via GitGitGadget
2026-09-14 11:31 ` [PATCH 4/6] pack-objects: sort --keep-pack list for lookup Qin ShiCheng via GitGitGadget
2026-09-14 11:31 ` [PATCH 5/6] pack-objects: add --keep-pack-from-file Qin ShiCheng via GitGitGadget
2026-09-14 11:31 ` [PATCH 6/6] repack: tell pack-objects which packs are kept Qin ShiCheng via GitGitGadget
2026-09-18 3:03 ` [PATCH v2 0/5] repack: don't lose objects to a ".keep" that appears mid-run qeesung via GitGitGadget
2026-09-18 3:03 ` [PATCH v2 1/5] pack-objects: keep --keep-pack open when following Qin ShiCheng via GitGitGadget
2026-09-18 3:03 ` [PATCH v2 2/5] pack-objects: reset kept-pack cache for cruft walk Qin ShiCheng via GitGitGadget
2026-09-22 22:31 ` Junio C Hamano
2026-09-23 3:08 ` Qin ShiCheng
2026-09-23 17:45 ` Junio C Hamano
2026-09-18 3:03 ` [PATCH v2 3/5] pack-objects: sort --keep-pack list for lookup Qin ShiCheng via GitGitGadget
2026-09-18 3:03 ` [PATCH v2 4/5] pack-objects: add --keep-pack-from-file Qin ShiCheng via GitGitGadget
2026-09-18 3:03 ` [PATCH v2 5/5] repack: tell pack-objects which packs are kept Qin ShiCheng via GitGitGadget
2026-10-08 9:52 ` [PATCH v3 0/5] repack: don't lose objects to a ".keep" that appears mid-run qeesung via GitGitGadget
2026-10-08 9:52 ` [PATCH v3 1/5] pack-objects: keep --keep-pack open when following Qin ShiCheng via GitGitGadget
2026-10-08 9:52 ` [PATCH v3 2/5] pack-objects: reset kept-pack cache for cruft walk Qin ShiCheng via GitGitGadget
2026-10-08 9:52 ` [PATCH v3 3/5] pack-objects: sort --keep-pack list for lookup Qin ShiCheng via GitGitGadget
2026-10-08 9:52 ` Qin ShiCheng via GitGitGadget [this message]
2026-10-08 9:52 ` [PATCH v3 5/5] repack: tell pack-objects which packs are kept Qin ShiCheng via GitGitGadget
2026-10-08 19:19 ` [PATCH v3 0/5] repack: don't lose objects to a ".keep" that appears mid-run Junio C Hamano
2026-10-09 3:07 ` Qin ShiCheng
2026-10-09 15:11 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=148175cfa00fd835a296dd1ed04703c2a80b7e3d.1791453141.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=jltobler@gmail.com \
--cc=ps@pks.im \
--cc=qeesung@live.com \
--cc=ttaylorr@openai.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.