* Tc
From: Rifani mei @ 2024-03-06 8:34 UTC (permalink / raw)
To: git, Ardi Syah Asen
Dikirim dari iPhone saya
^ permalink raw reply
* [PATCH v4] tests: modernize the test script t0010-racy-git.sh
From: Aryan Gupta via GitGitGadget @ 2024-03-06 9:14 UTC (permalink / raw)
To: git
Cc: Patrick Steinhardt [ ], Michal Suchánek [ ],
Jean-Noël AVILA [ ]
In-Reply-To: <pull.1675.v3.git.1709676557639.gitgitgadget@gmail.com>
From: Aryan Gupta <garyan447@gmail.com>
Modernize the formatting of the test script to align with current
standards and improve its overall readability.
Signed-off-by: Aryan Gupta <garyan447@gmail.com>
---
[GSOC][PATCH] Modernize a test script
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1675%2Faryangupta701%2Ftest-modernize-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1675/aryangupta701/test-modernize-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/1675
Range-diff vs v3:
1: 05ee9e8a458 = 1: 14c7137baea tests: modernize the test script t0010-racy-git.sh
t/t0010-racy-git.sh | 31 +++++++++++++++----------------
1 file changed, 15 insertions(+), 16 deletions(-)
diff --git a/t/t0010-racy-git.sh b/t/t0010-racy-git.sh
index 837c8b7228b..84172a37390 100755
--- a/t/t0010-racy-git.sh
+++ b/t/t0010-racy-git.sh
@@ -10,25 +10,24 @@ TEST_PASSES_SANITIZE_LEAK=true
for trial in 0 1 2 3 4
do
- rm -f .git/index
- echo frotz >infocom
- git update-index --add infocom
- echo xyzzy >infocom
-
- files=$(git diff-files -p)
- test_expect_success \
- "Racy GIT trial #$trial part A" \
- 'test "" != "$files"'
-
+ test_expect_success "Racy git trial #$trial part A" '
+ rm -f .git/index &&
+ echo frotz >infocom &&
+ git update-index --add infocom &&
+ echo xyzzy >infocom &&
+
+ git diff-files -p >out &&
+ test_file_not_empty out
+ '
sleep 1
- echo xyzzy >cornerstone
- git update-index --add cornerstone
- files=$(git diff-files -p)
- test_expect_success \
- "Racy GIT trial #$trial part B" \
- 'test "" != "$files"'
+ test_expect_success "Racy git trial #$trial part B" '
+ echo xyzzy >cornerstone &&
+ git update-index --add cornerstone &&
+ git diff-files -p >out &&
+ test_file_not_empty out
+ '
done
test_done
base-commit: 3c2a3fdc388747b9eaf4a4a4f2035c1c9ddb26d0
--
gitgitgadget
^ permalink raw reply related
* Re: [PATCH] show-ref: add --unresolved option
From: Jean-Noël Avila @ 2024-03-06 9:36 UTC (permalink / raw)
To: John Cai via GitGitGadget, git; +Cc: John Cai
In-Reply-To: <pull.1684.git.git.1709592718743.gitgitgadget@gmail.com>
Le 04/03/2024 à 23:51, John Cai via GitGitGadget a écrit :
> From: John Cai <johncai86@gmail.com>
> > @@ -76,6 +77,13 @@ OPTIONS
> it does, 2 if it is missing, and 1 in case looking up the reference
> failed with an error other than the reference being missing.
>
> +--unresolved::
> +
> + Prints out what the reference points to without resolving it. Returns
Style nitpicking: we use imperative form in the descriptions of options
→ Print out what... Return...
> + an exit code of 0 if it does, 2 if it is missing, and 1 in case looking
> + up the reference failed with an error other than the reference being
> + missing.
> +
> --abbrev[=<n>]::
>
> Abbreviate the object name. When using `--hash`, you do
^ permalink raw reply
* [PATCH] t0610: remove unused variable assignment
From: Patrick Steinhardt @ 2024-03-06 11:17 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Karthik Nayak
In-Reply-To: <ZecFXXqUdGEQ3YhC@tanuki>
[-- Attachment #1: Type: text/plain, Size: 936 bytes --]
In b0f6b6b523 (refs/reftable: don't fail empty transactions in repo
without HEAD, 2024-02-27), we have added a new test to t0610. This test
contains a useless assignment to a variable that is never actually used.
Remove it.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0610-reftable-basics.sh | 1 -
1 file changed, 1 deletion(-)
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index c5f4d23433..686781192e 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -332,7 +332,6 @@ test_expect_success 'ref transaction: empty transaction in empty repo' '
test_when_finished "rm -rf repo" &&
git init repo &&
test_commit -C repo --no-tag A &&
- COMMIT=$(git -C repo rev-parse HEAD) &&
git -C repo update-ref -d refs/heads/main &&
test-tool -C repo ref-store main delete-refs REF_NO_DEREF msg HEAD &&
git -C repo update-ref --stdin <<-EOF
--
2.44.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related
* [PATCH 0/8] builtin/config: introduce subcommands
From: Patrick Steinhardt @ 2024-03-06 11:31 UTC (permalink / raw)
To: git
[-- Attachment #1: Type: text/plain, Size: 3106 bytes --]
Hi,
the UI of git-config(1) is quite arcane and does not really conform to
the more modern UIs that we have nowadays in Git:
- While it does have modes, those modes come in the form of switches.
E.g. you have to say git config --get-all to execute the "get-all"
mode.
- Its interface depends on the number of args. Given one arg it will
print the value of the corresponding config key, given two args it
will set that key. Did you know you can even give it three args? I
didn't. Now guess what this mode does.
This patch series overhauls git-config(1) by introducing subcommands.
This results in the following UI that matches more closely what we have
in other Git commands which are more modern:
- `git config foo.bar` -> `git config get foo.bar`
- `git config foo.bar value` -> `git config set foo.bar value`
- `git config foo.bar value value-pattern` -> `git config set-all
foo.bar value value-pattern`
- `git config --get-urlmatch` -> `git config get-urlmatch`.
Most importantly, this should help discoverability quite a lot by now
also having a proper synopsis in both the manpage, but also in `git
config -h`.
Of course, backwards compatibility is a big concern. We don't want to
just switch over to the new syntax and break all existing scripts and
muscle memory. This patch series thus abuses the fact that the implicit
modes (`git config foo.bar`, `git config foo.bar value` and `git config
foo.bar value value-pattern`) all require a key as first argument. As
keys _must_ have a dot, this allows us to unambiguously discern those
from actual subcommands.
Thus, git-config(1) now supports both old and new style arguments in a
completely backwards compatible way, which is also demonstrated by the
adapted tests. Eventually, I think we should consider dropping the old
style syntax with e.g. Git v3.0.
We could of course iterate further from here and keep on improving the
UI of the new subcommands, e.g. by merging closely related subcommands.
But for the time being I think it's easier to stop at this point and
revisit the result at a later point in time.
Also, note that I see this as kind of an experiment for how to modernize
our UI slowly over time so that things become more consistent and thus
hopefully easier to use in the long term.
Patrick
Patrick Steinhardt (8):
builtin/config: move option array around
builtin/config: move "fixed-value" option to correct group
builtin/config: use `OPT_CMDMODE()` to specify modes
builtin/config: move modes into separate functions
builtin/config: track subcommands by action
builtin/config: introduce subcommands
t1300: exercise both old- and new-style modes
Documentation/git-config: update to new-style syntax
Documentation/git-config.txt | 204 +++++-----
builtin/config.c | 671 +++++++++++++++++++-------------
t/t0450/txt-help-mismatches | 1 -
t/t1300-config.sh | 734 ++++++++++++++++++-----------------
4 files changed, 900 insertions(+), 710 deletions(-)
--
2.44.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [PATCH 1/8] builtin/config: move option array around
From: Patrick Steinhardt @ 2024-03-06 11:31 UTC (permalink / raw)
To: git
In-Reply-To: <cover.1709724089.git.ps@pks.im>
[-- Attachment #1: Type: text/plain, Size: 8066 bytes --]
Move around the option array. This will help us with a follow-up commit
that introduces subcommands to git-config(1).
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/config.c | 94 ++++++++++++++++++++++++------------------------
1 file changed, 47 insertions(+), 47 deletions(-)
diff --git a/builtin/config.c b/builtin/config.c
index b55bfae7d6..6eb6aff917 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -134,53 +134,6 @@ static int option_parse_type(const struct option *opt, const char *arg,
return 0;
}
-static struct option builtin_config_options[] = {
- OPT_GROUP(N_("Config file location")),
- OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
- OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
- OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
- OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")),
- OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
- OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
- OPT_GROUP(N_("Action")),
- OPT_BIT(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET),
- OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
- OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
- OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
- OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
- OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
- OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
- OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
- OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
- OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
- OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
- OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
- OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
- OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
- OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
- OPT_GROUP(N_("Type")),
- OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type),
- OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
- OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
- OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
- OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR),
- OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
- OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
- OPT_GROUP(N_("Other")),
- OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")),
- OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
- OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
- OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
- OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
- OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
- OPT_END(),
-};
-
-static NORETURN void usage_builtin_config(void)
-{
- usage_with_options(builtin_config_usage, builtin_config_options);
-}
-
static void check_argc(int argc, int min, int max)
{
if (argc >= min && argc <= max)
@@ -669,6 +622,53 @@ static char *default_user_config(void)
return strbuf_detach(&buf, NULL);
}
+static struct option builtin_config_options[] = {
+ OPT_GROUP(N_("Config file location")),
+ OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
+ OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
+ OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
+ OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")),
+ OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
+ OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
+ OPT_GROUP(N_("Action")),
+ OPT_BIT(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET),
+ OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
+ OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
+ OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
+ OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
+ OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
+ OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
+ OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
+ OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
+ OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
+ OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
+ OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
+ OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
+ OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
+ OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
+ OPT_GROUP(N_("Type")),
+ OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type),
+ OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
+ OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
+ OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
+ OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR),
+ OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
+ OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
+ OPT_GROUP(N_("Other")),
+ OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")),
+ OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
+ OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
+ OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
+ OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
+ OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
+ OPT_END(),
+};
+
+static NORETURN void usage_builtin_config(void)
+{
+ usage_with_options(builtin_config_usage, builtin_config_options);
+}
+
int cmd_config(int argc, const char **argv, const char *prefix)
{
int nongit = !startup_info->have_repository;
--
2.44.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related
* [PATCH 2/8] builtin/config: move "fixed-value" option to correct group
From: Patrick Steinhardt @ 2024-03-06 11:31 UTC (permalink / raw)
To: git
In-Reply-To: <cover.1709724089.git.ps@pks.im>
[-- Attachment #1: Type: text/plain, Size: 1922 bytes --]
The `--fixed-value` option can be used to alter how the value-pattern
parameter is interpreted for the various submodes of git-config(1). But
while it is an option, it is currently listed as part of the submodes
group the command, which is wrong.
Move the option to the "Other" group, which hosts the various options
known to git-config(1).
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/config.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/builtin/config.c b/builtin/config.c
index 6eb6aff917..fcd6190f12 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -642,7 +642,6 @@ static struct option builtin_config_options[] = {
OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
- OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
@@ -661,6 +660,7 @@ static struct option builtin_config_options[] = {
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
+ OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
OPT_END(),
};
--
2.44.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related
* [PATCH 3/8] builtin/config: use `OPT_CMDMODE()` to specify modes
From: Patrick Steinhardt @ 2024-03-06 11:31 UTC (permalink / raw)
To: git
In-Reply-To: <cover.1709724089.git.ps@pks.im>
[-- Attachment #1: Type: text/plain, Size: 5386 bytes --]
The git-config(1) command has various different modes which are
accessible via e.g. `--get-urlmatch` or `--unset-all`. These modes are
declared with `OPT_BIT()`, which causes two minor issues:
- The respective modes also have a negated form `--no-get-urlmatch`,
which is unintended.
- We have to manually handle exclusiveness of the modes.
Switch these options to instead use `OPT_CMDMODE()`, which is made
exactly for this usecase. Remove the now-unneeded check that only a
single mode is given, which is now handled by the parse-options
interface.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/config.c | 32 ++++++++++++++------------------
t/t1300-config.sh | 13 +++++++++++++
2 files changed, 27 insertions(+), 18 deletions(-)
diff --git a/builtin/config.c b/builtin/config.c
index fcd6190f12..8a2d1a5de7 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -631,20 +631,20 @@ static struct option builtin_config_options[] = {
OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
OPT_GROUP(N_("Action")),
- OPT_BIT(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET),
- OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
- OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
- OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
- OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
- OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
- OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
- OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
- OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
- OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
- OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
- OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
- OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
- OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
+ OPT_CMDMODE(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET),
+ OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
+ OPT_CMDMODE(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
+ OPT_CMDMODE(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
+ OPT_CMDMODE(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
+ OPT_CMDMODE(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
+ OPT_CMDMODE(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
+ OPT_CMDMODE(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
+ OPT_CMDMODE(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
+ OPT_CMDMODE(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
+ OPT_CMDMODE('l', "list", &actions, N_("list all"), ACTION_LIST),
+ OPT_CMDMODE('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
+ OPT_CMDMODE(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
+ OPT_CMDMODE(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
OPT_GROUP(N_("Type")),
OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type),
OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
@@ -767,10 +767,6 @@ int cmd_config(int argc, const char **argv, const char *prefix)
usage_builtin_config();
}
- if (HAS_MULTI_BITS(actions)) {
- error(_("only one action at a time"));
- usage_builtin_config();
- }
if (actions == 0)
switch (argc) {
case 1: actions = ACTION_GET; break;
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 31c3878687..2d1bc1e27e 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -2612,4 +2612,17 @@ test_expect_success 'includeIf.hasconfig:remote.*.url forbids remote url in such
grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url" err
'
+test_expect_success 'negated mode causes failure' '
+ test_must_fail git config --no-get 2>err &&
+ grep "unknown option \`no-get${SQ}" err
+'
+
+test_expect_success 'specifying multiple modes causes failure' '
+ cat >expect <<-EOF &&
+ error: options ${SQ}--get-all${SQ} and ${SQ}--get${SQ} cannot be used together
+ EOF
+ test_must_fail git config --get --get-all 2>err &&
+ test_cmp expect err
+'
+
test_done
--
2.44.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related
* [PATCH 4/8] builtin/config: move modes into separate functions
From: Patrick Steinhardt @ 2024-03-06 11:31 UTC (permalink / raw)
To: git
In-Reply-To: <cover.1709724089.git.ps@pks.im>
[-- Attachment #1: Type: text/plain, Size: 15126 bytes --]
The git-config(1) command has several different modes which cause it to
do different things. The logic for each of these modes is hosted in a
giant switch in `cmd_config()` itself. For one, this is hard to read.
But second, we're about to introduce proper subcommands to git-config(1)
that will require separate functions for each of the modes.
Refactor the code and move each mode into its own function to prepare
for this.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/config.c | 410 +++++++++++++++++++++++++++++------------------
1 file changed, 255 insertions(+), 155 deletions(-)
diff --git a/builtin/config.c b/builtin/config.c
index 8a2d1a5de7..a6ab9b8204 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -44,6 +44,7 @@ static struct config_options config_options;
static int show_origin;
static int show_scope;
static int fixed_value;
+static int config_flags;
#define ACTION_GET (1<<0)
#define ACTION_GET_ALL (1<<1)
@@ -622,6 +623,225 @@ static char *default_user_config(void)
return strbuf_detach(&buf, NULL);
}
+static int cmd_config_list(int argc, const char **argv, const char *prefix)
+{
+ check_argc(argc, 0, 0);
+ if (config_with_options(show_all_config, NULL,
+ &given_config_source, the_repository,
+ &config_options) < 0) {
+ if (given_config_source.file)
+ die_errno(_("unable to read config file '%s'"),
+ given_config_source.file);
+ else
+ die(_("error processing config file(s)"));
+ }
+
+ return 0;
+}
+
+static int cmd_config_edit(int argc, const char **argv, const char *prefix)
+{
+ char *config_file;
+
+ check_argc(argc, 0, 0);
+ if (!given_config_source.file && !startup_info->have_repository)
+ die(_("not in a git directory"));
+ if (given_config_source.use_stdin)
+ die(_("editing stdin is not supported"));
+ if (given_config_source.blob)
+ die(_("editing blobs is not supported"));
+ git_config(git_default_config, NULL);
+ config_file = given_config_source.file ?
+ xstrdup(given_config_source.file) :
+ git_pathdup("config");
+ if (use_global_config) {
+ int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd >= 0) {
+ char *content = default_user_config();
+ write_str_in_full(fd, content);
+ free(content);
+ close(fd);
+ }
+ else if (errno != EEXIST)
+ die_errno(_("cannot create configuration file %s"), config_file);
+ }
+ launch_editor(config_file, NULL, NULL);
+ free(config_file);
+
+ return 0;
+}
+
+static int cmd_config_set(int argc, const char **argv, const char *prefix)
+{
+ struct key_value_info default_kvi = KVI_INIT;
+ char *value = NULL;
+ int ret;
+
+ check_write();
+ check_argc(argc, 2, 2);
+ value = normalize_value(argv[0], argv[1], &default_kvi);
+ ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
+ if (ret == CONFIG_NOTHING_SET)
+ error(_("cannot overwrite multiple values with a single value\n"
+ " Use a regexp, --add or --replace-all to change %s."), argv[0]);
+
+ free(value);
+ return ret;
+}
+
+static int cmd_config_set_all(int argc, const char **argv, const char *prefix)
+{
+ struct key_value_info default_kvi = KVI_INIT;
+ char *value = NULL;
+ int ret;
+
+ check_write();
+ check_argc(argc, 2, 3);
+ value = normalize_value(argv[0], argv[1], &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value, argv[2],
+ config_flags);
+
+ free(value);
+ return ret;
+}
+
+static int cmd_config_add(int argc, const char **argv, const char *prefix)
+{
+ struct key_value_info default_kvi = KVI_INIT;
+ char *value = NULL;
+ int ret;
+
+ check_write();
+ check_argc(argc, 2, 2);
+ value = normalize_value(argv[0], argv[1], &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value,
+ CONFIG_REGEX_NONE,
+ config_flags);
+
+ free(value);
+ return ret;
+}
+
+static int cmd_config_replace_all(int argc, const char **argv, const char *prefix)
+{
+ struct key_value_info default_kvi = KVI_INIT;
+ char *value = NULL;
+ int ret;
+
+ check_write();
+ check_argc(argc, 2, 3);
+ value = normalize_value(argv[0], argv[1], &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value, argv[2],
+ config_flags | CONFIG_FLAGS_MULTI_REPLACE);
+
+ free(value);
+ return ret;
+}
+
+static int cmd_config_get(int argc, const char **argv, const char *prefix)
+{
+ check_argc(argc, 1, 2);
+ return get_value(argv[0], argv[1], config_flags);
+}
+
+static int cmd_config_get_all(int argc, const char **argv, const char *prefix)
+{
+ do_all = 1;
+ check_argc(argc, 1, 2);
+ return get_value(argv[0], argv[1], config_flags);
+}
+
+static int cmd_config_get_regexp(int argc, const char **argv, const char *prefix)
+{
+ show_keys = 1;
+ use_key_regexp = 1;
+ do_all = 1;
+ check_argc(argc, 1, 2);
+ return get_value(argv[0], argv[1], config_flags);
+}
+
+static int cmd_config_get_urlmatch(int argc, const char **argv, const char *prefix)
+{
+ check_argc(argc, 2, 2);
+ return get_urlmatch(argv[0], argv[1]);
+}
+
+static int cmd_config_unset(int argc, const char **argv, const char *prefix)
+{
+ check_write();
+ check_argc(argc, 1, 2);
+ if (argc == 2)
+ return git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], NULL, argv[1],
+ config_flags);
+ else
+ return git_config_set_in_file_gently(given_config_source.file,
+ argv[0], NULL);
+}
+
+static int cmd_config_unset_all(int argc, const char **argv, const char *prefix)
+{
+ check_write();
+ check_argc(argc, 1, 2);
+ return git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], NULL, argv[1],
+ config_flags | CONFIG_FLAGS_MULTI_REPLACE);
+}
+
+static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)
+{
+ int ret;
+
+ check_write();
+ check_argc(argc, 2, 2);
+ ret = git_config_rename_section_in_file(given_config_source.file,
+ argv[0], argv[1]);
+ if (ret < 0)
+ return ret;
+ else if (!ret)
+ die(_("no such section: %s"), argv[0]);
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)
+{
+ int ret;
+
+ check_write();
+ check_argc(argc, 1, 1);
+ ret = git_config_rename_section_in_file(given_config_source.file,
+ argv[0], NULL);
+ if (ret < 0)
+ return ret;
+ else if (!ret)
+ die(_("no such section: %s"), argv[0]);
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static int cmd_config_get_color(int argc, const char **argv, const char *prefix)
+{
+ check_argc(argc, 1, 2);
+ get_color(argv[0], argv[1]);
+ return 0;
+}
+
+static int cmd_config_get_colorbool(int argc, const char **argv, const char *prefix)
+{
+ check_argc(argc, 1, 2);
+ if (argc == 2)
+ color_stdout_is_tty = git_config_bool("command line", argv[1]);
+ return get_colorbool(argv[0], argc == 2);
+}
+
static struct option builtin_config_options[] = {
OPT_GROUP(N_("Config file location")),
OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
@@ -671,12 +891,6 @@ static NORETURN void usage_builtin_config(void)
int cmd_config(int argc, const char **argv, const char *prefix)
{
- int nongit = !startup_info->have_repository;
- char *value = NULL;
- int flags = 0;
- int ret = 0;
- struct key_value_info default_kvi = KVI_INIT;
-
given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
argc = parse_options(argc, argv, prefix, builtin_config_options,
@@ -690,7 +904,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
usage_builtin_config();
}
- if (nongit) {
+ if (!startup_info->have_repository) {
if (use_local_config)
die(_("--local can only be used inside a git repository"));
if (given_config_source.blob)
@@ -751,7 +965,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
config_options.respect_includes = !given_config_source.file;
else
config_options.respect_includes = respect_includes_opt;
- if (!nongit) {
+ if (startup_info->have_repository) {
config_options.commondir = get_git_common_dir();
config_options.git_dir = get_git_dir();
}
@@ -826,159 +1040,45 @@ int cmd_config(int argc, const char **argv, const char *prefix)
usage_builtin_config();
}
- flags |= CONFIG_FLAGS_FIXED_VALUE;
+ config_flags |= CONFIG_FLAGS_FIXED_VALUE;
}
if (actions & PAGING_ACTIONS)
setup_auto_pager("config", 1);
if (actions == ACTION_LIST) {
- check_argc(argc, 0, 0);
- if (config_with_options(show_all_config, NULL,
- &given_config_source, the_repository,
- &config_options) < 0) {
- if (given_config_source.file)
- die_errno(_("unable to read config file '%s'"),
- given_config_source.file);
- else
- die(_("error processing config file(s)"));
- }
+ return cmd_config_list(argc, argv, prefix);
+ } else if (actions == ACTION_EDIT) {
+ return cmd_config_edit(argc, argv, prefix);
+ } else if (actions == ACTION_SET) {
+ return cmd_config_set(argc, argv, prefix);
+ } else if (actions == ACTION_SET_ALL) {
+ return cmd_config_set_all(argc, argv, prefix);
+ } else if (actions == ACTION_ADD) {
+ return cmd_config_add(argc, argv, prefix);
+ } else if (actions == ACTION_REPLACE_ALL) {
+ return cmd_config_replace_all(argc, argv, prefix);
+ } else if (actions == ACTION_GET) {
+ return cmd_config_get(argc, argv, prefix);
+ } else if (actions == ACTION_GET_ALL) {
+ return cmd_config_get_all(argc, argv, prefix);
+ } else if (actions == ACTION_GET_REGEXP) {
+ return cmd_config_get_regexp(argc, argv, prefix);
+ } else if (actions == ACTION_GET_URLMATCH) {
+ return cmd_config_get_urlmatch(argc, argv, prefix);
+ } else if (actions == ACTION_UNSET) {
+ return cmd_config_unset(argc, argv, prefix);
+ } else if (actions == ACTION_UNSET_ALL) {
+ return cmd_config_unset_all(argc, argv, prefix);
+ } else if (actions == ACTION_RENAME_SECTION) {
+ return cmd_config_rename_section(argc, argv, prefix);
+ } else if (actions == ACTION_REMOVE_SECTION) {
+ return cmd_config_remove_section(argc, argv, prefix);
+ } else if (actions == ACTION_GET_COLOR) {
+ return cmd_config_get_color(argc, argv, prefix);
+ } else if (actions == ACTION_GET_COLORBOOL) {
+ return cmd_config_get_colorbool(argc, argv, prefix);
}
- else if (actions == ACTION_EDIT) {
- char *config_file;
- check_argc(argc, 0, 0);
- if (!given_config_source.file && nongit)
- die(_("not in a git directory"));
- if (given_config_source.use_stdin)
- die(_("editing stdin is not supported"));
- if (given_config_source.blob)
- die(_("editing blobs is not supported"));
- git_config(git_default_config, NULL);
- config_file = given_config_source.file ?
- xstrdup(given_config_source.file) :
- git_pathdup("config");
- if (use_global_config) {
- int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
- if (fd >= 0) {
- char *content = default_user_config();
- write_str_in_full(fd, content);
- free(content);
- close(fd);
- }
- else if (errno != EEXIST)
- die_errno(_("cannot create configuration file %s"), config_file);
- }
- launch_editor(config_file, NULL, NULL);
- free(config_file);
- }
- else if (actions == ACTION_SET) {
- check_write();
- check_argc(argc, 2, 2);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
- if (ret == CONFIG_NOTHING_SET)
- error(_("cannot overwrite multiple values with a single value\n"
- " Use a regexp, --add or --replace-all to change %s."), argv[0]);
- }
- else if (actions == ACTION_SET_ALL) {
- check_write();
- check_argc(argc, 2, 3);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], value, argv[2],
- flags);
- }
- else if (actions == ACTION_ADD) {
- check_write();
- check_argc(argc, 2, 2);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], value,
- CONFIG_REGEX_NONE,
- flags);
- }
- else if (actions == ACTION_REPLACE_ALL) {
- check_write();
- check_argc(argc, 2, 3);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], value, argv[2],
- flags | CONFIG_FLAGS_MULTI_REPLACE);
- }
- else if (actions == ACTION_GET) {
- check_argc(argc, 1, 2);
- return get_value(argv[0], argv[1], flags);
- }
- else if (actions == ACTION_GET_ALL) {
- do_all = 1;
- check_argc(argc, 1, 2);
- return get_value(argv[0], argv[1], flags);
- }
- else if (actions == ACTION_GET_REGEXP) {
- show_keys = 1;
- use_key_regexp = 1;
- do_all = 1;
- check_argc(argc, 1, 2);
- return get_value(argv[0], argv[1], flags);
- }
- else if (actions == ACTION_GET_URLMATCH) {
- check_argc(argc, 2, 2);
- return get_urlmatch(argv[0], argv[1]);
- }
- else if (actions == ACTION_UNSET) {
- check_write();
- check_argc(argc, 1, 2);
- if (argc == 2)
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], NULL, argv[1],
- flags);
- else
- return git_config_set_in_file_gently(given_config_source.file,
- argv[0], NULL);
- }
- else if (actions == ACTION_UNSET_ALL) {
- check_write();
- check_argc(argc, 1, 2);
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], NULL, argv[1],
- flags | CONFIG_FLAGS_MULTI_REPLACE);
- }
- else if (actions == ACTION_RENAME_SECTION) {
- check_write();
- check_argc(argc, 2, 2);
- ret = git_config_rename_section_in_file(given_config_source.file,
- argv[0], argv[1]);
- if (ret < 0)
- return ret;
- else if (!ret)
- die(_("no such section: %s"), argv[0]);
- else
- ret = 0;
- }
- else if (actions == ACTION_REMOVE_SECTION) {
- check_write();
- check_argc(argc, 1, 1);
- ret = git_config_rename_section_in_file(given_config_source.file,
- argv[0], NULL);
- if (ret < 0)
- return ret;
- else if (!ret)
- die(_("no such section: %s"), argv[0]);
- else
- ret = 0;
- }
- else if (actions == ACTION_GET_COLOR) {
- check_argc(argc, 1, 2);
- get_color(argv[0], argv[1]);
- }
- else if (actions == ACTION_GET_COLORBOOL) {
- check_argc(argc, 1, 2);
- if (argc == 2)
- color_stdout_is_tty = git_config_bool("command line", argv[1]);
- return get_colorbool(argv[0], argc == 2);
- }
-
- free(value);
- return ret;
+ BUG("invalid action");
}
--
2.44.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related
* [PATCH 5/8] builtin/config: track subcommands by action
From: Patrick Steinhardt @ 2024-03-06 11:31 UTC (permalink / raw)
To: git
In-Reply-To: <cover.1709724089.git.ps@pks.im>
[-- Attachment #1: Type: text/plain, Size: 13026 bytes --]
Part of `cmd_config()` is a rather unwieldy switch statement that
invokes the correct subcommand function based on which action has been
requested by the user. Now that we have converted actions to be tracked
via a `OPT_CMDMODE()`, we know that the `actions` variable will
only ever have at most one bit set. This allows us to convert the
variable to use an `enum` instead, and thus to create an array that maps
from this newly introduced `enum` to the corresponding subcommand
function.
Refactor the code to do so. Besides allowing us to get rid of the giant
switch statement, this refactoring will also make it easier to introduce
proper subcommands to git-config(1).
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/config.c | 207 +++++++++++++++++++++++------------------------
1 file changed, 99 insertions(+), 108 deletions(-)
diff --git a/builtin/config.c b/builtin/config.c
index a6ab9b8204..0d58397ef5 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -20,6 +20,26 @@ static const char *const builtin_config_usage[] = {
NULL
};
+enum config_action {
+ ACTION_NONE,
+ ACTION_GET,
+ ACTION_GET_ALL,
+ ACTION_GET_REGEXP,
+ ACTION_REPLACE_ALL,
+ ACTION_ADD,
+ ACTION_UNSET,
+ ACTION_UNSET_ALL,
+ ACTION_RENAME_SECTION,
+ ACTION_REMOVE_SECTION,
+ ACTION_LIST,
+ ACTION_EDIT,
+ ACTION_SET,
+ ACTION_SET_ALL,
+ ACTION_GET_COLOR,
+ ACTION_GET_COLORBOOL,
+ ACTION_GET_URLMATCH,
+};
+
static char *key;
static regex_t *key_regexp;
static const char *value_pattern;
@@ -33,10 +53,12 @@ static char delim = '=';
static char key_delim = ' ';
static char term = '\n';
+static parse_opt_subcommand_fn *subcommand;
+static enum config_action action = ACTION_NONE;
static int use_global_config, use_system_config, use_local_config;
static int use_worktree_config;
static struct git_config_source given_config_source;
-static int actions, type;
+static int type;
static char *default_value;
static int end_nul;
static int respect_includes_opt = -1;
@@ -46,30 +68,6 @@ static int show_scope;
static int fixed_value;
static int config_flags;
-#define ACTION_GET (1<<0)
-#define ACTION_GET_ALL (1<<1)
-#define ACTION_GET_REGEXP (1<<2)
-#define ACTION_REPLACE_ALL (1<<3)
-#define ACTION_ADD (1<<4)
-#define ACTION_UNSET (1<<5)
-#define ACTION_UNSET_ALL (1<<6)
-#define ACTION_RENAME_SECTION (1<<7)
-#define ACTION_REMOVE_SECTION (1<<8)
-#define ACTION_LIST (1<<9)
-#define ACTION_EDIT (1<<10)
-#define ACTION_SET (1<<11)
-#define ACTION_SET_ALL (1<<12)
-#define ACTION_GET_COLOR (1<<13)
-#define ACTION_GET_COLORBOOL (1<<14)
-#define ACTION_GET_URLMATCH (1<<15)
-
-/*
- * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
- * one line of output and which should therefore be paged.
- */
-#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \
- ACTION_GET_REGEXP | ACTION_GET_URLMATCH)
-
#define TYPE_BOOL 1
#define TYPE_INT 2
#define TYPE_BOOL_OR_INT 3
@@ -842,6 +840,25 @@ static int cmd_config_get_colorbool(int argc, const char **argv, const char *pre
return get_colorbool(argv[0], argc == 2);
}
+static parse_opt_subcommand_fn *subcommands_by_action[] = {
+ [ACTION_LIST] = cmd_config_list,
+ [ACTION_EDIT] = cmd_config_edit,
+ [ACTION_SET] = cmd_config_set,
+ [ACTION_SET_ALL] = cmd_config_set_all,
+ [ACTION_ADD] = cmd_config_add,
+ [ACTION_REPLACE_ALL] = cmd_config_replace_all,
+ [ACTION_GET] = cmd_config_get,
+ [ACTION_GET_ALL] = cmd_config_get_all,
+ [ACTION_GET_REGEXP] = cmd_config_get_regexp,
+ [ACTION_GET_URLMATCH] = cmd_config_get_urlmatch,
+ [ACTION_UNSET] = cmd_config_unset,
+ [ACTION_UNSET_ALL] = cmd_config_unset_all,
+ [ACTION_RENAME_SECTION] = cmd_config_rename_section,
+ [ACTION_REMOVE_SECTION] = cmd_config_remove_section,
+ [ACTION_GET_COLOR] = cmd_config_get_color,
+ [ACTION_GET_COLORBOOL] = cmd_config_get_colorbool,
+};
+
static struct option builtin_config_options[] = {
OPT_GROUP(N_("Config file location")),
OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
@@ -851,20 +868,20 @@ static struct option builtin_config_options[] = {
OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
OPT_GROUP(N_("Action")),
- OPT_CMDMODE(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET),
- OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
- OPT_CMDMODE(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
- OPT_CMDMODE(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
- OPT_CMDMODE(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
- OPT_CMDMODE(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
- OPT_CMDMODE(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
- OPT_CMDMODE(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
- OPT_CMDMODE(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
- OPT_CMDMODE(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
- OPT_CMDMODE('l', "list", &actions, N_("list all"), ACTION_LIST),
- OPT_CMDMODE('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
- OPT_CMDMODE(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
- OPT_CMDMODE(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
+ OPT_CMDMODE(0, "get", &action, N_("get value: name [value-pattern]"), ACTION_GET),
+ OPT_CMDMODE(0, "get-all", &action, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
+ OPT_CMDMODE(0, "get-regexp", &action, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
+ OPT_CMDMODE(0, "get-urlmatch", &action, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
+ OPT_CMDMODE(0, "replace-all", &action, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
+ OPT_CMDMODE(0, "add", &action, N_("add a new variable: name value"), ACTION_ADD),
+ OPT_CMDMODE(0, "unset", &action, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
+ OPT_CMDMODE(0, "unset-all", &action, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
+ OPT_CMDMODE(0, "rename-section", &action, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
+ OPT_CMDMODE(0, "remove-section", &action, N_("remove a section: name"), ACTION_REMOVE_SECTION),
+ OPT_CMDMODE('l', "list", &action, N_("list all"), ACTION_LIST),
+ OPT_CMDMODE('e', "edit", &action, N_("open an editor"), ACTION_EDIT),
+ OPT_CMDMODE(0, "get-color", &action, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
+ OPT_CMDMODE(0, "get-colorbool", &action, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
OPT_GROUP(N_("Type")),
OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type),
OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
@@ -976,33 +993,43 @@ int cmd_config(int argc, const char **argv, const char *prefix)
key_delim = '\n';
}
- if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {
- error(_("--get-color and variable type are incoherent"));
- usage_builtin_config();
- }
-
- if (actions == 0)
+ if (action == ACTION_NONE) {
switch (argc) {
- case 1: actions = ACTION_GET; break;
- case 2: actions = ACTION_SET; break;
- case 3: actions = ACTION_SET_ALL; break;
+ case 1: action = ACTION_GET; break;
+ case 2: action = ACTION_SET; break;
+ case 3: action = ACTION_SET_ALL; break;
default:
usage_builtin_config();
}
+ }
+ if (action <= ACTION_NONE || action >= ARRAY_SIZE(subcommands_by_action))
+ BUG("invalid action %d", action);
+ subcommand = subcommands_by_action[action];
+
+ if (type && (subcommand == cmd_config_get_color ||
+ subcommand == cmd_config_get_colorbool)) {
+ error(_("--get-color and variable type are incoherent"));
+ usage_builtin_config();
+ }
+
if (omit_values &&
- !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {
+ subcommand != cmd_config_list &&
+ subcommand != cmd_config_get_regexp) {
error(_("--name-only is only applicable to --list or --get-regexp"));
usage_builtin_config();
}
- if (show_origin && !(actions &
- (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
+ if (show_origin &&
+ subcommand != cmd_config_get &&
+ subcommand != cmd_config_get_all &&
+ subcommand != cmd_config_get_regexp &&
+ subcommand != cmd_config_list) {
error(_("--show-origin is only applicable to --get, --get-all, "
"--get-regexp, and --list"));
usage_builtin_config();
}
- if (default_value && !(actions & ACTION_GET)) {
+ if (default_value && subcommand != cmd_config_get) {
error(_("--default is only applicable to --get"));
usage_builtin_config();
}
@@ -1011,28 +1038,19 @@ int cmd_config(int argc, const char **argv, const char *prefix)
if (fixed_value) {
int allowed_usage = 0;
- switch (actions) {
- /* git config --get <name> <value-pattern> */
- case ACTION_GET:
- /* git config --get-all <name> <value-pattern> */
- case ACTION_GET_ALL:
- /* git config --get-regexp <name-pattern> <value-pattern> */
- case ACTION_GET_REGEXP:
- /* git config --unset <name> <value-pattern> */
- case ACTION_UNSET:
- /* git config --unset-all <name> <value-pattern> */
- case ACTION_UNSET_ALL:
+ if (subcommand == cmd_config_get ||
+ subcommand == cmd_config_get_all ||
+ subcommand == cmd_config_get_regexp ||
+ subcommand == cmd_config_unset ||
+ subcommand == cmd_config_unset_all) {
+ /* git config --<action> <name> <value-pattern> */
allowed_usage = argc > 1 && !!argv[1];
- break;
-
- /* git config <name> <value> <value-pattern> */
- case ACTION_SET_ALL:
- /* git config --replace-all <name> <value> <value-pattern> */
- case ACTION_REPLACE_ALL:
+ } else if (subcommand == cmd_config_set_all ||
+ subcommand == cmd_config_replace_all) {
+ /* git config --<action> <name> <value> <value-pattern> */
allowed_usage = argc > 2 && !!argv[2];
- break;
-
- /* other options don't allow --fixed-value */
+ } else {
+ /* other options don't allow --fixed-value */
}
if (!allowed_usage) {
@@ -1043,42 +1061,15 @@ int cmd_config(int argc, const char **argv, const char *prefix)
config_flags |= CONFIG_FLAGS_FIXED_VALUE;
}
- if (actions & PAGING_ACTIONS)
+ /*
+ * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
+ * one line of output and which should therefore be paged.
+ */
+ if (subcommand == cmd_config_list ||
+ subcommand == cmd_config_get_all ||
+ subcommand == cmd_config_get_regexp ||
+ subcommand == cmd_config_get_urlmatch)
setup_auto_pager("config", 1);
- if (actions == ACTION_LIST) {
- return cmd_config_list(argc, argv, prefix);
- } else if (actions == ACTION_EDIT) {
- return cmd_config_edit(argc, argv, prefix);
- } else if (actions == ACTION_SET) {
- return cmd_config_set(argc, argv, prefix);
- } else if (actions == ACTION_SET_ALL) {
- return cmd_config_set_all(argc, argv, prefix);
- } else if (actions == ACTION_ADD) {
- return cmd_config_add(argc, argv, prefix);
- } else if (actions == ACTION_REPLACE_ALL) {
- return cmd_config_replace_all(argc, argv, prefix);
- } else if (actions == ACTION_GET) {
- return cmd_config_get(argc, argv, prefix);
- } else if (actions == ACTION_GET_ALL) {
- return cmd_config_get_all(argc, argv, prefix);
- } else if (actions == ACTION_GET_REGEXP) {
- return cmd_config_get_regexp(argc, argv, prefix);
- } else if (actions == ACTION_GET_URLMATCH) {
- return cmd_config_get_urlmatch(argc, argv, prefix);
- } else if (actions == ACTION_UNSET) {
- return cmd_config_unset(argc, argv, prefix);
- } else if (actions == ACTION_UNSET_ALL) {
- return cmd_config_unset_all(argc, argv, prefix);
- } else if (actions == ACTION_RENAME_SECTION) {
- return cmd_config_rename_section(argc, argv, prefix);
- } else if (actions == ACTION_REMOVE_SECTION) {
- return cmd_config_remove_section(argc, argv, prefix);
- } else if (actions == ACTION_GET_COLOR) {
- return cmd_config_get_color(argc, argv, prefix);
- } else if (actions == ACTION_GET_COLORBOOL) {
- return cmd_config_get_colorbool(argc, argv, prefix);
- }
-
- BUG("invalid action");
+ return subcommand(argc, argv, prefix);
}
--
2.44.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related
* [PATCH 6/8] builtin/config: introduce subcommands
From: Patrick Steinhardt @ 2024-03-06 11:31 UTC (permalink / raw)
To: git
In-Reply-To: <cover.1709724089.git.ps@pks.im>
[-- Attachment #1: Type: text/plain, Size: 5749 bytes --]
While git-config(1) has several modes, those modes are not exposed with
subcommands but instead by specifying e.g. `--unset` or `--list`. This
user interface is not really in line with how our more modern commands
work, where it is a lot more customary to say e.g. `git remote list`.
Furthermore, to add to the confusion, git-config(1) also allows the user
to request modes implicitly by just specifying the correct number of
arguments. Thus, `git config foo.bar` will retrieve the value of
"foo.bar" while `git config foo.bar baz` will set it to "baz".
Overall, this makes for a confusing interface that could really use a
makeover. It hurts discoverability of what you can do with git-config(1)
and is comparatively easy to get wrong.
Modernize git-config(1) so that it understands proper subcommands like
"list" or "unset". Like this, a user can say "git config get foo.bar" to
retrieve the config key's value and "git config set foo.bar baz" to set
it.
One concern in this context is backwards compatibility. Luckily, we can
introduce subcommands without breaking backwards compatibility at all.
This is because all the implicit modes of git-config(1) require that the
first argument is a properly formatted config key. And as config keys
_must_ have a dot in their name, any value without a dot would have been
discarded by git-config(1) previous to this change. Thus, given that
none of the subcommands do have a dot, they are unambiguous.
Consequently, we introduce subcommands in such a way that git-config(1)
understands both the old and the new syntax at the same time. This
should help to transition to the new-style syntax until we eventually
deprecate and remove the old-style syntax.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/config.c | 52 +++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 45 insertions(+), 7 deletions(-)
diff --git a/builtin/config.c b/builtin/config.c
index 0d58397ef5..10fa933931 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -859,6 +859,26 @@ static parse_opt_subcommand_fn *subcommands_by_action[] = {
[ACTION_GET_COLORBOOL] = cmd_config_get_colorbool,
};
+static struct option builtin_subcommand_options[] = {
+ OPT_SUBCOMMAND("list", &subcommand, cmd_config_list),
+ OPT_SUBCOMMAND("get", &subcommand, cmd_config_get),
+ OPT_SUBCOMMAND("get-all", &subcommand, cmd_config_get_all),
+ OPT_SUBCOMMAND("get-color", &subcommand, cmd_config_get_color),
+ OPT_SUBCOMMAND("get-colorbool", &subcommand, cmd_config_get_colorbool),
+ OPT_SUBCOMMAND("get-regexp", &subcommand, cmd_config_get_regexp),
+ OPT_SUBCOMMAND("get-urlmatch", &subcommand, cmd_config_get_urlmatch),
+ OPT_SUBCOMMAND("add", &subcommand, cmd_config_add),
+ OPT_SUBCOMMAND("set", &subcommand, cmd_config_set),
+ OPT_SUBCOMMAND("set-all", &subcommand, cmd_config_set_all),
+ OPT_SUBCOMMAND("unset", &subcommand, cmd_config_unset),
+ OPT_SUBCOMMAND("unset-all", &subcommand, cmd_config_unset_all),
+ OPT_SUBCOMMAND("replace-all", &subcommand, cmd_config_replace_all),
+ OPT_SUBCOMMAND("rename-section", &subcommand, cmd_config_rename_section),
+ OPT_SUBCOMMAND("remove-section", &subcommand, cmd_config_remove_section),
+ OPT_SUBCOMMAND("edit", &subcommand, cmd_config_edit),
+ OPT_END(),
+};
+
static struct option builtin_config_options[] = {
OPT_GROUP(N_("Config file location")),
OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
@@ -910,6 +930,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)
{
given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
+ /*
+ * This is somewhat hacky: we first parse the command line while
+ * keeping all args intact in order to determine whether a subcommand
+ * has been specified. If so, we re-parse it a second time, but this
+ * time we drop KEEP_ARGV0. This is so that we don't munge the command
+ * line in case no subcommand was given, which would otherwise confuse
+ * us when parsing the implicit modes.
+ */
+ argc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,
+ PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);
+ if (subcommand)
+ argc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,
+ PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);
+
argc = parse_options(argc, argv, prefix, builtin_config_options,
builtin_config_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
@@ -993,18 +1027,22 @@ int cmd_config(int argc, const char **argv, const char *prefix)
key_delim = '\n';
}
- if (action == ACTION_NONE) {
+ if (action != ACTION_NONE && subcommand) {
+ error(_("subcommand and action modes are incompatible"));
+ usage_builtin_config();
+ } else if (action == ACTION_NONE && !subcommand) {
switch (argc) {
- case 1: action = ACTION_GET; break;
- case 2: action = ACTION_SET; break;
- case 3: action = ACTION_SET_ALL; break;
+ case 1: subcommand = cmd_config_get; break;
+ case 2: subcommand = cmd_config_set; break;
+ case 3: subcommand = cmd_config_set_all; break;
default:
usage_builtin_config();
}
+ } else if (action != ACTION_NONE) {
+ if (action < ACTION_NONE || action >= ARRAY_SIZE(subcommands_by_action))
+ BUG("invalid action %d", action);
+ subcommand = subcommands_by_action[action];
}
- if (action <= ACTION_NONE || action >= ARRAY_SIZE(subcommands_by_action))
- BUG("invalid action %d", action);
- subcommand = subcommands_by_action[action];
if (type && (subcommand == cmd_config_get_color ||
subcommand == cmd_config_get_colorbool)) {
--
2.44.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related
* [PATCH 7/8] t1300: exercise both old- and new-style modes
From: Patrick Steinhardt @ 2024-03-06 11:31 UTC (permalink / raw)
To: git
In-Reply-To: <cover.1709724089.git.ps@pks.im>
[-- Attachment #1: Type: text/plain, Size: 80697 bytes --]
Convert t1300 so that tests exercise both the old and new-style way to
specify the mode.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t1300-config.sh | 721 ++++++++++++++++++++++++----------------------
1 file changed, 375 insertions(+), 346 deletions(-)
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 2d1bc1e27e..705c17a1fb 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -11,6 +11,24 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+for style in old new
+do
+
+case "$style" in
+old)
+ mode_prefix="--"
+ mode_get=""
+ mode_set=""
+ mode_set_all=""
+ ;;
+new)
+ mode_prefix=""
+ mode_get="get"
+ mode_set="set"
+ mode_set_all="set-all"
+ ;;
+esac
+
test_expect_success 'clear default config' '
rm -f .git/config
'
@@ -20,7 +38,7 @@ cat > expect << EOF
penguin = little blue
EOF
test_expect_success 'initial' '
- git config section.penguin "little blue" &&
+ git config ${mode_set} section.penguin "little blue" &&
test_cmp expect .git/config
'
@@ -30,7 +48,7 @@ cat > expect << EOF
Movie = BadPhysics
EOF
test_expect_success 'mixed case' '
- git config Section.Movie BadPhysics &&
+ git config ${mode_set} Section.Movie BadPhysics &&
test_cmp expect .git/config
'
@@ -42,7 +60,7 @@ cat > expect << EOF
WhatEver = Second
EOF
test_expect_success 'similar section' '
- git config Sections.WhatEver Second &&
+ git config ${mode_set} Sections.WhatEver Second &&
test_cmp expect .git/config
'
@@ -55,16 +73,16 @@ cat > expect << EOF
WhatEver = Second
EOF
test_expect_success 'uppercase section' '
- git config SECTION.UPPERCASE true &&
+ git config ${mode_set} SECTION.UPPERCASE true &&
test_cmp expect .git/config
'
test_expect_success 'replace with non-match' '
- git config section.penguin kingpin !blue
+ git config ${mode_set_all} section.penguin kingpin !blue
'
test_expect_success 'replace with non-match (actually matching)' '
- git config section.penguin "very blue" !kingpin
+ git config ${mode_set_all} section.penguin "very blue" !kingpin
'
cat > expect << EOF
@@ -102,7 +120,7 @@ test_missing_key () {
local key="$1" &&
local title="$2" &&
test_expect_success "value for $title is not printed" '
- test_must_fail git config "$key" >out 2>err &&
+ test_must_fail git config ${mode_get} "$key" >out 2>err &&
test_must_be_empty out &&
test_must_be_empty err
'
@@ -125,7 +143,7 @@ foo = bar
EOF
test_expect_success 'unset with cont. lines' '
- git config --unset beta.baz
+ git config ${mode_prefix}unset beta.baz
'
cat > expect <<\EOF
@@ -152,7 +170,7 @@ EOF
cp .git/config .git/config2
test_expect_success 'multiple unset' '
- git config --unset-all beta.haha
+ git config ${mode_prefix}unset-all beta.haha
'
cat > expect << EOF
@@ -178,7 +196,7 @@ test_expect_success '--replace-all missing value' '
rm .git/config2
test_expect_success '--replace-all' '
- git config --replace-all beta.haha gamma
+ git config ${mode_prefix}replace-all beta.haha gamma
'
cat > expect << EOF
@@ -220,7 +238,7 @@ noIndent= sillyValue ; 'nother silly comment
nonewline = wow
EOF
test_expect_success 'really really mean test' '
- git config nextsection.nonewline wow &&
+ git config ${mode_set} nextsection.nonewline wow &&
test_cmp expect .git/config
'
@@ -238,7 +256,7 @@ noIndent= sillyValue ; 'nother silly comment
nonewline = wow
EOF
test_expect_success 'unset' '
- git config --unset beta.haha &&
+ git config ${mode_prefix}unset beta.haha &&
test_cmp expect .git/config
'
@@ -258,15 +276,15 @@ test_expect_success 'multivar' '
'
test_expect_success 'non-match' '
- git config --get nextsection.nonewline !for
+ git config ${mode_prefix}get nextsection.nonewline !for
'
test_expect_success 'non-match value' '
- test_cmp_config wow --get nextsection.nonewline !for
+ test_cmp_config wow ${mode_prefix}get nextsection.nonewline !for
'
test_expect_success 'multi-valued get returns final one' '
- test_cmp_config "wow2 for me" --get nextsection.nonewline
+ test_cmp_config "wow2 for me" ${mode_prefix}get nextsection.nonewline
'
test_expect_success 'multi-valued get-all returns all' '
@@ -274,7 +292,7 @@ test_expect_success 'multi-valued get-all returns all' '
wow
wow2 for me
EOF
- git config --get-all nextsection.nonewline >actual &&
+ git config ${mode_prefix}get-all nextsection.nonewline >actual &&
test_cmp expect actual
'
@@ -289,16 +307,16 @@ noIndent= sillyValue ; 'nother silly comment
NoNewLine = wow2 for me
EOF
test_expect_success 'multivar replace' '
- git config nextsection.nonewline "wow3" "wow$" &&
+ git config ${mode_set_all} nextsection.nonewline "wow3" "wow$" &&
test_cmp expect .git/config
'
test_expect_success 'ambiguous unset' '
- test_must_fail git config --unset nextsection.nonewline
+ test_must_fail git config ${mode_prefix}unset nextsection.nonewline
'
test_expect_success 'invalid unset' '
- test_must_fail git config --unset somesection.nonewline
+ test_must_fail git config ${mode_prefix}unset somesection.nonewline
'
cat > expect << EOF
@@ -312,16 +330,16 @@ noIndent= sillyValue ; 'nother silly comment
EOF
test_expect_success 'multivar unset' '
- git config --unset nextsection.nonewline "wow3$" &&
+ git config ${mode_prefix}unset nextsection.nonewline "wow3$" &&
test_cmp expect .git/config
'
-test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla'
+test_expect_success 'invalid key' 'test_must_fail git config ${mode_set} inval.2key blabla'
-test_expect_success 'correct key' 'git config 123456.a123 987'
+test_expect_success 'correct key' 'git config ${mode_set} 123456.a123 987'
test_expect_success 'hierarchical section' '
- git config Version.1.2.3eX.Alpha beta
+ git config ${mode_set} Version.1.2.3eX.Alpha beta
'
cat > expect << EOF
@@ -350,11 +368,11 @@ version.1.2.3eX.alpha=beta
EOF
test_expect_success 'working --list' '
- git config --list > output &&
+ git config ${mode_prefix}list > output &&
test_cmp expect output
'
test_expect_success '--list without repo produces empty output' '
- git --git-dir=nonexistent config --list >output &&
+ git --git-dir=nonexistent config ${mode_prefix}list >output &&
test_must_be_empty output
'
@@ -366,7 +384,7 @@ version.1.2.3eX.alpha
EOF
test_expect_success '--name-only --list' '
- git config --name-only --list >output &&
+ git config ${mode_prefix}list --name-only >output &&
test_cmp expect output
'
@@ -376,7 +394,7 @@ nextsection.nonewline wow2 for me
EOF
test_expect_success '--get-regexp' '
- git config --get-regexp in >output &&
+ git config ${mode_prefix}get-regexp in >output &&
test_cmp expect output
'
@@ -386,7 +404,7 @@ nextsection.nonewline
EOF
test_expect_success '--name-only --get-regexp' '
- git config --name-only --get-regexp in >output &&
+ git config ${mode_prefix}get-regexp --name-only in >output &&
test_cmp expect output
'
@@ -396,8 +414,8 @@ wow4 for you
EOF
test_expect_success '--add' '
- git config --add nextsection.nonewline "wow4 for you" &&
- git config --get-all nextsection.nonewline > output &&
+ git config ${mode_prefix}add nextsection.nonewline "wow4 for you" &&
+ git config ${mode_prefix}get-all nextsection.nonewline > output &&
test_cmp expect output
'
@@ -409,45 +427,45 @@ cat > .git/config << EOF
EOF
test_expect_success 'get variable with no value' '
- git config --get novalue.variable ^$
+ git config ${mode_prefix}get novalue.variable ^$
'
test_expect_success 'get variable with empty value' '
- git config --get emptyvalue.variable ^$
+ git config ${mode_prefix}get emptyvalue.variable ^$
'
echo novalue.variable > expect
test_expect_success 'get-regexp variable with no value' '
- git config --get-regexp novalue > output &&
+ git config ${mode_prefix}get-regexp novalue > output &&
test_cmp expect output
'
echo 'novalue.variable true' > expect
test_expect_success 'get-regexp --bool variable with no value' '
- git config --bool --get-regexp novalue > output &&
+ git config ${mode_prefix}get-regexp --bool novalue > output &&
test_cmp expect output
'
echo 'emptyvalue.variable ' > expect
test_expect_success 'get-regexp variable with empty value' '
- git config --get-regexp emptyvalue > output &&
+ git config ${mode_prefix}get-regexp emptyvalue > output &&
test_cmp expect output
'
echo true > expect
test_expect_success 'get bool variable with no value' '
- git config --bool novalue.variable > output &&
+ git config ${mode_get} --bool novalue.variable > output &&
test_cmp expect output
'
echo false > expect
test_expect_success 'get bool variable with empty value' '
- git config --bool emptyvalue.variable > output &&
+ git config ${mode_get} --bool emptyvalue.variable > output &&
test_cmp expect output
'
@@ -469,7 +487,7 @@ cat > expect << EOF
EOF
test_expect_success 'new section is partial match of another' '
- git config a.x y &&
+ git config ${mode_set} a.x y &&
test_cmp expect .git/config
'
@@ -484,14 +502,14 @@ cat > expect << EOF
EOF
test_expect_success 'new variable inserts into proper section' '
- git config b.x y &&
- git config a.b c &&
+ git config ${mode_set} b.x y &&
+ git config ${mode_set} a.b c &&
test_cmp expect .git/config
'
test_expect_success 'alternative --file (non-existing file should fail)' '
- test_must_fail git config --file non-existing-config -l &&
- test_must_fail git config --file non-existing-config test.xyzzy
+ test_must_fail git config ${mode_prefix}list --file non-existing-config &&
+ test_must_fail git config ${mode_get} --file non-existing-config test.xyzzy
'
cat > other-config << EOF
@@ -504,29 +522,30 @@ ein.bahn=strasse
EOF
test_expect_success 'alternative GIT_CONFIG' '
- GIT_CONFIG=other-config git config --list >output &&
+ GIT_CONFIG=other-config git config ${mode_prefix}list >output &&
test_cmp expect output
'
test_expect_success 'alternative GIT_CONFIG (--file)' '
- git config --file other-config --list >output &&
+ git config ${mode_prefix}list --file other-config >output &&
test_cmp expect output
'
test_expect_success 'alternative GIT_CONFIG (--file=-)' '
- git config --file - --list <other-config >output &&
+ git config ${mode_prefix}list --file - <other-config >output &&
test_cmp expect output
'
test_expect_success 'setting a value in stdin is an error' '
- test_must_fail git config --file - some.value foo
+ test_must_fail git config ${mode_set} --file - some.value foo
'
test_expect_success 'editing stdin is an error' '
- test_must_fail git config --file - --edit
+ test_must_fail git config ${mode_prefix}edit --file -
'
test_expect_success 'refer config from subdirectory' '
+ test_when_finished "rm -r x" &&
mkdir x &&
test_cmp_config -C x strasse --file=../other-config --get ein.bahn
'
@@ -539,7 +558,7 @@ cat > expect << EOF
EOF
test_expect_success '--set in alternative file' '
- git config --file=other-config anwohner.park ausweis &&
+ git config ${mode_set} --file=other-config anwohner.park ausweis &&
test_cmp expect other-config
'
@@ -555,7 +574,7 @@ weird
EOF
test_expect_success 'rename section' '
- git config --rename-section branch.eins branch.zwei
+ git config ${mode_prefix}rename-section branch.eins branch.zwei
'
cat > expect << EOF
@@ -574,7 +593,7 @@ test_expect_success 'rename succeeded' '
'
test_expect_success 'rename non-existing section' '
- test_must_fail git config --rename-section \
+ test_must_fail git config ${mode_prefix}rename-section \
branch."world domination" branch.drei
'
@@ -583,7 +602,7 @@ test_expect_success 'rename succeeded' '
'
test_expect_success 'rename another section' '
- git config --rename-section branch."1 234 blabl/a" branch.drei
+ git config ${mode_prefix}rename-section branch."1 234 blabl/a" branch.drei
'
cat > expect << EOF
@@ -606,7 +625,7 @@ cat >> .git/config << EOF
EOF
test_expect_success 'rename a section with a var on the same line' '
- git config --rename-section branch.vier branch.zwei
+ git config ${mode_prefix}rename-section branch.vier branch.zwei
'
cat > expect << EOF
@@ -627,11 +646,11 @@ test_expect_success 'rename succeeded' '
'
test_expect_success 'renaming empty section name is rejected' '
- test_must_fail git config --rename-section branch.zwei ""
+ test_must_fail git config ${mode_prefix}rename-section branch.zwei ""
'
test_expect_success 'renaming to bogus section is rejected' '
- test_must_fail git config --rename-section branch.zwei "bogus name"
+ test_must_fail git config ${mode_prefix}rename-section branch.zwei "bogus name"
'
test_expect_success 'renaming a section with a long line' '
@@ -640,7 +659,7 @@ test_expect_success 'renaming a section with a long line' '
printf " c = d %1024s [a] e = f\\n" " " &&
printf "[a] g = h\\n"
} >y &&
- git config -f y --rename-section a xyz &&
+ git config ${mode_prefix}rename-section -f y a xyz &&
test_must_fail git config -f y b.e
'
@@ -650,7 +669,7 @@ test_expect_success 'renaming an embedded section with a long line' '
printf " c = d %1024s [a] [foo] e = f\\n" " " &&
printf "[a] g = h\\n"
} >y &&
- git config -f y --rename-section a xyz &&
+ git config ${mode_prefix}rename-section -f y a xyz &&
test_must_fail git config -f y foo.e
'
@@ -660,7 +679,7 @@ test_expect_success 'renaming a section with an overly-long line' '
printf " c = d %525000s e" " " &&
printf "[a] g = h\\n"
} >y &&
- test_must_fail git config -f y --rename-section a xyz 2>err &&
+ test_must_fail git config ${mode_prefix}rename-section -f y a xyz 2>err &&
grep "refusing to work with overly long line in .y. on line 2" err
'
@@ -669,7 +688,7 @@ cat >> .git/config << EOF
EOF
test_expect_success 'remove section' '
- git config --remove-section branch.zwei
+ git config ${mode_prefix}remove-section branch.zwei
'
cat > expect << EOF
@@ -693,20 +712,20 @@ EOF
test_expect_success 'section ending' '
rm -f .git/config &&
- git config gitcvs.enabled true &&
- git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
- git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+ git config ${mode_set} gitcvs.enabled true &&
+ git config ${mode_set} gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+ git config ${mode_set} gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
test_cmp expect .git/config
'
test_expect_success numbers '
- git config kilo.gram 1k &&
- git config mega.ton 1m &&
+ git config ${mode_set} kilo.gram 1k &&
+ git config ${mode_set} mega.ton 1m &&
echo 1024 >expect &&
echo 1048576 >>expect &&
- git config --int --get kilo.gram >actual &&
- git config --int --get mega.ton >>actual &&
+ git config ${mode_prefix}get --int kilo.gram >actual &&
+ git config ${mode_prefix}get --int mega.ton >>actual &&
test_cmp expect actual
'
@@ -719,25 +738,25 @@ test_expect_success '--int is at least 64 bits' '
test_expect_success 'invalid unit' '
git config aninvalid.unit "1auto" &&
test_cmp_config 1auto aninvalid.unit &&
- test_must_fail git config --int --get aninvalid.unit 2>actual &&
+ test_must_fail git config ${mode_prefix}get --int aninvalid.unit 2>actual &&
test_grep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
'
test_expect_success 'invalid unit boolean' '
git config commit.gpgsign "1true" &&
test_cmp_config 1true commit.gpgsign &&
- test_must_fail git config --bool --get commit.gpgsign 2>actual &&
+ test_must_fail git config ${mode_prefix}get --bool commit.gpgsign 2>actual &&
test_grep "bad boolean config value .1true. for .commit.gpgsign." actual
'
test_expect_success 'line number is reported correctly' '
printf "[bool]\n\tvar\n" >invalid &&
- test_must_fail git config -f invalid --path bool.var 2>actual &&
+ test_must_fail git config ${mode_get} -f invalid --path bool.var 2>actual &&
test_grep "line 2" actual
'
test_expect_success 'invalid stdin config' '
- echo "[broken" | test_must_fail git config --list --file - >output 2>&1 &&
+ echo "[broken" | test_must_fail git config ${mode_prefix}list --file - >output 2>&1 &&
test_grep "bad config line 1 in standard input" output
'
@@ -765,19 +784,19 @@ test_expect_success bool '
rm -f result &&
for i in 1 2 3 4
do
- git config --bool --get bool.true$i >>result &&
- git config --bool --get bool.false$i >>result || return 1
+ git config ${mode_prefix}get --bool bool.true$i >>result &&
+ git config ${mode_prefix}get --bool bool.false$i >>result || return 1
done &&
test_cmp expect result'
test_expect_success 'invalid bool (--get)' '
git config bool.nobool foobar &&
- test_must_fail git config --bool --get bool.nobool'
+ test_must_fail git config ${mode_prefix}get --bool bool.nobool'
test_expect_success 'invalid bool (set)' '
- test_must_fail git config --bool bool.nobool foobar'
+ test_must_fail git config ${mode_set} --bool bool.nobool foobar'
cat > expect <<\EOF
[bool]
@@ -794,14 +813,14 @@ EOF
test_expect_success 'set --bool' '
rm -f .git/config &&
- git config --bool bool.true1 01 &&
- git config --bool bool.true2 -1 &&
- git config --bool bool.true3 YeS &&
- git config --bool bool.true4 true &&
- git config --bool bool.false1 000 &&
- git config --bool bool.false2 "" &&
- git config --bool bool.false3 nO &&
- git config --bool bool.false4 FALSE &&
+ git config ${mode_set} --bool bool.true1 01 &&
+ git config ${mode_set} --bool bool.true2 -1 &&
+ git config ${mode_set} --bool bool.true3 YeS &&
+ git config ${mode_set} --bool bool.true4 true &&
+ git config ${mode_set} --bool bool.false1 000 &&
+ git config ${mode_set} --bool bool.false2 "" &&
+ git config ${mode_set} --bool bool.false3 nO &&
+ git config ${mode_set} --bool bool.false4 FALSE &&
test_cmp expect .git/config'
cat > expect <<\EOF
@@ -814,9 +833,9 @@ EOF
test_expect_success 'set --int' '
rm -f .git/config &&
- git config --int int.val1 01 &&
- git config --int int.val2 -1 &&
- git config --int int.val3 5m &&
+ git config ${mode_set} --int int.val1 01 &&
+ git config ${mode_set} --int int.val2 -1 &&
+ git config ${mode_set} --int int.val3 5m &&
test_cmp expect .git/config
'
@@ -840,12 +859,12 @@ test_expect_success 'get --bool-or-int' '
-1
EOF
{
- git config --bool-or-int bool.true1 &&
- git config --bool-or-int bool.true2 &&
- git config --bool-or-int bool.false &&
- git config --bool-or-int int.int1 &&
- git config --bool-or-int int.int2 &&
- git config --bool-or-int int.int3
+ git config ${mode_get} --bool-or-int bool.true1 &&
+ git config ${mode_get} --bool-or-int bool.true2 &&
+ git config ${mode_get} --bool-or-int bool.false &&
+ git config ${mode_get} --bool-or-int int.int1 &&
+ git config ${mode_get} --bool-or-int int.int2 &&
+ git config ${mode_get} --bool-or-int int.int3
} >actual &&
test_cmp expect actual
'
@@ -864,13 +883,13 @@ EOF
test_expect_success 'set --bool-or-int' '
rm -f .git/config &&
- git config --bool-or-int bool.true1 true &&
- git config --bool-or-int bool.false1 false &&
- git config --bool-or-int bool.true2 yes &&
- git config --bool-or-int bool.false2 no &&
- git config --bool-or-int int.int1 0 &&
- git config --bool-or-int int.int2 1 &&
- git config --bool-or-int int.int3 -1 &&
+ git config ${mode_set} --bool-or-int bool.true1 true &&
+ git config ${mode_set} --bool-or-int bool.false1 false &&
+ git config ${mode_set} --bool-or-int bool.true2 yes &&
+ git config ${mode_set} --bool-or-int bool.false2 no &&
+ git config ${mode_set} --bool-or-int int.int1 0 &&
+ git config ${mode_set} --bool-or-int int.int2 1 &&
+ git config ${mode_set} --bool-or-int int.int3 -1 &&
test_cmp expect .git/config
'
@@ -883,9 +902,9 @@ EOF
test_expect_success !MINGW 'set --path' '
rm -f .git/config &&
- git config --path path.home "~/" &&
- git config --path path.normal "/dev/null" &&
- git config --path path.trailingtilde "foo~" &&
+ git config ${mode_set} --path path.home "~/" &&
+ git config ${mode_set} --path path.normal "/dev/null" &&
+ git config ${mode_set} --path path.trailingtilde "foo~" &&
test_cmp expect .git/config'
if test_have_prereq !MINGW && test "${HOME+set}"
@@ -900,9 +919,9 @@ foo~
EOF
test_expect_success HOMEVAR 'get --path' '
- git config --get --path path.home > result &&
- git config --get --path path.normal >> result &&
- git config --get --path path.trailingtilde >> result &&
+ git config ${mode_prefix}get --path path.home > result &&
+ git config ${mode_prefix}get --path path.normal >> result &&
+ git config ${mode_prefix}get --path path.trailingtilde >> result &&
test_cmp expect result
'
@@ -914,10 +933,10 @@ EOF
test_expect_success !MINGW 'get --path copes with unset $HOME' '
(
sane_unset HOME &&
- test_must_fail git config --get --path path.home \
+ test_must_fail git config ${mode_prefix}get --path path.home \
>result 2>msg &&
- git config --get --path path.normal >>result &&
- git config --get --path path.trailingtilde >>result
+ git config ${mode_prefix}get --path path.normal >>result &&
+ git config ${mode_prefix}get --path path.trailingtilde >>result
) &&
test_grep "[Ff]ailed to expand.*~/" msg &&
test_cmp expect result
@@ -925,7 +944,7 @@ test_expect_success !MINGW 'get --path copes with unset $HOME' '
test_expect_success 'get --path barfs on boolean variable' '
echo "[path]bool" >.git/config &&
- test_must_fail git config --get --path path.bool
+ test_must_fail git config ${mode_prefix}get --path path.bool
'
test_expect_success 'get --expiry-date' '
@@ -949,20 +968,20 @@ test_expect_success 'get --expiry-date' '
EOF
: "work around heredoc parsing bug fixed in dash 0.5.7 (in ec2c84d)" &&
{
- echo "$rel_out $(git config --expiry-date date.valid1)" &&
- git config --expiry-date date.valid2 &&
- git config --expiry-date date.valid3 &&
- git config --expiry-date date.valid4 &&
- git config --expiry-date date.valid5
+ echo "$rel_out $(git config ${mode_get} --expiry-date date.valid1)" &&
+ git config ${mode_get} --expiry-date date.valid2 &&
+ git config ${mode_get} --expiry-date date.valid3 &&
+ git config ${mode_get} --expiry-date date.valid4 &&
+ git config ${mode_get} --expiry-date date.valid5
} >actual &&
test_cmp expect actual &&
- test_must_fail git config --expiry-date date.invalid1
+ test_must_fail git config ${mode_get} --expiry-date date.invalid1
'
test_expect_success 'get --type=color' '
rm .git/config &&
git config foo.color "red" &&
- git config --get --type=color foo.color >actual.raw &&
+ git config ${mode_prefix}get --type=color foo.color >actual.raw &&
test_decode_color <actual.raw >actual &&
echo "<RED>" >expect &&
test_cmp expect actual
@@ -975,17 +994,17 @@ EOF
test_expect_success 'set --type=color' '
rm .git/config &&
- git config --type=color foo.color "red" &&
+ git config ${mode_set} --type=color foo.color "red" &&
test_cmp expect .git/config
'
test_expect_success 'get --type=color barfs on non-color' '
echo "[foo]bar=not-a-color" >.git/config &&
- test_must_fail git config --get --type=color foo.bar
+ test_must_fail git config ${mode_prefix}get --type=color foo.bar
'
test_expect_success 'set --type=color barfs on non-color' '
- test_must_fail git config --type=color foo.color "not-a-color" 2>error &&
+ test_must_fail git config ${mode_set} --type=color foo.color "not-a-color" 2>error &&
test_grep "cannot parse color" error
'
@@ -998,18 +1017,18 @@ cat > expect << EOF
EOF
test_expect_success 'quoting' '
rm -f .git/config &&
- git config quote.leading " test" &&
- git config quote.ending "test " &&
- git config quote.semicolon "test;test" &&
- git config quote.hash "test#test" &&
+ git config ${mode_set} quote.leading " test" &&
+ git config ${mode_set} quote.ending "test " &&
+ git config ${mode_set} quote.semicolon "test;test" &&
+ git config ${mode_set} quote.hash "test#test" &&
test_cmp expect .git/config
'
test_expect_success 'key with newline' '
- test_must_fail git config "key.with
+ test_must_fail git config ${mode_set} "key.with
newline" 123'
-test_expect_success 'value with newline' 'git config key.sub value.with\\\
+test_expect_success 'value with newline' 'git config ${mode_set} key.sub value.with\\\
newline'
cat > .git/config <<\EOF
@@ -1029,7 +1048,7 @@ section.quotecont=cont;inued
EOF
test_expect_success 'value continued on next line' '
- git config --list > result &&
+ git config ${mode_prefix}list > result &&
test_cmp expect result
'
@@ -1053,14 +1072,14 @@ Qsection.sub=section.val4
Qsection.sub=section.val5Q
EOF
test_expect_success '--null --list' '
- git config --null --list >result.raw &&
+ git config ${mode_prefix}list --null >result.raw &&
nul_to_q <result.raw >result &&
echo >>result &&
test_cmp expect result
'
test_expect_success '--null --get-regexp' '
- git config --null --get-regexp "val[0-9]" >result.raw &&
+ git config ${mode_prefix}get-regexp --null "val[0-9]" >result.raw &&
nul_to_q <result.raw >result &&
echo >>result &&
test_cmp expect result
@@ -1072,12 +1091,13 @@ test_expect_success 'inner whitespace kept verbatim' '
'
test_expect_success SYMLINKS 'symlinked configuration' '
+ test_when_finished "rm myconfig" &&
ln -s notyet myconfig &&
- git config --file=myconfig test.frotz nitfol &&
+ git config ${mode_set} --file=myconfig test.frotz nitfol &&
test -h myconfig &&
test -f notyet &&
- test "z$(git config --file=notyet test.frotz)" = znitfol &&
- git config --file=myconfig test.xyzzy rezrov &&
+ test "z$(git config ${mode_get} --file=notyet test.frotz)" = znitfol &&
+ git config ${mode_set} --file=myconfig test.xyzzy rezrov &&
test -h myconfig &&
test -f notyet &&
cat >expect <<-\EOF &&
@@ -1085,17 +1105,18 @@ test_expect_success SYMLINKS 'symlinked configuration' '
rezrov
EOF
{
- git config --file=notyet test.frotz &&
- git config --file=notyet test.xyzzy
+ git config ${mode_get} --file=notyet test.frotz &&
+ git config ${mode_get} --file=notyet test.xyzzy
} >actual &&
test_cmp expect actual
'
test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
+ test_when_finished "rm linktonada linktolinktonada" &&
ln -s doesnotexist linktonada &&
ln -s linktonada linktolinktonada &&
- test_must_fail git config --file=linktonada --list &&
- test_must_fail git config --file=linktolinktonada --list
+ test_must_fail git config ${mode_prefix}list --file=linktonada &&
+ test_must_fail git config ${mode_prefix}list --file=linktolinktonada
'
test_expect_success 'check split_cmdline return' '
@@ -1103,12 +1124,12 @@ test_expect_success 'check split_cmdline return' '
git init repo &&
(
cd repo &&
- git config alias.split-cmdline-fix "echo \"" &&
+ git config ${mode_set} alias.split-cmdline-fix "echo \"" &&
test_must_fail git split-cmdline-fix &&
echo foo >foo &&
git add foo &&
git commit -m "initial commit" &&
- git config branch.main.mergeoptions "echo \"" &&
+ git config ${mode_set} branch.main.mergeoptions "echo \"" &&
test_must_fail git merge main
)
'
@@ -1122,10 +1143,10 @@ test_expect_success 'git -c "key=value" support' '
{
git -c section.name=value config section.name &&
git -c foo.CamelCase=value config foo.camelcase &&
- git -c foo.flag config --bool foo.flag
+ git -c foo.flag config ${mode_get} --bool foo.flag
} >actual &&
test_cmp expect actual &&
- test_must_fail git -c name=value config section.name
+ test_must_fail git -c name=value config ${mode_get} section.name
'
# We just need a type-specifier here that cares about the
@@ -1135,27 +1156,27 @@ test_expect_success 'git -c "key=value" support' '
# its semantics.
test_expect_success 'git -c can represent empty string' '
echo >expect &&
- git -c foo.empty= config --path foo.empty >actual &&
+ git -c foo.empty= config ${mode_get} --path foo.empty >actual &&
test_cmp expect actual
'
test_expect_success 'key sanity-checking' '
- test_must_fail git config foo=bar &&
- test_must_fail git config foo=.bar &&
- test_must_fail git config foo.ba=r &&
- test_must_fail git config foo.1bar &&
- test_must_fail git config foo."ba
+ test_must_fail git config ${mode_get} foo=bar &&
+ test_must_fail git config ${mode_get} foo=.bar &&
+ test_must_fail git config ${mode_get} foo.ba=r &&
+ test_must_fail git config ${mode_get} foo.1bar &&
+ test_must_fail git config ${mode_get} foo."ba
z".bar &&
- test_must_fail git config . false &&
- test_must_fail git config .foo false &&
- test_must_fail git config foo. false &&
- test_must_fail git config .foo. false &&
- git config foo.bar true &&
- git config foo."ba =z".bar false
+ test_must_fail git config ${mode_set} . false &&
+ test_must_fail git config ${mode_set} .foo false &&
+ test_must_fail git config ${mode_set} foo. false &&
+ test_must_fail git config ${mode_set} .foo. false &&
+ git config ${mode_set} foo.bar true &&
+ git config ${mode_set} foo."ba =z".bar false
'
test_expect_success 'git -c works with aliases of builtins' '
- git config alias.checkconfig "-c foo.check=bar config foo.check" &&
+ git config ${mode_set} alias.checkconfig "-c foo.check=bar config foo.check" &&
echo bar >expect &&
git checkconfig >actual &&
test_cmp expect actual
@@ -1167,7 +1188,7 @@ test_expect_success 'aliases can be CamelCased' '
(
cd repo &&
test_commit A &&
- git config alias.CamelCased "rev-parse HEAD" &&
+ git config ${mode_set} alias.CamelCased "rev-parse HEAD" &&
git CamelCased >out &&
git rev-parse HEAD >expect &&
test_cmp expect out
@@ -1176,7 +1197,7 @@ test_expect_success 'aliases can be CamelCased' '
test_expect_success 'git -c does not split values on equals' '
echo "value with = in it" >expect &&
- git -c section.foo="value with = in it" config section.foo >actual &&
+ git -c section.foo="value with = in it" config ${mode_get} section.foo >actual &&
test_cmp expect actual
'
@@ -1193,7 +1214,7 @@ test_expect_success 'git -c complains about empty key and value' '
'
test_expect_success 'multiple git -c appends config' '
- test_config alias.x "!git -c x.two=2 config --get-regexp ^x\.*" &&
+ test_config alias.x "!git -c x.two=2 config ${mode_prefix}get-regexp ^x\.*" &&
cat >expect <<-\EOF &&
x.one 1
x.two 2
@@ -1210,14 +1231,14 @@ test_expect_success 'last one wins: two level vars' '
echo VAL >expect &&
- git -c sec.var=val -c sec.VAR=VAL config --get sec.var >actual &&
+ git -c sec.var=val -c sec.VAR=VAL config ${mode_prefix}get sec.var >actual &&
test_cmp expect actual &&
- git -c SEC.var=val -c sec.var=VAL config --get sec.var >actual &&
+ git -c SEC.var=val -c sec.var=VAL config ${mode_prefix}get sec.var >actual &&
test_cmp expect actual &&
- git -c sec.var=val -c sec.VAR=VAL config --get SEC.var >actual &&
+ git -c sec.var=val -c sec.VAR=VAL config ${mode_prefix}get SEC.var >actual &&
test_cmp expect actual &&
- git -c SEC.var=val -c sec.var=VAL config --get sec.VAR >actual &&
+ git -c SEC.var=val -c sec.var=VAL config ${mode_prefix}get sec.VAR >actual &&
test_cmp expect actual
'
@@ -1228,9 +1249,9 @@ test_expect_success 'last one wins: three level vars' '
# case sensitive.
echo val >expect &&
- git -c v.a.r=val -c v.A.r=VAL config --get v.a.r >actual &&
+ git -c v.a.r=val -c v.A.r=VAL config ${mode_prefix}get v.a.r >actual &&
test_cmp expect actual &&
- git -c v.a.r=val -c v.A.r=VAL config --get V.a.R >actual &&
+ git -c v.a.r=val -c v.A.r=VAL config ${mode_prefix}get V.a.R >actual &&
test_cmp expect actual &&
# v.a.r and V.a.R are the same variable, as the first
@@ -1238,13 +1259,13 @@ test_expect_success 'last one wins: three level vars' '
# case insensitive.
echo VAL >expect &&
- git -c v.a.r=val -c v.a.R=VAL config --get v.a.r >actual &&
+ git -c v.a.r=val -c v.a.R=VAL config ${mode_prefix}get v.a.r >actual &&
test_cmp expect actual &&
- git -c v.a.r=val -c V.a.r=VAL config --get v.a.r >actual &&
+ git -c v.a.r=val -c V.a.r=VAL config ${mode_prefix}get v.a.r >actual &&
test_cmp expect actual &&
- git -c v.a.r=val -c v.a.R=VAL config --get V.a.R >actual &&
+ git -c v.a.r=val -c v.a.R=VAL config ${mode_prefix}get V.a.R >actual &&
test_cmp expect actual &&
- git -c v.a.r=val -c V.a.r=VAL config --get V.a.R >actual &&
+ git -c v.a.r=val -c V.a.r=VAL config ${mode_prefix}get V.a.R >actual &&
test_cmp expect actual
'
@@ -1259,7 +1280,7 @@ test_expect_success 'old-fashioned settings are case insensitive' '
[V.A]
Qr = value2
EOF
- git config -f testConfig_actual "v.a.r" value2 &&
+ git config ${mode_set} -f testConfig_actual "v.a.r" value2 &&
test_cmp testConfig_expect testConfig_actual &&
cat >testConfig_actual <<-EOF &&
@@ -1270,7 +1291,7 @@ test_expect_success 'old-fashioned settings are case insensitive' '
[V.A]
QR = value2
EOF
- git config -f testConfig_actual "V.a.R" value2 &&
+ git config ${mode_set} -f testConfig_actual "V.a.R" value2 &&
test_cmp testConfig_expect testConfig_actual &&
cat >testConfig_actual <<-EOF &&
@@ -1282,7 +1303,7 @@ test_expect_success 'old-fashioned settings are case insensitive' '
r = value1
Qr = value2
EOF
- git config -f testConfig_actual "V.A.r" value2 &&
+ git config ${mode_set} -f testConfig_actual "V.A.r" value2 &&
test_cmp testConfig_expect testConfig_actual &&
cat >testConfig_actual <<-EOF &&
@@ -1294,7 +1315,7 @@ test_expect_success 'old-fashioned settings are case insensitive' '
r = value1
Qr = value2
EOF
- git config -f testConfig_actual "v.A.r" value2 &&
+ git config ${mode_set} -f testConfig_actual "v.A.r" value2 &&
test_cmp testConfig_expect testConfig_actual
'
@@ -1324,21 +1345,21 @@ test_expect_success 'setting different case sensitive subsections ' '
Qf = v2
EOF
# exact match
- git config -f testConfig_actual a.b.c v2 &&
+ git config ${mode_set} -f testConfig_actual a.b.c v2 &&
# match section and subsection, key is cased differently.
- git config -f testConfig_actual K.E.y v2 &&
+ git config ${mode_set} -f testConfig_actual K.E.y v2 &&
# section and key are matched case insensitive, but subsection needs
# to match; When writing out new values only the key is adjusted
- git config -f testConfig_actual v.A.r v2 &&
+ git config ${mode_set} -f testConfig_actual v.A.r v2 &&
# subsection is not matched:
- git config -f testConfig_actual d.E.f v2 &&
+ git config ${mode_set} -f testConfig_actual d.E.f v2 &&
test_cmp testConfig_expect testConfig_actual
'
for VAR in a .a a. a.0b a."b c". a."b c".0d
do
test_expect_success "git -c $VAR=VAL rejects invalid '$VAR'" '
- test_must_fail git -c "$VAR=VAL" config -l
+ test_must_fail git -c "$VAR=VAL" config ${mode_prefix}list
'
done
@@ -1346,20 +1367,20 @@ for VAR in a.b a."b c".d
do
test_expect_success "git -c $VAR=VAL works with valid '$VAR'" '
echo VAL >expect &&
- git -c "$VAR=VAL" config --get "$VAR" >actual &&
+ git -c "$VAR=VAL" config ${mode_prefix}get "$VAR" >actual &&
test_cmp expect actual
'
done
test_expect_success 'git -c is not confused by empty environment' '
- GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
+ GIT_CONFIG_PARAMETERS="" git -c x.one=1 config ${mode_prefix}list
'
test_expect_success 'GIT_CONFIG_PARAMETERS handles old-style entries' '
v="${SQ}key.one=foo${SQ}" &&
v="$v ${SQ}key.two=bar${SQ}" &&
v="$v ${SQ}key.ambiguous=section.whatever=value${SQ}" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_prefix}get-regexp "key.*" >actual &&
cat >expect <<-EOF &&
key.one foo
key.two bar
@@ -1372,7 +1393,7 @@ test_expect_success 'GIT_CONFIG_PARAMETERS handles new-style entries' '
v="${SQ}key.one${SQ}=${SQ}foo${SQ}" &&
v="$v ${SQ}key.two${SQ}=${SQ}bar${SQ}" &&
v="$v ${SQ}key.ambiguous=section.whatever${SQ}=${SQ}value${SQ}" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_prefix}get-regexp "key.*" >actual &&
cat >expect <<-EOF &&
key.one foo
key.two bar
@@ -1386,7 +1407,7 @@ test_expect_success 'old and new-style entries can mix' '
v="$v ${SQ}key.newone${SQ}=${SQ}newfoo${SQ}" &&
v="$v ${SQ}key.oldtwo=oldbar${SQ}" &&
v="$v ${SQ}key.newtwo${SQ}=${SQ}newbar${SQ}" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_prefix}get-regexp "key.*" >actual &&
cat >expect <<-EOF &&
key.oldone oldfoo
key.newone newfoo
@@ -1399,7 +1420,7 @@ test_expect_success 'old and new-style entries can mix' '
test_expect_success 'old and new bools with ambiguous subsection' '
v="${SQ}key.with=equals.oldbool${SQ}" &&
v="$v ${SQ}key.with=equals.newbool${SQ}=" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_prefix}get-regexp "key.*" >actual &&
cat >expect <<-EOF &&
key.with equals.oldbool
key.with=equals.newbool
@@ -1413,7 +1434,7 @@ test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
env.two two
EOF
GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ} ${SQ}env.two=two${SQ}" \
- git config --get-regexp "env.*" >actual &&
+ git config ${mode_prefix}get-regexp "env.*" >actual &&
test_cmp expect actual &&
cat >expect <<-EOF &&
@@ -1421,12 +1442,12 @@ test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
env.two two
EOF
GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ}\\$SQ$SQ$SQ ${SQ}env.two=two${SQ}" \
- git config --get-regexp "env.*" >actual &&
+ git config ${mode_prefix}get-regexp "env.*" >actual &&
test_cmp expect actual &&
test_must_fail env \
GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ}\\$SQ ${SQ}env.two=two${SQ}" \
- git config --get-regexp "env.*"
+ git config ${mode_prefix}get-regexp "env.*"
'
test_expect_success 'git --config-env=key=envvar support' '
@@ -1439,12 +1460,12 @@ test_expect_success 'git --config-env=key=envvar support' '
false
EOF
{
- ENVVAR=value git --config-env=core.name=ENVVAR config core.name &&
- ENVVAR=value git --config-env core.name=ENVVAR config core.name &&
- ENVVAR=value git --config-env=foo.CamelCase=ENVVAR config foo.camelcase &&
- ENVVAR=value git --config-env foo.CamelCase=ENVVAR config foo.camelcase &&
- ENVVAR= git --config-env=foo.flag=ENVVAR config --bool foo.flag &&
- ENVVAR= git --config-env foo.flag=ENVVAR config --bool foo.flag
+ ENVVAR=value git --config-env=core.name=ENVVAR config ${mode_get} core.name &&
+ ENVVAR=value git --config-env core.name=ENVVAR config ${mode_get} core.name &&
+ ENVVAR=value git --config-env=foo.CamelCase=ENVVAR config ${mode_get} foo.camelcase &&
+ ENVVAR=value git --config-env foo.CamelCase=ENVVAR config ${mode_get} foo.camelcase &&
+ ENVVAR= git --config-env=foo.flag=ENVVAR config ${mode_get} --bool foo.flag &&
+ ENVVAR= git --config-env foo.flag=ENVVAR config ${mode_get} --bool foo.flag
} >actual &&
test_cmp expect actual
'
@@ -1452,17 +1473,17 @@ test_expect_success 'git --config-env=key=envvar support' '
test_expect_success 'git --config-env with missing value' '
test_must_fail env ENVVAR=value git --config-env 2>error &&
grep "no config key given for --config-env" error &&
- test_must_fail env ENVVAR=value git --config-env config core.name 2>error &&
+ test_must_fail env ENVVAR=value git --config-env config ${mode_get} core.name 2>error &&
grep "invalid config format: config" error
'
test_expect_success 'git --config-env fails with invalid parameters' '
- test_must_fail git --config-env=foo.flag config --bool foo.flag 2>error &&
+ test_must_fail git --config-env=foo.flag config ${mode_get} --bool foo.flag 2>error &&
test_grep "invalid config format: foo.flag" error &&
- test_must_fail git --config-env=foo.flag= config --bool foo.flag 2>error &&
+ test_must_fail git --config-env=foo.flag= config ${mode_get} --bool foo.flag 2>error &&
test_grep "missing environment variable name for configuration ${SQ}foo.flag${SQ}" error &&
sane_unset NONEXISTENT &&
- test_must_fail git --config-env=foo.flag=NONEXISTENT config --bool foo.flag 2>error &&
+ test_must_fail git --config-env=foo.flag=NONEXISTENT config ${mode_get} --bool foo.flag 2>error &&
test_grep "missing environment variable ${SQ}NONEXISTENT${SQ} for configuration ${SQ}foo.flag${SQ}" error
'
@@ -1474,7 +1495,7 @@ test_expect_success 'git -c and --config-env work together' '
ENVVAR=env-value git \
-c bar.cmd=cmd-value \
--config-env=bar.env=ENVVAR \
- config --get-regexp "^bar.*" >actual &&
+ config ${mode_prefix}get-regexp "^bar.*" >actual &&
test_cmp expect actual
'
@@ -1484,8 +1505,8 @@ test_expect_success 'git -c and --config-env override each other' '
cmd
EOF
{
- ENVVAR=env git -c bar.bar=cmd --config-env=bar.bar=ENVVAR config bar.bar &&
- ENVVAR=env git --config-env=bar.bar=ENVVAR -c bar.bar=cmd config bar.bar
+ ENVVAR=env git -c bar.bar=cmd --config-env=bar.bar=ENVVAR config ${config_get} bar.bar &&
+ ENVVAR=env git --config-env=bar.bar=ENVVAR -c bar.bar=cmd config ${config_get} bar.bar
} >actual &&
test_cmp expect actual
'
@@ -1494,7 +1515,7 @@ test_expect_success '--config-env handles keys with equals' '
echo value=with=equals >expect &&
ENVVAR=value=with=equals git \
--config-env=section.subsection=with=equals.key=ENVVAR \
- config section.subsection=with=equals.key >actual &&
+ config ${config_get} section.subsection=with=equals.key >actual &&
test_cmp expect actual
'
@@ -1502,7 +1523,7 @@ test_expect_success 'git config handles environment config pairs' '
GIT_CONFIG_COUNT=2 \
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="foo" \
GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="bar" \
- git config --get-regexp "pair.*" >actual &&
+ git config ${mode_prefix}get-regexp "pair.*" >actual &&
cat >expect <<-EOF &&
pair.one foo
pair.two bar
@@ -1520,7 +1541,7 @@ test_expect_success 'git config ignores pairs exceeding count' '
GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="value" \
- git config --get-regexp "pair.*" >actual 2>error &&
+ git config ${mode_prefix}get-regexp "pair.*" >actual 2>error &&
cat >expect <<-EOF &&
pair.one value
EOF
@@ -1531,43 +1552,43 @@ test_expect_success 'git config ignores pairs exceeding count' '
test_expect_success 'git config ignores pairs with zero count' '
test_must_fail env \
GIT_CONFIG_COUNT=0 GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one 2>error &&
+ git config ${config_get} pair.one 2>error &&
test_must_be_empty error
'
test_expect_success 'git config ignores pairs with empty count' '
test_must_fail env \
GIT_CONFIG_COUNT= GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one 2>error &&
+ git config ${config_get} pair.one 2>error &&
test_must_be_empty error
'
test_expect_success 'git config fails with invalid count' '
- test_must_fail env GIT_CONFIG_COUNT=10a git config --list 2>error &&
+ test_must_fail env GIT_CONFIG_COUNT=10a git config ${mode_prefix}list 2>error &&
test_grep "bogus count" error &&
- test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config --list 2>error &&
+ test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config ${mode_prefix}list 2>error &&
test_grep "too many entries" error
'
test_expect_success 'git config fails with missing config key' '
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_VALUE_0="value" \
- git config --list 2>error &&
+ git config ${mode_prefix}list 2>error &&
test_grep "missing config key" error
'
test_expect_success 'git config fails with missing config value' '
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="pair.one" \
- git config --list 2>error &&
+ git config ${mode_prefix}list 2>error &&
test_grep "missing config value" error
'
test_expect_success 'git config fails with invalid config pair key' '
test_must_fail env GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0= GIT_CONFIG_VALUE_0=value \
- git config --list &&
+ git config ${mode_prefix}list &&
test_must_fail env GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0=missing-section GIT_CONFIG_VALUE_0=value \
- git config --list
+ git config ${mode_prefix}list
'
test_expect_success 'environment overrides config file' '
@@ -1577,7 +1598,7 @@ test_expect_success 'environment overrides config file' '
one = value
EOF
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=override \
- git config pair.one >actual &&
+ git config ${mode_get} pair.one >actual &&
cat >expect <<-EOF &&
override
EOF
@@ -1587,7 +1608,7 @@ test_expect_success 'environment overrides config file' '
test_expect_success 'GIT_CONFIG_PARAMETERS overrides environment config' '
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=value \
GIT_CONFIG_PARAMETERS="${SQ}pair.one=override${SQ}" \
- git config pair.one >actual &&
+ git config ${mode_get} pair.one >actual &&
cat >expect <<-EOF &&
override
EOF
@@ -1596,7 +1617,7 @@ test_expect_success 'GIT_CONFIG_PARAMETERS overrides environment config' '
test_expect_success 'command line overrides environment config' '
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=value \
- git -c pair.one=override config pair.one >actual &&
+ git -c pair.one=override config ${mode_get} pair.one >actual &&
cat >expect <<-EOF &&
override
EOF
@@ -1606,8 +1627,8 @@ test_expect_success 'command line overrides environment config' '
test_expect_success 'git config --edit works' '
git config -f tmp test.value no &&
echo test.value=yes >expect &&
- GIT_EDITOR="echo [test]value=yes >" git config -f tmp --edit &&
- git config -f tmp --list >actual &&
+ GIT_EDITOR="echo [test]value=yes >" git config ${mode_prefix}edit -f tmp &&
+ git config ${mode_prefix}list -f tmp >actual &&
test_cmp expect actual
'
@@ -1615,8 +1636,8 @@ test_expect_success 'git config --edit respects core.editor' '
git config -f tmp test.value no &&
echo test.value=yes >expect &&
test_config core.editor "echo [test]value=yes >" &&
- git config -f tmp --edit &&
- git config -f tmp --list >actual &&
+ git config ${mode_prefix}edit -f tmp &&
+ git config ${mode_prefix}list -f tmp >actual &&
test_cmp expect actual
'
@@ -1627,7 +1648,7 @@ test_expect_success 'barf on syntax error' '
[section]
key garbage
EOF
- test_must_fail git config --get section.key 2>error &&
+ test_must_fail git config ${mode_prefix}get section.key 2>error &&
test_grep " line 3 " error
'
@@ -1637,7 +1658,7 @@ test_expect_success 'barf on incomplete section header' '
[section
key = value
EOF
- test_must_fail git config --get section.key 2>error &&
+ test_must_fail git config ${mode_prefix}get section.key 2>error &&
test_grep " line 2 " error
'
@@ -1647,7 +1668,7 @@ test_expect_success 'barf on incomplete string' '
[section]
key = "value string
EOF
- test_must_fail git config --get section.key 2>error &&
+ test_must_fail git config ${mode_prefix}get section.key 2>error &&
test_grep " line 3 " error
'
@@ -1660,22 +1681,22 @@ test_expect_success 'urlmatch' '
cookieFile = /tmp/cookie.txt
EOF
- test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&
+ test_expect_code 1 git config ${mode_prefix}get-urlmatch --bool doesnt.exist https://good.example.com >actual &&
test_must_be_empty actual &&
echo true >expect &&
- git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual &&
+ git config ${mode_prefix}get-urlmatch --bool http.SSLverify https://good.example.com >actual &&
test_cmp expect actual &&
echo false >expect &&
- git config --bool --get-urlmatch http.sslverify https://weak.example.com >actual &&
+ git config ${mode_prefix}get-urlmatch --bool http.sslverify https://weak.example.com >actual &&
test_cmp expect actual &&
{
echo http.cookiefile /tmp/cookie.txt &&
echo http.sslverify false
} >expect &&
- git config --get-urlmatch HTTP https://weak.example.com >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://weak.example.com >actual &&
test_cmp expect actual
'
@@ -1690,7 +1711,7 @@ test_expect_success 'urlmatch with --show-scope' '
local http.cookiefile /tmp/cookie.txt
local http.sslverify false
EOF
- git config --get-urlmatch --show-scope HTTP https://weak.example.com >actual &&
+ git config ${mode_prefix}get-urlmatch --show-scope HTTP https://weak.example.com >actual &&
test_cmp expect actual
'
@@ -1721,47 +1742,47 @@ test_expect_success 'urlmatch favors more specific URLs' '
EOF
echo http.cookiefile /tmp/root.txt >expect &&
- git config --get-urlmatch HTTP https://example.com >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://example.com >actual &&
test_cmp expect actual &&
echo http.cookiefile /tmp/subdirectory.txt >expect &&
- git config --get-urlmatch HTTP https://example.com/subdirectory >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://example.com/subdirectory >actual &&
test_cmp expect actual &&
echo http.cookiefile /tmp/subdirectory.txt >expect &&
- git config --get-urlmatch HTTP https://example.com/subdirectory/nested >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://example.com/subdirectory/nested >actual &&
test_cmp expect actual &&
echo http.cookiefile /tmp/user.txt >expect &&
- git config --get-urlmatch HTTP https://user@example.com/ >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://user@example.com/ >actual &&
test_cmp expect actual &&
echo http.cookiefile /tmp/subdirectory.txt >expect &&
- git config --get-urlmatch HTTP https://averylonguser@example.com/subdirectory >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://averylonguser@example.com/subdirectory >actual &&
test_cmp expect actual &&
echo http.cookiefile /tmp/preceding.txt >expect &&
- git config --get-urlmatch HTTP https://preceding.example.com >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://preceding.example.com >actual &&
test_cmp expect actual &&
echo http.cookiefile /tmp/wildcard.txt >expect &&
- git config --get-urlmatch HTTP https://wildcard.example.com >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://wildcard.example.com >actual &&
test_cmp expect actual &&
echo http.cookiefile /tmp/sub.txt >expect &&
- git config --get-urlmatch HTTP https://sub.example.com/wildcardwithsubdomain >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://sub.example.com/wildcardwithsubdomain >actual &&
test_cmp expect actual &&
echo http.cookiefile /tmp/trailing.txt >expect &&
- git config --get-urlmatch HTTP https://trailing.example.com >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://trailing.example.com >actual &&
test_cmp expect actual &&
echo http.cookiefile /tmp/sub.txt >expect &&
- git config --get-urlmatch HTTP https://user@sub.example.com >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://user@sub.example.com >actual &&
test_cmp expect actual &&
echo http.cookiefile /tmp/multiwildcard.txt >expect &&
- git config --get-urlmatch HTTP https://wildcard.example.org >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://wildcard.example.org >actual &&
test_cmp expect actual
'
@@ -1774,34 +1795,34 @@ test_expect_success 'urlmatch with wildcard' '
cookieFile = /tmp/cookie.txt
EOF
- test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&
+ test_expect_code 1 git config ${mode_prefix}get-urlmatch --bool doesnt.exist https://good.example.com >actual &&
test_must_be_empty actual &&
echo true >expect &&
- git config --bool --get-urlmatch http.SSLverify https://example.com >actual &&
+ git config ${mode_prefix}get-urlmatch --bool http.SSLverify https://example.com >actual &&
test_cmp expect actual &&
echo true >expect &&
- git config --bool --get-urlmatch http.SSLverify https://good-example.com >actual &&
+ git config ${mode_prefix}get-urlmatch --bool http.SSLverify https://good-example.com >actual &&
test_cmp expect actual &&
echo true >expect &&
- git config --bool --get-urlmatch http.sslverify https://deep.nested.example.com >actual &&
+ git config ${mode_prefix}get-urlmatch --bool http.sslverify https://deep.nested.example.com >actual &&
test_cmp expect actual &&
echo false >expect &&
- git config --bool --get-urlmatch http.sslverify https://good.example.com >actual &&
+ git config ${mode_prefix}get-urlmatch --bool http.sslverify https://good.example.com >actual &&
test_cmp expect actual &&
{
echo http.cookiefile /tmp/cookie.txt &&
echo http.sslverify false
} >expect &&
- git config --get-urlmatch HTTP https://good.example.com >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://good.example.com >actual &&
test_cmp expect actual &&
echo http.sslverify >expect &&
- git config --get-urlmatch HTTP https://more.example.com.au >actual &&
+ git config ${mode_prefix}get-urlmatch HTTP https://more.example.com.au >actual &&
test_cmp expect actual
'
@@ -1828,7 +1849,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
# please be careful when you update the above variable
EOF
- git config --unset section.key &&
+ git config ${mode_prefix}unset section.key &&
test_cmp expect .git/config &&
cat >.git/config <<-\EOF &&
@@ -1841,7 +1862,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
[next-section]
EOF
- git config --unset section.key &&
+ git config ${mode_prefix}unset section.key &&
test_cmp expect .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -1851,7 +1872,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
[two]
key = true
EOF
- git config --unset two.key &&
+ git config ${mode_prefix}unset two.key &&
! grep two .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -1861,7 +1882,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
[one]
key = true
EOF
- git config --unset-all one.key &&
+ git config ${mode_prefix}unset-all one.key &&
test_line_count = 0 .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -1871,7 +1892,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
[two]
Qkey = true
EOF
- git config --unset two.key &&
+ git config ${mode_prefix}unset two.key &&
grep two .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -1883,7 +1904,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
[TWO "subsection"]
[one]
EOF
- git config --unset two.subsection.key &&
+ git config ${mode_prefix}unset two.subsection.key &&
test "not [two subsection]" = "$(git config one.key)" &&
test_line_count = 3 .git/config
'
@@ -1895,7 +1916,7 @@ test_expect_success '--unset-all removes section if empty & uncommented' '
key = value2
EOF
- git config --unset-all section.key &&
+ git config ${mode_prefix}unset-all section.key &&
test_line_count = 0 .git/config
'
@@ -1909,7 +1930,7 @@ test_expect_success 'adding a key into an empty section reuses header' '
Qkey = value
EOF
- git config section.key value &&
+ git config ${mode_set} section.key value &&
test_cmp expect .git/config
'
@@ -1918,7 +1939,7 @@ test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
git config imap.pass Hunter2 &&
perl -e \
"die q(badset) if ((stat(q(.git/config)))[2] & 07777) != 0600" &&
- git config --rename-section imap pop &&
+ git config ${mode_prefix}rename-section imap pop &&
perl -e \
"die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
'
@@ -1967,7 +1988,7 @@ test_expect_success '--show-origin with --list' '
command line: user.cmdline=true
EOF
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=user.environ GIT_CONFIG_VALUE_0=true\
- git -c user.cmdline=true config --list --show-origin >output &&
+ git -c user.cmdline=true config ${mode_prefix}list --show-origin >output &&
test_cmp expect output
'
@@ -1984,7 +2005,7 @@ test_expect_success '--show-origin with --list --null' '
includeQcommand line:Quser.cmdline
trueQ
EOF
- git -c user.cmdline=true config --null --list --show-origin >output.raw &&
+ git -c user.cmdline=true config ${mode_prefix}list --null --show-origin >output.raw &&
nul_to_q <output.raw >output &&
# The here-doc above adds a newline that the --null output would not
# include. Add it here to make the two comparable.
@@ -1998,7 +2019,7 @@ test_expect_success '--show-origin with single file' '
file:.git/config user.override=local
file:.git/config include.path=../include/relative.include
EOF
- git config --local --list --show-origin >output &&
+ git config ${mode_prefix}list --local --show-origin >output &&
test_cmp expect output
'
@@ -2007,7 +2028,7 @@ test_expect_success '--show-origin with --get-regexp' '
file:$HOME/.gitconfig user.global true
file:.git/config user.local true
EOF
- git config --show-origin --get-regexp "user\.[g|l].*" >output &&
+ git config ${mode_prefix}get-regexp --show-origin "user\.[g|l].*" >output &&
test_cmp expect output
'
@@ -2015,7 +2036,7 @@ test_expect_success '--show-origin getting a single key' '
cat >expect <<-\EOF &&
file:.git/config local
EOF
- git config --show-origin user.override >output &&
+ git config ${mode_get} --show-origin user.override >output &&
test_cmp expect output
'
@@ -2036,7 +2057,7 @@ test_expect_success !MINGW '--show-origin escape special file name characters' '
cat >expect <<-\EOF &&
file:"file\" (dq) and spaces.conf" user.custom=true
EOF
- git config --file "$WEIRDLY_NAMED_FILE" --show-origin --list >output &&
+ git config ${mode_prefix}list --file "$WEIRDLY_NAMED_FILE" --show-origin >output &&
test_cmp expect output
'
@@ -2044,7 +2065,7 @@ test_expect_success '--show-origin stdin' '
cat >expect <<-\EOF &&
standard input: user.custom=true
EOF
- git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output &&
+ git config ${mode_prefix}list --file - --show-origin <"$CUSTOM_CONFIG_FILE" >output &&
test_cmp expect output
'
@@ -2057,7 +2078,7 @@ test_expect_success '--show-origin stdin with file include' '
file:$INCLUDE_DIR/stdin.include include
EOF
echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" |
- git config --show-origin --includes --file - user.stdin >output &&
+ git config ${mode_get} --show-origin --includes --file - user.stdin >output &&
test_cmp expect output
'
@@ -2071,7 +2092,7 @@ test_expect_success '--show-origin blob' '
cat >expect <<-EOF &&
blob:$blob user.custom=true
EOF
- git config --blob=$blob --show-origin --list >output &&
+ git config ${mode_prefix}list --blob=$blob --show-origin >output &&
test_cmp expect output
)
'
@@ -2087,13 +2108,13 @@ test_expect_success '--show-origin blob ref' '
cp "$CUSTOM_CONFIG_FILE" custom.conf &&
git add custom.conf &&
git commit -m "new config file" &&
- git config --blob=main:custom.conf --show-origin --list >output &&
+ git config ${mode_prefix}list --blob=main:custom.conf --show-origin >output &&
test_cmp expect output
)
'
test_expect_success '--show-origin with --default' '
- git config --show-origin --default foo some.key >actual &&
+ git config ${mode_get} --show-origin --default foo some.key >actual &&
echo "command line: foo" >expect &&
test_cmp expect actual
'
@@ -2113,13 +2134,14 @@ test_expect_success '--show-scope with --list' '
worktree user.worktree=true
command user.cmdline=true
EOF
+ test_when_finished "git worktree remove wt1" &&
git worktree add wt1 &&
# We need these to test for worktree scope, but outside of this
# test, this is just noise
test_config core.repositoryformatversion 1 &&
test_config extensions.worktreeConfig true &&
- git config --worktree user.worktree true &&
- git -c user.cmdline=true config --list --show-scope >output &&
+ git config ${mode_get} --worktree user.worktree true &&
+ git -c user.cmdline=true config ${mode_prefix}list --show-scope >output &&
test_cmp expect output
'
@@ -2128,7 +2150,7 @@ test_expect_success !MINGW '--show-scope with --blob' '
cat >expect <<-EOF &&
command user.custom=true
EOF
- git config --blob=$blob --show-scope --list >output &&
+ git config ${mode_prefix}list --blob=$blob --show-scope >output &&
test_cmp expect output
'
@@ -2138,7 +2160,7 @@ test_expect_success '--show-scope with --local' '
local user.override=local
local include.path=../include/relative.include
EOF
- git config --local --list --show-scope >output &&
+ git config ${mode_prefix}list --local --show-scope >output &&
test_cmp expect output
'
@@ -2146,7 +2168,7 @@ test_expect_success '--show-scope getting a single value' '
cat >expect <<-\EOF &&
local true
EOF
- git config --show-scope --get user.local >output &&
+ git config ${mode_prefix}get --show-scope user.local >output &&
test_cmp expect output
'
@@ -2162,12 +2184,12 @@ test_expect_success '--show-scope with --show-origin' '
local file:.git/../include/relative.include user.relative=include
command command line: user.cmdline=true
EOF
- git -c user.cmdline=true config --list --show-origin --show-scope >output &&
+ git -c user.cmdline=true config ${mode_prefix}list --show-origin --show-scope >output &&
test_cmp expect output
'
test_expect_success '--show-scope with --default' '
- git config --show-scope --default foo some.key >actual &&
+ git config ${mode_get} --show-scope --default foo some.key >actual &&
echo "command foo" >expect &&
test_cmp expect actual
'
@@ -2203,7 +2225,7 @@ test_expect_success 'override global and system config' '
global home.config=true
local local.config=true
EOF
- git config --show-scope --list >output &&
+ git config ${mode_prefix}list --show-scope >output &&
test_cmp expect output &&
cat >expect <<-EOF &&
@@ -2212,20 +2234,20 @@ test_expect_success 'override global and system config' '
local local.config=true
EOF
GIT_CONFIG_NOSYSTEM=false GIT_CONFIG_SYSTEM=custom-system-config GIT_CONFIG_GLOBAL=custom-global-config \
- git config --show-scope --list >output &&
+ git config ${mode_prefix}list --show-scope >output &&
test_cmp expect output &&
cat >expect <<-EOF &&
local local.config=true
EOF
GIT_CONFIG_NOSYSTEM=false GIT_CONFIG_SYSTEM=/dev/null GIT_CONFIG_GLOBAL=/dev/null \
- git config --show-scope --list >output &&
+ git config ${mode_prefix}list --show-scope >output &&
test_cmp expect output
'
test_expect_success 'override global and system config with missing file' '
- test_must_fail env GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=/dev/null git config --global --list &&
- test_must_fail env GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=does-not-exist git config --system --list &&
+ test_must_fail env GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=/dev/null git config ${mode_prefix}list --global &&
+ test_must_fail env GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=does-not-exist git config ${mode_prefix}list --system &&
GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=does-not-exist git version
'
@@ -2251,10 +2273,10 @@ test_expect_success 'write to overridden global and system config' '
key = value
EOF
- GIT_CONFIG_GLOBAL=write-to-global git config --global config.key value &&
+ GIT_CONFIG_GLOBAL=write-to-global git config ${mode_set} --global config.key value &&
test_cmp expect write-to-global &&
- GIT_CONFIG_SYSTEM=write-to-system git config --system config.key value &&
+ GIT_CONFIG_SYSTEM=write-to-system git config ${mode_set} --system config.key value &&
test_cmp expect write-to-system
'
@@ -2263,7 +2285,7 @@ do
test_expect_success "$opt requires a repo" '
# we expect 128 to ensure that we do not simply
# fail to find anything and return code "1"
- test_expect_code 128 nongit git config $opt foo.bar
+ test_expect_code 128 nongit git config ${mode_get} $opt foo.bar
'
done
@@ -2287,17 +2309,17 @@ test_expect_success 'identical mixed --type specifiers are allowed' '
'
test_expect_success 'non-identical modern --type specifiers are not allowed' '
- test_must_fail git config --type=int --type=bool section.big 2>error &&
+ test_must_fail git config ${mode_get} --type=int --type=bool section.big 2>error &&
test_grep "only one type at a time" error
'
test_expect_success 'non-identical legacy --type specifiers are not allowed' '
- test_must_fail git config --int --bool section.big 2>error &&
+ test_must_fail git config ${mode_get} --int --bool section.big 2>error &&
test_grep "only one type at a time" error
'
test_expect_success 'non-identical mixed --type specifiers are not allowed' '
- test_must_fail git config --type=int --bool section.big 2>error &&
+ test_must_fail git config ${mode_get} --type=int --bool section.big 2>error &&
test_grep "only one type at a time" error
'
@@ -2314,12 +2336,12 @@ test_expect_success 'unset type specifiers may be reset to conflicting ones' '
'
test_expect_success '--type rejects unknown specifiers' '
- test_must_fail git config --type=nonsense section.foo 2>error &&
+ test_must_fail git config ${mode_get} --type=nonsense section.foo 2>error &&
test_grep "unrecognized --type argument" error
'
test_expect_success '--type=int requires at least one digit' '
- test_must_fail git config --type int --default m some.key >out 2>error &&
+ test_must_fail git config ${mode_get} --type int --default m some.key >out 2>error &&
grep "bad numeric config value" error &&
test_must_be_empty out
'
@@ -2341,18 +2363,18 @@ test_expect_success '--replace-all does not invent newlines' '
[abc]
Qkey = b
EOF
- git config --replace-all abc.key b &&
+ git config ${mode_prefix}replace-all abc.key b &&
test_cmp expect .git/config
'
test_expect_success 'set all config with value-pattern' '
test_when_finished rm -f config initial &&
- git config --file=initial abc.key one &&
+ git config ${mode_set} --file=initial abc.key one &&
# no match => add new entry
cp initial config &&
- git config --file=config abc.key two a+ &&
- git config --file=config --list >actual &&
+ git config ${mode_set_all} --file=config abc.key two a+ &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=one
abc.key=two
@@ -2360,12 +2382,12 @@ test_expect_success 'set all config with value-pattern' '
test_cmp expect actual &&
# multiple matches => failure
- test_must_fail git config --file=config abc.key three o+ 2>err &&
+ test_must_fail git config ${mode_set_all} --file=config abc.key three o+ 2>err &&
test_grep "has multiple values" err &&
# multiple values, no match => add
- git config --file=config abc.key three a+ &&
- git config --file=config --list >actual &&
+ git config ${mode_set_all} --file=config abc.key three a+ &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=one
abc.key=two
@@ -2374,8 +2396,8 @@ test_expect_success 'set all config with value-pattern' '
test_cmp expect actual &&
# single match => replace
- git config --file=config abc.key four h+ &&
- git config --file=config --list >actual &&
+ git config ${mode_set_all} --file=config abc.key four h+ &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=one
abc.key=two
@@ -2386,11 +2408,11 @@ test_expect_success 'set all config with value-pattern' '
test_expect_success '--replace-all and value-pattern' '
test_when_finished rm -f config &&
- git config --file=config --add abc.key one &&
- git config --file=config --add abc.key two &&
- git config --file=config --add abc.key three &&
- git config --file=config --replace-all abc.key four "o+" &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}add --file=config abc.key one &&
+ git config ${mode_prefix}add --file=config abc.key two &&
+ git config ${mode_prefix}add --file=config abc.key three &&
+ git config ${mode_prefix}replace-all --file=config abc.key four "o+" &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=four
abc.key=three
@@ -2403,33 +2425,33 @@ test_expect_success 'refuse --fixed-value for incompatible actions' '
git config --file=config dev.null bogus &&
# These modes do not allow --fixed-value at all
- test_must_fail git config --file=config --fixed-value --add dev.null bogus &&
- test_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&
- test_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&
- test_must_fail git config --file=config --fixed-value --rename-section dev null &&
- test_must_fail git config --file=config --fixed-value --remove-section dev &&
- test_must_fail git config --file=config --fixed-value --list &&
- test_must_fail git config --file=config --fixed-value --get-color dev.null &&
- test_must_fail git config --file=config --fixed-value --get-colorbool dev.null &&
+ test_must_fail git config ${mode_prefix}add --file=config --fixed-value dev.null bogus &&
+ test_must_fail git config ${mode_prefix}get-urlmatch --file=config --fixed-value dev.null bogus &&
+ test_must_fail git config ${mode_prefix}get-urlmatch --file=config --fixed-value dev.null bogus &&
+ test_must_fail git config ${mode_prefix}rename-section --file=config --fixed-value dev null &&
+ test_must_fail git config ${mode_prefix}remove-section --file=config --fixed-value dev &&
+ test_must_fail git config ${mode_prefix}list --file=config --fixed-value &&
+ test_must_fail git config ${mode_prefix}get-color --file=config --fixed-value dev.null &&
+ test_must_fail git config ${mode_prefix}get-colorbool --file=config --fixed-value dev.null &&
# These modes complain when --fixed-value has no value-pattern
- test_must_fail git config --file=config --fixed-value dev.null bogus &&
- test_must_fail git config --file=config --fixed-value --replace-all dev.null bogus &&
- test_must_fail git config --file=config --fixed-value --get dev.null &&
- test_must_fail git config --file=config --fixed-value --get-all dev.null &&
- test_must_fail git config --file=config --fixed-value --get-regexp "dev.*" &&
- test_must_fail git config --file=config --fixed-value --unset dev.null &&
- test_must_fail git config --file=config --fixed-value --unset-all dev.null
+ test_must_fail git config ${mode_set} --file=config --fixed-value dev.null bogus &&
+ test_must_fail git config ${mode_prefix}replace-all --file=config --fixed-value dev.null bogus &&
+ test_must_fail git config ${mode_prefix}get --file=config --fixed-value dev.null &&
+ test_must_fail git config ${mode_prefix}get-all --file=config --fixed-value dev.null &&
+ test_must_fail git config ${mode_prefix}get-regexp --file=config --fixed-value "dev.*" &&
+ test_must_fail git config ${mode_prefix}unset --file=config --fixed-value dev.null &&
+ test_must_fail git config ${mode_prefix}unset-all --file=config --fixed-value dev.null
'
test_expect_success '--fixed-value uses exact string matching' '
test_when_finished rm -f config initial &&
META="a+b*c?d[e]f.g" &&
- git config --file=initial fixed.test "$META" &&
+ git config ${mode_set} --file=initial fixed.test "$META" &&
cp initial config &&
- git config --file=config fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config ${mode_set_all} --file=config fixed.test bogus "$META" &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-EOF &&
fixed.test=$META
fixed.test=bogus
@@ -2437,34 +2459,34 @@ test_expect_success '--fixed-value uses exact string matching' '
test_cmp expect actual &&
cp initial config &&
- git config --file=config --fixed-value fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config ${mode_set_all} --file=config --fixed-value fixed.test bogus "$META" &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
fixed.test=bogus
EOF
test_cmp expect actual &&
cp initial config &&
- test_must_fail git config --file=config --unset fixed.test "$META" &&
- git config --file=config --fixed-value --unset fixed.test "$META" &&
- test_must_fail git config --file=config fixed.test &&
+ test_must_fail git config ${mode_prefix}unset --file=config fixed.test "$META" &&
+ git config ${mode_prefix}unset --file=config --fixed-value fixed.test "$META" &&
+ test_must_fail git config ${mode_get} --file=config fixed.test &&
cp initial config &&
- test_must_fail git config --file=config --unset-all fixed.test "$META" &&
- git config --file=config --fixed-value --unset-all fixed.test "$META" &&
- test_must_fail git config --file=config fixed.test &&
+ test_must_fail git config ${mode_prefix}unset-all --file=config fixed.test "$META" &&
+ git config ${mode_prefix}unset-all --file=config --fixed-value fixed.test "$META" &&
+ test_must_fail git config ${mode_get} --file=config fixed.test &&
cp initial config &&
- git config --file=config --replace-all fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}replace-all --file=config fixed.test bogus "$META" &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-EOF &&
fixed.test=$META
fixed.test=bogus
EOF
test_cmp expect actual &&
- git config --file=config --fixed-value --replace-all fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}replace-all --file=config --fixed-value fixed.test bogus "$META" &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-EOF &&
fixed.test=bogus
fixed.test=bogus
@@ -2475,23 +2497,23 @@ test_expect_success '--fixed-value uses exact string matching' '
test_expect_success '--get and --get-all with --fixed-value' '
test_when_finished rm -f config &&
META="a+b*c?d[e]f.g" &&
- git config --file=config fixed.test bogus &&
- git config --file=config --add fixed.test "$META" &&
+ git config ${mode_set} --file=config fixed.test bogus &&
+ git config ${mode_prefix}add --file=config fixed.test "$META" &&
- git config --file=config --get fixed.test bogus &&
- test_must_fail git config --file=config --get fixed.test "$META" &&
- git config --file=config --get --fixed-value fixed.test "$META" &&
- test_must_fail git config --file=config --get --fixed-value fixed.test non-existent &&
+ git config ${mode_prefix}get --file=config fixed.test bogus &&
+ test_must_fail git config ${mode_prefix}get --file=config fixed.test "$META" &&
+ git config ${mode_prefix}get --file=config --fixed-value fixed.test "$META" &&
+ test_must_fail git config ${mode_prefix}get --file=config --fixed-value fixed.test non-existent &&
- git config --file=config --get-all fixed.test bogus &&
- test_must_fail git config --file=config --get-all fixed.test "$META" &&
- git config --file=config --get-all --fixed-value fixed.test "$META" &&
- test_must_fail git config --file=config --get-all --fixed-value fixed.test non-existent &&
+ git config ${mode_prefix}get-all --file=config fixed.test bogus &&
+ test_must_fail git config ${mode_prefix}get-all --file=config fixed.test "$META" &&
+ git config ${mode_prefix}get-all --file=config --fixed-value fixed.test "$META" &&
+ test_must_fail git config ${mode_prefix}get-all --file=config --fixed-value fixed.test non-existent &&
- git config --file=config --get-regexp fixed+ bogus &&
- test_must_fail git config --file=config --get-regexp fixed+ "$META" &&
- git config --file=config --get-regexp --fixed-value fixed+ "$META" &&
- test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent
+ git config ${mode_prefix}get-regexp --file=config fixed+ bogus &&
+ test_must_fail git config ${mode_prefix}get-regexp --file=config fixed+ "$META" &&
+ git config ${mode_prefix}get-regexp --file=config --fixed-value fixed+ "$META" &&
+ test_must_fail git config ${mode_prefix}get-regexp --file=config --fixed-value fixed+ non-existent
'
test_expect_success 'includeIf.hasconfig:remote.*.url' '
@@ -2516,10 +2538,10 @@ test_expect_success 'includeIf.hasconfig:remote.*.url' '
EOF
echo this-is-included >expect-this &&
- git -C hasremoteurlTest config --get user.this >actual-this &&
+ git -C hasremoteurlTest config ${mode_prefix}get user.this >actual-this &&
test_cmp expect-this actual-this &&
- test_must_fail git -C hasremoteurlTest config --get user.that
+ test_must_fail git -C hasremoteurlTest config ${mode_prefix}get user.that
'
test_expect_success 'includeIf.hasconfig:remote.*.url respects last-config-wins' '
@@ -2546,13 +2568,13 @@ test_expect_success 'includeIf.hasconfig:remote.*.url respects last-config-wins'
echo main-config >expect-main-config &&
echo included-config >expect-included-config &&
- git -C hasremoteurlTest config --get user.one >actual &&
+ git -C hasremoteurlTest config ${mode_prefix}get user.one >actual &&
test_cmp expect-main-config actual &&
- git -C hasremoteurlTest config --get user.two >actual &&
+ git -C hasremoteurlTest config ${mode_prefix}get user.two >actual &&
test_cmp expect-included-config actual &&
- git -C hasremoteurlTest config --get user.three >actual &&
+ git -C hasremoteurlTest config ${mode_prefix}get user.three >actual &&
test_cmp expect-main-config actual
'
@@ -2587,11 +2609,11 @@ test_expect_success 'includeIf.hasconfig:remote.*.url globs' '
path = "$(pwd)/no"
EOF
- git -C hasremoteurlTest config --get user.dss &&
- git -C hasremoteurlTest config --get user.dse &&
- git -C hasremoteurlTest config --get user.dsm &&
- git -C hasremoteurlTest config --get user.ssm &&
- test_must_fail git -C hasremoteurlTest config --get user.no
+ git -C hasremoteurlTest config ${mode_prefix}get user.dss &&
+ git -C hasremoteurlTest config ${mode_prefix}get user.dse &&
+ git -C hasremoteurlTest config ${mode_prefix}get user.dsm &&
+ git -C hasremoteurlTest config ${mode_prefix}get user.ssm &&
+ test_must_fail git -C hasremoteurlTest config ${mode_prefix}get user.no
'
test_expect_success 'includeIf.hasconfig:remote.*.url forbids remote url in such included files' '
@@ -2625,4 +2647,11 @@ test_expect_success 'specifying multiple modes causes failure' '
test_cmp expect err
'
+test_expect_success 'specifying mode and subcommand causes failure' '
+ test_must_fail git config get --get 2>err &&
+ grep "subcommand and action modes are incompatible" err
+'
+
+done
+
test_done
--
2.44.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related
* [PATCH 8/8] Documentation/git-config: update to new-style syntax
From: Patrick Steinhardt @ 2024-03-06 11:32 UTC (permalink / raw)
To: git
In-Reply-To: <cover.1709724089.git.ps@pks.im>
[-- Attachment #1: Type: text/plain, Size: 21396 bytes --]
Update our documentation of git-config(1) to stop mentioning the old
syntax while starting to mention the new syntax. Remove the help
mismatch in t0450 so that we start to ensure that the manpage and
builtin synopsis match.
Note that there will still be lots of mentions of the old style syntax
of git-config(1) throughout our documentation. This documentation will
be updated over time.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
Documentation/git-config.txt | 204 ++++++++++++++++++-----------------
builtin/config.c | 46 +++++---
t/t0450/txt-help-mismatches | 1 -
3 files changed, 137 insertions(+), 114 deletions(-)
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index dff39093b5..edf67c85f7 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -9,21 +9,22 @@ git-config - Get and set repository or global options
SYNOPSIS
--------
[verse]
-'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
-'git config' [<file-option>] [--type=<type>] --add <name> <value>
-'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp <name-regex> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch <name> <URL>
-'git config' [<file-option>] [--fixed-value] --unset <name> [<value-pattern>]
-'git config' [<file-option>] [--fixed-value] --unset-all <name> [<value-pattern>]
-'git config' [<file-option>] --rename-section <old-name> <new-name>
-'git config' [<file-option>] --remove-section <name>
-'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list
-'git config' [<file-option>] --get-color <name> [<default>]
-'git config' [<file-option>] --get-colorbool <name> [<stdout-is-tty>]
-'git config' [<file-option>] -e | --edit
+'git config list' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only]
+'git config get' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] <name> [<value-pattern>]
+'git config get-all' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] <name> [<value-pattern>]
+'git config get-color' [<file-option>] <name> [<default>]
+'git config get-colorbool' [<file-option>] <name> [<stdout-is-tty>]
+'git config get-regexp' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] <name-regex> [<value-pattern>]
+'git config get-urlmatch' [<file-option>] [--type=<type>] [-z|--null] <name> <URL>
+'git config add' [<file-option>] [--type=<type>] <name> <value>
+'git config set' [<file-option>] [--type=<type>] <name> <value>
+'git config set-all' [<file-option>] [--type=<type>] [--fixed-value] <name> <value> [<value-pattern>]
+'git config unset' [<file-option>] [--fixed-value] <name> [<value-pattern>]
+'git config unset-all' [<file-option>] [--fixed-value] <name> [<value-pattern>]
+'git config replace-all' [<file-option>] [--type=<type>] [--fixed-value] <name> <value> [<value-pattern>]
+'git config rename-section' [<file-option>] <old-name> <new-name>
+'git config remove-section' [<file-option>] <name>
+'git config edit' [<file-option>]
DESCRIPTION
-----------
@@ -74,35 +75,50 @@ On success, the command returns the exit code 0.
A list of all available configuration variables can be obtained using the
`git help --config` command.
-[[OPTIONS]]
-OPTIONS
--------
-
---replace-all::
- Default behavior is to replace at most one line. This replaces
- all lines matching the key (and optionally the `value-pattern`).
+COMMANDS
+--------
---add::
- Adds a new line to the option without altering any existing
- values. This is the same as providing '^$' as the `value-pattern`
- in `--replace-all`.
+list::
+ List all variables set in config file, along with their values.
---get::
+get <name> [<value-pattern>]::
Get the value for a given key (optionally filtered by a regex
matching the value). Returns error code 1 if the key was not
found and the last value if multiple key values were found.
---get-all::
+get-all <name> [<value-pattern>]::
Like get, but returns all values for a multi-valued key.
---get-regexp::
- Like --get-all, but interprets the name as a regular expression and
+get-color <name> [<default>]::
+
+ Find the color configured for `name` (e.g. `color.diff.new`) and
+ output it as the ANSI color escape sequence to the standard
+ output. The optional `default` parameter is used instead, if
+ there is no color configured for `name`.
++
+`--type=color [--default=<default>]` is preferred over `get-color`
+(but note that `get-color` will omit the trailing newline printed by
+`--type=color`).
+
+get-colorbool <name> [<stdout-is-tty>]::
+
+ Find the color setting for `<name>` (e.g. `color.diff`) and output
+ "true" or "false". `<stdout-is-tty>` should be either "true" or
+ "false", and is taken into account when configuration says
+ "auto". If `<stdout-is-tty>` is missing, then checks the standard
+ output of the command itself, and exits with status 0 if color
+ is to be used, or exits with status 1 otherwise.
+ When the color setting for `name` is undefined, the command uses
+ `color.ui` as fallback.
+
+get-regex <name-regex> [<value-pattern>]::
+ Like `get-all`, but interprets the name as a regular expression and
writes out the key names. Regular expression matching is currently
case-sensitive and done against a canonicalized version of the key
in which section and variable names are lowercased, but subsection
names are not.
---get-urlmatch <name> <URL>::
+get-urlmatch <name> <URL>::
When given a two-part <name> as <section>.<key>, the value for
<section>.<URL>.<key> whose <URL> part matches the best to the
given URL is returned (if no such key exists, the value for
@@ -110,6 +126,43 @@ OPTIONS
<section> as name, do so for all the keys in the section and
list them. Returns error code 1 if no value is found.
+add <name> <value>::
+ Adds a new line to the option without altering any existing
+ values. This is the same as providing '^$' as the `value-pattern`
+ in `--replace-all`.
+
+set <name> <value>::
+ Set the config key to the given value. Adds a new key to the
+ configuration in case it does not yet exist. This subcommand fails when
+ the key has multiple values.
+
+set-all <name> <value> [<value-pattern>]::
+ Set all config keys to the given value.
+
+unset <name> [<value-pattern>]::
+ Remove the line matching the key from config file.
+
+unset-all <name> [<value-pattern>]::
+ Remove all lines matching the key from config file.
+
+replace-all <name> <value> [<value-pattern>]::
+ Default behavior is to replace at most one line. This replaces
+ all lines matching the key (and optionally the `value-pattern`).
+
+remove-section <name>::
+ Remove the given section from the configuration file.
+
+rename-section <old-name> <new-name>::
+ Rename the given section to a new name.
+
+edit::
+ Opens an editor to modify the specified config file; either
+ `--system`, `--global`, or repository (default).
+
+[[OPTIONS]]
+OPTIONS
+-------
+
--global::
For writing options: write to global `~/.gitconfig` file
rather than the repository `.git/config`, write to
@@ -166,22 +219,6 @@ See also <<FILES>>.
section in linkgit:gitrevisions[7] for a more complete list of
ways to spell blob names.
---remove-section::
- Remove the given section from the configuration file.
-
---rename-section::
- Rename the given section to a new name.
-
---unset::
- Remove the line matching the key from config file.
-
---unset-all::
- Remove all lines matching the key from config file.
-
--l::
---list::
- List all variables set in config file, along with their values.
-
--fixed-value::
When used with the `value-pattern` argument, treat `value-pattern` as
an exact string instead of a regular expression. This will restrict
@@ -236,8 +273,8 @@ Valid `<type>`'s include:
contain line breaks.
--name-only::
- Output only the names of config variables for `--list` or
- `--get-regexp`.
+ Output only the names of config variables for `list` or
+ `get-regexp`.
--show-origin::
Augment the output of all queried config options with the
@@ -250,33 +287,6 @@ Valid `<type>`'s include:
all queried config options with the scope of that value
(worktree, local, global, system, command).
---get-colorbool <name> [<stdout-is-tty>]::
-
- Find the color setting for `<name>` (e.g. `color.diff`) and output
- "true" or "false". `<stdout-is-tty>` should be either "true" or
- "false", and is taken into account when configuration says
- "auto". If `<stdout-is-tty>` is missing, then checks the standard
- output of the command itself, and exits with status 0 if color
- is to be used, or exits with status 1 otherwise.
- When the color setting for `name` is undefined, the command uses
- `color.ui` as fallback.
-
---get-color <name> [<default>]::
-
- Find the color configured for `name` (e.g. `color.diff.new`) and
- output it as the ANSI color escape sequence to the standard
- output. The optional `default` parameter is used instead, if
- there is no color configured for `name`.
-+
-`--type=color [--default=<default>]` is preferred over `--get-color`
-(but note that `--get-color` will omit the trailing newline printed by
-`--type=color`).
-
--e::
---edit::
- Opens an editor to modify the specified config file; either
- `--system`, `--global`, or repository (default).
-
--[no-]includes::
Respect `include.*` directives in config files when looking up
values. Defaults to `off` when a specific file is given (e.g.,
@@ -284,13 +294,13 @@ Valid `<type>`'s include:
config files.
--default <value>::
- When using `--get`, and the requested variable is not found, behave as if
+ When using `get`, and the requested variable is not found, behave as if
<value> were the value assigned to the that variable.
CONFIGURATION
-------------
`pager.config` is only respected when listing configuration, i.e., when
-using `--list` or any of the `--get-*` which may return multiple results.
+using `list` or any of the `get-*` subcommands which may return multiple results.
The default is to use a pager.
[[FILES]]
@@ -333,8 +343,8 @@ precedence over values read earlier. When multiple values are taken then all
values of a key from all files will be used.
By default, options are only written to the repository specific
-configuration file. Note that this also affects options like `--replace-all`
-and `--unset`. *'git config' will only ever change one file at a time*.
+configuration file. Note that this also affects subcommands like `replace-all`
+and `unset`. *'git config' will only ever change one file at a time*.
You can limit which configuration sources are read from or written to by
specifying the path of a file with the `--file` option, or by specifying a
@@ -469,7 +479,7 @@ Given a .git/config like this:
you can set the filemode to true with
------------
-% git config core.filemode true
+% git config set core.filemode true
------------
The hypothetical proxy command entries actually have a postfix to discern
@@ -477,7 +487,7 @@ what URL they apply to. Here is how to change the entry for kernel.org
to "ssh".
------------
-% git config core.gitproxy '"ssh" for kernel.org' 'for kernel.org$'
+% git config set core.gitproxy '"ssh" for kernel.org' 'for kernel.org$'
------------
This makes sure that only the key/value pair for kernel.org is replaced.
@@ -485,7 +495,7 @@ This makes sure that only the key/value pair for kernel.org is replaced.
To delete the entry for renames, do
------------
-% git config --unset diff.renames
+% git config unset diff.renames
------------
If you want to delete an entry for a multivar (like core.gitproxy above),
@@ -494,51 +504,51 @@ you have to provide a regex matching the value of exactly one line.
To query the value for a given key, do
------------
-% git config --get core.filemode
+% git config get core.filemode
------------
or
------------
-% git config core.filemode
+% git config get core.filemode
------------
or, to query a multivar:
------------
-% git config --get core.gitproxy "for kernel.org$"
+% git config get core.gitproxy "for kernel.org$"
------------
If you want to know all the values for a multivar, do:
------------
-% git config --get-all core.gitproxy
+% git config get-all core.gitproxy
------------
If you like to live dangerously, you can replace *all* core.gitproxy by a
new one with
------------
-% git config --replace-all core.gitproxy ssh
+% git config replace-all core.gitproxy ssh
------------
However, if you really only want to replace the line for the default proxy,
i.e. the one without a "for ..." postfix, do something like this:
------------
-% git config core.gitproxy ssh '! for '
+% git config get core.gitproxy ssh '! for '
------------
To actually match only values with an exclamation mark, you have to
------------
-% git config section.key value '[!]'
+% git config get section.key value '[!]'
------------
To add a new proxy, without altering any of the existing ones, use
------------
-% git config --add core.gitproxy '"proxy-command" for example.com'
+% git config add core.gitproxy '"proxy-command" for example.com'
------------
An example to use customized color from the configuration in your
@@ -546,8 +556,8 @@ script:
------------
#!/bin/sh
-WS=$(git config --get-color color.diff.whitespace "blue reverse")
-RESET=$(git config --get-color "" "reset")
+WS=$(git config get-color color.diff.whitespace "blue reverse")
+RESET=$(git config get-color "" "reset")
echo "${WS}your whitespace color or blue reverse${RESET}"
------------
@@ -555,11 +565,11 @@ For URLs in `https://weak.example.com`, `http.sslVerify` is set to
false, while it is set to `true` for all others:
------------
-% git config --type=bool --get-urlmatch http.sslverify https://good.example.com
+% git config get-urlmatch --type=bool http.sslverify https://good.example.com
true
-% git config --type=bool --get-urlmatch http.sslverify https://weak.example.com
+% git config get-urlmatch --type=bool http.sslverify https://weak.example.com
false
-% git config --get-urlmatch http https://weak.example.com
+% git config get-urlmatch http https://weak.example.com
http.cookieFile /tmp/cookie.txt
http.sslverify false
------------
@@ -578,7 +588,7 @@ looks like
key = value1
--------
-and running `git config section.Subsection.key value2` will result in
+and running `git config add section.Subsection.key value2` will result in
--------
[section.subsection]
diff --git a/builtin/config.c b/builtin/config.c
index 10fa933931..61a9ad3eaa 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -16,7 +16,22 @@
#include "worktree.h"
static const char *const builtin_config_usage[] = {
- N_("git config [<options>]"),
+ N_("git config list [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only]"),
+ N_("git config get [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] <name> [<value-pattern>]"),
+ N_("git config get-all [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] <name> [<value-pattern>]"),
+ N_("git config get-color [<file-option>] <name> [<default>]"),
+ N_("git config get-colorbool [<file-option>] <name> [<stdout-is-tty>]"),
+ N_("git config get-regexp [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] <name-regex> [<value-pattern>]"),
+ N_("git config get-urlmatch [<file-option>] [--type=<type>] [-z|--null] <name> <URL>"),
+ N_("git config add [<file-option>] [--type=<type>] <name> <value>"),
+ N_("git config set [<file-option>] [--type=<type>] <name> <value>"),
+ N_("git config set-all [<file-option>] [--type=<type>] [--fixed-value] <name> <value> [<value-pattern>]"),
+ N_("git config unset [<file-option>] [--fixed-value] <name> [<value-pattern>]"),
+ N_("git config unset-all [<file-option>] [--fixed-value] <name> [<value-pattern>]"),
+ N_("git config replace-all [<file-option>] [--type=<type>] [--fixed-value] <name> <value> [<value-pattern>]"),
+ N_("git config rename-section [<file-option>] <old-name> <new-name>"),
+ N_("git config remove-section [<file-option>] <name>"),
+ N_("git config edit [<file-option>]"),
NULL
};
@@ -887,21 +902,20 @@ static struct option builtin_config_options[] = {
OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")),
OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
- OPT_GROUP(N_("Action")),
- OPT_CMDMODE(0, "get", &action, N_("get value: name [value-pattern]"), ACTION_GET),
- OPT_CMDMODE(0, "get-all", &action, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
- OPT_CMDMODE(0, "get-regexp", &action, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
- OPT_CMDMODE(0, "get-urlmatch", &action, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
- OPT_CMDMODE(0, "replace-all", &action, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
- OPT_CMDMODE(0, "add", &action, N_("add a new variable: name value"), ACTION_ADD),
- OPT_CMDMODE(0, "unset", &action, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
- OPT_CMDMODE(0, "unset-all", &action, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
- OPT_CMDMODE(0, "rename-section", &action, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
- OPT_CMDMODE(0, "remove-section", &action, N_("remove a section: name"), ACTION_REMOVE_SECTION),
- OPT_CMDMODE('l', "list", &action, N_("list all"), ACTION_LIST),
- OPT_CMDMODE('e', "edit", &action, N_("open an editor"), ACTION_EDIT),
- OPT_CMDMODE(0, "get-color", &action, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
- OPT_CMDMODE(0, "get-colorbool", &action, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
+ OPT_CMDMODE_F(0, "get", &action, N_("get value: name [value-pattern]"), ACTION_GET, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "get-all", &action, N_("get all values: key [value-pattern]"), ACTION_GET_ALL, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "get-regexp", &action, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "get-urlmatch", &action, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "replace-all", &action, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "add", &action, N_("add a new variable: name value"), ACTION_ADD, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "unset", &action, N_("remove a variable: name [value-pattern]"), ACTION_UNSET, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "unset-all", &action, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "rename-section", &action, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "remove-section", &action, N_("remove a section: name"), ACTION_REMOVE_SECTION, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F('l', "list", &action, N_("list all"), ACTION_LIST, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F('e', "edit", &action, N_("open an editor"), ACTION_EDIT, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "get-color", &action, N_("find the color configured: slot [default]"), ACTION_GET_COLOR, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "get-colorbool", &action, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL, PARSE_OPT_HIDDEN),
OPT_GROUP(N_("Type")),
OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type),
OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
diff --git a/t/t0450/txt-help-mismatches b/t/t0450/txt-help-mismatches
index a0777acd66..28003f18c9 100644
--- a/t/t0450/txt-help-mismatches
+++ b/t/t0450/txt-help-mismatches
@@ -10,7 +10,6 @@ checkout
checkout-index
clone
column
-config
credential
credential-cache
credential-store
--
2.44.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related
* Re: [PATCH 3/4] reftable/stack: register lockfiles during compaction
From: Patrick Steinhardt @ 2024-03-06 11:59 UTC (permalink / raw)
To: git
In-Reply-To: <mwhby7dxpiyrvknqe2uoli4ulygjy6hbxcxpqt3alw3dthwntr@4o24tp5jp6h7>
[-- Attachment #1: Type: text/plain, Size: 4131 bytes --]
On Tue, Mar 05, 2024 at 05:30:48PM -0600, Justin Tobler wrote:
> On 24/03/04 12:10PM, Patrick Steinhardt wrote:
> > We do not register any of the locks we acquire when compacting the
> > reftable stack via our lockfiles interfaces. These locks will thus not
> > be released when Git gets killed.
> >
> > Refactor the code to register locks as lockfiles.
> >
> > Signed-off-by: Patrick Steinhardt <ps@pks.im>
> > ---
> > ...
> > + /*
> > + * Write the new "tables.list" contents with the compacted table we
> > + * have just written. In case the compacted table became empty we
> > + * simply skip writing it.
> > + */
> > + for (i = 0; i < first; i++)
> > + strbuf_addf(&tables_list_buf, "%s\n", st->readers[i]->name);
> > + if (!is_empty_table)
> > + strbuf_addf(&tables_list_buf, "%s\n", new_table_name.buf);
>
> Something not really related to this patch, but I noticed and had a
> question about.
>
> If I'm understanding this correctly, when a newly compacted table is
> empty, it becomes possible for a range of indexes to no longer exist
> within the stack. If this occurs in the middle of the stack, future
> compaction will likely combine the tables on either side and restore the
> missing index range. If the empty table was at the end of the stack,
> would this effectly reset the max index to something lower for future
> tables written to the stack? If so, could this lead to issues with
> separate concurrent table writes?
Very good question indeed, but I think we should be fine here. This is
mostly because concurrent writers will notice when "tables.list" has
changed, and, if so, abort the transaction with an out-of-date error.
A few scenarios with concurrent processes, one process which compacts
the stack (C) and one which modifies it (M):
- M acquires the lock before C compacts: M sees the whole stack and
uses the latest update index to update it, resulting in a newly
written table. When C then locks afterwards, it may decide to
compact and drop some tables in the middle of the stack. This may
lead to a gap in update indices, but this is fine.
- M acquires the lock while C compacts: M sees the whole stack and
uses the latest update index to update the stack. C then acquires
the lock to write the merged tables, notices that its compacted
tables still exist and are in the same order, and thus removes them.
We now have a gap in update indices, but this is totally fine.
- M acquires the lock after C compacts: M will refresh "tables.list"
after it has acquired the lock itself. Thus, it won't ever see the
now-dropped empty table.
M cannot write its table when C has the "tables.list" lock, so this
scenario cannot happen. In the same spirit, two Ms cannot race with each
other either as only one can have the "tables.list" lock, and the other
one would abort with an out-of-date error when it has subsequently
acquired the lock and found the "tables.list" contents to have been
updated concurrently.
> > ...
> > diff --git a/reftable/system.h b/reftable/system.h
> > index 6b74a81514..5d8b6dede5 100644
> > --- a/reftable/system.h
> > +++ b/reftable/system.h
> > @@ -12,7 +12,9 @@ license that can be found in the LICENSE file or at
> > /* This header glues the reftable library to the rest of Git */
> >
> > #include "git-compat-util.h"
> > +#include "lockfile.h"
> > #include "strbuf.h"
> > +#include "tempfile.h"
> > #include "hash-ll.h" /* hash ID, sizes.*/
> > #include "dir.h" /* remove_dir_recursively, for tests.*/
>
> Naive question, why do we include the headers in `system.h`? I assume
> this is because they are common? Are there other benefits to this
> indirection?
Well, "system.h" is supposedly the glue between the common Git codebase
and the reftable library, so all Git-specific headers should be added
here instead of being added individually to the respective files in the
library. Whether that is ultimately a sensible thing and whether it
really helps us all that much is a different question though.
Patrick
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH 2/4] reftable/stack: register new tables as tempfiles
From: Patrick Steinhardt @ 2024-03-06 11:59 UTC (permalink / raw)
To: git
In-Reply-To: <6cw6d3ubo2kbogzdbniyoznij2zfoh5t3htwb4oaghaltcgeqg@kkrw4g6atr2k>
[-- Attachment #1: Type: text/plain, Size: 2409 bytes --]
On Tue, Mar 05, 2024 at 04:30:18PM -0600, Justin Tobler wrote:
> On 24/03/04 12:10PM, Patrick Steinhardt wrote:
> > We do not register new tables which we're about to add to the stack with
> > the tempfile API. Those tables will thus not be deleted in case Git gets
> > killed.
> >
> > Refactor the code to register tables as tempfiles.
> >
> > Signed-off-by: Patrick Steinhardt <ps@pks.im>
> > ---
> > reftable/stack.c | 29 ++++++++++++-----------------
> > 1 file changed, 12 insertions(+), 17 deletions(-)
> >
> > diff --git a/reftable/stack.c b/reftable/stack.c
> > index b64e55648a..81544fbfa0 100644
> > --- a/reftable/stack.c
> > +++ b/reftable/stack.c
> > @@ -737,8 +737,9 @@ int reftable_addition_add(struct reftable_addition *add,
> > struct strbuf tab_file_name = STRBUF_INIT;
> > struct strbuf next_name = STRBUF_INIT;
> > struct reftable_writer *wr = NULL;
> > + struct tempfile *tab_file = NULL;
> > int err = 0;
> > - int tab_fd = 0;
> > + int tab_fd;
> >
> > strbuf_reset(&next_name);
> > format_name(&next_name, add->next_update_index, add->next_update_index);
> > @@ -746,17 +747,20 @@ int reftable_addition_add(struct reftable_addition *add,
> > stack_filename(&temp_tab_file_name, add->stack, next_name.buf);
> > strbuf_addstr(&temp_tab_file_name, ".temp.XXXXXX");
> >
> > - tab_fd = mkstemp(temp_tab_file_name.buf);
> > - if (tab_fd < 0) {
> > + tab_file = mks_tempfile(temp_tab_file_name.buf);
> > + if (!tab_file) {
> > err = REFTABLE_IO_ERROR;
> > goto done;
> > }
> > if (add->stack->config.default_permissions) {
> > - if (chmod(temp_tab_file_name.buf, add->stack->config.default_permissions)) {
> > + if (chmod(get_tempfile_path(tab_file),
> > + add->stack->config.default_permissions)) {
> > err = REFTABLE_IO_ERROR;
> > goto done;
> > }
> > }
>
> Since the tempfile is now being created through the tempfile API, I
> think the file mode can be set directly through `mks_tempfile_m()`
> instead of creating the tempfile and then using chmod. Just something I
> thought to mention.
Unfortunately not. The problem is that `mks_tempfile_m()` will munge
passed-in permissions via "core.sharedRepository", but we already pre
calculated the target mode in `config.default_permissions`. Thus, the
result would have wrong permissions if we used `mks_tempfile_m()`.
Patrick
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH 1/4] lockfile: report when rollback fails
From: Patrick Steinhardt @ 2024-03-06 12:00 UTC (permalink / raw)
To: git
In-Reply-To: <lyarkcrkcb7ww542lc6y7t534yxnpahvrwephottvcbhr5dkn2@wwd5wk5bzlw7>
[-- Attachment #1: Type: text/plain, Size: 1924 bytes --]
On Tue, Mar 05, 2024 at 04:09:07PM -0600, Justin Tobler wrote:
> On 24/03/04 12:10PM, Patrick Steinhardt wrote:
> > We do not report to the caller when rolling back a lockfile fails, which
> > will be needed by the reftable compaction logic in a subsequent commit.
> > It also cannot really report on all errors because the function calls
> > `delete_tempfile()`, which doesn't return an error either.
> >
> > Refactor the code so that both `delete_tempfile()` and
> > `rollback_lock_file()` return an error code.
> >
> > Signed-off-by: Patrick Steinhardt <ps@pks.im>
> > ---
> > lockfile.h | 4 ++--
> > tempfile.c | 21 +++++++++++++--------
> > tempfile.h | 2 +-
> > 3 files changed, 16 insertions(+), 11 deletions(-)
> >
> > diff --git a/lockfile.h b/lockfile.h
> > index 90af4e66b2..4ed570d3f7 100644
> > --- a/lockfile.h
> > +++ b/lockfile.h
> > @@ -323,9 +323,9 @@ static inline int commit_lock_file_to(struct lock_file *lk, const char *path)
> > * for a `lock_file` object that has already been committed or rolled
> > * back.
> > */
> > -static inline void rollback_lock_file(struct lock_file *lk)
> > +static inline int rollback_lock_file(struct lock_file *lk)
> > {
> > - delete_tempfile(&lk->tempfile);
> > + return delete_tempfile(&lk->tempfile);
> > }
>
> question: For a lockfile that is already committed or rolled back, is
> the underlying tempfile still active? I'm trying to figure out if it
> possible for an error to be returned in this scenerio. If not, it might
> be nice to clarify in the comment above that, in addition to being a
> NOOP, no error is returned.
No, it's not. I thought that it being a no-op should be clear enough,
but on the other hand it doesn't hurt either to clarify even further.
I'll refrain from sending a v2 only to add this comment as there haven't
been any other things that need to be addressed yet.
Patrick
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH] reftable/stack: use geometric table compaction
From: Patrick Steinhardt @ 2024-03-06 12:30 UTC (permalink / raw)
To: Justin Tobler via GitGitGadget; +Cc: git, Justin Tobler
In-Reply-To: <pull.1683.git.1709669025722.gitgitgadget@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 9502 bytes --]
On Tue, Mar 05, 2024 at 08:03:45PM +0000, Justin Tobler via GitGitGadget wrote:
> From: Justin Tobler <jltobler@gmail.com>
>
> To reduce the number of on-disk reftables, compaction is performed.
> Contiguous tables with the same binary log value of size are grouped
> into segments. The segment that has both the lowest binary log value and
> contains more than one table is set as the starting point when
> identifying the compaction segment.
>
> Since segments containing a single table are not initially considered
> for compaction, if the table appended to the list does not match the
> previous table log value, no compaction occurs for the new table. It is
> therefore possible for unbounded growth of the table list. This can be
> demonstrated by repeating the following sequence:
>
> git branch -f foo
> git branch -d foo
>
> Each operation results in a new table being written with no compaction
> occurring until a separate operation produces a table matching the
> previous table log value.
>
> To avoid unbounded growth of the table list, walk through each table and
> evaluate if it needs to be included in the compaction segment to restore
> a geometric sequence.
I think the description of what exactly changes could use some more
explanation and some arguments why the new behaviour is okay, too. It's
quite a large rewrite of the compaction logic, so pinpointing exactly
how these are different would go a long way.
> Some tests in `t0610-reftable-basics.sh` assert the on-disk state of
> tables and are therefore updated to specify the correct new table count.
> Since compaction is more aggressive in ensuring tables maintain a
> geometric sequence, the expected table count is reduced in these tests.
> In `reftable/stack_test.c` tests related to `sizes_to_segments()` are
> removed because the function is no longer needed. Also, the
> `test_suggest_compaction_segment()` test is updated to better showcase
> and reflect the new geometric compaction behavior.
>
> Signed-off-by: Justin Tobler <jltobler@gmail.com>
> ---
> reftable/stack: use geometric table compaction
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1683%2Fjltobler%2Fjt%2Freftable-geometric-compaction-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1683/jltobler/jt/reftable-geometric-compaction-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/1683
>
> reftable/stack.c | 106 +++++++++++++++----------------------
> reftable/stack.h | 3 --
> reftable/stack_test.c | 66 +++++------------------
> t/t0610-reftable-basics.sh | 24 ++++-----
> 4 files changed, 70 insertions(+), 129 deletions(-)
>
> diff --git a/reftable/stack.c b/reftable/stack.c
> index b64e55648aa..e4ea8753977 100644
> --- a/reftable/stack.c
> +++ b/reftable/stack.c
> @@ -1214,75 +1214,57 @@ static int segment_size(struct segment *s)
> return s->end - s->start;
> }
>
> -int fastlog2(uint64_t sz)
> -{
> - int l = 0;
> - if (sz == 0)
> - return 0;
> - for (; sz; sz /= 2) {
> - l++;
> - }
> - return l - 1;
> -}
> -
> -struct segment *sizes_to_segments(size_t *seglen, uint64_t *sizes, size_t n)
> -{
> - struct segment *segs = reftable_calloc(n, sizeof(*segs));
> - struct segment cur = { 0 };
> - size_t next = 0, i;
> -
> - if (n == 0) {
> - *seglen = 0;
> - return segs;
> - }
> - for (i = 0; i < n; i++) {
> - int log = fastlog2(sizes[i]);
> - if (cur.log != log && cur.bytes > 0) {
> - struct segment fresh = {
> - .start = i,
> - };
> -
> - segs[next++] = cur;
> - cur = fresh;
> - }
> -
> - cur.log = log;
> - cur.end = i + 1;
> - cur.bytes += sizes[i];
> - }
> - segs[next++] = cur;
> - *seglen = next;
> - return segs;
> -}
> -
> struct segment suggest_compaction_segment(uint64_t *sizes, size_t n)
> {
> - struct segment min_seg = {
> - .log = 64,
> - };
> - struct segment *segs;
> - size_t seglen = 0, i;
> -
> - segs = sizes_to_segments(&seglen, sizes, n);
> - for (i = 0; i < seglen; i++) {
> - if (segment_size(&segs[i]) == 1)
> - continue;
> + struct segment seg = { 0 };
> + uint64_t bytes;
> + size_t i;
>
> - if (segs[i].log < min_seg.log)
> - min_seg = segs[i];
> - }
> + /*
> + * If there are no tables or only a single one then we don't have to
> + * compact anything. The sequence is geometric by definition already.
> + */
> + if (n <= 1)
> + return seg;
>
> - while (min_seg.start > 0) {
> - size_t prev = min_seg.start - 1;
> - if (fastlog2(min_seg.bytes) < fastlog2(sizes[prev]))
> + /*
> + * Find the ending table of the compaction segment needed to restore the
> + * geometric sequence.
> + *
> + * To do so, we iterate backwards starting from the most recent table
> + * until a valid segment end is found. If the preceding table is smaller
> + * than the current table multiplied by the geometric factor (2), the
> + * current table is set as the compaction segment end.
> + */
> + for (i = n - 1; i > 0; i--) {
> + if (sizes[i - 1] < sizes[i] * 2) {
> + seg.end = i;
> + bytes = sizes[i];
> break;
> + }
> + }
I was briefly wondering whether we have to compare the _sum_ of all
preceding table sizes to the next size here. Otherwise it may happen
that compaction will lead to a new table that is immediately violating
the geometric sequence again.
But I think due to properties of the geometric sequence, the sum of all
entries preceding the current value cannot be greater than the value
itself. So this should be fine.
This might be worth a comment.
> +
> + /*
> + * Find the starting table of the compaction segment by iterating
> + * through the remaing tables and keeping track of the accumulated size
s/remaing/remaining/
> + * of all tables seen from the segment end table.
> + *
> + * Note that we keep iterating even after we have found the first
> + * first starting point. This is because there may be tables in the
Nit: s/first//, duplicate word.
> + * stack preceding that first starting point which violate the geometric
> + * sequence.
> + */
> + for (; i > 0; i--) {
> + uint64_t curr = bytes;
> + bytes += sizes[i - 1];
>
> - min_seg.start = prev;
> - min_seg.bytes += sizes[prev];
> + if (sizes[i - 1] < curr * 2) {
> + seg.start = i - 1;
> + seg.bytes = bytes;
> + }
> }
Overall I really like the rewritten algorithm, it's a ton easier to
understand compared to the preceding code.
One thing I'd suggest doing though is to provide a benchmark of how the
new compaction strategy compares to the old one. A comparatively easy
way to do this is to write N refs sequentially -- with a big enough N
(e.g. 1 million), compaction time will eventually become an important
factor. So something like the following (untested):
hyperfine \
--prepare "rm -rf repo && git init --ref-format=reftable repo && git -C repo commit --allow-empty --message msg" \
'for ((i = 0 ; i < 1000000; i++ )); do git -C repo update-ref refs/heads/branch-$i HEAD'
>
> - reftable_free(segs);
> - return min_seg;
> + return seg;
> }
>
> static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)
[snip]
> @@ -737,10 +737,10 @@ test_expect_success 'worktree: pack-refs in main repo packs main refs' '
> test_commit -C repo A &&
> git -C repo worktree add ../worktree &&
>
> - test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
> - test_line_count = 4 repo/.git/reftable/tables.list &&
> + test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
> + test_line_count = 1 repo/.git/reftable/tables.list &&
> git -C repo pack-refs &&
> - test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
> + test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
> test_line_count = 1 repo/.git/reftable/tables.list
> '
This test needs updating as git-pack-refs(1) has become a no-op here.
> @@ -750,11 +750,11 @@ test_expect_success 'worktree: pack-refs in worktree packs worktree refs' '
> test_commit -C repo A &&
> git -C repo worktree add ../worktree &&
>
> - test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
> - test_line_count = 4 repo/.git/reftable/tables.list &&
> + test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
> + test_line_count = 1 repo/.git/reftable/tables.list &&
> git -C worktree pack-refs &&
> test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
> - test_line_count = 4 repo/.git/reftable/tables.list
> + test_line_count = 1 repo/.git/reftable/tables.list
> '
Same.
> test_expect_success 'worktree: creating shared ref updates main stack' '
> @@ -770,7 +770,7 @@ test_expect_success 'worktree: creating shared ref updates main stack' '
>
> git -C worktree update-ref refs/heads/shared HEAD &&
> test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
> - test_line_count = 2 repo/.git/reftable/tables.list
> + test_line_count = 1 repo/.git/reftable/tables.list
> '
Same.
One thing missing is a test that demonstrates the previously-broken
behaviour.
Patrick
> test_expect_success 'worktree: creating per-worktree ref updates worktree stack' '
>
> base-commit: b387623c12f3f4a376e4d35a610fd3e55d7ea907
> --
> gitgitgadget
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* (No Subject)
From: Emilis Kiškis @ 2024-03-06 12:36 UTC (permalink / raw)
To: git@vger.kernel.org
What did you do before the bug happened? (Steps to reproduce your issue)
1. Create a new worktree with `git worktree add new-worktree`
2. Modify files in the new worktree
3. Move new worktree back to main working tree (i.e. master)
What did you expect to happen? (Expected behavior)
I expected uncommited changes from `new-worktree` to merge with changes from `master`
What happened instead? (Actual behavior)
The changes from `new-worktree` were lost
What's different between what you expected and what actually happened?
The uncommited changes were lost instead of preserved
Anything else you want to add:
I lost 4 hours of work :(((
Please review the rest of the bug report below.
You can delete any lines you don't wish to share.
[System Info]
git version:
git version 2.39.3 (Apple Git-145)
cpu: arm64
no commit associated with this build
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
feature: fsmonitor--daemon
uname: Darwin 23.3.0 Darwin Kernel Version 23.3.0: Wed Dec 20 21:30:44 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T6000 arm64
compiler info: clang: 15.0.0 (clang-1500.1.0.2.5)
libc info: no libc information available
$SHELL (typically, interactive shell): /bin/zsh
[Enabled Hooks]
pre-push
^ permalink raw reply
* Re: [PATCH] reftable/stack: use geometric table compaction
From: Patrick Steinhardt @ 2024-03-06 12:37 UTC (permalink / raw)
To: Justin Tobler via GitGitGadget; +Cc: git, Justin Tobler
In-Reply-To: <pull.1683.git.1709669025722.gitgitgadget@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 866 bytes --]
On Tue, Mar 05, 2024 at 08:03:45PM +0000, Justin Tobler via GitGitGadget wrote:
> From: Justin Tobler <jltobler@gmail.com>
> @@ -1305,7 +1287,7 @@ int reftable_stack_auto_compact(struct reftable_stack *st)
> suggest_compaction_segment(sizes, st->merged->stack_len);
> reftable_free(sizes);
> if (segment_size(&seg) > 0)
> - return stack_compact_range_stats(st, seg.start, seg.end - 1,
> + return stack_compact_range_stats(st, seg.start, seg.end,
> NULL);
>
> return 0;
One more thing: I think it would make sense to move the refactoring
where you change whether the end segment index is inclusive or exclusive
into a separate patch so that it's easier to reason about. Also, the
fact that no tests would require changes would further stress the point
that this is a mere refactoring without unintended side effects.
Patrick
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH v3 00/14] FSMonitor edge cases on case-insensitive file systems
From: Patrick Steinhardt @ 2024-03-06 12:58 UTC (permalink / raw)
To: Jeff Hostetler via GitGitGadget
Cc: git, Jeff Hostetler, Torsten Bögershausen, Jeff Hostetler
In-Reply-To: <pull.1662.v3.git.1708983565.gitgitgadget@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 822 bytes --]
On Mon, Feb 26, 2024 at 09:39:11PM +0000, Jeff Hostetler via GitGitGadget wrote:
> Here is version 3. I think I have addressed the remaining comments.
>
> I cleaned up the test code to use the test_expect_failure at the beginning
> and squashed in the test_expect_success version of tests into the final
> commit in the series.
>
> I moved the invalidate_ce_fsm() commit earlier in the series, so that the
> final commit actually uses all of the up-to-this-point changes to fix the
> problem.
>
> I converted a few "should not happens" to BUG()s.
>
> Thanks to everyone for their time and attention reviewing this. Jeff
I gave this whole patch series a read and didn't much to complain about.
There are a couple of nits, but none of them really require a reroll in
my opinion.
Thanks!
Patrick
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH v3 11/14] fsmonitor: return invalided cache-entry count on non-directory event
From: Patrick Steinhardt @ 2024-03-06 12:58 UTC (permalink / raw)
To: Jeff Hostetler via GitGitGadget
Cc: git, Jeff Hostetler, Torsten Bögershausen, Jeff Hostetler
In-Reply-To: <53f73c1515d0f64bf13f0b30e9039dedbf441f2c.1708983566.git.gitgitgadget@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2126 bytes --]
Nit, not worth a reroll: the subject says "invalided" instead of
"invalidated".
Patrick
On Mon, Feb 26, 2024 at 09:39:22PM +0000, Jeff Hostetler via GitGitGadget wrote:
> From: Jeff Hostetler <jeffhostetler@github.com>
>
> Teach the refresh callback helper function for unqualified FSEvents
> (pathnames without a trailing slash) to return the number of
> cache-entries that were invalided in response to the event.
>
> This will be used in a later commit to help determine if the observed
> pathname was (possibly) case-incorrect when (on a case-insensitive
> file system).
>
> Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
> ---
> fsmonitor.c | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/fsmonitor.c b/fsmonitor.c
> index a51c17cda70..c16ed5d8758 100644
> --- a/fsmonitor.c
> +++ b/fsmonitor.c
> @@ -196,8 +196,10 @@ static size_t handle_path_with_trailing_slash(
> * do not know it is case-correct or -incorrect.
> *
> * Assume it is case-correct and try an exact match.
> + *
> + * Return the number of cache-entries that we invalidated.
> */
> -static void handle_path_without_trailing_slash(
> +static size_t handle_path_without_trailing_slash(
> struct index_state *istate, const char *name, int pos)
> {
> /*
> @@ -218,7 +220,9 @@ static void handle_path_without_trailing_slash(
> * at that directory. (That is, assume no D/F conflicts.)
> */
> istate->cache[pos]->ce_flags &= ~CE_FSMONITOR_VALID;
> + return 1;
> } else {
> + size_t nr_in_cone;
> struct strbuf work_path = STRBUF_INIT;
>
> /*
> @@ -232,8 +236,10 @@ static void handle_path_without_trailing_slash(
> strbuf_add(&work_path, name, strlen(name));
> strbuf_addch(&work_path, '/');
> pos = index_name_pos(istate, work_path.buf, work_path.len);
> - handle_path_with_trailing_slash(istate, work_path.buf, pos);
> + nr_in_cone = handle_path_with_trailing_slash(
> + istate, work_path.buf, pos);
> strbuf_release(&work_path);
> + return nr_in_cone;
> }
> }
>
> --
> gitgitgadget
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH v3 14/14] fsmonitor: support case-insensitive events
From: Patrick Steinhardt @ 2024-03-06 12:58 UTC (permalink / raw)
To: Jeff Hostetler via GitGitGadget
Cc: git, Jeff Hostetler, Torsten Bögershausen, Jeff Hostetler
In-Reply-To: <ec036c04d1b003385683f0ed1dec80cc3bbfb813.1708983566.git.gitgitgadget@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 10382 bytes --]
On Mon, Feb 26, 2024 at 09:39:25PM +0000, Jeff Hostetler via GitGitGadget wrote:
> From: Jeff Hostetler <jeffhostetler@github.com>
>
> Teach fsmonitor_refresh_callback() to handle case-insensitive
> lookups if case-sensitive lookups fail on case-insensitive systems.
> This can cause 'git status' to report stale status for files if there
> are case issues/errors in the worktree.
>
> The FSMonitor daemon sends FSEvents using the observed spelling
> of each pathname. On case-insensitive file systems this may be
> different than the expected case spelling.
>
> The existing code uses index_name_pos() to find the cache-entry for
> the pathname in the FSEvent and clear the CE_FSMONITOR_VALID bit so
> that the worktree scan/index refresh will revisit and revalidate the
> path.
>
> On a case-insensitive file system, the exact match lookup may fail
> to find the associated cache-entry. This causes status to think that
> the cached CE flags are correct and skip over the file.
>
> Update event handling to optionally use the name-hash and dir-name-hash
> if necessary.
>
> Also update t7527 to convert the "test_expect_failure" to "_success"
> now that we have fixed the bug.
>
> Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
> ---
> fsmonitor.c | 121 +++++++++++++++++++++++++++++++++++
> t/t7527-builtin-fsmonitor.sh | 26 +++++---
> 2 files changed, 137 insertions(+), 10 deletions(-)
>
> diff --git a/fsmonitor.c b/fsmonitor.c
> index 3c87449be87..2b17d60bbbe 100644
> --- a/fsmonitor.c
> +++ b/fsmonitor.c
> @@ -5,6 +5,7 @@
> #include "ewah/ewok.h"
> #include "fsmonitor.h"
> #include "fsmonitor-ipc.h"
> +#include "name-hash.h"
> #include "run-command.h"
> #include "strbuf.h"
> #include "trace2.h"
> @@ -202,6 +203,113 @@ static void invalidate_ce_fsm(struct cache_entry *ce)
> static size_t handle_path_with_trailing_slash(
> struct index_state *istate, const char *name, int pos);
>
> +/*
> + * Use the name-hash to do a case-insensitive cache-entry lookup with
> + * the pathname and invalidate the cache-entry.
> + *
> + * Returns the number of cache-entries that we invalidated.
> + */
> +static size_t handle_using_name_hash_icase(
> + struct index_state *istate, const char *name)
> +{
> + struct cache_entry *ce = NULL;
> +
> + ce = index_file_exists(istate, name, strlen(name), 1);
> + if (!ce)
> + return 0;
> +
> + /*
> + * A case-insensitive search in the name-hash using the
> + * observed pathname found a cache-entry, so the observed path
> + * is case-incorrect. Invalidate the cache-entry and use the
> + * correct spelling from the cache-entry to invalidate the
> + * untracked-cache. Since we now have sparse-directories in
> + * the index, the observed pathname may represent a regular
> + * file or a sparse-index directory.
> + *
> + * Note that we should not have seen FSEvents for a
> + * sparse-index directory, but we handle it just in case.
> + *
> + * Either way, we know that there are not any cache-entries for
> + * children inside the cone of the directory, so we don't need to
> + * do the usual scan.
> + */
> + trace_printf_key(&trace_fsmonitor,
> + "fsmonitor_refresh_callback MAP: '%s' '%s'",
> + name, ce->name);
> +
> + /*
> + * NEEDSWORK: We used the name-hash to find the correct
> + * case-spelling of the pathname in the cache-entry[], so
> + * technically this is a tracked file or a sparse-directory.
> + * It should not have any entries in the untracked-cache, so
> + * we should not need to use the case-corrected spelling to
> + * invalidate the the untracked-cache. So we may not need to
> + * do this. For now, I'm going to be conservative and always
> + * do it; we can revisit this later.
> + */
> + untracked_cache_invalidate_trimmed_path(istate, ce->name, 0);
> +
> + invalidate_ce_fsm(ce);
> + return 1;
> +}
> +
> +/*
> + * Use the dir-name-hash to find the correct-case spelling of the
> + * directory. Use the canonical spelling to invalidate all of the
> + * cache-entries within the matching cone.
> + *
> + * Returns the number of cache-entries that we invalidated.
> + */
> +static size_t handle_using_dir_name_hash_icase(
> + struct index_state *istate, const char *name)
> +{
> + struct strbuf canonical_path = STRBUF_INIT;
> + int pos;
> + size_t len = strlen(name);
> + size_t nr_in_cone;
> +
> + if (name[len - 1] == '/')
> + len--;
Nit: this could use `strip_suffix()`.
> +
> + if (!index_dir_find(istate, name, len, &canonical_path))
> + return 0; /* name is untracked */
> +
> + if (!memcmp(name, canonical_path.buf, canonical_path.len)) {
> + strbuf_release(&canonical_path);
> + /*
> + * NEEDSWORK: Our caller already tried an exact match
> + * and failed to find one. They called us to do an
> + * ICASE match, so we should never get an exact match,
> + * so we could promote this to a BUG() here if we
> + * wanted to. It doesn't hurt anything to just return
Nit: this comment is stale as this has been promoted to a BUG already.
Patrick
> + * 0 and go on because we should never get here. Or we
> + * could just get rid of the memcmp() and this "if"
> + * clause completely.
> + */
> + BUG("handle_using_dir_name_hash_icase(%s) did not exact match",
> + name);
> + }
> +
> + trace_printf_key(&trace_fsmonitor,
> + "fsmonitor_refresh_callback MAP: '%s' '%s'",
> + name, canonical_path.buf);
> +
> + /*
> + * The dir-name-hash only tells us the corrected spelling of
> + * the prefix. We have to use this canonical path to do a
> + * lookup in the cache-entry array so that we repeat the
> + * original search using the case-corrected spelling.
> + */
> + strbuf_addch(&canonical_path, '/');
> + pos = index_name_pos(istate, canonical_path.buf,
> + canonical_path.len);
> + nr_in_cone = handle_path_with_trailing_slash(
> + istate, canonical_path.buf, pos);
> + strbuf_release(&canonical_path);
> + return nr_in_cone;
> +}
> +
> /*
> * The daemon sent an observed pathname without a trailing slash.
> * (This is the normal case.) We do not know if it is a tracked or
> @@ -335,6 +443,19 @@ static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
> else
> nr_in_cone = handle_path_without_trailing_slash(istate, name, pos);
>
> + /*
> + * If we did not find an exact match for this pathname or any
> + * cache-entries with this directory prefix and we're on a
> + * case-insensitive file system, try again using the name-hash
> + * and dir-name-hash.
> + */
> + if (!nr_in_cone && ignore_case) {
> + nr_in_cone = handle_using_name_hash_icase(istate, name);
> + if (!nr_in_cone)
> + nr_in_cone = handle_using_dir_name_hash_icase(
> + istate, name);
> + }
> +
> if (nr_in_cone)
> trace_printf_key(&trace_fsmonitor,
> "fsmonitor_refresh_callback CNT: %d",
> diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
> index 830f2d9de33..730f3c7f810 100755
> --- a/t/t7527-builtin-fsmonitor.sh
> +++ b/t/t7527-builtin-fsmonitor.sh
> @@ -1051,7 +1051,7 @@ test_expect_success 'split-index and FSMonitor work well together' '
> #
> # The setup is a little contrived.
> #
> -test_expect_failure CASE_INSENSITIVE_FS 'fsmonitor subdir case wrong on disk' '
> +test_expect_success CASE_INSENSITIVE_FS 'fsmonitor subdir case wrong on disk' '
> test_when_finished "stop_daemon_delete_repo subdir_case_wrong" &&
>
> git init subdir_case_wrong &&
> @@ -1116,19 +1116,19 @@ test_expect_failure CASE_INSENSITIVE_FS 'fsmonitor subdir case wrong on disk' '
>
> grep -q "dir1/DIR2/dir3/file3.*pos -3" "$PWD/subdir_case_wrong.log1" &&
>
> + # Verify that we get a mapping event to correct the case.
> + grep -q "MAP:.*dir1/DIR2/dir3/file3.*dir1/dir2/dir3/file3" \
> + "$PWD/subdir_case_wrong.log1" &&
> +
> # The refresh-callbacks should have caused "git status" to clear
> # the CE_FSMONITOR_VALID bit on each of those files and caused
> # the worktree scan to visit them and mark them as modified.
> grep -q " M AAA" "$PWD/subdir_case_wrong.out" &&
> grep -q " M zzz" "$PWD/subdir_case_wrong.out" &&
> -
> - # Expect Breakage: with the case confusion, the "(pos -3)" causes
> - # the client to not clear the CE_FSMONITOR_VALID bit and therefore
> - # status will not rescan the file and therefore not report it as dirty.
> grep -q " M dir1/dir2/dir3/file3" "$PWD/subdir_case_wrong.out"
> '
>
> -test_expect_failure CASE_INSENSITIVE_FS 'fsmonitor file case wrong on disk' '
> +test_expect_success CASE_INSENSITIVE_FS 'fsmonitor file case wrong on disk' '
> test_when_finished "stop_daemon_delete_repo file_case_wrong" &&
>
> git init file_case_wrong &&
> @@ -1242,14 +1242,20 @@ test_expect_failure CASE_INSENSITIVE_FS 'fsmonitor file case wrong on disk' '
> GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try3.log" \
> git -C file_case_wrong --no-optional-locks status --short \
> >"$PWD/file_case_wrong-try3.out" &&
> +
> + # Verify that we get a mapping event to correct the case.
> + grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir3/FILE-3-A.*dir1/dir2/dir3/file-3-a" \
> + "$PWD/file_case_wrong-try3.log" &&
> + grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir4/file-4-a.*dir1/dir2/dir4/FILE-4-A" \
> + "$PWD/file_case_wrong-try3.log" &&
> +
> # FSEvents are in observed case.
> grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try3.log" &&
> grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try3.log" &&
>
> - # Expect Breakage: with the case confusion, the "(pos-3)" and
> - # "(pos -9)" causes the client to not clear the CE_FSMONITOR_VALID
> - # bit and therefore status will not rescan the files and therefore
> - # not report them as dirty.
> + # The refresh-callbacks should have caused "git status" to clear
> + # the CE_FSMONITOR_VALID bit on each of those files and caused
> + # the worktree scan to visit them and mark them as modified.
> grep -q " M dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-try3.out" &&
> grep -q " M dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-try3.out"
> '
> --
> gitgitgadget
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Discuss GSoC: Implement consistency checks for refs
From: shejialuo @ 2024-03-06 13:20 UTC (permalink / raw)
To: git; +Cc: Patrick Steinhardt
Hi All,
I am interested in "Implement consistency checks for refs" GSoC idea.
However, implementing a feautre is much harder. So I wanna ask you some
questions to better work on.
As [1] shows, I think the idea is easy to understand. We need to ensure
the consistency of the refs. The current `git-fsck` only checks the
connectivity from ref to the object file. There is a possiblity that ref
itself could be corrupted. And we should avoid it through this project.
I have read some source codes. Based on what I have learned, I know
there are two backends. One is file and another is reftable. I have
no idea about the reftable currently. So at now, I will focus on the
file backend.
I think the principle behind the `git-fsck` is that it will traverse
every object file, read its content and use SHA-1 to hash the content
and compare the value with the stored ref value. So if we want to add
consistency checks for refs. We may need to add a new file to store the
last commit state (not only last commit state, do we need to consider
the stash state). However, from my perspective, it's a bad idea to use a
file to store the refs' states and we cannot use object file to check
whether the ref is corrupted.
So this is my first question, what mechanism should we use to provide
consistency? And to what extend for the consistency. And I think this
mechanism should be general for both text-based and binary-based refs.
And I have a more general qeustion, I think I need understand `fsck.c`
and of couse the reftable format. However, I am confused whether I need
to understand the ref internal. And could you please provide me more
infomration to make this idea more clear.
Thanks,
Jialuo
[1] https://lore.kernel.org/git/ZakIPEytlxHGCB9Y@tanuki/
^ permalink raw reply
* Re: [Outreachy][PATCH v2 2/2] Port helper/test-sha256.c and helper/test-sha1.c to unit-tests/t-hash.c
From: Christian Couder @ 2024-03-06 14:25 UTC (permalink / raw)
To: Achu Luma; +Cc: git, chriscool, gitster
In-Reply-To: <20240229054004.3807-2-ach.lumap@gmail.com>
On Thu, Feb 29, 2024 at 6:41 AM Achu Luma <ach.lumap@gmail.com> wrote:
>
> In the recent codebase update (8bf6fbd (Merge branch
> 'js/doc-unit-tests', 2023-12-09)), a new unit testing framework was
> merged, providing a standardized approach for testing C code. Prior to
> this update, some unit tests relied on the test helper mechanism,
> lacking a dedicated unit testing framework. It's more natural to perform
> these unit tests using the new unit test framework.
>
> This commit migrates the unit tests for hash functionality from the
> legacy approach using the test-tool command `test-tool sha1`and
> `test-tool sha256` in helper/test-sha256.c and helper/test-sha1.c to the
> new unit testing framework (t/unit-tests/test-lib.h). Porting
> t0013-sha1dc.sh is left for later.
>
> The migration involves refactoring the tests to utilize the testing
> macros provided by the framework (TEST() and check_*()).
>
> Mentored-by: Christian Couder <chriscool@tuxfamily.org>
> Signed-off-by: Achu Luma <ach.lumap@gmail.com>
> ---
> The change between version 1 and version 2 is:
> - Deleted t/helper/test-sha256.c
>
> Here is a diff between v1 and v2:
When sending a patch series, even a small one, I think it's better to
have a cover letter (using for example `git format-patch
--cover-letter ...`) and list all the changes from the previous
version in the cover letter, rather than listing some changes in each
patch. Also, instead of `git diff`, `git range-diff` can be used and
its output can be put into the cover letter too.
I don't think it's worth resending the series just for this though.
Maybe I missed it but it seems that this patch and patch 1/2
(https://lore.kernel.org/git/20240229054004.3807-1-ach.lumap@gmail.com/)
in the series fell into the cracks.
^ permalink raw reply
* Re: [PATCH v6 0/9] Enrich Trailer API
From: Christian Couder @ 2024-03-06 14:41 UTC (permalink / raw)
To: Josh Steadmon, Junio C Hamano, Linus Arver via GitGitGadget, git,
Christian Couder, Emily Shaffer, Randall S. Becker,
Christian Couder, Kristoffer Haugsbakk, Linus Arver
In-Reply-To: <Zedtd6esmIgayeoU@google.com>
On Tue, Mar 5, 2024 at 8:07 PM Josh Steadmon <steadmon@google.com> wrote:
>
> On 2024.03.05 10:03, Junio C Hamano wrote:
> >
> > It's been nearly a week since this was posted. Any more comments,
> > or is everybody happy with this iteration? Otherwise I am tempted
> > to mark the topic for 'next' soon.
> >
> > Thanks.
>
> I scanned through v6 yesterday and have nothing new to add. LGTM.
I took another look at it, and I am fine with it now too. Acked.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox