summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2025-05-27 06:46:41 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2025-05-27 06:46:41 -0500
commitb9bf390c64120104cfae92a5843e15d482ffeade (patch)
tree205e0cbcf97eef48e2d6c0b7e0822bc3934ec512
parentb068788aef5d32875d3312a38a421b9a2952801f (diff)
downloadpackages-awilfox/system-next-25Q2.tar.gz
packages-awilfox/system-next-25Q2.tar.bz2
packages-awilfox/system-next-25Q2.tar.xz
packages-awilfox/system-next-25Q2.zip
system/git: Update to 2.49.0awilfox/system-next-25Q2
Lots of user-facing changes. Backported fixes to ensure proper functioning on big endian systems.
-rw-r--r--system/git/APKBUILD20
-rw-r--r--system/git/disable-t2082-2.patch36
-rw-r--r--system/git/dont-test-other-encodings.patch24
-rw-r--r--system/git/endian.patch2305
-rw-r--r--system/git/perl-getopt-long.patch459
5 files changed, 2321 insertions, 523 deletions
diff --git a/system/git/APKBUILD b/system/git/APKBUILD
index 0562bfba2..2b9c75221 100644
--- a/system/git/APKBUILD
+++ b/system/git/APKBUILD
@@ -1,8 +1,8 @@
# Contributor: Łukasz Jendrysik <scadu@yandex.com>
# Maintainer: A. Wilcox <awilfox@adelielinux.org>
pkgname=git
-pkgver=2.40.0
-pkgrel=1
+pkgver=2.49.0
+pkgrel=0
pkgdesc="Distributed version control system"
url="https://www.git-scm.com/"
arch="all"
@@ -26,11 +26,9 @@ subpackages="$pkgname-doc
replaces="git-perl perl-git"
source="https://www.kernel.org/pub/software/scm/git/git-$pkgver.tar.xz
dont-test-other-encodings.patch
+ endian.patch
git-daemon.initd
git-daemon.confd
-
- disable-t2082-2.patch
- perl-getopt-long.patch
"
_gitcoredir=/usr/libexec/git-core
@@ -59,6 +57,7 @@ prepare() {
cat >> config.mak <<-EOF
NO_SVN_TESTS=YesPlease
NO_REGEX=YesPlease
+ NO_ICONV=YesPlease
USE_ASCIIDOCTOR=1
USE_LIBPCRE2=YesPlease
NO_NSEC=YesPlease
@@ -72,7 +71,7 @@ build() {
}
check() {
- make prefix=/usr DESTDIR="$pkgdir" -j1 test
+ GIT_SKIP_TESTS='t5564.6' make prefix=/usr DESTDIR="$pkgdir" -j1 test
}
package() {
@@ -168,9 +167,8 @@ subtree() {
make install prefix=/usr DESTDIR="$subpkgdir"
}
-sha512sums="a2720f8f9a0258c0bb5e23badcfd68a147682e45a5d039a42c47128296c508109d5039029db89311a35db97a9008585e84ed11b400846502c9be913d67f0fd90 git-2.40.0.tar.xz
-4bcc8367478601c856e0977d46fc4842f62daf300093a576704ad27ccd9fae975f95d3fbfcb00e9fa7254b1db64cd074f49a94fb5cf0abd8d72d7edc9ab8798c dont-test-other-encodings.patch
+sha512sums="81a16415890305fc6cfd14ade8bee76779feba01f51c5446f40c14211654342c68ef0911859fa6e8e9ff0a718847bb44ee4156d03a19c9165df19ba91e09e1f0 git-2.49.0.tar.xz
+829d7a5cb1cfc162eee2920cfcd09798af967657751a9040f18046c9537d0b7a7b314fb84edef1ebe4114cdabafb41253cff60709779ecf91cfa49cd7542f923 dont-test-other-encodings.patch
+e8c63db09327ff01527f4d5f56185c602ef8cb10e2c24f0e3e02b567166ae226053fc1428263ddecd2708d89e6ea8d99d8bf4c1f19541b7a4d4c367898e1777f endian.patch
89528cdd14c51fd568aa61cf6c5eae08ea0844e59f9af9292da5fc6c268261f4166017d002d494400945e248df6b844e2f9f9cd2d9345d516983f5a110e4c42a git-daemon.initd
-fbf1f425206a76e2a8f82342537ed939ff7e623d644c086ca2ced5f69b36734695f9f80ebda1728f75a94d6cd2fcb71bf845b64239368caab418e4d368c141ec git-daemon.confd
-6d7cbb701584a078328056a67bfd32dde5795a80c0911734b38bd534699fb0165ac2b486b267c5c39b90bbb0d7c5ab0ab6ada1d068748865617326da28304eb4 disable-t2082-2.patch
-9800318f9e6a8b6bfd8c700cce5cc326522a607b89236a868ef46940efe0566fdadf5d69dc3e72f989d61df66be8510b8989bd4ce3fc780f017f30652c7e9efa perl-getopt-long.patch"
+fbf1f425206a76e2a8f82342537ed939ff7e623d644c086ca2ced5f69b36734695f9f80ebda1728f75a94d6cd2fcb71bf845b64239368caab418e4d368c141ec git-daemon.confd"
diff --git a/system/git/disable-t2082-2.patch b/system/git/disable-t2082-2.patch
deleted file mode 100644
index b18a9caa7..000000000
--- a/system/git/disable-t2082-2.patch
+++ /dev/null
@@ -1,36 +0,0 @@
---- a/t/t2082-parallel-checkout-attributes.sh 2021-11-24 19:29:21.000000000 +0000
-+++ b/t/t2082-parallel-checkout-attributes.sh 2022-01-12 17:29:58.890000000 +0000
-@@ -33,33 +33,6 @@
- )
- '
-
--test_expect_success 'parallel-checkout with re-encoding' '
-- set_checkout_config 2 0 &&
-- git init encoding &&
-- (
-- cd encoding &&
-- echo text >utf8-text &&
-- write_utf16 <utf8-text >utf16-text &&
--
-- echo "A working-tree-encoding=UTF-16" >.gitattributes &&
-- cp utf16-text A &&
-- cp utf8-text B &&
-- git add A B .gitattributes &&
-- git commit -m encoding &&
--
-- # Check that A is stored in UTF-8
-- git cat-file -p :A >A.internal &&
-- test_cmp_bin utf8-text A.internal &&
--
-- rm A B &&
-- test_checkout_workers 2 git checkout A B &&
--
-- # Check that A (and only A) is re-encoded during checkout
-- test_cmp_bin utf16-text A &&
-- test_cmp_bin utf8-text B
-- )
--'
--
- test_expect_success 'parallel-checkout with eol conversions' '
- set_checkout_config 2 0 &&
- git init eol &&
diff --git a/system/git/dont-test-other-encodings.patch b/system/git/dont-test-other-encodings.patch
index fed4a6e21..d1c917821 100644
--- a/system/git/dont-test-other-encodings.patch
+++ b/system/git/dont-test-other-encodings.patch
@@ -1,18 +1,8 @@
We can't support Japanese people using musl
---- git-2.14.1/t/t5100-mailinfo.sh.old 2017-08-09 14:54:31.000000000 -0500
-+++ git-2.14.1/t/t5100-mailinfo.sh 2017-09-10 23:26:39.206126739 -0500
-@@ -27,6 +27,7 @@
-
- for mail in 00*
- do
-+ if [ $mail == 0004 ]; then continue; fi
- test_expect_success "mailinfo $mail" '
- check_mailinfo "$mail" "" &&
- if test -f "$DATA/msg$mail--scissors"
--- git-2.14.1/t/t8005-blame-i18n.sh.old 2017-08-09 14:54:31.000000000 -0500
+++ git-2.14.1/t/t8005-blame-i18n.sh 2017-09-16 00:42:35.123728809 -0500
-@@ -24,43 +24,10 @@
+@@ -31,43 +31,10 @@
git commit --author "$SJIS_NAME <sjis@localhost>" -m "$SJIS_MSG"
'
@@ -58,7 +48,7 @@ We can't support Japanese people using musl
summary $UTF8_MSG
--- git-2.21.0/t/t0028-working-tree-encoding.sh.old 2019-02-24 16:31:46.000000000 +0000
+++ git-2.21.0/t/t0028-working-tree-encoding.sh 2019-03-08 00:49:07.580000000 +0000
-@@ -51,14 +51,6 @@
+@@ -58,14 +58,6 @@
test_cmp_bin test.utf8.raw test.utf16.git
'
@@ -73,17 +63,17 @@ We can't support Japanese people using musl
test_expect_success 're-encode to UTF-16-LE-BOM on checkout' '
rm test.utf16lebom &&
git checkout test.utf16lebom &&
-@@ -123,41 +115,6 @@
- test_i18ngrep "fatal: BOM is required .* utf-${i}" err.out &&
- test_i18ngrep "use UTF-${i}BE or UTF-${i}LE" err.out
+@@ -131,41 +123,6 @@
+ test_grep "fatal: BOM is required .* utf-${i}" err.out &&
+ test_grep "use UTF-${i}BE or UTF-${i}LE" err.out
'
-
- test_expect_success "eol conversion for UTF-${i} encoded files on checkout" '
- test_when_finished "rm -f crlf.utf${i}.raw lf.utf${i}.raw" &&
- test_when_finished "git reset --hard HEAD^" &&
-
-- cat lf.utf8.raw | write_utf${i} >lf.utf${i}.raw &&
-- cat crlf.utf8.raw | write_utf${i} >crlf.utf${i}.raw &&
+- write_utf${i} <lf.utf8.raw >lf.utf${i}.raw &&
+- write_utf${i} <crlf.utf8.raw >crlf.utf${i}.raw &&
- cp crlf.utf${i}.raw eol.utf${i} &&
-
- cat >expectIndexLF <<-EOF &&
diff --git a/system/git/endian.patch b/system/git/endian.patch
new file mode 100644
index 000000000..83fa3e4f1
--- /dev/null
+++ b/system/git/endian.patch
@@ -0,0 +1,2305 @@
+From mboxrd@z Thu Jan 1 00:00:00 1970
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 17 Apr 2025 12:49:36 +0200
+Subject: [PATCH v4 1/7] parse: fix off-by-one for minimum signed values
+References: <20250417-b4-pks-parse-options-integers-v4-0-9cbc76b61cfe@pks.im>
+To: git@vger.kernel.org
+X-Mailer: b4 0.14.2
+
+We accept a maximum value in `git_parse_signed()` that restricts the
+range of accepted integers. As the intent is to pass `INT*_MAX` values
+here, this maximum doesn't only act as the upper bound, but also as the
+implicit lower bound of the accepted range.
+
+This lower bound is calculated by negating the maximum. But given that
+the maximum value of a signed integer with N bits is `2^(N-1)-1` whereas
+the minimum value is `-2^(N-1)` we have an off-by-one error in the lower
+bound.
+
+Fix this off-by-one error by using `-max - 1` as lower bound instead.
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+---
+ parse.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/parse.c b/parse.c
+index 7a60a4f816c..3c47448ca67 100644
+--- a/parse.c
++++ b/parse.c
+@@ -38,7 +38,7 @@ int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
+ errno = EINVAL;
+ return 0;
+ }
+- if ((val < 0 && -max / factor > val) ||
++ if ((val < 0 && (-max - 1) / factor > val) ||
+ (val > 0 && max / factor < val)) {
+ errno = ERANGE;
+ return 0;
+
+--
+2.49.0.805.g082f7c87e0.dirty
+
+
+From mboxrd@z Thu Jan 1 00:00:00 1970
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 17 Apr 2025 12:49:37 +0200
+Subject: [PATCH v4 2/7] global: use designated initializers for options
+References: <20250417-b4-pks-parse-options-integers-v4-0-9cbc76b61cfe@pks.im>
+To: git@vger.kernel.org
+X-Mailer: b4 0.14.2
+
+While we expose macros for most of our different option types understood
+by the "parse-options" subsystem, not every combination of fields that
+has one as that would otherwise quickly lead to an explosion of macros.
+Instead, we just initialize structures manually for those variants of
+fields that don't have a macro.
+
+Callsites that open-code these structure initialization don't use
+designated initializers though and instead just provide values for each
+of the fields that they want to initialize. This has three significant
+downsides:
+
+ - Callsites need to specify all values up to the last field that they
+ care about. This often includes fields that should simply be left at
+ their default zero-initialized state, which adds distraction.
+
+ - Any reader not deeply familiar with the layout of the structure
+ has a hard time figuring out what the respective initializers mean.
+
+ - Reordering or introducing new fields in the middle of the structure
+ is impossible without adapting all callsites.
+
+Convert all sites to instead use designated initializers, which we have
+started using in our codebase quite a while ago. This allows us to skip
+any default-initialized fields, gives the reader context by specifying
+the field names and allows us to reorder or introduce new fields where
+we want to.
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+---
+ archive.c | 35 ++++++++---
+ builtin/am.c | 28 ++++++---
+ builtin/clone.c | 13 ++++-
+ builtin/commit-tree.c | 12 +++-
+ builtin/commit.c | 62 +++++++++++++++-----
+ builtin/config.c | 13 ++++-
+ builtin/describe.c | 24 ++++++--
+ builtin/fetch.c | 10 +++-
+ builtin/fmt-merge-msg.c | 25 +++++---
+ builtin/gc.c | 12 +++-
+ builtin/grep.c | 14 +++--
+ builtin/init-db.c | 13 +++--
+ builtin/ls-remote.c | 11 +++-
+ builtin/merge.c | 37 +++++++++---
+ builtin/read-tree.c | 11 +++-
+ builtin/rebase.c | 25 ++++++--
+ builtin/revert.c | 12 +++-
+ builtin/show-branch.c | 12 +++-
+ builtin/tag.c | 23 ++++++--
+ builtin/update-index.c | 131 +++++++++++++++++++++++++++++-------------
+ builtin/write-tree.c | 12 ++--
+ diff.c | 13 +++--
+ ref-filter.h | 15 +++--
+ t/helper/test-parse-options.c | 38 +++++++++---
+ 24 files changed, 443 insertions(+), 158 deletions(-)
+
+diff --git a/archive.c b/archive.c
+index 8be4e7ac8db..67bba3cd301 100644
+--- a/archive.c
++++ b/archive.c
+@@ -650,20 +650,37 @@ static int parse_archive_args(int argc, const char **argv,
+ OPT_STRING(0, "format", &format, N_("fmt"), N_("archive format")),
+ OPT_STRING(0, "prefix", &base, N_("prefix"),
+ N_("prepend prefix to each pathname in the archive")),
+- { OPTION_CALLBACK, 0, "add-file", args, N_("file"),
+- N_("add untracked file to archive"), 0, add_file_cb,
+- (intptr_t)&base },
+- { OPTION_CALLBACK, 0, "add-virtual-file", args,
+- N_("path:content"), N_("add untracked file to archive"), 0,
+- add_file_cb, (intptr_t)&base },
++ {
++ .type = OPTION_CALLBACK,
++ .long_name = "add-file",
++ .value = args,
++ .argh = N_("file"),
++ .help = N_("add untracked file to archive"),
++ .callback = add_file_cb,
++ .defval = (intptr_t) &base,
++ },
++ {
++ .type = OPTION_CALLBACK,
++ .long_name = "add-virtual-file",
++ .value = args,
++ .argh = N_("path:content"),
++ .help = N_("add untracked file to archive"),
++ .callback = add_file_cb,
++ .defval = (intptr_t) &base,
++ },
+ OPT_STRING('o', "output", &output, N_("file"),
+ N_("write the archive to this file")),
+ OPT_BOOL(0, "worktree-attributes", &worktree_attributes,
+ N_("read .gitattributes in working directory")),
+ OPT__VERBOSE(&verbose, N_("report archived files on stderr")),
+- { OPTION_STRING, 0, "mtime", &mtime_option, N_("time"),
+- N_("set modification time of archive entries"),
+- PARSE_OPT_NONEG },
++ {
++ .type = OPTION_STRING,
++ .long_name = "mtime",
++ .value = &mtime_option,
++ .argh = N_("time"),
++ .help = N_("set modification time of archive entries"),
++ .flags = PARSE_OPT_NONEG,
++ },
+ OPT_NUMBER_CALLBACK(&compression_level,
+ N_("set compression level"), number_callback),
+ OPT_GROUP(""),
+diff --git a/builtin/am.c b/builtin/am.c
+index 3b61bd4c333..4afb519830f 100644
+--- a/builtin/am.c
++++ b/builtin/am.c
+@@ -2399,11 +2399,16 @@ int cmd_am(int argc,
+ OPT_CMDMODE(0, "quit", &resume_mode,
+ N_("abort the patching operation but keep HEAD where it is"),
+ RESUME_QUIT),
+- { OPTION_CALLBACK, 0, "show-current-patch", &resume_mode,
+- "(diff|raw)",
+- N_("show the patch being applied"),
+- PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+- parse_opt_show_current_patch, RESUME_SHOW_PATCH_RAW },
++ {
++ .type = OPTION_CALLBACK,
++ .long_name = "show-current-patch",
++ .value = &resume_mode,
++ .argh = "(diff|raw)",
++ .help = N_("show the patch being applied"),
++ .flags = PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
++ .callback = parse_opt_show_current_patch,
++ .defval = RESUME_SHOW_PATCH_RAW,
++ },
+ OPT_CMDMODE(0, "retry", &resume_mode,
+ N_("try to apply current patch again"),
+ RESUME_APPLY),
+@@ -2416,9 +2421,16 @@ int cmd_am(int argc,
+ OPT_BOOL(0, "ignore-date", &state.ignore_date,
+ N_("use current timestamp for author date")),
+ OPT_RERERE_AUTOUPDATE(&state.allow_rerere_autoupdate),
+- { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
+- N_("GPG-sign commits"),
+- PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
++ {
++ .type = OPTION_STRING,
++ .short_name = 'S',
++ .long_name = "gpg-sign",
++ .value = &state.sign_commit,
++ .argh = N_("key-id"),
++ .help = N_("GPG-sign commits"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t) "",
++ },
+ OPT_CALLBACK_F(0, "empty", &state.empty_type, "(stop|drop|keep)",
+ N_("how to handle empty patches"),
+ PARSE_OPT_NONEG, am_option_parse_empty),
+diff --git a/builtin/clone.c b/builtin/clone.c
+index 88276e5b7ab..9c3547f41e3 100644
+--- a/builtin/clone.c
++++ b/builtin/clone.c
+@@ -928,9 +928,16 @@ int cmd_clone(int argc,
+ N_("don't use local hardlinks, always copy")),
+ OPT_BOOL('s', "shared", &option_shared,
+ N_("setup as shared repository")),
+- { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules,
+- N_("pathspec"), N_("initialize submodules in the clone"),
+- PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." },
++ {
++ .type = OPTION_CALLBACK,
++ .long_name = "recurse-submodules",
++ .value = &option_recurse_submodules,
++ .argh = N_("pathspec"),
++ .help = N_("initialize submodules in the clone"),
++ .flags = PARSE_OPT_OPTARG,
++ .callback = recurse_submodules_cb,
++ .defval = (intptr_t)".",
++ },
+ OPT_ALIAS(0, "recursive", "recurse-submodules"),
+ OPT_INTEGER('j', "jobs", &max_jobs,
+ N_("number of submodules cloned in parallel")),
+diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
+index 38457600a4e..c787133d004 100644
+--- a/builtin/commit-tree.c
++++ b/builtin/commit-tree.c
+@@ -111,8 +111,16 @@ int cmd_commit_tree(int argc,
+ OPT_CALLBACK_F('F', NULL, &buffer, N_("file"),
+ N_("read commit log message from file"), PARSE_OPT_NONEG,
+ parse_file_arg_callback),
+- { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
+- N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
++ {
++ .type = OPTION_STRING,
++ .short_name = 'S',
++ .long_name = "gpg-sign",
++ .value = &sign_commit,
++ .argh = N_("key-id"),
++ .help = N_("GPG sign commit"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t) "",
++ },
+ OPT_END()
+ };
+ int ret;
+diff --git a/builtin/commit.c b/builtin/commit.c
+index 2f459682221..66bd91fd523 100644
+--- a/builtin/commit.c
++++ b/builtin/commit.c
+@@ -1542,17 +1542,34 @@ struct repository *repo UNUSED)
+ STATUS_FORMAT_LONG),
+ OPT_BOOL('z', "null", &s.null_termination,
+ N_("terminate entries with NUL")),
+- { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
+- N_("mode"),
+- N_("show untracked files, optional modes: all, normal, no. (Default: all)"),
+- PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+- { OPTION_STRING, 0, "ignored", &ignored_arg,
+- N_("mode"),
+- N_("show ignored files, optional modes: traditional, matching, no. (Default: traditional)"),
+- PARSE_OPT_OPTARG, NULL, (intptr_t)"traditional" },
+- { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"),
+- N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"),
+- PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
++ {
++ .type = OPTION_STRING,
++ .short_name = 'u',
++ .long_name = "untracked-files",
++ .value = &untracked_files_arg,
++ .argh = N_("mode"),
++ .help = N_("show untracked files, optional modes: all, normal, no. (Default: all)"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t)"all",
++ },
++ {
++ .type = OPTION_STRING,
++ .long_name = "ignored",
++ .value = &ignored_arg,
++ .argh = N_("mode"),
++ .help = N_("show ignored files, optional modes: traditional, matching, no. (Default: traditional)"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t)"traditional",
++ },
++ {
++ .type = OPTION_STRING,
++ .long_name = "ignore-submodules",
++ .value = &ignore_submodule_arg,
++ .argh = N_("when"),
++ .help = N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t)"all",
++ },
+ OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")),
+ OPT_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")),
+ OPT_CALLBACK_F('M', "find-renames", &rename_score_arg,
+@@ -1688,8 +1705,16 @@ int cmd_commit(int argc,
+ OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
+ OPT_CLEANUP(&cleanup_arg),
+ OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
+- { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
+- N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
++ {
++ .type = OPTION_STRING,
++ .short_name = 'S',
++ .long_name = "gpg-sign",
++ .value = &sign_commit,
++ .argh = N_("key-id"),
++ .help = N_("GPG sign commit"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t) "",
++ },
+ /* end commit message options */
+
+ OPT_GROUP(N_("Commit contents options")),
+@@ -1714,7 +1739,16 @@ int cmd_commit(int argc,
+ N_("terminate entries with NUL")),
+ OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),
+ OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
+- { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
++ {
++ .type = OPTION_STRING,
++ .short_name = 'u',
++ .long_name = "untracked-files",
++ .value = &untracked_files_arg,
++ .argh = N_("mode"),
++ .help = N_("show untracked files, optional modes: all, normal, no. (Default: all)"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t)"all",
++ },
+ OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+ OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
+ /* end commit contents options */
+diff --git a/builtin/config.c b/builtin/config.c
+index 53a90094e31..f70d6354772 100644
+--- a/builtin/config.c
++++ b/builtin/config.c
+@@ -131,9 +131,16 @@ struct config_display_options {
+ #define TYPE_COLOR 6
+ #define TYPE_BOOL_OR_STR 7
+
+-#define OPT_CALLBACK_VALUE(s, l, v, h, i) \
+- { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
+- PARSE_OPT_NONEG, option_parse_type, (i) }
++#define OPT_CALLBACK_VALUE(s, l, v, h, i) { \
++ .type = OPTION_CALLBACK, \
++ .short_name = (s), \
++ .long_name = (l), \
++ .value = (v), \
++ .help = (h), \
++ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, \
++ .callback = option_parse_type, \
++ .defval = (i), \
++}
+
+ static int option_parse_type(const struct option *opt, const char *arg,
+ int unset)
+diff --git a/builtin/describe.c b/builtin/describe.c
+index e2e73f3d757..2da9f4fed01 100644
+--- a/builtin/describe.c
++++ b/builtin/describe.c
+@@ -601,12 +601,24 @@ int cmd_describe(int argc,
+ N_("do not consider tags matching <pattern>")),
+ OPT_BOOL(0, "always", &always,
+ N_("show abbreviated commit object as fallback")),
+- {OPTION_STRING, 0, "dirty", &dirty, N_("mark"),
+- N_("append <mark> on dirty working tree (default: \"-dirty\")"),
+- PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
+- {OPTION_STRING, 0, "broken", &broken, N_("mark"),
+- N_("append <mark> on broken working tree (default: \"-broken\")"),
+- PARSE_OPT_OPTARG, NULL, (intptr_t) "-broken"},
++ {
++ .type = OPTION_STRING,
++ .long_name = "dirty",
++ .value = &dirty,
++ .argh = N_("mark"),
++ .help = N_("append <mark> on dirty working tree (default: \"-dirty\")"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t) "-dirty",
++ },
++ {
++ .type = OPTION_STRING,
++ .long_name = "broken",
++ .value = &broken,
++ .argh = N_("mark"),
++ .help = N_("append <mark> on broken working tree (default: \"-broken\")"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t) "-broken",
++ },
+ OPT_END(),
+ };
+
+diff --git a/builtin/fetch.c b/builtin/fetch.c
+index 02af5054690..3a5159d9e69 100644
+--- a/builtin/fetch.c
++++ b/builtin/fetch.c
+@@ -2367,8 +2367,14 @@ int cmd_fetch(int argc,
+ OPT_SET_INT_F(0, "refetch", &refetch,
+ N_("re-fetch without negotiating common commits"),
+ 1, PARSE_OPT_NONEG),
+- { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
+- N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
++ {
++ .type = OPTION_STRING,
++ .long_name = "submodule-prefix",
++ .value = &submodule_prefix,
++ .argh = N_("dir"),
++ .help = N_("prepend this to submodule path output"),
++ .flags = PARSE_OPT_HIDDEN,
++ },
+ OPT_CALLBACK_F(0, "recurse-submodules-default",
+ &recurse_submodules_default, N_("on-demand"),
+ N_("default for recursive fetching of submodules "
+diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
+index 189cd1096a0..240cdb474bc 100644
+--- a/builtin/fmt-merge-msg.c
++++ b/builtin/fmt-merge-msg.c
+@@ -20,13 +20,24 @@ int cmd_fmt_merge_msg(int argc,
+ char *into_name = NULL;
+ int shortlog_len = -1;
+ struct option options[] = {
+- { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
+- N_("populate log with at most <n> entries from shortlog"),
+- PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
+- { OPTION_INTEGER, 0, "summary", &shortlog_len, N_("n"),
+- N_("alias for --log (deprecated)"),
+- PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, NULL,
+- DEFAULT_MERGE_LOG_LEN },
++ {
++ .type = OPTION_INTEGER,
++ .long_name = "log",
++ .value = &shortlog_len,
++ .argh = N_("n"),
++ .help = N_("populate log with at most <n> entries from shortlog"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = DEFAULT_MERGE_LOG_LEN,
++ },
++ {
++ .type = OPTION_INTEGER,
++ .long_name = "summary",
++ .value = &shortlog_len,
++ .argh = N_("n"),
++ .help = N_("alias for --log (deprecated)"),
++ .flags = PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN,
++ .defval = DEFAULT_MERGE_LOG_LEN,
++ },
+ OPT_STRING('m', "message", &message, N_("text"),
+ N_("use <text> as start of message")),
+ OPT_STRING(0, "into-name", &into_name, N_("name"),
+diff --git a/builtin/gc.c b/builtin/gc.c
+index 99431fd4674..6707a26bc6e 100644
+--- a/builtin/gc.c
++++ b/builtin/gc.c
+@@ -699,9 +699,15 @@ struct repository *repo UNUSED)
+ int ret;
+ struct option builtin_gc_options[] = {
+ OPT__QUIET(&quiet, N_("suppress progress reporting")),
+- { OPTION_STRING, 0, "prune", &prune_expire_arg, N_("date"),
+- N_("prune unreferenced objects"),
+- PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire_arg },
++ {
++ .type = OPTION_STRING,
++ .long_name = "prune",
++ .value = &prune_expire_arg,
++ .argh = N_("date"),
++ .help = N_("prune unreferenced objects"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t)prune_expire_arg,
++ },
+ OPT_BOOL(0, "cruft", &cfg.cruft_packs, N_("pack unreferenced objects separately")),
+ OPT_MAGNITUDE(0, "max-cruft-size", &cfg.max_cruft_size,
+ N_("with --cruft, limit the size of new cruft packs")),
+diff --git a/builtin/grep.c b/builtin/grep.c
+index d1427290f77..c4869733e1b 100644
+--- a/builtin/grep.c
++++ b/builtin/grep.c
+@@ -1017,10 +1017,16 @@ int cmd_grep(int argc,
+ OPT_BOOL(0, "all-match", &opt.all_match,
+ N_("show only matches from files that match all patterns")),
+ OPT_GROUP(""),
+- { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
+- N_("pager"), N_("show matching files in the pager"),
+- PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE,
+- NULL, (intptr_t)default_pager },
++ {
++ .type = OPTION_STRING,
++ .short_name = 'O',
++ .long_name = "open-files-in-pager",
++ .value = &show_in_pager,
++ .argh = N_("pager"),
++ .help = N_("show matching files in the pager"),
++ .flags = PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE,
++ .defval = (intptr_t)default_pager,
++ },
+ OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored,
+ N_("allow calling of grep(1) (ignored by this build)"),
+ PARSE_OPT_NOCOMPLETE),
+diff --git a/builtin/init-db.c b/builtin/init-db.c
+index 196dccdd77a..4a950e44d8d 100644
+--- a/builtin/init-db.c
++++ b/builtin/init-db.c
+@@ -93,10 +93,15 @@ int cmd_init_db(int argc,
+ N_("directory from which templates will be used")),
+ OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
+ N_("create a bare repository"), 1),
+- { OPTION_CALLBACK, 0, "shared", &init_shared_repository,
+- N_("permissions"),
+- N_("specify that the git repository is to be shared amongst several users"),
+- PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
++ {
++ .type = OPTION_CALLBACK,
++ .long_name = "shared",
++ .value = &init_shared_repository,
++ .argh = N_("permissions"),
++ .help = N_("specify that the git repository is to be shared amongst several users"),
++ .flags = PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
++ .callback = shared_callback
++ },
+ OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET),
+ OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
+ N_("separate git dir from working tree")),
+diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
+index 42f34e12361..01a4d4daa1f 100644
+--- a/builtin/ls-remote.c
++++ b/builtin/ls-remote.c
+@@ -67,9 +67,14 @@ int cmd_ls_remote(int argc,
+ OPT__QUIET(&quiet, N_("do not print remote URL")),
+ OPT_STRING(0, "upload-pack", &uploadpack, N_("exec"),
+ N_("path of git-upload-pack on the remote host")),
+- { OPTION_STRING, 0, "exec", &uploadpack, N_("exec"),
+- N_("path of git-upload-pack on the remote host"),
+- PARSE_OPT_HIDDEN },
++ {
++ .type = OPTION_STRING,
++ .long_name = "exec",
++ .value = &uploadpack,
++ .argh = N_("exec"),
++ .help = N_("path of git-upload-pack on the remote host"),
++ .flags = PARSE_OPT_HIDDEN,
++ },
+ OPT_BIT('t', "tags", &flags, N_("limit to tags"), REF_TAGS),
+ OPT_BIT('b', "branches", &flags, N_("limit to branches"), REF_BRANCHES),
+ OPT_BIT_F('h', "heads", &flags,
+diff --git a/builtin/merge.c b/builtin/merge.c
+index ba9faf126aa..21787d45165 100644
+--- a/builtin/merge.c
++++ b/builtin/merge.c
+@@ -250,9 +250,15 @@ static struct option builtin_merge_options[] = {
+ OPT_BOOL(0, "stat", &show_diffstat,
+ N_("show a diffstat at the end of the merge")),
+ OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")),
+- { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
+- N_("add (at most <n>) entries from shortlog to merge commit message"),
+- PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
++ {
++ .type = OPTION_INTEGER,
++ .long_name = "log",
++ .value = &shortlog_len,
++ .argh = N_("n"),
++ .help = N_("add (at most <n>) entries from shortlog to merge commit message"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = DEFAULT_MERGE_LOG_LEN,
++ },
+ OPT_BOOL(0, "squash", &squash,
+ N_("create a single commit instead of doing a merge")),
+ OPT_BOOL(0, "commit", &option_commit,
+@@ -274,9 +280,16 @@ static struct option builtin_merge_options[] = {
+ OPT_CALLBACK('m', "message", &merge_msg, N_("message"),
+ N_("merge commit message (for a non-fast-forward merge)"),
+ option_parse_message),
+- { OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"),
+- N_("read message from file"), PARSE_OPT_NONEG,
+- NULL, 0, option_read_message },
++ {
++ .type = OPTION_LOWLEVEL_CALLBACK,
++ .short_name = 'F',
++ .long_name = "file",
++ .value = &merge_msg,
++ .argh = N_("path"),
++ .help = N_("read message from file"),
++ .flags = PARSE_OPT_NONEG,
++ .ll_callback = option_read_message,
++ },
+ OPT_STRING(0, "into-name", &into_name, N_("name"),
+ N_("use <name> instead of the real target")),
+ OPT__VERBOSITY(&verbosity),
+@@ -289,8 +302,16 @@ static struct option builtin_merge_options[] = {
+ OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories,
+ N_("allow merging unrelated histories")),
+ OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
+- { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
+- N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
++ {
++ .type = OPTION_STRING,
++ .short_name = 'S',
++ .long_name = "gpg-sign",
++ .value = &sign_commit,
++ .argh = N_("key-id"),
++ .help = N_("GPG sign commit"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t) "",
++ },
+ OPT_AUTOSTASH(&autostash),
+ OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
+ OPT_BOOL(0, "signoff", &signoff, N_("add a Signed-off-by trailer")),
+diff --git a/builtin/read-tree.c b/builtin/read-tree.c
+index d2a807a828b..a8f352f7cd9 100644
+--- a/builtin/read-tree.c
++++ b/builtin/read-tree.c
+@@ -135,9 +135,14 @@ int cmd_read_tree(int argc,
+ N_("3-way merge in presence of adds and removes")),
+ OPT_BOOL(0, "reset", &opts.reset,
+ N_("same as -m, but discard unmerged entries")),
+- { OPTION_STRING, 0, "prefix", &opts.prefix, N_("<subdirectory>/"),
+- N_("read the tree into the index under <subdirectory>/"),
+- PARSE_OPT_NONEG },
++ {
++ .type = OPTION_STRING,
++ .long_name = "prefix",
++ .value = &opts.prefix,
++ .argh = N_("<subdirectory>/"),
++ .help = N_("read the tree into the index under <subdirectory>/"),
++ .flags = PARSE_OPT_NONEG,
++ },
+ OPT_BOOL('u', NULL, &opts.update,
+ N_("update working tree with merge result")),
+ OPT_CALLBACK_F(0, "exclude-per-directory", &opts,
+diff --git a/builtin/rebase.c b/builtin/rebase.c
+index d4715ed35d7..d4083350090 100644
+--- a/builtin/rebase.c
++++ b/builtin/rebase.c
+@@ -1122,9 +1122,15 @@ int cmd_rebase(int argc,
+ OPT_BIT('v', "verbose", &options.flags,
+ N_("display a diffstat of what changed upstream"),
+ REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
+- {OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL,
+- N_("do not show diffstat of what changed upstream"),
+- PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
++ {
++ .type = OPTION_NEGBIT,
++ .short_name = 'n',
++ .long_name = "no-stat",
++ .value = &options.flags,
++ .help = N_("do not show diffstat of what changed upstream"),
++ .flags = PARSE_OPT_NOARG,
++ .defval = REBASE_DIFFSTAT,
++ },
+ OPT_BOOL(0, "signoff", &options.signoff,
+ N_("add a Signed-off-by trailer to each commit")),
+ OPT_BOOL(0, "committer-date-is-author-date",
+@@ -1190,9 +1196,16 @@ int cmd_rebase(int argc,
+ OPT_BOOL(0, "update-refs", &options.update_refs,
+ N_("update branches that point to commits "
+ "that are being rebased")),
+- { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
+- N_("GPG-sign commits"),
+- PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
++ {
++ .type = OPTION_STRING,
++ .short_name = 'S',
++ .long_name = "gpg-sign",
++ .value = &gpg_sign,
++ .argh = N_("key-id"),
++ .help = N_("GPG-sign commits"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t) "",
++ },
+ OPT_AUTOSTASH(&options.autostash),
+ OPT_STRING_LIST('x', "exec", &options.exec, N_("exec"),
+ N_("add exec lines after each commit of the "
+diff --git a/builtin/revert.c b/builtin/revert.c
+index aca6c293cdf..4f5ef975494 100644
+--- a/builtin/revert.c
++++ b/builtin/revert.c
+@@ -132,8 +132,16 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
+ OPT_STRING(0, "strategy", &strategy, N_("strategy"), N_("merge strategy")),
+ OPT_STRVEC('X', "strategy-option", &opts->xopts, N_("option"),
+ N_("option for merge strategy")),
+- { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
+- N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
++ {
++ .type = OPTION_STRING,
++ .short_name = 'S',
++ .long_name = "gpg-sign",
++ .value = &gpg_sign,
++ .argh = N_("key-id"),
++ .help = N_("GPG sign commit"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = (intptr_t) "",
++ },
+ OPT_END()
+ };
+ struct option *options = base_options;
+diff --git a/builtin/show-branch.c b/builtin/show-branch.c
+index fce6b404e92..dab37019d29 100644
+--- a/builtin/show-branch.c
++++ b/builtin/show-branch.c
+@@ -667,9 +667,15 @@ int cmd_show_branch(int ac,
+ N_("show remote-tracking branches")),
+ OPT__COLOR(&showbranch_use_color,
+ N_("color '*!+-' corresponding to the branch")),
+- { OPTION_INTEGER, 0, "more", &extra, N_("n"),
+- N_("show <n> more commits after the common ancestor"),
+- PARSE_OPT_OPTARG, NULL, (intptr_t)1 },
++ {
++ .type = OPTION_INTEGER,
++ .long_name = "more",
++ .value = &extra,
++ .argh = N_("n"),
++ .help = N_("show <n> more commits after the common ancestor"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = 1,
++ },
+ OPT_SET_INT(0, "list", &extra, N_("synonym to more=-1"), -1),
+ OPT_BOOL(0, "no-name", &no_name, N_("suppress naming strings")),
+ OPT_BOOL(0, "current", &with_current_branch,
+diff --git a/builtin/tag.c b/builtin/tag.c
+index d3e0943b734..b266f12bb48 100644
+--- a/builtin/tag.c
++++ b/builtin/tag.c
+@@ -479,9 +479,15 @@ int cmd_tag(int argc,
+ int edit_flag = 0;
+ struct option options[] = {
+ OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
+- { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
+- N_("print <n> lines of each tag message"),
+- PARSE_OPT_OPTARG, NULL, 1 },
++ {
++ .type = OPTION_INTEGER,
++ .short_name = 'n',
++ .value = &filter.lines,
++ .argh = N_("n"),
++ .help = N_("print <n> lines of each tag message"),
++ .flags = PARSE_OPT_OPTARG,
++ .defval = 1,
++ },
+ OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'),
+ OPT_CMDMODE('v', "verify", &cmdmode, N_("verify tags"), 'v'),
+
+@@ -513,9 +519,14 @@ int cmd_tag(int argc,
+ N_("do not output a newline after empty formatted refs")),
+ OPT_REF_SORT(&sorting_options),
+ {
+- OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
+- N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
+- parse_opt_object_name, (intptr_t) "HEAD"
++ .type = OPTION_CALLBACK,
++ .long_name = "points-at",
++ .value = &filter.points_at,
++ .argh = N_("object"),
++ .help = N_("print only tags of the object"),
++ .flags = PARSE_OPT_LASTARG_DEFAULT,
++ .callback = parse_opt_object_name,
++ .defval = (intptr_t) "HEAD",
+ },
+ OPT_STRING( 0 , "format", &format.format, N_("format"),
+ N_("format to use for the output")),
+diff --git a/builtin/update-index.c b/builtin/update-index.c
+index b2f6b1a3fbb..ee64b022679 100644
+--- a/builtin/update-index.c
++++ b/builtin/update-index.c
+@@ -964,29 +964,51 @@ int cmd_update_index(int argc,
+ N_("like --refresh, but ignore assume-unchanged setting"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+ really_refresh_callback),
+- {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL,
+- N_("<mode>,<object>,<path>"),
+- N_("add the specified entry to the index"),
+- PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */
+- PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+- NULL, 0,
+- cacheinfo_callback},
++ {
++ .type = OPTION_LOWLEVEL_CALLBACK,
++ .long_name = "cacheinfo",
++ .argh = N_("<mode>,<object>,<path>"),
++ .help = N_("add the specified entry to the index"),
++ .flags = PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */
++ PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
++ .ll_callback = cacheinfo_callback,
++ },
+ OPT_CALLBACK_F(0, "chmod", &set_executable_bit, "(+|-)x",
+ N_("override the executable bit of the listed files"),
+ PARSE_OPT_NONEG,
+ chmod_callback),
+- {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL,
+- N_("mark files as \"not changing\""),
+- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+- {OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL,
+- N_("clear assumed-unchanged bit"),
+- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+- {OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL,
+- N_("mark files as \"index-only\""),
+- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+- {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
+- N_("clear skip-worktree bit"),
+- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
++ {
++ .type = OPTION_SET_INT,
++ .long_name = "assume-unchanged",
++ .value = &mark_valid_only,
++ .help = N_("mark files as \"not changing\""),
++ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
++ .defval = MARK_FLAG,
++ },
++ {
++ .type = OPTION_SET_INT,
++ .long_name = "no-assume-unchanged",
++ .value = &mark_valid_only,
++ .help = N_("clear assumed-unchanged bit"),
++ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
++ .defval = UNMARK_FLAG,
++ },
++ {
++ .type = OPTION_SET_INT,
++ .long_name = "skip-worktree",
++ .value = &mark_skip_worktree_only,
++ .help = N_("mark files as \"index-only\""),
++ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
++ .defval = MARK_FLAG,
++ },
++ {
++ .type = OPTION_SET_INT,
++ .long_name = "no-skip-worktree",
++ .value = &mark_skip_worktree_only,
++ .help = N_("clear skip-worktree bit"),
++ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
++ .defval = UNMARK_FLAG,
++ },
+ OPT_BOOL(0, "ignore-skip-worktree-entries", &ignore_skip_worktree_entries,
+ N_("do not touch index-only entries")),
+ OPT_SET_INT(0, "info-only", &info_only,
+@@ -995,22 +1017,39 @@ int cmd_update_index(int argc,
+ N_("remove named paths even if present in worktree"), 1),
+ OPT_BOOL('z', NULL, &nul_term_line,
+ N_("with --stdin: input lines are terminated by null bytes")),
+- {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL,
+- N_("read list of paths to be updated from standard input"),
+- PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+- NULL, 0, stdin_callback},
+- {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &nul_term_line, NULL,
+- N_("add entries from standard input to the index"),
+- PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+- NULL, 0, stdin_cacheinfo_callback},
+- {OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL,
+- N_("repopulate stages #2 and #3 for the listed paths"),
+- PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+- NULL, 0, unresolve_callback},
+- {OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL,
+- N_("only update entries that differ from HEAD"),
+- PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+- NULL, 0, reupdate_callback},
++ {
++ .type = OPTION_LOWLEVEL_CALLBACK,
++ .long_name = "stdin",
++ .value = &read_from_stdin,
++ .help = N_("read list of paths to be updated from standard input"),
++ .flags = PARSE_OPT_NONEG | PARSE_OPT_NOARG,
++ .ll_callback = stdin_callback,
++ },
++ {
++ .type = OPTION_LOWLEVEL_CALLBACK,
++ .long_name = "index-info",
++ .value = &nul_term_line,
++ .help = N_("add entries from standard input to the index"),
++ .flags = PARSE_OPT_NONEG | PARSE_OPT_NOARG,
++ .ll_callback = stdin_cacheinfo_callback,
++ },
++ {
++ .type = OPTION_LOWLEVEL_CALLBACK,
++ .long_name = "unresolve",
++ .value = &has_errors,
++ .help = N_("repopulate stages #2 and #3 for the listed paths"),
++ .flags = PARSE_OPT_NONEG | PARSE_OPT_NOARG,
++ .ll_callback = unresolve_callback,
++ },
++ {
++ .type = OPTION_LOWLEVEL_CALLBACK,
++ .short_name = 'g',
++ .long_name = "again",
++ .value = &has_errors,
++ .help = N_("only update entries that differ from HEAD"),
++ .flags = PARSE_OPT_NONEG | PARSE_OPT_NOARG,
++ .ll_callback = reupdate_callback,
++ },
+ OPT_BIT(0, "ignore-missing", &refresh_args.flags,
+ N_("ignore files missing from worktree"),
+ REFRESH_IGNORE_MISSING),
+@@ -1036,12 +1075,22 @@ int cmd_update_index(int argc,
+ N_("write out the index even if is not flagged as changed"), 1),
+ OPT_BOOL(0, "fsmonitor", &fsmonitor,
+ N_("enable or disable file system monitor")),
+- {OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL,
+- N_("mark files as fsmonitor valid"),
+- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+- {OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL,
+- N_("clear fsmonitor valid bit"),
+- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
++ {
++ .type = OPTION_SET_INT,
++ .long_name = "fsmonitor-valid",
++ .value = &mark_fsmonitor_only,
++ .help = N_("mark files as fsmonitor valid"),
++ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
++ .defval = MARK_FLAG,
++ },
++ {
++ .type = OPTION_SET_INT,
++ .long_name = "no-fsmonitor-valid",
++ .value = &mark_fsmonitor_only,
++ .help = N_("clear fsmonitor valid bit"),
++ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
++ .defval = UNMARK_FLAG,
++ },
+ OPT_END()
+ };
+
+diff --git a/builtin/write-tree.c b/builtin/write-tree.c
+index 43f233e69b0..5a8dc377ec0 100644
+--- a/builtin/write-tree.c
++++ b/builtin/write-tree.c
+@@ -31,10 +31,14 @@ int cmd_write_tree(int argc,
+ WRITE_TREE_MISSING_OK),
+ OPT_STRING(0, "prefix", &tree_prefix, N_("<prefix>/"),
+ N_("write tree object for a subdirectory <prefix>")),
+- { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL,
+- N_("only useful for debugging"),
+- PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL,
+- WRITE_TREE_IGNORE_CACHE_TREE },
++ {
++ .type = OPTION_BIT,
++ .long_name = "ignore-cache-tree",
++ .value = &flags,
++ .help = N_("only useful for debugging"),
++ .flags = PARSE_OPT_HIDDEN | PARSE_OPT_NOARG,
++ .defval = WRITE_TREE_IGNORE_CACHE_TREE,
++ },
+ OPT_END()
+ };
+
+diff --git a/diff.c b/diff.c
+index 08f5e00a2cc..f2fcc7f3c22 100644
+--- a/diff.c
++++ b/diff.c
+@@ -5892,10 +5892,15 @@ struct option *add_diff_options(const struct option *opts,
+ OPT_CALLBACK_F(0, "diff-filter", options, N_("[(A|C|D|M|R|T|U|X|B)...[*]]"),
+ N_("select files by diff type"),
+ PARSE_OPT_NONEG, diff_opt_diff_filter),
+- { OPTION_CALLBACK, 0, "output", options, N_("<file>"),
+- N_("output to a specific file"),
+- PARSE_OPT_NONEG, NULL, 0, diff_opt_output },
+-
++ {
++ .type = OPTION_CALLBACK,
++ .long_name = "output",
++ .value = options,
++ .argh = N_("<file>"),
++ .help = N_("output to a specific file"),
++ .flags = PARSE_OPT_NONEG,
++ .ll_callback = diff_opt_output,
++ },
+ OPT_END()
+ };
+
+diff --git a/ref-filter.h b/ref-filter.h
+index 013d4cfa64b..c98c4fbd4c1 100644
+--- a/ref-filter.h
++++ b/ref-filter.h
+@@ -114,11 +114,16 @@ struct ref_format {
+ }
+
+ /* Macros for checking --merged and --no-merged options */
+-#define _OPT_MERGED_NO_MERGED(option, filter, h) \
+- { OPTION_CALLBACK, 0, option, (filter), N_("commit"), (h), \
+- PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, \
+- parse_opt_merge_filter, (intptr_t) "HEAD" \
+- }
++#define _OPT_MERGED_NO_MERGED(option, filter, h) { \
++ .type = OPTION_CALLBACK, \
++ .long_name = option, \
++ .value = (filter), \
++ .argh = N_("commit"), \
++ .help = (h), \
++ .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, \
++ .callback = parse_opt_merge_filter, \
++ .defval = (intptr_t) "HEAD", \
++}
+ #define OPT_MERGED(f, h) _OPT_MERGED_NO_MERGED("merged", f, h)
+ #define OPT_NO_MERGED(f, h) _OPT_MERGED_NO_MERGED("no-merged", f, h)
+
+diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
+index bfe45ec68b0..997f55fd45b 100644
+--- a/t/helper/test-parse-options.c
++++ b/t/helper/test-parse-options.c
+@@ -124,8 +124,15 @@ int cmd__parse_options(int argc, const char **argv)
+ struct option options[] = {
+ OPT_BOOL(0, "yes", &boolean, "get a boolean"),
+ OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"),
+- { OPTION_SET_INT, 'B', "no-fear", &boolean, NULL,
+- "be brave", PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
++ {
++ .type = OPTION_SET_INT,
++ .short_name = 'B',
++ .long_name = "no-fear",
++ .value = &boolean,
++ .help = "be brave",
++ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
++ .defval = 1,
++ },
+ OPT_COUNTUP('b', "boolean", &boolean, "increment by one"),
+ OPT_BIT('4', "or4", &boolean,
+ "bitwise-or boolean with ...0100", 4),
+@@ -155,12 +162,27 @@ int cmd__parse_options(int argc, const char **argv)
+ OPT_GROUP("Magic arguments"),
+ OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
+ number_callback),
+- { OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b",
+- PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH },
+- { OPTION_COUNTUP, 0, "ambiguous", &ambiguous, NULL,
+- "positive ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+- { OPTION_COUNTUP, 0, "no-ambiguous", &ambiguous, NULL,
+- "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
++ {
++ .type = OPTION_COUNTUP,
++ .short_name = '+',
++ .value = &boolean,
++ .help = "same as -b",
++ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
++ },
++ {
++ .type = OPTION_COUNTUP,
++ .long_name = "ambiguous",
++ .value = &ambiguous,
++ .help = "positive ambiguity",
++ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
++ },
++ {
++ .type = OPTION_COUNTUP,
++ .long_name = "no-ambiguous",
++ .value = &ambiguous,
++ .help = "negative ambiguity",
++ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
++ },
+ OPT_GROUP("Standard options"),
+ OPT__ABBREV(&abbrev),
+ OPT__VERBOSE(&verbose, "be verbose"),
+
+--
+2.49.0.805.g082f7c87e0.dirty
+
+
+From mboxrd@z Thu Jan 1 00:00:00 1970
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 17 Apr 2025 12:49:38 +0200
+Subject: [PATCH v4 3/7] parse-options: support unit factors in
+ `OPT_INTEGER()`
+References: <20250417-b4-pks-parse-options-integers-v4-0-9cbc76b61cfe@pks.im>
+To: git@vger.kernel.org
+X-Mailer: b4 0.14.2
+
+There are two main differences between `OPT_INTEGER()` and
+`OPT_MAGNITUDE()`:
+
+ - The former parses signed integers whereas the latter parses unsigned
+ integers.
+
+ - The latter parses unit factors like 'k', 'm' or 'g'.
+
+While the first difference makes obvious sense, there isn't really a
+good reason why signed integers shouldn't support unit factors, too.
+
+This inconsistency will also become a bit of a problem with subsequent
+commits, where we will fix a couple of callsites that pass an unsigned
+integer to `OPT_INTEGER()`. There are three options:
+
+ - We could adapt those users to instead pass a signed integer, but
+ this would needlessly extend the range of accepted integer values.
+
+ - We could convert them to use `OPT_MAGNITUDE()`, as it only accepts
+ unsigned integers. But now we have the inconsistency that we also
+ start to accept unit factors.
+
+ - We could introduce `OPT_UNSIGNED()` as equivalent to `OPT_INTEGER()`
+ so that it knows to only accept unsigned integers without unit
+ suffix.
+
+Introducing a whole new option type feels a bit excessive. There also
+isn't really a good reason why `OPT_INTEGER()` cannot be extended to
+also accept unit factors: all valid values passed to such options cannot
+have a unit factors right now, so there wouldn't be any ambiguity.
+
+Refactor `OPT_INTEGER()` to use `git_parse_int()`, which knows to
+interpret unit factors. This removes the inconsistency between the
+signed and unsigned options so that we can easily fix up callsites that
+pass the wrong integer type right now.
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+---
+ Documentation/technical/api-parse-options.adoc | 6 ++++--
+ parse-options.c | 8 ++++----
+ t/t0040-parse-options.sh | 4 +++-
+ 3 files changed, 11 insertions(+), 7 deletions(-)
+
+diff --git a/Documentation/technical/api-parse-options.adoc b/Documentation/technical/api-parse-options.adoc
+index 61fa6ee1678..63acfb419bd 100644
+--- a/Documentation/technical/api-parse-options.adoc
++++ b/Documentation/technical/api-parse-options.adoc
+@@ -211,8 +211,10 @@ There are some macros to easily define options:
+ Use of `--no-option` will clear the list of preceding values.
+
+ `OPT_INTEGER(short, long, &int_var, description)`::
+- Introduce an option with integer argument.
+- The integer is put into `int_var`.
++ Introduce an option with integer argument. The argument must be a
++ integer and may include a suffix of 'k', 'm' or 'g' to
++ scale the provided value by 1024, 1024^2 or 1024^3 respectively.
++ The scaled value is put into `int_var`.
+
+ `OPT_MAGNITUDE(short, long, &unsigned_long_var, description)`::
+ Introduce an option with a size argument. The argument must be a
+diff --git a/parse-options.c b/parse-options.c
+index 35fbb3b0d63..b287436e81a 100644
+--- a/parse-options.c
++++ b/parse-options.c
+@@ -73,7 +73,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
+ enum opt_parsed flags,
+ const char **argp)
+ {
+- const char *s, *arg;
++ const char *arg;
+ const int unset = flags & OPT_UNSET;
+ int err;
+
+@@ -185,9 +185,9 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
+ if (!*arg)
+ return error(_("%s expects a numerical value"),
+ optname(opt, flags));
+- *(int *)opt->value = strtol(arg, (char **)&s, 10);
+- if (*s)
+- return error(_("%s expects a numerical value"),
++ if (!git_parse_int(arg, opt->value))
++ return error(_("%s expects an integer value"
++ " with an optional k/m/g suffix"),
+ optname(opt, flags));
+ return 0;
+
+diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
+index 2fe3522305f..0c538c4b437 100755
+--- a/t/t0040-parse-options.sh
++++ b/t/t0040-parse-options.sh
+@@ -111,7 +111,9 @@ test_expect_success 'OPT_BOOL() no negation #2' 'check_unknown_i18n --no-no-fear
+
+ test_expect_success 'OPT_BOOL() positivation' 'check boolean: 0 -D --doubt'
+
+-test_expect_success 'OPT_INT() negative' 'check integer: -2345 -i -2345'
++test_expect_success 'OPT_INTEGER() negative' 'check integer: -2345 -i -2345'
++test_expect_success 'OPT_INTEGER() kilo' 'check integer: 239616 -i 234k'
++test_expect_success 'OPT_INTEGER() negative kilo' 'check integer: -239616 -i -234k'
+
+ test_expect_success 'OPT_MAGNITUDE() simple' '
+ check magnitude: 2345678 -m 2345678
+
+--
+2.49.0.805.g082f7c87e0.dirty
+
+
+From mboxrd@z Thu Jan 1 00:00:00 1970
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 17 Apr 2025 12:49:39 +0200
+Subject: [PATCH v4 4/7] parse-options: rename `OPT_MAGNITUDE()` to
+ `OPT_UNSIGNED()`
+References: <20250417-b4-pks-parse-options-integers-v4-0-9cbc76b61cfe@pks.im>
+To: git@vger.kernel.org
+X-Mailer: b4 0.14.2
+
+With the preceding commit, `OPT_INTEGER()` has learned to support unit
+factors. Consequently, the major differencen between `OPT_INTEGER()` and
+`OPT_MAGNITUDE()` isn't the support of unit factors anymore, as both of
+them do support them now. Instead, the difference is that one handles
+signed and the other handles unsigned integers.
+
+Adapt the name of `OPT_MAGNITUDE()` accordingly by renaming it to
+`OPT_UNSIGNED()`.
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+---
+ Documentation/technical/api-parse-options.adoc | 4 +--
+ builtin/gc.c | 4 +--
+ builtin/multi-pack-index.c | 2 +-
+ builtin/pack-objects.c | 8 ++---
+ builtin/repack.c | 8 ++---
+ parse-options.c | 6 ++--
+ parse-options.h | 6 ++--
+ t/helper/test-parse-options.c | 6 ++--
+ t/t0040-parse-options.sh | 50 +++++++++++++-------------
+ 9 files changed, 47 insertions(+), 47 deletions(-)
+
+diff --git a/Documentation/technical/api-parse-options.adoc b/Documentation/technical/api-parse-options.adoc
+index 63acfb419bd..880eb946425 100644
+--- a/Documentation/technical/api-parse-options.adoc
++++ b/Documentation/technical/api-parse-options.adoc
+@@ -216,8 +216,8 @@ There are some macros to easily define options:
+ scale the provided value by 1024, 1024^2 or 1024^3 respectively.
+ The scaled value is put into `int_var`.
+
+-`OPT_MAGNITUDE(short, long, &unsigned_long_var, description)`::
+- Introduce an option with a size argument. The argument must be a
++`OPT_UNSIGNED(short, long, &unsigned_long_var, description)`::
++ Introduce an option with an unsigned integer argument. The argument must be a
+ non-negative integer and may include a suffix of 'k', 'm' or 'g' to
+ scale the provided value by 1024, 1024^2 or 1024^3 respectively.
+ The scaled value is put into `unsigned_long_var`.
+diff --git a/builtin/gc.c b/builtin/gc.c
+index 6707a26bc6e..b32cf937cdf 100644
+--- a/builtin/gc.c
++++ b/builtin/gc.c
+@@ -709,8 +709,8 @@ struct repository *repo UNUSED)
+ .defval = (intptr_t)prune_expire_arg,
+ },
+ OPT_BOOL(0, "cruft", &cfg.cruft_packs, N_("pack unreferenced objects separately")),
+- OPT_MAGNITUDE(0, "max-cruft-size", &cfg.max_cruft_size,
+- N_("with --cruft, limit the size of new cruft packs")),
++ OPT_UNSIGNED(0, "max-cruft-size", &cfg.max_cruft_size,
++ N_("with --cruft, limit the size of new cruft packs")),
+ OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
+ OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"),
+ PARSE_OPT_NOCOMPLETE),
+diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
+index 2a938466f53..e4820fd721a 100644
+--- a/builtin/multi-pack-index.c
++++ b/builtin/multi-pack-index.c
+@@ -245,7 +245,7 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv,
+ {
+ struct option *options;
+ static struct option builtin_multi_pack_index_repack_options[] = {
+- OPT_MAGNITUDE(0, "batch-size", &opts.batch_size,
++ OPT_UNSIGNED(0, "batch-size", &opts.batch_size,
+ N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")),
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
+diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
+index 79e1e6fb52b..9328812e286 100644
+--- a/builtin/pack-objects.c
++++ b/builtin/pack-objects.c
+@@ -4399,16 +4399,16 @@ int cmd_pack_objects(int argc,
+ OPT_CALLBACK_F(0, "index-version", &pack_idx_opts, N_("<version>[,<offset>]"),
+ N_("write the pack index file in the specified idx format version"),
+ PARSE_OPT_NONEG, option_parse_index_version),
+- OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit,
+- N_("maximum size of each output pack file")),
++ OPT_UNSIGNED(0, "max-pack-size", &pack_size_limit,
++ N_("maximum size of each output pack file")),
+ OPT_BOOL(0, "local", &local,
+ N_("ignore borrowed objects from alternate object store")),
+ OPT_BOOL(0, "incremental", &incremental,
+ N_("ignore packed objects")),
+ OPT_INTEGER(0, "window", &window,
+ N_("limit pack window by objects")),
+- OPT_MAGNITUDE(0, "window-memory", &window_memory_limit,
+- N_("limit pack window by memory in addition to object limit")),
++ OPT_UNSIGNED(0, "window-memory", &window_memory_limit,
++ N_("limit pack window by memory in addition to object limit")),
+ OPT_INTEGER(0, "depth", &depth,
+ N_("maximum length of delta chain allowed in the resulting pack")),
+ OPT_BOOL(0, "reuse-delta", &reuse_delta,
+diff --git a/builtin/repack.c b/builtin/repack.c
+index 75e3752353a..8bf9941b2c2 100644
+--- a/builtin/repack.c
++++ b/builtin/repack.c
+@@ -1202,8 +1202,8 @@ int cmd_repack(int argc,
+ PACK_CRUFT),
+ OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"),
+ N_("with --cruft, expire objects older than this")),
+- OPT_MAGNITUDE(0, "max-cruft-size", &cruft_po_args.max_pack_size,
+- N_("with --cruft, limit the size of new cruft packs")),
++ OPT_UNSIGNED(0, "max-cruft-size", &cruft_po_args.max_pack_size,
++ N_("with --cruft, limit the size of new cruft packs")),
+ OPT_BOOL('d', NULL, &delete_redundant,
+ N_("remove redundant packs, and run git-prune-packed")),
+ OPT_BOOL('f', NULL, &po_args.no_reuse_delta,
+@@ -1233,8 +1233,8 @@ int cmd_repack(int argc,
+ N_("limits the maximum delta depth")),
+ OPT_STRING(0, "threads", &opt_threads, N_("n"),
+ N_("limits the maximum number of threads")),
+- OPT_MAGNITUDE(0, "max-pack-size", &po_args.max_pack_size,
+- N_("maximum size of each packfile")),
++ OPT_UNSIGNED(0, "max-pack-size", &po_args.max_pack_size,
++ N_("maximum size of each packfile")),
+ OPT_PARSE_LIST_OBJECTS_FILTER(&po_args.filter_options),
+ OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
+ N_("repack objects in packs marked with .keep")),
+diff --git a/parse-options.c b/parse-options.c
+index b287436e81a..d23e587e98b 100644
+--- a/parse-options.c
++++ b/parse-options.c
+@@ -191,7 +191,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
+ optname(opt, flags));
+ return 0;
+
+- case OPTION_MAGNITUDE:
++ case OPTION_UNSIGNED:
+ if (unset) {
+ *(unsigned long *)opt->value = 0;
+ return 0;
+@@ -656,7 +656,7 @@ static void show_negated_gitcomp(const struct option *opts, int show_all,
+ case OPTION_STRING:
+ case OPTION_FILENAME:
+ case OPTION_INTEGER:
+- case OPTION_MAGNITUDE:
++ case OPTION_UNSIGNED:
+ case OPTION_CALLBACK:
+ case OPTION_BIT:
+ case OPTION_NEGBIT:
+@@ -708,7 +708,7 @@ static int show_gitcomp(const struct option *opts, int show_all)
+ case OPTION_STRING:
+ case OPTION_FILENAME:
+ case OPTION_INTEGER:
+- case OPTION_MAGNITUDE:
++ case OPTION_UNSIGNED:
+ case OPTION_CALLBACK:
+ if (opts->flags & PARSE_OPT_NOARG)
+ break;
+diff --git a/parse-options.h b/parse-options.h
+index 997ffbee805..14e4df1ee21 100644
+--- a/parse-options.h
++++ b/parse-options.h
+@@ -25,7 +25,7 @@ enum parse_opt_type {
+ /* options with arguments (usually) */
+ OPTION_STRING,
+ OPTION_INTEGER,
+- OPTION_MAGNITUDE,
++ OPTION_UNSIGNED,
+ OPTION_CALLBACK,
+ OPTION_LOWLEVEL_CALLBACK,
+ OPTION_FILENAME
+@@ -270,8 +270,8 @@ struct option {
+ #define OPT_CMDMODE(s, l, v, h, i) OPT_CMDMODE_F(s, l, v, h, i, 0)
+
+ #define OPT_INTEGER(s, l, v, h) OPT_INTEGER_F(s, l, v, h, 0)
+-#define OPT_MAGNITUDE(s, l, v, h) { \
+- .type = OPTION_MAGNITUDE, \
++#define OPT_UNSIGNED(s, l, v, h) { \
++ .type = OPTION_UNSIGNED, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
+index 997f55fd45b..fc3e2861c26 100644
+--- a/t/helper/test-parse-options.c
++++ b/t/helper/test-parse-options.c
+@@ -6,7 +6,7 @@
+
+ static int boolean = 0;
+ static int integer = 0;
+-static unsigned long magnitude = 0;
++static unsigned long unsigned_integer = 0;
+ static timestamp_t timestamp;
+ static int abbrev = 7;
+ static int verbose = -1; /* unspecified */
+@@ -140,7 +140,7 @@ int cmd__parse_options(int argc, const char **argv)
+ OPT_GROUP(""),
+ OPT_INTEGER('i', "integer", &integer, "get a integer"),
+ OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
+- OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"),
++ OPT_UNSIGNED('u', "unsigned", &unsigned_integer, "get an unsigned integer"),
+ OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
+ OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1),
+ OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2),
+@@ -210,7 +210,7 @@ int cmd__parse_options(int argc, const char **argv)
+ }
+ show(&expect, &ret, "boolean: %d", boolean);
+ show(&expect, &ret, "integer: %d", integer);
+- show(&expect, &ret, "magnitude: %lu", magnitude);
++ show(&expect, &ret, "unsigned: %lu", unsigned_integer);
+ show(&expect, &ret, "timestamp: %"PRItime, timestamp);
+ show(&expect, &ret, "string: %s", string ? string : "(not set)");
+ show(&expect, &ret, "abbrev: %d", abbrev);
+diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
+index 0c538c4b437..65a11c8dbc8 100755
+--- a/t/t0040-parse-options.sh
++++ b/t/t0040-parse-options.sh
+@@ -23,7 +23,7 @@ usage: test-tool parse-options <options>
+ -i, --[no-]integer <n>
+ get a integer
+ -j <n> get a integer, too
+- -m, --magnitude <n> get a magnitude
++ -u, --unsigned <n> get an unsigned integer
+ --[no-]set23 set integer to 23
+ --mode1 set integer to 1 (cmdmode option)
+ --mode2 set integer to 2 (cmdmode option)
+@@ -115,30 +115,30 @@ test_expect_success 'OPT_INTEGER() negative' 'check integer: -2345 -i -2345'
+ test_expect_success 'OPT_INTEGER() kilo' 'check integer: 239616 -i 234k'
+ test_expect_success 'OPT_INTEGER() negative kilo' 'check integer: -239616 -i -234k'
+
+-test_expect_success 'OPT_MAGNITUDE() simple' '
+- check magnitude: 2345678 -m 2345678
++test_expect_success 'OPT_UNSIGNED() simple' '
++ check unsigned: 2345678 -u 2345678
+ '
+
+-test_expect_success 'OPT_MAGNITUDE() kilo' '
+- check magnitude: 239616 -m 234k
++test_expect_success 'OPT_UNSIGNED() kilo' '
++ check unsigned: 239616 -u 234k
+ '
+
+-test_expect_success 'OPT_MAGNITUDE() mega' '
+- check magnitude: 104857600 -m 100m
++test_expect_success 'OPT_UNSIGNED() mega' '
++ check unsigned: 104857600 -u 100m
+ '
+
+-test_expect_success 'OPT_MAGNITUDE() giga' '
+- check magnitude: 1073741824 -m 1g
++test_expect_success 'OPT_UNSIGNED() giga' '
++ check unsigned: 1073741824 -u 1g
+ '
+
+-test_expect_success 'OPT_MAGNITUDE() 3giga' '
+- check magnitude: 3221225472 -m 3g
++test_expect_success 'OPT_UNSIGNED() 3giga' '
++ check unsigned: 3221225472 -u 3g
+ '
+
+ cat >expect <<\EOF
+ boolean: 2
+ integer: 1729
+-magnitude: 16384
++unsigned: 16384
+ timestamp: 0
+ string: 123
+ abbrev: 7
+@@ -149,7 +149,7 @@ file: prefix/my.file
+ EOF
+
+ test_expect_success 'short options' '
+- test-tool parse-options -s123 -b -i 1729 -m 16k -b -vv -n -F my.file \
++ test-tool parse-options -s123 -b -i 1729 -u 16k -b -vv -n -F my.file \
+ >output 2>output.err &&
+ test_cmp expect output &&
+ test_must_be_empty output.err
+@@ -158,7 +158,7 @@ test_expect_success 'short options' '
+ cat >expect <<\EOF
+ boolean: 2
+ integer: 1729
+-magnitude: 16384
++unsigned: 16384
+ timestamp: 0
+ string: 321
+ abbrev: 10
+@@ -169,7 +169,7 @@ file: prefix/fi.le
+ EOF
+
+ test_expect_success 'long options' '
+- test-tool parse-options --boolean --integer 1729 --magnitude 16k \
++ test-tool parse-options --boolean --integer 1729 --unsigned 16k \
+ --boolean --string2=321 --verbose --verbose --no-dry-run \
+ --abbrev=10 --file fi.le --obsolete \
+ >output 2>output.err &&
+@@ -181,7 +181,7 @@ test_expect_success 'abbreviate to something longer than SHA1 length' '
+ cat >expect <<-EOF &&
+ boolean: 0
+ integer: 0
+- magnitude: 0
++ unsigned: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 100
+@@ -255,7 +255,7 @@ test_expect_success 'superfluous value provided: cmdmode' '
+ cat >expect <<\EOF
+ boolean: 1
+ integer: 13
+-magnitude: 0
++unsigned: 0
+ timestamp: 0
+ string: 123
+ abbrev: 7
+@@ -278,7 +278,7 @@ test_expect_success 'intermingled arguments' '
+ cat >expect <<\EOF
+ boolean: 0
+ integer: 2
+-magnitude: 0
++unsigned: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 7
+@@ -345,7 +345,7 @@ cat >expect <<\EOF
+ Callback: "four", 0
+ boolean: 5
+ integer: 4
+-magnitude: 0
++unsigned: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 7
+@@ -370,7 +370,7 @@ test_expect_success 'OPT_CALLBACK() and callback errors work' '
+ cat >expect <<\EOF
+ boolean: 1
+ integer: 23
+-magnitude: 0
++unsigned: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 7
+@@ -449,7 +449,7 @@ test_expect_success 'OPT_NUMBER_CALLBACK() works' '
+ cat >expect <<\EOF
+ boolean: 0
+ integer: 0
+-magnitude: 0
++unsigned: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 7
+@@ -773,14 +773,14 @@ test_expect_success 'subcommands are incompatible with KEEP_DASHDASH unless in c
+ grep ^BUG err
+ '
+
+-test_expect_success 'negative magnitude' '
+- test_must_fail test-tool parse-options --magnitude -1 >out 2>err &&
++test_expect_success 'negative unsigned' '
++ test_must_fail test-tool parse-options --unsigned -1 >out 2>err &&
+ grep "non-negative integer" err &&
+ test_must_be_empty out
+ '
+
+-test_expect_success 'magnitude with units but no numbers' '
+- test_must_fail test-tool parse-options --magnitude m >out 2>err &&
++test_expect_success 'unsigned with units but no numbers' '
++ test_must_fail test-tool parse-options --unsigned m >out 2>err &&
+ grep "non-negative integer" err &&
+ test_must_be_empty out
+ '
+
+--
+2.49.0.805.g082f7c87e0.dirty
+
+
+From mboxrd@z Thu Jan 1 00:00:00 1970
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 17 Apr 2025 12:49:40 +0200
+Subject: [PATCH v4 5/7] parse-options: introduce precision handling for
+ `OPTION_INTEGER`
+References: <20250417-b4-pks-parse-options-integers-v4-0-9cbc76b61cfe@pks.im>
+To: git@vger.kernel.org
+X-Mailer: b4 0.14.2
+
+The `OPTION_INTEGER` option type accepts a signed integer. The type of
+the underlying integer is a simple `int`, which restricts the range of
+values accepted by such options. But there is a catch: because the
+caller provides a pointer to the value via the `.value` field, which is
+a simple void pointer. This has two consequences:
+
+ - There is no check whether the passed value is sufficiently long to
+ store the entire range of `int`. This can lead to integer wraparound
+ in the best case and out-of-bounds writes in the worst case.
+
+ - Even when a caller knows that they want to store a value larger than
+ `INT_MAX` they don't have a way to do so.
+
+In practice this doesn't tend to be a huge issue because users typically
+don't end up passing huge values to most commands. But the parsing logic
+is demonstrably broken, and it is too easy to get the calling convention
+wrong.
+
+Improve the situation by introducing a new `precision` field into the
+structure. This field gets assigned automatically by `OPT_INTEGER_F()`
+and tracks the size of the passed value. Like this it becomes possible
+for the caller to pass arbitrarily-sized integers and the underlying
+logic knows to handle it correctly by doing range checks. Furthermore,
+convert the code to use `strtoimax()` intstead of `strtol()` so that we
+can also parse values larger than `LONG_MAX`.
+
+Note that we do not yet assert signedness of the passed variable, which
+is another source of bugs. This will be handled in a subsequent commit.
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+---
+ builtin/fmt-merge-msg.c | 2 ++
+ builtin/merge.c | 1 +
+ builtin/show-branch.c | 1 +
+ builtin/tag.c | 1 +
+ parse-options.c | 52 ++++++++++++++++++++++++++++++++-----------
+ parse-options.h | 6 +++++
+ t/helper/test-parse-options.c | 3 +++
+ t/t0040-parse-options.sh | 23 ++++++++++++++++++-
+ 8 files changed, 75 insertions(+), 14 deletions(-)
+
+diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
+index 240cdb474bc..3b6aac2cf7f 100644
+--- a/builtin/fmt-merge-msg.c
++++ b/builtin/fmt-merge-msg.c
+@@ -24,6 +24,7 @@ int cmd_fmt_merge_msg(int argc,
+ .type = OPTION_INTEGER,
+ .long_name = "log",
+ .value = &shortlog_len,
++ .precision = sizeof(shortlog_len),
+ .argh = N_("n"),
+ .help = N_("populate log with at most <n> entries from shortlog"),
+ .flags = PARSE_OPT_OPTARG,
+@@ -33,6 +34,7 @@ int cmd_fmt_merge_msg(int argc,
+ .type = OPTION_INTEGER,
+ .long_name = "summary",
+ .value = &shortlog_len,
++ .precision = sizeof(shortlog_len),
+ .argh = N_("n"),
+ .help = N_("alias for --log (deprecated)"),
+ .flags = PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN,
+diff --git a/builtin/merge.c b/builtin/merge.c
+index 21787d45165..9ab10c7db0a 100644
+--- a/builtin/merge.c
++++ b/builtin/merge.c
+@@ -254,6 +254,7 @@ static struct option builtin_merge_options[] = {
+ .type = OPTION_INTEGER,
+ .long_name = "log",
+ .value = &shortlog_len,
++ .precision = sizeof(shortlog_len),
+ .argh = N_("n"),
+ .help = N_("add (at most <n>) entries from shortlog to merge commit message"),
+ .flags = PARSE_OPT_OPTARG,
+diff --git a/builtin/show-branch.c b/builtin/show-branch.c
+index dab37019d29..b549d8c3f5b 100644
+--- a/builtin/show-branch.c
++++ b/builtin/show-branch.c
+@@ -671,6 +671,7 @@ int cmd_show_branch(int ac,
+ .type = OPTION_INTEGER,
+ .long_name = "more",
+ .value = &extra,
++ .precision = sizeof(extra),
+ .argh = N_("n"),
+ .help = N_("show <n> more commits after the common ancestor"),
+ .flags = PARSE_OPT_OPTARG,
+diff --git a/builtin/tag.c b/builtin/tag.c
+index b266f12bb48..7597d93c71b 100644
+--- a/builtin/tag.c
++++ b/builtin/tag.c
+@@ -483,6 +483,7 @@ int cmd_tag(int argc,
+ .type = OPTION_INTEGER,
+ .short_name = 'n',
+ .value = &filter.lines,
++ .precision = sizeof(filter.lines),
+ .argh = N_("n"),
+ .help = N_("print <n> lines of each tag message"),
+ .flags = PARSE_OPT_OPTARG,
+diff --git a/parse-options.c b/parse-options.c
+index d23e587e98b..768718a3972 100644
+--- a/parse-options.c
++++ b/parse-options.c
+@@ -172,25 +172,51 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
+ return (*opt->ll_callback)(p, opt, p_arg, p_unset);
+ }
+ case OPTION_INTEGER:
++ {
++ intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - CHAR_BIT * opt->precision);
++ intmax_t lower_bound = -upper_bound - 1;
++ intmax_t value;
++
+ if (unset) {
+- *(int *)opt->value = 0;
+- return 0;
+- }
+- if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+- *(int *)opt->value = opt->defval;
+- return 0;
+- }
+- if (get_arg(p, opt, flags, &arg))
++ value = 0;
++ } else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
++ value = opt->defval;
++ } else if (get_arg(p, opt, flags, &arg)) {
+ return -1;
+- if (!*arg)
++ } else if (!*arg) {
+ return error(_("%s expects a numerical value"),
+ optname(opt, flags));
+- if (!git_parse_int(arg, opt->value))
+- return error(_("%s expects an integer value"
+- " with an optional k/m/g suffix"),
++ } else if (!git_parse_signed(arg, &value, upper_bound)) {
++ if (errno == ERANGE)
++ return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"),
++ arg, optname(opt, flags), lower_bound, upper_bound);
++
++ return error(_("%s expects an integer value with an optional k/m/g suffix"),
+ optname(opt, flags));
+- return 0;
++ }
++
++ if (value < lower_bound)
++ return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"),
++ arg, optname(opt, flags), lower_bound, upper_bound);
+
++ switch (opt->precision) {
++ case 1:
++ *(int8_t *)opt->value = value;
++ return 0;
++ case 2:
++ *(int16_t *)opt->value = value;
++ return 0;
++ case 4:
++ *(int32_t *)opt->value = value;
++ return 0;
++ case 8:
++ *(int64_t *)opt->value = value;
++ return 0;
++ default:
++ BUG("invalid precision for option %s",
++ optname(opt, flags));
++ }
++ }
+ case OPTION_UNSIGNED:
+ if (unset) {
+ *(unsigned long *)opt->value = 0;
+diff --git a/parse-options.h b/parse-options.h
+index 14e4df1ee21..4c430c7273c 100644
+--- a/parse-options.h
++++ b/parse-options.h
+@@ -92,6 +92,10 @@ typedef int parse_opt_subcommand_fn(int argc, const char **argv,
+ * `value`::
+ * stores pointers to the values to be filled.
+ *
++ * `precision`::
++ * precision of the integer pointed to by `value` in number of bytes. Should
++ * typically be its `sizeof()`.
++ *
+ * `argh`::
+ * token to explain the kind of argument this option wants. Does not
+ * begin in capital letter, and does not end with a full stop.
+@@ -151,6 +155,7 @@ struct option {
+ int short_name;
+ const char *long_name;
+ void *value;
++ size_t precision;
+ const char *argh;
+ const char *help;
+
+@@ -214,6 +219,7 @@ struct option {
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
++ .precision = sizeof(*v), \
+ .argh = N_("n"), \
+ .help = (h), \
+ .flags = (f), \
+diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
+index fc3e2861c26..3689aee8315 100644
+--- a/t/helper/test-parse-options.c
++++ b/t/helper/test-parse-options.c
+@@ -120,6 +120,7 @@ int cmd__parse_options(int argc, const char **argv)
+ };
+ struct string_list expect = STRING_LIST_INIT_NODUP;
+ struct string_list list = STRING_LIST_INIT_NODUP;
++ int16_t i16 = 0;
+
+ struct option options[] = {
+ OPT_BOOL(0, "yes", &boolean, "get a boolean"),
+@@ -139,6 +140,7 @@ int cmd__parse_options(int argc, const char **argv)
+ OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4),
+ OPT_GROUP(""),
+ OPT_INTEGER('i', "integer", &integer, "get a integer"),
++ OPT_INTEGER(0, "i16", &i16, "get a 16 bit integer"),
+ OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
+ OPT_UNSIGNED('u', "unsigned", &unsigned_integer, "get an unsigned integer"),
+ OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
+@@ -210,6 +212,7 @@ int cmd__parse_options(int argc, const char **argv)
+ }
+ show(&expect, &ret, "boolean: %d", boolean);
+ show(&expect, &ret, "integer: %d", integer);
++ show(&expect, &ret, "i16: %"PRIdMAX, (intmax_t) i16);
+ show(&expect, &ret, "unsigned: %lu", unsigned_integer);
+ show(&expect, &ret, "timestamp: %"PRItime, timestamp);
+ show(&expect, &ret, "string: %s", string ? string : "(not set)");
+diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
+index 65a11c8dbc8..be785547ead 100755
+--- a/t/t0040-parse-options.sh
++++ b/t/t0040-parse-options.sh
+@@ -22,6 +22,7 @@ usage: test-tool parse-options <options>
+
+ -i, --[no-]integer <n>
+ get a integer
++ --[no-]i16 <n> get a 16 bit integer
+ -j <n> get a integer, too
+ -u, --unsigned <n> get an unsigned integer
+ --[no-]set23 set integer to 23
+@@ -138,6 +139,7 @@ test_expect_success 'OPT_UNSIGNED() 3giga' '
+ cat >expect <<\EOF
+ boolean: 2
+ integer: 1729
++i16: 0
+ unsigned: 16384
+ timestamp: 0
+ string: 123
+@@ -158,6 +160,7 @@ test_expect_success 'short options' '
+ cat >expect <<\EOF
+ boolean: 2
+ integer: 1729
++i16: 9000
+ unsigned: 16384
+ timestamp: 0
+ string: 321
+@@ -169,7 +172,7 @@ file: prefix/fi.le
+ EOF
+
+ test_expect_success 'long options' '
+- test-tool parse-options --boolean --integer 1729 --unsigned 16k \
++ test-tool parse-options --boolean --integer 1729 --i16 9000 --unsigned 16k \
+ --boolean --string2=321 --verbose --verbose --no-dry-run \
+ --abbrev=10 --file fi.le --obsolete \
+ >output 2>output.err &&
+@@ -181,6 +184,7 @@ test_expect_success 'abbreviate to something longer than SHA1 length' '
+ cat >expect <<-EOF &&
+ boolean: 0
+ integer: 0
++ i16: 0
+ unsigned: 0
+ timestamp: 0
+ string: (not set)
+@@ -255,6 +259,7 @@ test_expect_success 'superfluous value provided: cmdmode' '
+ cat >expect <<\EOF
+ boolean: 1
+ integer: 13
++i16: 0
+ unsigned: 0
+ timestamp: 0
+ string: 123
+@@ -278,6 +283,7 @@ test_expect_success 'intermingled arguments' '
+ cat >expect <<\EOF
+ boolean: 0
+ integer: 2
++i16: 0
+ unsigned: 0
+ timestamp: 0
+ string: (not set)
+@@ -345,6 +351,7 @@ cat >expect <<\EOF
+ Callback: "four", 0
+ boolean: 5
+ integer: 4
++i16: 0
+ unsigned: 0
+ timestamp: 0
+ string: (not set)
+@@ -370,6 +377,7 @@ test_expect_success 'OPT_CALLBACK() and callback errors work' '
+ cat >expect <<\EOF
+ boolean: 1
+ integer: 23
++i16: 0
+ unsigned: 0
+ timestamp: 0
+ string: (not set)
+@@ -449,6 +457,7 @@ test_expect_success 'OPT_NUMBER_CALLBACK() works' '
+ cat >expect <<\EOF
+ boolean: 0
+ integer: 0
++i16: 0
+ unsigned: 0
+ timestamp: 0
+ string: (not set)
+@@ -785,4 +794,16 @@ test_expect_success 'unsigned with units but no numbers' '
+ test_must_be_empty out
+ '
+
++test_expect_success 'i16 limits range' '
++ test-tool parse-options --i16 32767 >out &&
++ test_grep "i16: 32767" out &&
++ test_must_fail test-tool parse-options --i16 32768 2>err &&
++ test_grep "value 32768 for option .i16. not in range \[-32768,32767\]" err &&
++
++ test-tool parse-options --i16 -32768 >out &&
++ test_grep "i16: -32768" out &&
++ test_must_fail test-tool parse-options --i16 -32769 2>err &&
++ test_grep "value -32769 for option .i16. not in range \[-32768,32767\]" err
++'
++
+ test_done
+
+--
+2.49.0.805.g082f7c87e0.dirty
+
+
+From mboxrd@z Thu Jan 1 00:00:00 1970
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 17 Apr 2025 12:49:41 +0200
+Subject: [PATCH v4 6/7] parse-options: introduce precision handling for
+ `OPTION_UNSIGNED`
+References: <20250417-b4-pks-parse-options-integers-v4-0-9cbc76b61cfe@pks.im>
+To: git@vger.kernel.org
+X-Mailer: b4 0.14.2
+
+This commit is the equivalent to the preceding commit, but instead of
+introducing precision handling for `OPTION_INTEGER` we introduce it for
+`OPTION_UNSIGNED`.
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+---
+ parse-options.c | 48 +++++++++++++++++++++++++++++++++----------
+ parse-options.h | 1 +
+ parse.c | 2 +-
+ parse.h | 1 +
+ t/helper/test-parse-options.c | 3 +++
+ t/t0040-parse-options.sh | 18 +++++++++++++++-
+ 6 files changed, 60 insertions(+), 13 deletions(-)
+
+diff --git a/parse-options.c b/parse-options.c
+index 768718a3972..a9a39ecaef6 100644
+--- a/parse-options.c
++++ b/parse-options.c
+@@ -197,7 +197,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
+
+ if (value < lower_bound)
+ return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"),
+- arg, optname(opt, flags), lower_bound, upper_bound);
++ arg, optname(opt, flags), (intmax_t)lower_bound, (intmax_t)upper_bound);
+
+ switch (opt->precision) {
+ case 1:
+@@ -218,21 +218,47 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
+ }
+ }
+ case OPTION_UNSIGNED:
++ {
++ uintmax_t upper_bound = UINTMAX_MAX >> (bitsizeof(uintmax_t) - CHAR_BIT * opt->precision);
++ uintmax_t value;
++
+ if (unset) {
+- *(unsigned long *)opt->value = 0;
+- return 0;
+- }
+- if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+- *(unsigned long *)opt->value = opt->defval;
+- return 0;
+- }
+- if (get_arg(p, opt, flags, &arg))
++ value = 0;
++ } else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
++ value = opt->defval;
++ } else if (get_arg(p, opt, flags, &arg)) {
+ return -1;
+- if (!git_parse_ulong(arg, opt->value))
++ } else if (!*arg) {
++ return error(_("%s expects a numerical value"),
++ optname(opt, flags));
++ } else if (!git_parse_unsigned(arg, &value, upper_bound)) {
++ if (errno == ERANGE)
++ return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"),
++ arg, optname(opt, flags), (uintmax_t) 0, upper_bound);
++
+ return error(_("%s expects a non-negative integer value"
+ " with an optional k/m/g suffix"),
+ optname(opt, flags));
+- return 0;
++ }
++
++ switch (opt->precision) {
++ case 1:
++ *(uint8_t *)opt->value = value;
++ return 0;
++ case 2:
++ *(uint16_t *)opt->value = value;
++ return 0;
++ case 4:
++ *(uint32_t *)opt->value = value;
++ return 0;
++ case 8:
++ *(uint64_t *)opt->value = value;
++ return 0;
++ default:
++ BUG("invalid precision for option %s",
++ optname(opt, flags));
++ }
++ }
+
+ default:
+ BUG("opt->type %d should not happen", opt->type);
+diff --git a/parse-options.h b/parse-options.h
+index 4c430c7273c..dc460a26ff1 100644
+--- a/parse-options.h
++++ b/parse-options.h
+@@ -281,6 +281,7 @@ struct option {
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
++ .precision = sizeof(*v), \
+ .argh = N_("n"), \
+ .help = (h), \
+ .flags = PARSE_OPT_NONEG, \
+diff --git a/parse.c b/parse.c
+index 3c47448ca67..48313571aab 100644
+--- a/parse.c
++++ b/parse.c
+@@ -51,7 +51,7 @@ int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
+ return 0;
+ }
+
+-static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
++int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
+ {
+ if (value && *value) {
+ char *end;
+diff --git a/parse.h b/parse.h
+index 6bb9a54d9ac..ea32de9a91f 100644
+--- a/parse.h
++++ b/parse.h
+@@ -2,6 +2,7 @@
+ #define PARSE_H
+
+ int git_parse_signed(const char *value, intmax_t *ret, intmax_t max);
++int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max);
+ int git_parse_ssize_t(const char *, ssize_t *);
+ int git_parse_ulong(const char *, unsigned long *);
+ int git_parse_int(const char *value, int *ret);
+diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
+index 3689aee8315..f2663dd0c07 100644
+--- a/t/helper/test-parse-options.c
++++ b/t/helper/test-parse-options.c
+@@ -120,6 +120,7 @@ int cmd__parse_options(int argc, const char **argv)
+ };
+ struct string_list expect = STRING_LIST_INIT_NODUP;
+ struct string_list list = STRING_LIST_INIT_NODUP;
++ uint16_t u16 = 0;
+ int16_t i16 = 0;
+
+ struct option options[] = {
+@@ -143,6 +144,7 @@ int cmd__parse_options(int argc, const char **argv)
+ OPT_INTEGER(0, "i16", &i16, "get a 16 bit integer"),
+ OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
+ OPT_UNSIGNED('u', "unsigned", &unsigned_integer, "get an unsigned integer"),
++ OPT_UNSIGNED(0, "u16", &u16, "get a 16 bit unsigned integer"),
+ OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
+ OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1),
+ OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2),
+@@ -214,6 +216,7 @@ int cmd__parse_options(int argc, const char **argv)
+ show(&expect, &ret, "integer: %d", integer);
+ show(&expect, &ret, "i16: %"PRIdMAX, (intmax_t) i16);
+ show(&expect, &ret, "unsigned: %lu", unsigned_integer);
++ show(&expect, &ret, "u16: %"PRIuMAX, (uintmax_t) u16);
+ show(&expect, &ret, "timestamp: %"PRItime, timestamp);
+ show(&expect, &ret, "string: %s", string ? string : "(not set)");
+ show(&expect, &ret, "abbrev: %d", abbrev);
+diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
+index be785547ead..ca55ea8228c 100755
+--- a/t/t0040-parse-options.sh
++++ b/t/t0040-parse-options.sh
+@@ -25,6 +25,7 @@ usage: test-tool parse-options <options>
+ --[no-]i16 <n> get a 16 bit integer
+ -j <n> get a integer, too
+ -u, --unsigned <n> get an unsigned integer
++ --u16 <n> get a 16 bit unsigned integer
+ --[no-]set23 set integer to 23
+ --mode1 set integer to 1 (cmdmode option)
+ --mode2 set integer to 2 (cmdmode option)
+@@ -141,6 +142,7 @@ boolean: 2
+ integer: 1729
+ i16: 0
+ unsigned: 16384
++u16: 0
+ timestamp: 0
+ string: 123
+ abbrev: 7
+@@ -162,6 +164,7 @@ boolean: 2
+ integer: 1729
+ i16: 9000
+ unsigned: 16384
++u16: 32768
+ timestamp: 0
+ string: 321
+ abbrev: 10
+@@ -173,7 +176,7 @@ EOF
+
+ test_expect_success 'long options' '
+ test-tool parse-options --boolean --integer 1729 --i16 9000 --unsigned 16k \
+- --boolean --string2=321 --verbose --verbose --no-dry-run \
++ --u16 32k --boolean --string2=321 --verbose --verbose --no-dry-run \
+ --abbrev=10 --file fi.le --obsolete \
+ >output 2>output.err &&
+ test_must_be_empty output.err &&
+@@ -186,6 +189,7 @@ test_expect_success 'abbreviate to something longer than SHA1 length' '
+ integer: 0
+ i16: 0
+ unsigned: 0
++ u16: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 100
+@@ -261,6 +265,7 @@ boolean: 1
+ integer: 13
+ i16: 0
+ unsigned: 0
++u16: 0
+ timestamp: 0
+ string: 123
+ abbrev: 7
+@@ -285,6 +290,7 @@ boolean: 0
+ integer: 2
+ i16: 0
+ unsigned: 0
++u16: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 7
+@@ -353,6 +359,7 @@ boolean: 5
+ integer: 4
+ i16: 0
+ unsigned: 0
++u16: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 7
+@@ -379,6 +386,7 @@ boolean: 1
+ integer: 23
+ i16: 0
+ unsigned: 0
++u16: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 7
+@@ -459,6 +467,7 @@ boolean: 0
+ integer: 0
+ i16: 0
+ unsigned: 0
++u16: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 7
+@@ -806,4 +815,11 @@ test_expect_success 'i16 limits range' '
+ test_grep "value -32769 for option .i16. not in range \[-32768,32767\]" err
+ '
+
++test_expect_success 'u16 limits range' '
++ test-tool parse-options --u16 65535 >out &&
++ test_grep "u16: 65535" out &&
++ test_must_fail test-tool parse-options --u16 65536 2>err &&
++ test_grep "value 65536 for option .u16. not in range \[0,65535\]" err
++'
++
+ test_done
+
+--
+2.49.0.805.g082f7c87e0.dirty
+
+
+From mboxrd@z Thu Jan 1 00:00:00 1970
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 17 Apr 2025 12:49:42 +0200
+Subject: [PATCH v4 7/7] parse-options: detect mismatches in integer
+ signedness
+References: <20250417-b4-pks-parse-options-integers-v4-0-9cbc76b61cfe@pks.im>
+To: git@vger.kernel.org
+X-Mailer: b4 0.14.2
+
+It was reported that "t5620-backfill.sh" fails on s390x and sparc64 in a
+test that exercises the "--min-batch-size" command line option. The
+symptom was that the option didn't seem to have an effect: we didn't
+fetch objects with a batch size of 20, but instead fetched all objects
+at once.
+
+As it turns out, the root cause is that `--min-batch-size` uses
+`OPT_INTEGER()` to parse the command line option. While this macro
+expects the caller to pass a pointer to an integer, we instead pass a
+pointer to a `size_t`. This coincidentally works on most platforms, but
+it breaks apart on the mentioned platforms because they are big endian.
+
+This issue isn't specific to git-backfill(1): there are a couple of
+other places where we have the same type confusion going on. This
+indicates that the issue really is the interface that the parse-options
+subsystem provides -- it is simply too easy to get this wrong as there
+isn't any kind of compiler warning, and things just work on the most
+common systems.
+
+Address the systemic issue by introducing two new build asserts
+`BARF_UNLESS_SIGNED()` and `BARF_UNLESS_UNSIGNED()`. As the names
+already hint at, those macros will cause a compiler error when passed a
+value that is not signed or unsigned, respectively.
+
+Adapt `OPT_INTEGER()`, `OPT_UNSIGNED()` as well as `OPT_MAGNITUDE()` to
+use those asserts. This uncovers a small set of sites where we indeed
+have the same bug as in git-backfill(1). Adapt all of them to use the
+correct option.
+
+Reported-by: Todd Zullinger <tmz@pobox.com>
+Reported-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
+Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
+Helped-by: Jeff King <peff@peff.net>
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+---
+ apply.c | 4 ++--
+ builtin/backfill.c | 4 ++--
+ builtin/column.c | 2 +-
+ builtin/grep.c | 4 ++--
+ git-compat-util.h | 7 +++++++
+ parse-options.h | 4 ++--
+ 6 files changed, 16 insertions(+), 9 deletions(-)
+
+diff --git a/apply.c b/apply.c
+index f274a379487..a850c7d75fe 100644
+--- a/apply.c
++++ b/apply.c
+@@ -5123,8 +5123,8 @@ int apply_parse_options(int argc, const char **argv,
+ /* Think twice before adding "--nul" synonym to this */
+ OPT_SET_INT('z', NULL, &state->line_termination,
+ N_("paths are separated with NUL character"), '\0'),
+- OPT_INTEGER('C', NULL, &state->p_context,
+- N_("ensure at least <n> lines of context match")),
++ OPT_UNSIGNED('C', NULL, &state->p_context,
++ N_("ensure at least <n> lines of context match")),
+ OPT_CALLBACK(0, "whitespace", state, N_("action"),
+ N_("detect new or modified lines that have whitespace errors"),
+ apply_option_parse_whitespace),
+diff --git a/builtin/backfill.c b/builtin/backfill.c
+index 33e1ea2f84f..d95d7a2d4d6 100644
+--- a/builtin/backfill.c
++++ b/builtin/backfill.c
+@@ -123,8 +123,8 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
+ .sparse = 0,
+ };
+ struct option options[] = {
+- OPT_INTEGER(0, "min-batch-size", &ctx.min_batch_size,
+- N_("Minimum number of objects to request at a time")),
++ OPT_UNSIGNED(0, "min-batch-size", &ctx.min_batch_size,
++ N_("Minimum number of objects to request at a time")),
+ OPT_BOOL(0, "sparse", &ctx.sparse,
+ N_("Restrict the missing objects to the current sparse-checkout")),
+ OPT_END(),
+diff --git a/builtin/column.c b/builtin/column.c
+index 50314cc2559..ce6443d5fac 100644
+--- a/builtin/column.c
++++ b/builtin/column.c
+@@ -31,7 +31,7 @@ int cmd_column(int argc,
+ struct option options[] = {
+ OPT_STRING(0, "command", &real_command, N_("name"), N_("lookup config vars")),
+ OPT_COLUMN(0, "mode", &colopts, N_("layout to use")),
+- OPT_INTEGER(0, "raw-mode", &colopts, N_("layout to use")),
++ OPT_UNSIGNED(0, "raw-mode", &colopts, N_("layout to use")),
+ OPT_INTEGER(0, "width", &copts.width, N_("maximum width")),
+ OPT_STRING(0, "indent", &copts.indent, N_("string"), N_("padding space on left border")),
+ OPT_STRING(0, "nl", &copts.nl, N_("string"), N_("padding space on right border")),
+diff --git a/builtin/grep.c b/builtin/grep.c
+index c4869733e1b..f23a6f1dc86 100644
+--- a/builtin/grep.c
++++ b/builtin/grep.c
+@@ -983,9 +983,9 @@ int cmd_grep(int argc,
+ OPT_CALLBACK('C', "context", &opt, N_("n"),
+ N_("show <n> context lines before and after matches"),
+ context_callback),
+- OPT_INTEGER('B', "before-context", &opt.pre_context,
++ OPT_UNSIGNED('B', "before-context", &opt.pre_context,
+ N_("show <n> context lines before matches")),
+- OPT_INTEGER('A', "after-context", &opt.post_context,
++ OPT_UNSIGNED('A', "after-context", &opt.post_context,
+ N_("show <n> context lines after matches")),
+ OPT_INTEGER(0, "threads", &num_threads,
+ N_("use <n> worker threads")),
+diff --git a/git-compat-util.h b/git-compat-util.h
+index cf733b38acd..1218fcf81a4 100644
+--- a/git-compat-util.h
++++ b/git-compat-util.h
+@@ -110,12 +110,19 @@ DISABLE_WARNING(-Wsign-compare)
+ # define BARF_UNLESS_COPYABLE(dst, src) \
+ BUILD_ASSERT_OR_ZERO(__builtin_types_compatible_p(__typeof__(*(dst)), \
+ __typeof__(*(src))))
++
++# define BARF_UNLESS_SIGNED(var) BUILD_ASSERT_OR_ZERO(((__typeof__(var)) -1) < 0)
++# define BARF_UNLESS_UNSIGNED(var) BUILD_ASSERT_OR_ZERO(((__typeof__(var)) -1) > 0)
+ #else
+ # define BARF_UNLESS_AN_ARRAY(arr) 0
+ # define BARF_UNLESS_COPYABLE(dst, src) \
+ BUILD_ASSERT_OR_ZERO(0 ? ((*(dst) = *(src)), 0) : \
+ sizeof(*(dst)) == sizeof(*(src)))
++
++# define BARF_UNLESS_SIGNED(var) 0
++# define BARF_UNLESS_UNSIGNED(var) 0
+ #endif
++
+ /*
+ * ARRAY_SIZE - get the number of elements in a visible array
+ * @x: the array whose size you want.
+diff --git a/parse-options.h b/parse-options.h
+index dc460a26ff1..91c3e3c29b3 100644
+--- a/parse-options.h
++++ b/parse-options.h
+@@ -218,7 +218,7 @@ struct option {
+ .type = OPTION_INTEGER, \
+ .short_name = (s), \
+ .long_name = (l), \
+- .value = (v), \
++ .value = (v) + BARF_UNLESS_SIGNED(*(v)), \
+ .precision = sizeof(*v), \
+ .argh = N_("n"), \
+ .help = (h), \
+@@ -280,7 +280,7 @@ struct option {
+ .type = OPTION_UNSIGNED, \
+ .short_name = (s), \
+ .long_name = (l), \
+- .value = (v), \
++ .value = (v) + BARF_UNLESS_UNSIGNED(*(v)), \
+ .precision = sizeof(*v), \
+ .argh = N_("n"), \
+ .help = (h), \
+
+--
+2.49.0.805.g082f7c87e0.dirty
+
+
diff --git a/system/git/perl-getopt-long.patch b/system/git/perl-getopt-long.patch
deleted file mode 100644
index dd5776033..000000000
--- a/system/git/perl-getopt-long.patch
+++ /dev/null
@@ -1,459 +0,0 @@
-From 46edab516bf04c190cb2e100419dee817d3f33f6 Mon Sep 17 00:00:00 2001
-From: Todd Zullinger <tmz@pobox.com>
-Date: Wed, 15 Nov 2023 12:39:44 -0500
-Subject: [PATCH] send-email: remove stray characters from usage
-
-A few stray single quotes crept into the usage string in a2ce608244
-(send-email docs: add format-patch options, 2021-10-25). Remove them.
-
-Signed-off-by: Todd Zullinger <tmz@pobox.com>
-Signed-off-by: Junio C Hamano <gitster@pobox.com>
----
- git-send-email.perl | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/git-send-email.perl b/git-send-email.perl
-index 897cea6564fb50..85544a18bfe37e 100755
---- a/git-send-email.perl
-+++ b/git-send-email.perl
-@@ -28,8 +28,8 @@
-
- sub usage {
- print <<EOT;
--git send-email' [<options>] <file|directory>
--git send-email' [<options>] <format-patch options>
-+git send-email [<options>] <file|directory>
-+git send-email [<options>] <format-patch options>
- git send-email --dump-aliases
-
- Composing:
-From d13a73e383076636534e10ba799af0c9a2b85357 Mon Sep 17 00:00:00 2001
-From: Todd Zullinger <tmz@pobox.com>
-Date: Thu, 16 Nov 2023 14:30:10 -0500
-Subject: [PATCH] perl: bump the required Perl version to 5.8.1 from 5.8.0
-
-The following commit will make use of a Getopt::Long feature which is
-only present in Perl >= 5.8.1. Document that as the minimum version we
-support.
-
-Many of our Perl scripts will continue to run with 5.8.0 but this change
-allows us to adjust them as needed without breaking any promises to our
-users.
-
-The Perl requirement was last changed in d48b284183 (perl: bump the
-required Perl version to 5.8 from 5.6.[21], 2010-09-24). At that time,
-5.8.0 was 8 years old. It is now over 21 years old.
-
-Signed-off-by: Todd Zullinger <tmz@pobox.com>
-Signed-off-by: Junio C Hamano <gitster@pobox.com>
----
- Documentation/CodingGuidelines | 2 +-
- INSTALL | 2 +-
- contrib/diff-highlight/DiffHighlight.pm | 2 +-
- contrib/mw-to-git/Git/Mediawiki.pm | 2 +-
- git-archimport.perl | 2 +-
- git-cvsexportcommit.perl | 2 +-
- git-cvsimport.perl | 2 +-
- git-cvsserver.perl | 2 +-
- git-send-email.perl | 4 ++--
- git-svn.perl | 2 +-
- gitweb/INSTALL | 2 +-
- gitweb/gitweb.perl | 2 +-
- perl/Git.pm | 2 +-
- perl/Git/I18N.pm | 2 +-
- perl/Git/LoadCPAN.pm | 2 +-
- perl/Git/LoadCPAN/Error.pm | 2 +-
- perl/Git/LoadCPAN/Mail/Address.pm | 2 +-
- perl/Git/Packet.pm | 2 +-
- t/t0202/test.pl | 2 +-
- t/t5562/invoke-with-content-length.pl | 2 +-
- t/t9700/test.pl | 2 +-
- t/test-terminal.perl | 2 +-
- 22 files changed, 23 insertions(+), 23 deletions(-)
-
-diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
-index 65af8d82cedd5f..aa0195d6ebab0b 100644
---- a/Documentation/CodingGuidelines
-+++ b/Documentation/CodingGuidelines
-@@ -490,7 +490,7 @@ For Perl programs:
-
- - Most of the C guidelines above apply.
-
-- - We try to support Perl 5.8 and later ("use Perl 5.008").
-+ - We try to support Perl 5.8.1 and later ("use Perl 5.008001").
-
- - use strict and use warnings are strongly preferred.
-
-diff --git a/INSTALL b/INSTALL
-index 4b422888828d0e..06f29a8ae70022 100644
---- a/INSTALL
-+++ b/INSTALL
-@@ -119,7 +119,7 @@ Issues of note:
- - A POSIX-compliant shell is required to run some scripts needed
- for everyday use (e.g. "bisect", "request-pull").
-
-- - "Perl" version 5.8 or later is needed to use some of the
-+ - "Perl" version 5.8.1 or later is needed to use some of the
- features (e.g. sending patches using "git send-email",
- interacting with svn repositories with "git svn"). If you can
- live without these, use NO_PERL. Note that recent releases of
-diff --git a/contrib/diff-highlight/DiffHighlight.pm b/contrib/diff-highlight/DiffHighlight.pm
-index 376f577737591e..636add69680675 100644
---- a/contrib/diff-highlight/DiffHighlight.pm
-+++ b/contrib/diff-highlight/DiffHighlight.pm
-@@ -1,6 +1,6 @@
- package DiffHighlight;
-
--use 5.008;
-+use 5.008001;
- use warnings FATAL => 'all';
- use strict;
-
-diff --git a/contrib/mw-to-git/Git/Mediawiki.pm b/contrib/mw-to-git/Git/Mediawiki.pm
-index 917d9e2d3222c1..ff7811225ee671 100644
---- a/contrib/mw-to-git/Git/Mediawiki.pm
-+++ b/contrib/mw-to-git/Git/Mediawiki.pm
-@@ -1,6 +1,6 @@
- package Git::Mediawiki;
-
--use 5.008;
-+use 5.008001;
- use strict;
- use POSIX;
- use Git;
-diff --git a/git-archimport.perl b/git-archimport.perl
-index b7c173c345544d..f5a317b89961ce 100755
---- a/git-archimport.perl
-+++ b/git-archimport.perl
-@@ -54,7 +54,7 @@ =head1 Devel Notes
-
- =cut
-
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings;
- use Getopt::Std;
-diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
-index 289d4bc684dc26..1e03ba94d1b271 100755
---- a/git-cvsexportcommit.perl
-+++ b/git-cvsexportcommit.perl
-@@ -1,6 +1,6 @@
- #!/usr/bin/perl
-
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings;
- use Getopt::Std;
-diff --git a/git-cvsimport.perl b/git-cvsimport.perl
-index 7bf3c12d678974..07ea3443f7f336 100755
---- a/git-cvsimport.perl
-+++ b/git-cvsimport.perl
-@@ -13,7 +13,7 @@
- # The head revision is on branch "origin" by default.
- # You can change that with the '-o' option.
-
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings;
- use Getopt::Long;
-diff --git a/git-cvsserver.perl b/git-cvsserver.perl
-index 7b757360e28c01..124f598bdc0705 100755
---- a/git-cvsserver.perl
-+++ b/git-cvsserver.perl
-@@ -15,7 +15,7 @@
- ####
- ####
-
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings;
- use bytes;
-diff --git a/git-send-email.perl b/git-send-email.perl
-index 897cea6564fb50..041db702d46fcc 100755
---- a/git-send-email.perl
-+++ b/git-send-email.perl
-@@ -16,7 +16,7 @@
- # and second line is the subject of the message.
- #
-
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
- use Getopt::Long;
-@@ -228,7 +228,7 @@ sub system_or_msg {
- my @sprintf_args = ($cmd_name ? $cmd_name : $args->[0], $exit_code);
- if (defined $msg) {
- # Quiet the 'redundant' warning category, except we
-- # need to support down to Perl 5.8, so we can't do a
-+ # need to support down to Perl 5.8.1, so we can't do a
- # "no warnings 'redundant'", since that category was
- # introduced in perl 5.22, and asking for it will die
- # on older perls.
-diff --git a/git-svn.perl b/git-svn.perl
-index be987e316f92ac..1d1c52f42468b1 100755
---- a/git-svn.perl
-+++ b/git-svn.perl
-@@ -1,7 +1,7 @@
- #!/usr/bin/perl
- # Copyright (C) 2006, Eric Wong <normalperson@yhbt.net>
- # License: GPL v2 or later
--use 5.008;
-+use 5.008001;
- use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
- use strict;
- use vars qw/ $AUTHOR $VERSION
-diff --git a/gitweb/INSTALL b/gitweb/INSTALL
-index a58e6b3c44b0ef..dadc6efa81f035 100644
---- a/gitweb/INSTALL
-+++ b/gitweb/INSTALL
-@@ -29,7 +29,7 @@ Requirements
- ------------
-
- - Core git tools
-- - Perl 5.8
-+ - Perl 5.8.1
- - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
- - web server
-
-diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
-index e66eb3d9bad7cf..55e7c6567e6c47 100755
---- a/gitweb/gitweb.perl
-+++ b/gitweb/gitweb.perl
-@@ -7,7 +7,7 @@
- #
- # This program is licensed under the GPLv2
-
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings;
- # handle ACL in file access tests
-diff --git a/perl/Git.pm b/perl/Git.pm
-index 117765dc73c4a8..03bf570bf4c852 100644
---- a/perl/Git.pm
-+++ b/perl/Git.pm
-@@ -7,7 +7,7 @@ Git - Perl interface to the Git version control system
-
- package Git;
-
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
-
-diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
-index 895e759c57a9d9..5454c3a6d2c433 100644
---- a/perl/Git/I18N.pm
-+++ b/perl/Git/I18N.pm
-@@ -1,5 +1,5 @@
- package Git::I18N;
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
- BEGIN {
-diff --git a/perl/Git/LoadCPAN.pm b/perl/Git/LoadCPAN.pm
-index 0c360bc7998607..8c7fa805f97390 100644
---- a/perl/Git/LoadCPAN.pm
-+++ b/perl/Git/LoadCPAN.pm
-@@ -1,5 +1,5 @@
- package Git::LoadCPAN;
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
-
-diff --git a/perl/Git/LoadCPAN/Error.pm b/perl/Git/LoadCPAN/Error.pm
-index 5d84c202884b7c..5cecb0fcd6930a 100644
---- a/perl/Git/LoadCPAN/Error.pm
-+++ b/perl/Git/LoadCPAN/Error.pm
-@@ -1,5 +1,5 @@
- package Git::LoadCPAN::Error;
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
- use Git::LoadCPAN (
-diff --git a/perl/Git/LoadCPAN/Mail/Address.pm b/perl/Git/LoadCPAN/Mail/Address.pm
-index 340e88a7a56be3..9f808090a66a16 100644
---- a/perl/Git/LoadCPAN/Mail/Address.pm
-+++ b/perl/Git/LoadCPAN/Mail/Address.pm
-@@ -1,5 +1,5 @@
- package Git::LoadCPAN::Mail::Address;
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
- use Git::LoadCPAN (
-diff --git a/perl/Git/Packet.pm b/perl/Git/Packet.pm
-index d144f5168f37ad..d896e6952399b0 100644
---- a/perl/Git/Packet.pm
-+++ b/perl/Git/Packet.pm
-@@ -1,5 +1,5 @@
- package Git::Packet;
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
- BEGIN {
-diff --git a/t/t0202/test.pl b/t/t0202/test.pl
-index 2cbf7b95907384..47d96a2a13f93b 100755
---- a/t/t0202/test.pl
-+++ b/t/t0202/test.pl
-@@ -1,5 +1,5 @@
- #!/usr/bin/perl
--use 5.008;
-+use 5.008001;
- use lib (split(/:/, $ENV{GITPERLLIB}));
- use strict;
- use warnings;
-diff --git a/t/t5562/invoke-with-content-length.pl b/t/t5562/invoke-with-content-length.pl
-index 718dd9b49d493e..9babb9a375e5fb 100644
---- a/t/t5562/invoke-with-content-length.pl
-+++ b/t/t5562/invoke-with-content-length.pl
-@@ -1,4 +1,4 @@
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings;
-
-diff --git a/t/t9700/test.pl b/t/t9700/test.pl
-index 6d753708d2acb6..d8e85482ab2ba7 100755
---- a/t/t9700/test.pl
-+++ b/t/t9700/test.pl
-@@ -1,7 +1,7 @@
- #!/usr/bin/perl
- use lib (split(/:/, $ENV{GITPERLLIB}));
-
--use 5.008;
-+use 5.008001;
- use warnings;
- use strict;
-
-diff --git a/t/test-terminal.perl b/t/test-terminal.perl
-index 1bcf01a9a42a61..3810e9bb431903 100755
---- a/t/test-terminal.perl
-+++ b/t/test-terminal.perl
-@@ -1,5 +1,5 @@
- #!/usr/bin/perl
--use 5.008;
-+use 5.008001;
- use strict;
- use warnings;
- use IO::Pty;
-From 6ff658cc78f36baa74c0f25314b0043a8f4b4fc6 Mon Sep 17 00:00:00 2001
-From: Todd Zullinger <tmz@pobox.com>
-Date: Thu, 16 Nov 2023 14:30:11 -0500
-Subject: [PATCH] send-email: avoid duplicate specification warnings
-
-A warning is issued for options which are specified more than once
-beginning with perl-Getopt-Long >= 2.55. In addition to causing users
-to see warnings, this results in test failures which compare the output.
-An example, from t9001-send-email.37:
-
- | +++ diff -u expect actual
- | --- expect 2023-11-14 10:38:23.854346488 +0000
- | +++ actual 2023-11-14 10:38:23.848346466 +0000
- | @@ -1,2 +1,7 @@
- | +Duplicate specification "no-chain-reply-to" for option "no-chain-reply-to"
- | +Duplicate specification "to-cover|to-cover!" for option "to-cover"
- | +Duplicate specification "cc-cover|cc-cover!" for option "cc-cover"
- | +Duplicate specification "no-thread" for option "no-thread"
- | +Duplicate specification "no-to-cover" for option "no-to-cover"
- | fatal: longline.patch:35 is longer than 998 characters
- | warning: no patches were sent
- | error: last command exited with $?=1
- | not ok 37 - reject long lines
-
-Remove the duplicate option specs. These are primarily the explicit
-'--no-' prefix opts which were added in f471494303 (git-send-email.perl:
-support no- prefix with older GetOptions, 2015-01-30). This was done
-specifically to support perl-5.8.0 which includes Getopt::Long 2.32[1].
-
-Getopt::Long 2.33 added support for the '--no-' prefix natively by
-appending '!' to the option specification string, which was included in
-perl-5.8.1 and is not present in perl-5.8.0. The previous commit bumped
-the minimum supported Perl version to 5.8.1 so we no longer need to
-provide the '--no-' variants for negatable options manually.
-
-Teach `--git-completion-helper` to output the '--no-' options. They are
-not included in the options hash and would otherwise be lost.
-
-Signed-off-by: Todd Zullinger <tmz@pobox.com>
-Signed-off-by: Junio C Hamano <gitster@pobox.com>
----
- git-send-email.perl | 19 ++++++-------------
- 1 file changed, 6 insertions(+), 13 deletions(-)
-
-diff --git a/git-send-email.perl b/git-send-email.perl
-index 041db702d46fcc..60afafb375d8f4 100755
---- a/git-send-email.perl
-+++ b/git-send-email.perl
-@@ -119,13 +119,16 @@ sub completion_helper {
-
- foreach my $key (keys %$original_opts) {
- unless (exists $not_for_completion{$key}) {
-- $key =~ s/!$//;
-+ my $negatable = ($key =~ s/!$//);
-
- if ($key =~ /[:=][si]$/) {
- $key =~ s/[:=][si]$//;
- push (@send_email_opts, "--$_=") foreach (split (/\|/, $key));
- } else {
- push (@send_email_opts, "--$_") foreach (split (/\|/, $key));
-+ if ($negatable) {
-+ push (@send_email_opts, "--no-$_") foreach (split (/\|/, $key));
-+ }
- }
- }
- }
-@@ -491,7 +494,6 @@ sub config_regexp {
- "bcc=s" => \@getopt_bcc,
- "no-bcc" => \$no_bcc,
- "chain-reply-to!" => \$chain_reply_to,
-- "no-chain-reply-to" => sub {$chain_reply_to = 0},
- "sendmail-cmd=s" => \$sendmail_cmd,
- "smtp-server=s" => \$smtp_server,
- "smtp-server-option=s" => \@smtp_server_options,
-@@ -506,34 +508,25 @@ sub config_regexp {
- "smtp-auth=s" => \$smtp_auth,
- "no-smtp-auth" => sub {$smtp_auth = 'none'},
- "annotate!" => \$annotate,
-- "no-annotate" => sub {$annotate = 0},
- "compose" => \$compose,
- "quiet" => \$quiet,
- "cc-cmd=s" => \$cc_cmd,
- "suppress-from!" => \$suppress_from,
-- "no-suppress-from" => sub {$suppress_from = 0},
- "suppress-cc=s" => \@suppress_cc,
- "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
-- "no-signed-off-cc|no-signed-off-by-cc" => sub {$signed_off_by_cc = 0},
-- "cc-cover|cc-cover!" => \$cover_cc,
-- "no-cc-cover" => sub {$cover_cc = 0},
-- "to-cover|to-cover!" => \$cover_to,
-- "no-to-cover" => sub {$cover_to = 0},
-+ "cc-cover!" => \$cover_cc,
-+ "to-cover!" => \$cover_to,
- "confirm=s" => \$confirm,
- "dry-run" => \$dry_run,
- "envelope-sender=s" => \$envelope_sender,
- "thread!" => \$thread,
-- "no-thread" => sub {$thread = 0},
- "validate!" => \$validate,
-- "no-validate" => sub {$validate = 0},
- "transfer-encoding=s" => \$target_xfer_encoding,
- "format-patch!" => \$format_patch,
-- "no-format-patch" => sub {$format_patch = 0},
- "8bit-encoding=s" => \$auto_8bit_encoding,
- "compose-encoding=s" => \$compose_encoding,
- "force" => \$force,
- "xmailer!" => \$use_xmailer,
-- "no-xmailer" => sub {$use_xmailer = 0},
- "batch-size=i" => \$batch_size,
- "relogin-delay=i" => \$relogin_delay,
- "git-completion-helper" => \$git_completion_helper,