config-batch: a new builtin for tools querying config
Subsystems (3)
cover config-batch: a new builtin for tools querying config
This RFC explores a new git config-batch builtin that allows tools to interact with Git's config data with multiple queries using a single process. This is an orthogonal alternative to the effort to create a stable, linkable config API. Both approaches have different strengths.
My main motivation is the performance of git-credential-manager on Windows platforms as it can call git config get dozens of times. At 150-200ms per execution, that adds up significantly, leading to multiple seconds just to load a credential that already exists. I believe that there are other benefits to having this interface available, but I can't recall any specifics at the moment.
This RFC adds git config-batch with a protocol over stdin/stdout for executing multiple config queries. The implementation has a limited set of potential queries, but also creates a model for compatibility for tools to automatically adapt to different Git versions.
I'm submitting this as an RFC before I've polished all of the details because I want to make sure I'm going down a good direction. Please focus feedback in these questions:
* Is this a worthwhile feature to add to Git?
* Is this a reasonable protocol for stdin/stdout?
* How can we structure the code to make it easier to contribute new commands in the future?
* This seems like a place where parallel contributions can be made once the baseline is implemented. Is there interest in further contributions to expand the commands?
This RFC adds the following commands over stdin:
1. help lists the available commands, giving the caller an understanding of what is available in this Git version.
2. get loads a value for a given key within a certain scope, with optional value patterns.
3. set assigns a key-value pair in a given scope.
4. unset removes a key-value pair in a given scope with optional value patterns.
Each command has an associated version, in case we need to expand or alter the functionality in the future. This includes the potential to deprecate and remove certain versions that we no longer want to support, such as replacing set version 1 with a version 2 and making version 1 no longer available. I do hope that we will mostly be able to move with new command names, such as a set-all command including the options for git config set --all ... instead of increasing the version of the set command.
There is a -z option that changes the command interface to use NUL-terminated strings. Two NULs specify a command boundary, which promotes compatibility with a caller that sends an unknown command. However, this means that we cannot specify an empty string as a token within a command unless we add more data. This format uses <N>:<string> to provide the integer <N> which specifies the length of <string>. This is a little cumbersome, but the format is intended for tools, not humans.
I have a test integration with git-credential-manager available [1] for testing. This includes a model for interacting with git config-batch in a compatible way that will respond to certain features not being available:
1. If git config-batch fails immediately, then all queries are handled by git config.
2. If git config-batch starts without failure, then the first query is for the help command.
3. As queries come to the config system, the query is checked against the available commands advertised by git config-batch. If the appropriate command is available, then the query is made in that process. If not, then the query uses the existing git config command.
One thing that I think would be valuable to include is a reload command that signals that the git config-batch process should reload the configset into memory due to config manipulations in other processes, especially while git config-batch doesn't have all capabilities from git config. I'll include that in the first version for review, if this RFC leads to positive support.
[1] https://github.com/git-ecosystem/git-credential-manager/pull/2245
I have a few concerns with this implementation that I'd like to improve before submitting a version for full review. I list them here so you can see the flaws that I already see, but also so you can add to this list:
* We need a reload command (as mentioned above).
* The tests need to include a submodule and submodule-level config.
* When specifying the local scope to the get command, the matched value does not include worktree or submodule config in the same way that git config get --local <key> would.
* The token-parsing API in this helper is still too complicated to use. I should create parsing tooling similar to the parse-opts API so each command could specify its use of positional values and optional arguments.
* The use of arg:<arg> to specify an optional argument creates the inability to submit a value that starts with arg:. Consider alternative ways to specify arguments or to specify that the remaining data in the command (including spaces) is a final positional argument.
* In general, I found myself implementing behavior based on the deprecated forms of git config that use the --get or --unset style arguments instead of git config (set|unset|get) subcommands. It's worth making sure that any references to equivalent git config commands use the new modes.
* I need to add an --[no-]includes option as a command-line argument that signals whether include sections should be followed. I don't believe this should be specified on a per-command basis, but I'm open to suggestions.
* I have an early draft of a technical document detailing the plan for this builtin. It has some lists of intended future commands that have not been implemented. This would also be a good place to document any parsing APIs built to help contributors adding to this builtin.
Thanks, -Stolee
Derrick Stolee (11):
config-batch: basic boilerplate of new builtin
config-batch: create parse loop and unknown command
config-batch: implement get v1
config-batch: create 'help' command
config-batch: add NUL-terminated I/O format
docs: add design doc for config-batch
config: extract location structs from builtin
config-batch: pass prefix through commands
config-batch: add 'set' v1 command
t1312: create read/write test
config-batch: add unset v1 command
.gitignore | 1 + Documentation/git-config-batch.adoc | 214 ++++++ Documentation/meson.build | 1 + Documentation/technical/config-batch.adoc | 70 ++ Makefile | 1 + builtin.h | 7 + builtin/config-batch.c | 772 ++++++++++++++++++++++ builtin/config.c | 117 +--- command-list.txt | 1 + config.c | 116 ++++ config.h | 26 + git.c | 1 + meson.build | 1 + t/meson.build | 1 + t/t1312-config-batch.sh | 372 +++++++++++ 15 files changed, 1592 insertions(+), 109 deletions(-) create mode 100644 Documentation/git-config-batch.adoc create mode 100644 Documentation/technical/config-batch.adoc create mode 100644 builtin/config-batch.c create mode 100755 t/t1312-config-batch.sh
base-commit: 83a69f19359e6d9bc980563caca38b2b5729808c
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2033%2Fderrickstolee%2Fbatched-config-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2033/derrickstolee/batched-config-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2033
Signature · 1 line
gitgitgadget
PATCH 1/11 config-batch: basic boilerplate of new builtin
From: Derrick Stolee <[email protected]>
Later changes will document, implement, and test this new builtin. For now, this serves as the latest example of the minimum boilerplate to introduce a new builtin.
Recently, we updated the comment in builtin.h about how to create a new builtin, but failed to mention the required change to meson.build files for some CI builds to pass. Fix that oversight.
--- .gitignore | 1 + Documentation/git-config-batch.adoc | 24 +++++++++++++++++++++++ Documentation/meson.build | 1 + Makefile | 1 + builtin.h | 7 +++++++ builtin/config-batch.c | 30 +++++++++++++++++++++++++++++ command-list.txt | 1 + git.c | 1 + meson.build | 1 + t/meson.build | 1 + t/t1312-config-batch.sh | 12 ++++++++++++ 11 files changed, 80 insertions(+) create mode 100644 Documentation/git-config-batch.adoc create mode 100644 builtin/config-batch.c create mode 100755 t/t1312-config-batch.sh
.gitignore +1 -0
@@ -44,6 +44,7 @@ /git-commit-graph /git-commit-tree /git-config +/git-config-batch /git-count-objects /git-credential /git-credential-cache
Documentation/git-config-batch.adoc new file +24 -0
@@ -0,0 +1,24 @@ +git-config-batch(1) +=================== + +NAME +---- +git-config-batch - Get and set options using machine-parseable interface + + +SYNOPSIS +-------- +[verse] +'git config-batch' <options> + +DESCRIPTION +----------- +TODO + +SEE ALSO +-------- +linkgit:git-config[1] + +GIT +--- +Part of the linkgit:git[1] suite
Documentation/meson.build +1 -0
@@ -29,6 +29,7 @@ manpages = { 'git-commit-tree.adoc' : 1, 'git-commit.adoc' : 1, 'git-config.adoc' : 1, + 'git-config-batch.adoc' : 1, 'git-count-objects.adoc' : 1, 'git-credential-cache--daemon.adoc' : 1, 'git-credential-cache.adoc' : 1,
Makefile +1 -0
@@ -1390,6 +1390,7 @@ BUILTIN_OBJS += builtin/commit-graph.o BUILTIN_OBJS += builtin/commit-tree.o BUILTIN_OBJS += builtin/commit.o BUILTIN_OBJS += builtin/config.o +BUILTIN_OBJS += builtin/config-batch.o BUILTIN_OBJS += builtin/count-objects.o BUILTIN_OBJS += builtin/credential-cache--daemon.o BUILTIN_OBJS += builtin/credential-cache.o
builtin.h +7 -0
@@ -68,12 +68,18 @@* * . Add `builtin/foo.o` to `BUILTIN_OBJS` in `Makefile`. * +* . Add 'builtin/foo.c' to the 'builtin_sources' array in 'meson.build'. +* * Additionally, if `foo` is a new command, there are 4 more things to do: * * . Add tests to `t/` directory. * +* . Add the test script to 'integration_tests' in 't/meson.build'. +* * . Write documentation in `Documentation/git-foo.adoc`. * +* . Add 'git-foo.adoc' to the manpages list in 'Documentation/meson.build'. +* * . Add an entry for `git-foo` to `command-list.txt`. * * . Add an entry for `/git-foo` to `.gitignore`.
@@ -167,6 +173,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix, struct repositorint cmd_commit_graph (int argc,const char **argv,const char *prefix,struct repository *repo);int cmd_commit_tree (int argc,const char **argv,const char *prefix,struct repository *repo);int cmd_config (int argc,const char **argv,const char *prefix,struct repository *repo); +int cmd_config_batch (int argc,const char **argv,const char *prefix,struct repository *repo);int cmd_count_objects (int argc,const char **argv,const char *prefix,struct repository *repo);int cmd_credential (int argc,const char **argv,const char *prefix,struct repository *repo);int cmd_credential_cache (int argc,const char **argv,const char *prefix,struct repository *repo);
builtin/config-batch.c new file +30 -0
@@ -0,0 +1,30 @@ +#define USE_THE_REPOSITORY_VARIABLE +#include "builtin.h" +#include "config.h" +#include "environment.h" +#include "parse-options.h" + +static const char *const builtin_config_batch_usage[] = { +N_ ("git config-batch <options>" ), +NULL +}; + +int cmd_config_batch (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; + +show_usage_with_options_if_asked (argc, argv, + builtin_config_batch_usage, options); + + argc =parse_options (argc, argv, prefix, options, builtin_config_batch_usage, +0 ); + +repo_config (repo, git_default_config,NULL ); + +return 0 ; +}
command-list.txt +1 -0
@@ -83,6 +83,7 @@ git-commit mainporcelain history git-commit-graph plumbingmanipulators git-commit-tree plumbingmanipulators git-config ancillarymanipulators complete +git-config-batch plumbinginterrogators git-count-objects ancillaryinterrogators git-credential purehelpers git-credential-cache purehelpers
git.c +1 -0
@@ -557,6 +557,7 @@ static struct cmd_struct commands[] = { { "commit-graph", cmd_commit_graph, RUN_SETUP }, { "commit-tree", cmd_commit_tree, RUN_SETUP }, { "config", cmd_config, RUN_SETUP_GENTLY | DELAY_PAGER_CONFIG }, + { "config-batch", cmd_config_batch, RUN_SETUP_GENTLY }, { "count-objects", cmd_count_objects, RUN_SETUP }, { "credential", cmd_credential, RUN_SETUP_GENTLY | NO_PARSEOPT }, { "credential-cache", cmd_credential_cache },
meson.build +1 -0
@@ -582,6 +582,7 @@ builtin_sources = [ 'builtin/commit-tree.c', 'builtin/commit.c', 'builtin/config.c', + 'builtin/config-batch.c', 'builtin/count-objects.c', 'builtin/credential-cache--daemon.c', 'builtin/credential-cache.c',
t/meson.build +1 -0
@@ -186,6 +186,7 @@ integration_tests = [ 't1309-early-config.sh', 't1310-config-default.sh', 't1311-config-optional.sh', + 't1312-config-batch.sh', 't1350-config-hooks-path.sh', 't1400-update-ref.sh', 't1401-symbolic-ref.sh',
t/t1312-config-batch.sh new file +12 -0
@@ -0,0 +1,12 @@ +#!/bin/sh + +test_description='Test git config-batch' + +. ./test-lib.sh + +test_expect_success 'help text' ' + test_must_fail git config-batch -h >out && + grep usage out +' + +test_done
Signature · 1 line
gitgitgadget
PATCH 2/11 config-batch: create parse loop and unknown command
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
As we build new features in the config-batch command, we define the plaintext protocol with line-by-line output and responses. To think to the future, we make sure that the protocol has a clear way to respond to an unknown command or an unknown version of that command.
As some commands will allow the final argument to contain spaces or even be able to parse "\ " as a non-split token, we only provide the remaining line as data.
--- Documentation/git-config-batch.adoc | 23 ++++- builtin/config-batch.c | 133 +++++++++++++++++++++++++++- t/t1312-config-batch.sh | 19 +++- 3 files changed, 170 insertions(+), 5 deletions(-)
Documentation/git-config-batch.adoc +22 -1
@@ -13,7 +13,28 @@ SYNOPSIS DESCRIPTION ----------- -TODO +Tools frequently need to change their behavior based on values stored in +Git's configuration files. These files may have complicated conditions +for including extra files, so it is difficult to produce an independent +parser. To avoid executing multiple processes to discover or modify +multiple configuration values, the `git config-batch` command allows a +single process to handle multiple requests using a machine-parseable +interface across `stdin` and `stdout`. + +PROTOCOL +-------- +By default, the protocol uses line feeds (`LF`) to signal the end of a +command over `stdin` or a response over `stdout`. + +The protocol will be extended in the future, and consumers should be +resilient to older Git versions not understanding the latest command +set. Thus, if the Git version includes the `git config-batch` builtin +but doesn't understand an input command, it will return a single line +response: + +``` +unknown_command LF +``` SEE ALSO --------
builtin/config-batch.c +132 -1
154 lines not on this page · raw message
t/t1312-config-batch.sh +16 -3
@@ -4,9 +4,22 @@ test_description='Test git config-batch' . ./test-lib.sh -test_expect_success 'help text' ' - test_must_fail git config-batch -h >out && - grep usage out +test_expect_success 'no commands' ' + echo | git config-batch >out && + test_must_be_empty out +' + +test_expect_success 'unknown_command' ' + echo unknown_command >expect && + echo "bogus 1 line of tokens" >in && + git config-batch >out <in && + test_cmp expect out +' + +test_expect_success 'failed to parse version' ' + echo "bogus BAD_VERSION line of tokens" >in && + test_must_fail git config-batch 2>err <in && + test_grep BAD_VERSION err ' test_done
Signature · 1 line
gitgitgadget
PATCH 3/11 config-batch: implement get v1
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The 'get' command for the 'git config-batch' builtin is the first command and is currently at version 1. It returns at most one value, the same as 'git config --get <key>' with optional value-based filtering.
The documentation and tests detail the specifics of how to format requests of this format and how to parse the results.
Future versions could consider multi-valued responses or regex-based key matching.
For the sake of incremental exploration of the potential in the 'git config-batch' command, this is the only implementation being presented in the first patch series.
Future extensions could include a '-z' parameter that uses NUL bytes in the command and output format to allow for spaces or newlines in the input or newlines in the output.
--- Documentation/git-config-batch.adoc | 53 +++++- builtin/config-batch.c | 251 +++++++++++++++++++++++++++- config.h | 3 + t/t1312-config-batch.sh | 101 +++++++++++ 4 files changed, 405 insertions(+), 3 deletions(-)
Documentation/git-config-batch.adoc +51 -2
@@ -32,9 +32,58 @@ set. Thus, if the Git version includes the `git config-batch` builtin but doesn't understand an input command, it will return a single line response: -``` +------------ unknown_command LF -``` +------------ + +These are the commands that are currently understood: + +`get` version 1:: + The `get` command searches the config key-value pairs within a + given `<scope>` for values that match the fixed `<key>` and + filters the resulting value based on an optional `<value-filter>`. + This can either be a regex or a fixed value. The command format + is one of the following formats: ++ +------------ +get 1 <scope> <key> +get 1 <scope> <key> arg:regex <value-pattern> +get 1 <scope> <key> arg:fixed-value <value> +------------ ++ +The `<scope>` value can be one of `inherited`, `system`, `global`, +`local`, `worktree`, `submodule`, or `command`. If `inherited`, then all +config key-value pairs will be considered regardless of scope. Otherwise, +only the given scope will be considered. ++ +If no optional arguments are given, then the value will not be filtered +by any pattern matching. If `arg:regex` is specified, then the rest of +the line is considered a single string, `<value-pattern>`, and is +interpreted as a regular expression for matching against stored values, +similar to specifying a value to `get config --get <key> "<value-pattern>"`. +If `arg:fixed-value` is specified, then the rest of the line is +considered a single string, `<value>`, and is checked for an exact +match against the key-value pairs, simmilar to `git config --get <key> +--fixed-value "<value>"`. ++ +At mmost one key-value pair is returned, that being the last key-value +pair in the standard config order by scope and sequence within each scope. ++ +If a key-value pair is found, then the following output is given: ++ +------------ +get 1 found <key> <scope> <value> +------------ ++ +If no matching key-value pair is found, then the following output is +given: ++ +------------ +get 1 missing <key> [<value-pattern>|<value>] +------------ ++ +where `<value-pattern>` or `<value>` is only supplied if provided in +the command. SEE ALSO --------
builtin/config-batch.c +250 -1
279 lines not on this page · raw message
config.h +3 -0
@@ -44,6 +44,9 @@ enum config_scope {CONFIG_SCOPE_WORKTREE ,CONFIG_SCOPE_COMMAND ,CONFIG_SCOPE_SUBMODULE , + +/* Must be last */ + CONFIG_SCOPE__NR };const char *config_scope_name (enum config_scope scope);
t/t1312-config-batch.sh +101 -0
111 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH 4/11 config-batch: create 'help' command
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
Tools that use the 'git config-batch' tool will want to know which commands are available in the current Git version. Having a 'help' command assists greatly to give a clear set of available commands and their versions.
--- Documentation/git-config-batch.adoc | 17 +++++++++++++++ builtin/config-batch.c | 32 +++++++++++++++++++++++++++++ t/t1312-config-batch.sh | 13 ++++++++++++ 3 files changed, 62 insertions(+)
Documentation/git-config-batch.adoc +17 -0
@@ -38,6 +38,23 @@ unknown_command LF These are the commands that are currently understood: +`help` version 1:: + The `help` command lists the currently-available commands in + this version of Git. The output is multi-line, but the first + line provides the count of possible commands via `help count <N>`. + The next `<N>` lines are of the form `help <command> <version>` + to state that this Git version supports that `<command>` at + version `<version>`. Note that the same command may have multiple + available versions. ++ +Here is the currentl output of the help text at the latest version: ++ +------------ +help 1 count 2 +help 1 help 1 +help 1 get 1 +------------ + `get` version 1:: The `get` command searches the config key-value pairs within a given `<scope>` for values that match the fixed `<key>` and
builtin/config-batch.c +32 -0
@@ -12,6 +12,7 @@ static const char *const builtin_config_batch_usage[] = { };#define UNKNOWN_COMMAND "unknown_command" +#define HELP_COMMAND "help"#define GET_COMMAND "get"#define COMMAND_PARSE_ERROR "command_parse_error"
@@ -104,6 +105,9 @@ static size_t parse_token(char **data, size_t *data_len,return parse_whitespace_token (data, data_len, token, err); } +static int help_command_1 (struct repository *repo, +char *data,size_t data_len); +enum value_match_mode {MATCH_ALL ,MATCH_EXACT ,
@@ -302,6 +306,11 @@ struct command { };static struct command commands[] = { + { + .name =HELP_COMMAND , + .fn = help_command_1, + .version =1 , + }, { .name =GET_COMMAND , .fn = get_command_1,
@@ -316,6 +325,29 @@ static struct command commands[] = {#define COMMAND_COUNT ((size_t)(sizeof(commands) / sizeof(*commands))) +static int help_command_1(struct repository *repo UNUSED, + char *data UNUSED, size_t data_len UNUSED) +{ +struct strbuf fmt_str =STRBUF_INIT ; + +strbuf_addf (&fmt_str,"%" PRIu32, (uint32_t )(COMMAND_COUNT -1 )); +emit_response (HELP_COMMAND ,"1" ,"count" , fmt_str.buf,NULL ); +strbuf_reset (&fmt_str); + +for (size_t i =0 ; i <COMMAND_COUNT ; i++) { +/* Halt at unknown command. */ +if (!commands[i].name[0 ]) +break ; + +strbuf_addf (&fmt_str,"%d" , commands[i].version); +emit_response (HELP_COMMAND ,"1" , commands[i].name, fmt_str.buf,NULL ); +strbuf_reset (&fmt_str); + } + +strbuf_release (&fmt_str); +return 0 ; +} +/** * Process a single line from stdin and process the command. *
t/t1312-config-batch.sh +13 -0
@@ -23,6 +23,19 @@ test_expect_success 'completely broken input' ' test_grep "an unrecoverable error occurred during command execution" err ' +test_expect_success 'help command' ' + echo "help 1" >in && + + cat >expect <<-\EOF && + help 1 count 2 + help 1 help 1 + help 1 get 1 + EOF + + git config-batch >out <in && + test_cmp expect out +' + test_expect_success 'failed to parse version' ' echo "bogus BAD_VERSION line of tokens" >in && test_must_fail git config-batch 2>err <in &&
Signature · 1 line
gitgitgadget
PATCH 5/11 config-batch: add NUL-terminated I/O format
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
When using automated tools, it is critical to allow for input/output formats that include special characters such as spaces and newlines. While the existing protocol for 'git config-batch' is human-readable and has some capacity for some spaces in certain positions, it is not available for spaces in the config key or newlines in the config values.
Add the '-z' option to signal the use of NUL-terminated strings. To understand where commands end regardless of potential future formats, use two NUL bytes in a row to terminate a command. To allow for empty string values, each token is provided in a <length>:<value> format, making "0:" the empty string value.
Update the existing 'help' and 'get' commands to match this format. Create helper methods that make it easy to parse and print in both formats simultaneously.
--- Documentation/git-config-batch.adoc | 57 ++++++++- builtin/config-batch.c | 188 +++++++++++++++++++++++++--- t/t1312-config-batch.sh | 69 ++++++++++ 3 files changed, 293 insertions(+), 21 deletions(-)
Documentation/git-config-batch.adoc +54 -3
@@ -21,6 +21,15 @@ multiple configuration values, the `git config-batch` command allows a single process to handle multiple requests using a machine-parseable interface across `stdin` and `stdout`. +OPTIONS +------- + +`-z`:: + If specified, then use the NUL-terminated input and output + format instead of the space and newline format. This format is + useful when the strings involved may include spaces or newlines. + See PROTOCOL for more details. + PROTOCOL -------- By default, the protocol uses line feeds (`LF`) to signal the end of a
@@ -41,13 +50,13 @@ These are the commands that are currently understood: `help` version 1:: The `help` command lists the currently-available commands in this version of Git. The output is multi-line, but the first - line provides the count of possible commands via `help count <N>`. - The next `<N>` lines are of the form `help <command> <version>` + line provides the count of possible commands via `help 1 count <N>`. + The next `<N>` lines are of the form `help 1 <command> <version>` to state that this Git version supports that `<command>` at version `<version>`. Note that the same command may have multiple available versions. + -Here is the currentl output of the help text at the latest version: +Here is the current output of the help text at the latest version: + ------------ help 1 count 2
@@ -102,6 +111,48 @@ get 1 missing <key> [<value-pattern>|<value>] where `<value-pattern>` or `<value>` is only supplied if provided in the command. +NUL-Terminated Format +~~~~~~~~~~~~~~~~~~~~~ + +When `-z` is given, the protocol changes in some structural ways. + +First, each command is terminated with two NUL bytes, providing a clear +boundary between commands regardless of future possibilities of new +command formats. + +Second, any time that a space _would_ be used to partition tokens in a +command, a NUL byte is used instead. Further, each token is prefixed +with `<N>:` where `<N>` is a decimal representation of the length of +the string between the `:` and the next NUL byte. Any disagreement in +these lengths is treated as a parsing error. This use of a length does +imply that "`0:`" is the representation of an empty string, if relevant. + +The decimal representation must have at most five numerals, thus the +maximum length of a string token can have 99999 characters. + +For example, the `get` command, version 1, could have any of the +following forms: + +------------ +3:get NUL 1:1 NUL 5:local NUL 14:key.with space NUL NUL +3:get NUL 1:1 NUL 9:inherit NUL 8:test.key NUL 9:arg:regex NUL 6:.*\ .* NUL NUL +3:get NUL 1:1 NUL 6:global NUL 8:test.key NUL 15:arg:fixed-value NUL 3:a b NUL NUL +------------ + +The output is modified similarly, such as the following output examples, +as if the input has a parse error, a valid `help` command, a `get` +command that had a match, and a `get` command that did not match. + +------------ +15:unknown_command NUL NUL +4:help NUL 1:1 NUL 5:count NUL 1:2 NUL NUL +4:help NUL 1:1 NUL 4:help NUL 1:1 NUL NUL +4:help NUL 1:1 NUL 3:get NUL 1:1 NUL NUL +3:get NUL 1:1 NUL 5:found NUL 8:test.key NUL 5:value NUL NUL +3:get NUL 1:1 NUL 7:missing NUL 8:test.key NUL NUL +------------ + + SEE ALSO -------- linkgit:git-config[1]
builtin/config-batch.c +170 -18
251 lines not on this page · raw message
t/t1312-config-batch.sh +69 -0
85 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH 6/11 docs: add design doc for config-batch
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
This document will be a place that tracks the future directions of the 'git config-batch' builtin. We plan to remove items as they are implemented in new commands and documented in the builtin documentation.
--- Documentation/technical/config-batch.adoc | 70 +++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/technical/config-batch.adoc
Documentation/technical/config-batch.adoc new file +70 -0
@@ -0,0 +1,70 @@ +Git Config-Batch Design Notes +============================= + +The `git config-batch` builtin has a robust protocol for parsing multiple +commands over `stdin` and providing structured output over `stdout`. The +intended use is for scripts or third-party software to interact with the +config settings of a repository multiple times within the same Git process. +The protocol is built with versioning that allows the consumer to know when +a certain command is available and to fall back to single-use `git config` +processes if the installed Git version does not have the latest commands +at the required versions. + +Recommended interaction pattern +------------------------------- + +This section provides a guide for ideal interaction with the `git +config-batch` command and its protocol. + +For maximum compatibility, do not attempt parsing the output of `git +version` to determine which commands are available. Instead, first check +if the `git config-batch` command succeeds and does not die immediately +due to the builtin being unavailable. Then, use the v1 of the `help` +command to get a list of available commands and versions. Use this list to +determine if your capabilities are available or should be replaced with an +appropriate `git config` single-use process. + +Further, all automated tooling would be better off using the +NUL-terminated format instead of the whitespace-delimited format, in case +config keys contain spaces or config values contain newlines. The +whitespace-delimited version is available for simpler integration and +human inspection. + +Current commands +---------------- + +See the documentation in linkgit::config-batch[1] for the latest set of +available commands and their protocols. + +Future commands +--------------- + +The following modes of `git config` are not currently available as commands +in `git config-batch`, but are planned for future integration: + +`git config list [--<scope>]`:: + Getting all values, regardless of config key, would require a + multi-valued output similar to the `help` command. This tool will + likely assume advanced options such as `--show-origin`. + +`git config set [--<scope>] <key> <value>`:: + It will be desirable to set a config key at a given scope as a + single value, replacing the current value at that scope, if it + exists and is a single value. A `set` command could satisfy this + purpose. + +`git config set --all [<value-pattern>|--fixed-value=<fixedvalue>] <key> <value>`:: + When replacing multiple values, it may be necessary to have a different + output describing the places those values were set, so it may need to + be implemented via a `set-all` command to differentiate from a `set` + command. + +`git config unset <key>`:: + +`git config unset --all [<value-pattern>|--fixed-value=<fixedvalue>] <key>`:: + +`git config get --all --rexexp <key-pattern> [<value-options>]`:: + +`--replace-all` option:: + +`--type=<type>` option::
Signature · 1 line
gitgitgadget
PATCH 7/11 config: extract location structs from builtin
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
Before reusing these concepts in builtin/config-batch.c, extract the config_location_options struct from builtin/config.c to config.h with implementation in config.c.
The only modification in this conversion is the use of a repository parameter instead of the_repository.
--- builtin/config.c | 117 ++++------------------------------------------- config.c | 89 +++++++++++++++++++++++++++++++++++ config.h | 20 ++++++++ 3 files changed, 117 insertions(+), 109 deletions(-)
builtin/config.c +8 -109
@@ -71,20 +71,6 @@ static const char *const builtin_config_edit_usage[] = {OPT_STRING ('f' ,"file" , &opts.source.file,N_ ("file" ),N_ ("use given config file" )), \ OPT_STRING(0, "blob", &opts.source.blob, N_("blob-id"), N_("read config from given blob object")) -struct config_location_options { -struct git_config_source source; -struct config_options options; -char *file_to_free; -int use_global_config; -int use_system_config; -int use_local_config; -int use_worktree_config; -int respect_includes_opt; -}; -#define CONFIG_LOCATION_OPTIONS_INIT { \ - .respect_includes_opt = -1, \ -} -#define CONFIG_TYPE_OPTIONS (type) \ OPT_GROUP(N_("Type")), \ OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type), \
@@ -772,93 +758,6 @@ static char *default_user_config(void)return strbuf_detach (&buf,NULL ); } -static void location_options_init (struct config_location_options *opts, -const char *prefix) -{ -if (!opts->source.file) - opts->source.file = opts->file_to_free = -xstrdup_or_null (getenv (CONFIG_ENVIRONMENT )); - -if (opts->use_global_config + opts->use_system_config + - opts->use_local_config + opts->use_worktree_config + - !!opts->source.file + !!opts->source.blob >1 ) { -error (_ ("only one config file at a time" )); -exit (129 ); - } - -if (!startup_info->have_repository) { -if (opts->use_local_config) -die (_ ("--local can only be used inside a git repository" )); -if (opts->source.blob) -die (_ ("--blob can only be used inside a git repository" )); -if (opts->use_worktree_config) -die (_ ("--worktree can only be used inside a git repository" )); - } - -if (opts->source.file && - !strcmp (opts->source.file,"-" )) { - opts->source.file =NULL ; - opts->source.use_stdin =1 ; - opts->source.scope =CONFIG_SCOPE_COMMAND ; - } - -if (opts->use_global_config) { - opts->source.file = opts->file_to_free =git_global_config (); -if (!opts->source.file) -/* -* It is unknown if HOME/.gitconfig exists, so -* we do not know if we should write to XDG -* location; error out even if XDG_CONFIG_HOME -* is set and points at a sane location. -*/ -die (_ ("$HOME not set" )); - opts->source.scope =CONFIG_SCOPE_GLOBAL ; - }else if (opts->use_system_config) { - opts->source.file = opts->file_to_free =git_system_config (); - opts->source.scope =CONFIG_SCOPE_SYSTEM ; - }else if (opts->use_local_config) { - opts->source.file = opts->file_to_free =repo_git_path (the_repository,"config" ); - opts->source.scope =CONFIG_SCOPE_LOCAL ; - }else if (opts->use_worktree_config) { -struct worktree **worktrees =get_worktrees (); -if (the_repository->repository_format_worktree_config) - opts->source.file = opts->file_to_free = -repo_git_path (the_repository,"config.worktree" ); -else if (worktrees[0 ] && worktrees[1 ]) -die (_ ("--worktree cannot be used with multiple " -"working trees unless the config\n" -"extension worktreeConfig is enabled. " -"Please read \"CONFIGURATION FILE\"\n" -"section in \"git help worktree\" for details" )); -else - opts->source.file = opts->file_to_free = -repo_git_path (the_repository,"config" ); - opts->source.scope =CONFIG_SCOPE_LOCAL ; -free_worktrees (worktrees); - }else if (opts->source.file) { -if (!is_absolute_path (opts->source.file) && prefix) - opts->source.file = opts->file_to_free = -prefix_filename (prefix, opts->source.file); - opts->source.scope =CONFIG_SCOPE_COMMAND ; - }else if (opts->source.blob) { - opts->source.scope =CONFIG_SCOPE_COMMAND ; - } - -if (opts->respect_includes_opt ==-1 ) - opts->options.respect_includes = !opts->source.file; -else - opts->options.respect_includes = opts->respect_includes_opt; -if (startup_info->have_repository) { - opts->options.commondir =repo_get_common_dir (the_repository); - opts->options.git_dir =repo_get_git_dir (the_repository); - } -} - -static void location_options_release (struct config_location_options *opts) -{ -free (opts->file_to_free); -} -static void display_options_init (struct config_display_options *opts) {if (opts->end_nul) {
@@ -885,7 +784,7 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix, argc =parse_options (argc, argv, prefix, opts, builtin_config_list_usage,0 );check_argc (argc,0 ,0 ); -location_options_init (&location_opts, prefix); +location_options_init (the_repository, &location_opts, prefix);display_options_init (&display_opts);setup_auto_pager ("config" ,1 );
@@ -944,7 +843,7 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix, value_pattern)) die(_("--url= cannot be used with --all, --regexp or --value")); -location_options_init (&location_opts, prefix); +location_options_init (the_repository, &location_opts, prefix);display_options_init (&display_opts);if (display_opts.type !=TYPE_COLOR )
@@ -998,7 +897,7 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix, comment =git_config_prepare_comment_string (comment_arg); -location_options_init (&location_opts, prefix); +location_options_init (the_repository, &location_opts, prefix);check_write (&location_opts.source); value =normalize_value (argv[0 ], argv[1 ], type, &default_kvi);
@@ -1044,7 +943,7 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix,if ((flags &CONFIG_FLAGS_FIXED_VALUE ) && !value_pattern)die (_ ("--fixed-value only applies with 'value-pattern'" )); -location_options_init (&location_opts, prefix); +location_options_init (the_repository, &location_opts, prefix);check_write (&location_opts.source);if ((flags &CONFIG_FLAGS_MULTI_REPLACE ) || value_pattern)
@@ -1073,7 +972,7 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr PARSE_OPT_STOP_AT_NON_OPTION);check_argc (argc,2 ,2 ); -location_options_init (&location_opts, prefix); +location_options_init (the_repository, &location_opts, prefix);check_write (&location_opts.source); ret = repo_config_rename_section_in_file(the_repository, location_opts.source.file,
@@ -1103,7 +1002,7 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr PARSE_OPT_STOP_AT_NON_OPTION);check_argc (argc,1 ,1 ); -location_options_init (&location_opts, prefix); +location_options_init (the_repository, &location_opts, prefix);check_write (&location_opts.source); ret = repo_config_rename_section_in_file(the_repository, location_opts.source.file,
@@ -1163,7 +1062,7 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix, argc =parse_options (argc, argv, prefix, opts, builtin_config_edit_usage,0 );check_argc (argc,0 ,0 ); -location_options_init (&location_opts, prefix); +location_options_init (the_repository, &location_opts, prefix);check_write (&location_opts.source); ret =show_editor (&location_opts);
@@ -1231,7 +1130,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix) builtin_config_usage, PARSE_OPT_STOP_AT_NON_OPTION); -location_options_init (&location_opts, prefix); +location_options_init (the_repository, &location_opts, prefix);display_options_init (&display_opts);if ((actions & (ACTION_GET_COLOR |ACTION_GET_COLORBOOL )) && display_opts.type) {
config.c +89 -0
98 lines not on this page · raw message
config.h +20 -0
@@ -166,6 +166,26 @@ struct config_context {typedef int (*config_fn_t )(const char *,const char *,const struct config_context *,void *); +struct config_location_options { +struct git_config_source source; +struct config_options options; +char *file_to_free; +int use_global_config; +int use_system_config; +int use_local_config; +int use_worktree_config; +int respect_includes_opt; +}; +#define CONFIG_LOCATION_OPTIONS_INIT { \ + .respect_includes_opt = -1, \ +} + +void location_options_init (struct repository *repo, +struct config_location_options *opts, +const char *prefix); + +void location_options_release (struct config_location_options *opts); +/** * Read a specific file in git-config format. * This function takes the same callback and data parameters as `repo_config`.
Signature · 1 line
gitgitgadget
PATCH 8/11 config-batch: pass prefix through commands
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The 'help' and 'get' commands of 'git config-batch' have not needed the prefix parameter from the builtin entrance point, but an upcoming command will need it in order to identify the location of the appropriate config file. Pass it through the appropriate functions and function pointers.
--- builtin/config-batch.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-)
builtin/config-batch.c +17 -9
@@ -67,9 +67,11 @@ static int command_parse_error(const char *command)* Return 0 on success. */ typedef int (*command_fn )(struct repository *repo, +const char *prefix,char *data,size_t data_len); static int unknown_command(struct repository *repo UNUSED, + const char *prefix UNUSED, char *data UNUSED, size_t data_len UNUSED) {return emit_response (UNKNOWN_COMMAND ,NULL );
@@ -176,6 +178,7 @@ static size_t parse_token(char **data, size_t *data_len, }static int help_command_1 (struct repository *repo, + const char *prefix UNUSED,char *data,size_t data_len);enum value_match_mode {
@@ -292,6 +295,7 @@ static int parse_scope(const char *str, enum config_scope *scope)* [N*] indicates optional parameters that are not needed. */ static int get_command_1 (struct repository *repo, + const char *prefix UNUSED,char *data,size_t data_len) {
@@ -402,6 +406,7 @@ static struct command commands[] = {#define COMMAND_COUNT ((size_t)(sizeof(commands) / sizeof(*commands))) static int help_command_1(struct repository *repo UNUSED, + const char *prefix UNUSED, char *data UNUSED, size_t data_len UNUSED) {struct strbuf fmt_str =STRBUF_INIT ;
@@ -424,7 +429,8 @@ static int help_command_1(struct repository *repo UNUSED,return 0 ; } -static int process_command_nul (struct repository *repo) +static int process_command_nul (struct repository *repo, +const char *prefix) {static struct strbuf line =STRBUF_INIT ;char *data, *command, *versionstr;
@@ -476,7 +482,7 @@ static int process_command_nul(struct repository *repo)if (!commands[i].name[0 ] || (!strcmp (command, commands[i].name) && commands[i].version == version)) { - res = commands[i].fn (repo, data, data_len); + res = commands[i].fn (repo, prefix, data, data_len); goto cleanup; } }
@@ -484,14 +490,15 @@ static int process_command_nul(struct repository *repo)BUG (_ ("scanned to end of command list, including 'unknown_command'" )); parse_error: - res =unknown_command (repo,NULL ,0 ); + res =unknown_command (repo, prefix,NULL ,0 ); cleanup:strbuf_release (&line);return res; } -static int process_command_whitespace (struct repository *repo) +static int process_command_whitespace (struct repository *repo, +const char *prefix) {static struct strbuf line =STRBUF_INIT ;struct string_list tokens =STRING_LIST_INIT_NODUP ;
@@ -536,7 +543,7 @@ static int process_command_whitespace(struct repository *repo)if (!commands[i].name[0 ] || (!strcmp (command, commands[i].name) && commands[i].version == version)) { - res = commands[i].fn (repo, data, data_len); + res = commands[i].fn (repo, prefix, data, data_len); goto cleanup; } }
@@ -559,11 +566,12 @@ cleanup:* * Returns negative value on other catastrophic error. */ -static int process_command (struct repository *repo) +static int process_command (struct repository *repo, +const char *prefix) {if (zformat) -return process_command_nul (repo); -return process_command_whitespace (repo); +return process_command_nul (repo, prefix); +return process_command_whitespace (repo, prefix); } int cmd_config_batch(int argc,
@@ -586,7 +594,7 @@ int cmd_config_batch(int argc,repo_config (repo, git_default_config,NULL ); -while (!(res =process_command (repo))); +while (!(res =process_command (repo, prefix)));if (res ==1 )return 0 ;
Signature · 1 line
gitgitgadget
PATCH 9/11 config-batch: add 'set' v1 command
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
This new command is intended for single-value assignments to a specific chosen scope. More complicated versions of the 'git config set' command will be incorporated into future commands.
--- Documentation/git-config-batch.adoc | 24 ++++++++ builtin/config-batch.c | 71 ++++++++++++++++++++++ config.c | 27 +++++++++ config.h | 3 + t/t1312-config-batch.sh | 94 ++++++++++++++++++++++++++++- 5 files changed, 217 insertions(+), 2 deletions(-)
Documentation/git-config-batch.adoc +24 -0
@@ -111,6 +111,30 @@ get 1 missing <key> [<value-pattern>|<value>] where `<value-pattern>` or `<value>` is only supplied if provided in the command. +`set` version 1:: + The `set` command writes a single key-value pair to a config + file. It specifies which file by a `<scope>` parameter from + among `system`, `global`, `local`, and `worktree`. The `<key>` + is the next positional argument. The remaining data in the line + is provided as the `<value>` to assign the config. ++ +------------ +set 1 <scope> <key> <value> +------------ ++ +These uses will match the behavior of `git config --set --<scope> <key> +<value>`. Note that replacing all values with the `--all` option or +matching specific value patterns are not supported by this command. ++ +The response of these commands will include a `success` message if the +value is written as expected or `failed` if an unexpected failure +occurs: ++ +------------ +set 1 success <scope> <key> <value> +set 1 failed <scope> <key> <value> +------------ + NUL-Terminated Format ~~~~~~~~~~~~~~~~~~~~~
builtin/config-batch.c +71 -0
89 lines not on this page · raw message
config.c +27 -0
@@ -3594,6 +3594,33 @@ int lookup_config(const char **mapping, int nr_mapping, const char *var)return -1 ; } +int location_options_set_scope (struct config_location_options *opts, +enum config_scope scope) +{ +switch (scope) { +case CONFIG_SCOPE_SYSTEM : + opts->use_system_config =1 ; +break ; + +case CONFIG_SCOPE_GLOBAL : + opts->use_global_config =1 ; +break ; + +case CONFIG_SCOPE_LOCAL : + opts->use_local_config =1 ; +break ; + +case CONFIG_SCOPE_WORKTREE : + opts->use_worktree_config =1 ; +break ; + +default : +return -1 ; + } + +return 0 ; +} +void location_options_init (struct repository *repo,struct config_location_options *opts,const char *prefix)
config.h +3 -0
@@ -180,6 +180,9 @@ struct config_location_options { .respect_includes_opt = -1, \ } +int location_options_set_scope (struct config_location_options *opts, +enum config_scope scope); +void location_options_init (struct repository *repo,struct config_location_options *opts,const char *prefix);
t/t1312-config-batch.sh +92 -2
114 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH 10/11 t1312: create read/write test
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
This new test will be extended in the future to ensure that multiple commands that execute in order update the configuration state enough to reflect new written values as we read them in later commands.
--- t/t1312-config-batch.sh | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
t/t1312-config-batch.sh +27 -0
@@ -295,4 +295,31 @@ test_expect_success 'set config by scope with -z' ' test_cmp expect-values values ' +test_expect_success 'read/write interactions in sequence' ' + test_when_finished git config remove-section test.rw && + + cat >in <<-\EOF && + get 1 local test.rw.missing + set 1 local test.rw.found found + get 1 local test.rw.found + set 1 local test.rw.found updated + get 1 local test.rw.found + EOF + + cat >expect <<-\EOF && + get 1 missing test.rw.missing + set 1 success local test.rw.found found + get 1 found test.rw.found local found + set 1 success local test.rw.found updated + get 1 found test.rw.found local updated + EOF + + git config-batch <in >out 2>err && + + test_must_be_empty err && + test_cmp expect out && + + test_cmp_config updated test.rw.found +' + test_done
Signature · 1 line
gitgitgadget
PATCH 11/11 config-batch: add unset v1 command
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
Add a new 'unset' command with version 1 that mimics 'git config --unset' with optional regex pattern or '--fixed-value' arguments.
--- Documentation/git-config-batch.adoc | 28 ++++++++ builtin/config-batch.c | 99 +++++++++++++++++++++++++++++ t/t1312-config-batch.sh | 61 ++++++++++++++++-- 3 files changed, 181 insertions(+), 7 deletions(-)
Documentation/git-config-batch.adoc +28 -0
@@ -135,6 +135,34 @@ set 1 success <scope> <key> <value> set 1 failed <scope> <key> <value> ------------ +`unset` version 1:: + The `unset` command removes a single value from a config file. + It specifies which file by a `<scope>` parameter from among + `system`, `global`, `local`, and `worktree`. The `<key>` is the + next positional argument. There could be two additional + arguments used to match specific config values, where the first + is either `arg:regex` or `arg:fixed-value` to specify the type + of match. ++ +------------ +unset 1 <scope> <key> +unset 1 <scope> <key> arg:regex <value-pattern> +unset 1 <scope> <key> arg:fixed-value <value> +------------ ++ +These uses will match the behavior of `git config --unset --<scope> <key>` +with the additional arguments of `<value-pattern>` if `arg:regex` is +given or `--fixed-value <value>` if `arg:fixed-value` is given. ++ +The response of these commands will include a `success` message +if matched values are found and removed as expected or `failed` if an +unexpected failure occurs: ++ +------------ +unset 1 success <scope> <key> +unset 1 failed <scope> <key> +------------ + NUL-Terminated Format ~~~~~~~~~~~~~~~~~~~~~
builtin/config-batch.c +99 -0
117 lines not on this page · raw message
t/t1312-config-batch.sh +54 -7
103 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Quoted 4 lines · Derrick Stolee via GitGitGadget
This RFC explores a new git config-batch builtin that allows tools to interact with Git's config data with multiple queries using a single process. This is an orthogonal alternative to the effort to create a stable, linkable config API. Both approaches have different strengths.
Just a few random thoughts before diving into the patches.
Quoted 6 lines
My main motivation is the performance of git-credential-manager on Windows platforms as it can call git config get dozens of times. At 150-200ms per execution, that adds up significantly, leading to multiple seconds just to load a credential that already exists. I believe that there are other benefits to having this interface available, but I can't recall any specifics at the moment.
So this would be "credential-manager gets started, and instead of having to spawn 'git config' many times, spawn a single instance of 'git config --batch' and talk with it". Would it be beneficial to further think about a long-running 'git config --server' that can be contacted by a credential-manager (or other processes) whose lifetime is totally independent, possibly over local transport mechanisms like named pipes, or is it a key to keep the mechanism and design simple to limit the number of customer this service supports to only one at a time and we would prefer to keep it that way?
Quoted 5 lines
One thing that I think would be valuable to include is a reload command that signals that the git config-batch process should reload the configset into memory due to config manipulations in other processes, especially while git config-batch doesn't have all capabilities from git config. I'll include that in the first version for review, if this RFC leads to positive support.
Can "git config --batch" write/modify configuration, and if so, when does it make its modification available to the outside world? Would we have a "flush" command, or it would pretty much be immediate?
Can we do without an explicit "reload" command by noticing when the configuration files are updated and automatically reload?
I am trying to figure out how more than one "git config --batch" processes can coordinate with each other with minimum overhead. It is not a goal to have multiple such processes, but it would be a goal to support multiple clients each of which would benefit from having access to the configuration data service (which is why I brought up a single and shared long-running daemon as a possible alternative earlier).
Re: [PATCH 01/11] config-batch: basic boilerplate of new builtin
In reply to Derrick Stolee via GitGitGadgetQuoted 6 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
Later changes will document, implement, and test this new builtin. For now, this serves as the latest example of the minimum boilerplate to introduce a new builtin.
Quoted 6 lines
Recently, we updated the comment in
builtin.habout how to create a new builtin, but failed to mention the required change to meson.build files for some CI builds to pass. Fix that oversight.Signed-off-by Derrick Stolee <[email protected]>---
We have had a bad reputation for having too many commands; would it be better to present it as a new mode of existing "git config" command at the end-user level, I wonder?
Also after reading patches for a few early steps, I do not quite see "batch"-ness in this protocol; it is strictly "a single request is met with a single response".
Re: [PATCH 02/11] config-batch: create parse loop and unknown command
In reply to Derrick Stolee via GitGitGadgetbuiltin/config-batch.c line 66
Quoted 7 lines · Derrick Stolee via GitGitGadget
builtin/config-batch.c +7
-0+static struct command commands[] = { +/* unknown_command must be last. */ + { + .name ="" , + .fn = unknown_command, + }, +};
A useful trick is to deliberately omit the trailing comma after the element that MUST be last. You did that for the __NR enum element in a later step.
Quoted 1 line
+#define
COMMAND_COUNT((size_t)(sizeof(commands) /sizeof(*commands)))
Isn't this ARRAY_SIZE(commands)?
builtin/config-batch.c int cmd_config_batch(int argc, · line 156
Quoted 1 line
builtin/config-batch.c +1
-0+while (!(res =process_command (repo)));
Please write an empty statement on its own line, i.e.
while (!(res = process_command(repo))) ;
Quoted 17 lines · Derrick Stolee via GitGitGadget
This RFC explores a new git config-batch builtin that allows tools to interact with Git's config data with multiple queries using a single process. This is an orthogonal alternative to the effort to create a stable, linkable config API. Both approaches have different strengths.
My main motivation is the performance of git-credential-manager on Windows platforms as it can call git config get dozens of times. At 150-200ms per execution, that adds up significantly, leading to multiple seconds just to load a credential that already exists. I believe that there are other benefits to having this interface available, but I can't recall any specifics at the moment.
This RFC adds git config-batch with a protocol over stdin/stdout for executing multiple config queries. The implementation has a limited set of potential queries, but also creates a model for compatibility for tools to automatically adapt to different Git versions.
Quoted 5 lines
I'm submitting this as an RFC before I've polished all of the details because I want to make sure I'm going down a good direction. Please focus feedback in these questions:
* Is this a worthwhile feature to add to Git?
Git LFS has the same needs, but I believe it can use git config -l -z to do that and parse the config options itself. If this is just config fetching, I'm not sure of the additional utility that such a feature would add. If that interface _almost_ meets your needs, could we add functionality there instead of a new interface?
If you need to set many keys, I'm curious as to why that is.
Signature · 2 lines
brian m. carlson (they/them) Toronto, Ontario, CA
Quoted 13 lines · brian m. carlson
Quoted 11 lines · Derrick Stolee via GitGitGadget
This RFC explores a new git config-batch builtin that allows tools to interact with Git's config data with multiple queries using a single process. This is an orthogonal alternative to the effort to create a stable, linkable config API. Both approaches have different strengths.
My main motivation is the performance of git-credential-manager on Windows platforms as it can call git config get dozens of times. At 150-200ms per execution, that adds up significantly, leading to multiple seconds just to load a credential that already exists. I believe that there are other benefits to having this interface available, but I can't recall any specifics at the moment.
Quoted 7 lines
Quoted 1 line
* Is this a worthwhile feature to add to Git?
Git LFS has the same needs, but I believe it can use
git config -l -zto do that and parse the config options itself. If this is just config fetching, I'm not sure of the additional utility that such a feature would add. If that interface _almost_ meets your needs, could we add functionality there instead of a new interface?
This is a good suggestion to look into as a potentially-easier solution.
There may be some work required on the consumer to interpret multiple values and the right inheritance rules. This is relatively minor compared to attempting a full parser with complicated 'includeIf' logic.
Quoted 1 line
If you need to set many keys, I'm curious as to why that is.
I know that the credential manager does more than just query the config, but also sets and unsets config. The full interface is here [1]. However, the performance-critical parts may not require mutating configuration values, and hence such a
Thanks for the pointer to git-lfs as a similar use case. I see that it has a way to get the full list of config values [2] with '-l' (but not '-z'). It also has methods for getting values on a per-key (or even per-file) basis. I have not tracked the uses of config code into its consumers to know how often one is used over the other.
[2] https://github.com/git-lfs/git-lfs/blob/bb65882304a655ffa8abf2be6922e53ff18af5a5/git/config.go#L208
Thanks,
-Stolee
Quoted 16 lines · Junio C Hamano
Quoted 4 lines · Derrick Stolee via GitGitGadget
This RFC explores a new git config-batch builtin that allows tools to interact with Git's config data with multiple queries using a single process. This is an orthogonal alternative to the effort to create a stable, linkable config API. Both approaches have different strengths.
Just a few random thoughts before diving into the patches.
Quoted 6 lines
My main motivation is the performance of git-credential-manager on Windows platforms as it can call git config get dozens of times. At 150-200ms per execution, that adds up significantly, leading to multiple seconds just to load a credential that already exists. I believe that there are other benefits to having this interface available, but I can't recall any specifics at the moment.
Quoted 9 lines
So this would be "credential-manager gets started, and instead of having to spawn 'git config' many times, spawn a single instance of 'git config --batch' and talk with it". Would it be beneficial to further think about a long-running 'git config --server' that can be contacted by a credential-manager (or other processes) whose lifetime is totally independent, possibly over local transport mechanisms like named pipes, or is it a key to keep the mechanism and design simple to limit the number of customer this service supports to only one at a time and we would prefer to keep it that way?
I could imagine a world where we have this approach, similar to the fsmonitor server. I should do more research and refresh my memory on that I/O model, if only to potentially reuse some of the parsing logic.
But this would be an interesting potential direction, saving the process start-up time entirely.
The one big difficulty that I see is that the config will need to be refreshed proactively by the server, potentially by watching the config files themselves (including any files included along the way) and also any changes to repo state, such as the current branch. Any repo state that could impact the 'includeIf' logic would need to be checked carefully.
Quoted 9 lines
Quoted 5 lines
One thing that I think would be valuable to include is a reload command that signals that the git config-batch process should reload the configset into memory due to config manipulations in other processes, especially while git config-batch doesn't have all capabilities from git config. I'll include that in the first version for review, if this RFC leads to positive support.
Can "git config --batch" write/modify configuration, and if so, when does it make its modification available to the outside world? Would we have a "flush" command, or it would pretty much be immediate?
The 'set' command in this series calls methods that reach into repo_config_set_multivar_in_file_gently() which updates the config file as part of that call, including using the .lock file technique to avoid concurrent writes. Looking closely, it appears we do the right thing by parsing the existing file so we only update the new values while allowing any concurrent writes to the file to be respected, even if they disagree with our current view of the config.
Such assignments also update our in-memory view _of those keys_ but it may be a good time to automatically refresh the entire set of config values.
Quoted 2 lines
Can we do without an explicit "reload" command by noticing when the configuration files are updated and automatically reload?
This would be an interesting approach, especially for the server concept.
Quoted 7 lines
I am trying to figure out how more than one "git config --batch" processes can coordinate with each other with minimum overhead. It is not a goal to have multiple such processes, but it would be a goal to support multiple clients each of which would benefit from having access to the configuration data service (which is why I brought up a single and shared long-running daemon as a possible alternative earlier).
You're right to bring up these concerns. While 'git config-batch' is intended to be relatively short-lived, users could build tools that keep it alive for a long time. Thus, it is important to consider these automatically-refreshing scenarios. And if we are automatically refreshing, then should we instead consider a client/server model?
I have some things to explore at the highest levels. I will likely start by exploring brian's 'git config -l -z' suggestion to see if that solves the short-term need. But I will consider these other ideas to see where they lead in terms of complexity and potential applications.
Thanks,
-Stolee
Re: [PATCH 01/11] config-batch: basic boilerplate of new builtin
In reply to Junio C HamanoQuoted 12 lines · Junio C Hamano
Quoted 10 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
Later changes will document, implement, and test this new builtin. For now, this serves as the latest example of the minimum boilerplate to introduce a new builtin.
Recently, we updated the comment in
builtin.habout how to create a new builtin, but failed to mention the required change to meson.build files for some CI builds to pass. Fix that oversight.
Quoted 6 lines
Quoted 2 lines
Signed-off-by Derrick Stolee <[email protected]>---
We have had a bad reputation for having too many commands; would it be better to present it as a new mode of existing "git config" command at the end-user level, I wonder?
Interesting thought. I think we also have a bad reputation of commands that are overloaded with too many purposes.
In this case, though, I do think that the modern 'git config <subcommand>' model presents some clear boundaries for how the command should behave with the 'batch' (or 'server') subcommand. Grouping all config-related operations in the same builtin may be ideal.
Quoted 3 lines
Also after reading patches for a few early steps, I do not quite see "batch"-ness in this protocol; it is strictly "a single request is met with a single response".
The batch-ness is that multiple requests can eventually go to the same process. The client could collect multiple commands in a batch and send them all without processing the responses one-by-one. This is how it works in the tests: a single input file is prepared and all responses are scanned after-the-fact.
The back-and-forth mechanism is how the git-credential-manager tool would use it, because it dynamically explores certain config keys. For example:
it checks the deepest possible URL for a specific key then peels away the last segment of the URL to see if there is a directory-prefix match in a key. (This is the main reason that there are so many requests in this application.)
I believe this is similar to how 'git cat-file --batch' or 'git cat-file --batch-check' work, which was my inspiration for this word. If we regret those names, then I'm happy to move towards a better name.
Thanks,
-Stolee
Hi Stolee
Quoted 11 lines · Derrick Stolee via GitGitGadget
This RFC explores a new git config-batch builtin that allows tools to interact with Git's config data with multiple queries using a single process. This is an orthogonal alternative to the effort to create a stable, linkable config API. Both approaches have different strengths.
My main motivation is the performance of git-credential-manager on Windows platforms as it can call git config get dozens of times. At 150-200ms per execution, that adds up significantly, leading to multiple seconds just to load a credential that already exists. I believe that there are other benefits to having this interface available, but I can't recall any specifics at the moment.
It would be helpful to explain what the advantage of this new command is over using "git config --list -z" or "git config --get-regex '^(some|section|names)\.' -z". I've found those to be effective in programs that read several config keys. Elsewhere brian has mentioned that git-lfs does something similar and I believe git-filter-repo uses "git config --list -z" as well.
One potential advantage would be if this command supported specifying the type of the value. When using "git config --list" it is a pain to have to normalize boolean values and parse color descriptions into terminal escape codes.
Being able to set multiple keys at once would also be an advantage if there is a convincing use case for it.
Quoted 10 lines
This RFC adds git config-batch with a protocol over stdin/stdout for executing multiple config queries. The implementation has a limited set of potential queries, but also creates a model for compatibility for tools to automatically adapt to different Git versions.
I'm submitting this as an RFC before I've polished all of the details because I want to make sure I'm going down a good direction. Please focus feedback in these questions:
* Is this a worthwhile feature to add to Git?
Possibly, if there are clear benefits over "git config --list". I'm not sure it needs to be a separate command though - I agree with Junio that it would be more discoverable if this was a subcommand of "git config"
Quoted 1 line
* Is this a reasonable protocol for stdin/stdout?
The protocol sounds quite complicated with capability queries and versioning. At the same time it looks like the line oriented version does not support keys that contain spaces, values that contain newlines or retrieving settings from a file whose path contains a newline. I think it might be better to have a single protocol variant based using NUL delimiters like "git merge-tree --stdin" and "git diff-pairs". Those commands use a simple NUL terminated protocol without the need to specify the length of the input.
Quoted 7 lines
Each command has an associated version, in case we need to expand or alter the functionality in the future. This includes the potential to deprecate and remove certain versions that we no longer want to support, such as replacing set version 1 with a version 2 and making version 1 no longer available. I do hope that we will mostly be able to move with new command names, such as a set-all command including the options for git config set --all ... instead of increasing the version of the set command.
It's good that you're thinking about future functionality but I wonder if we really need to specify the version on a per command basis rather than as a command line option or simply adding commands like "get-v2".
Quoted 3 lines
There is a -z option that changes the command interface to use NUL-terminated strings. Two NULs specify a command boundary, which promotes compatibility with a caller that sends an unknown command.
That also allows optional fields such as the file to read the config from or the value to match to be NUL delimited while making the end of a record unambiguous.
Quoted 3 lines
However, this
means that we cannot specify an empty string as a token within a command unless we add more data.
Is there a need to do that? Can we require key=value pairs or possibly a fixed number of positional parameters if there is a chance the value will be empty?
Quoted 3 lines
This format uses <N>:<string> to provide the
integer <N> which specifies the length of <string>. This is a little cumbersome, but the format is intended for tools, not humans.
It does seem cumbersome. I can see that it might be helpful to have the length of the complete query and response to avoid deadlocks when reading and writing but I'm not sure requiring the length of each field is helpful.
Quoted 4 lines
I have a test integration with git-credential-manager available [1] for testing. This includes a model for interacting with git config-batch in a compatible way that will respond to certain features not being available:
Quoted 8 lines
1. If git config-batch fails immediately, then all queries are handled by git config.
2. If git config-batch starts without failure, then the first query is for the help command.
3. As queries come to the config system, the query is checked against the available commands advertised by git config-batch. If the appropriate command is available, then the query is made in that process. If not, then the query uses the existing git config command.
This seems like quite a lot of effort just to check a few config settings.
Quoted 8 lines
I have a few concerns with this implementation that I'd like to improve before submitting a version for full review. I list them here so you can see the flaws that I already see, but also so you can add to this list:
* The use of arg:<arg> to specify an optional argument creates the inability to submit a value that starts with arg:. Consider alternative ways to specify arguments or to specify that the remaining data in the command (including spaces) is a final positional argument.
The protocol should be unambiguous. Requiring key=value pairs for all fields would be one way to achieve that
set key=my.key scope=global value-regex=my-regex value=new-value
or we could force all optional fields to come first and count the number of fields to figure out whether any optional fields have been passed
set value-regex=my-regex global my.key new-value
(I've used spaces above to delimit fields but we'd want to use NUL in the protocol)
Thanks
Phillip
Quoted 53 lines
* In general, I found myself implementing behavior based on the deprecated forms of git config that use the --get or --unset style arguments instead of git config (set|unset|get) subcommands. It's worth making sure that any references to equivalent git config commands use the new modes.
* I need to add an --[no-]includes option as a command-line argument that signals whether include sections should be followed. I don't believe this should be specified on a per-command basis, but I'm open to suggestions.
* I have an early draft of a technical document detailing the plan for this builtin. It has some lists of intended future commands that have not been implemented. This would also be a good place to document any parsing APIs built to help contributors adding to this builtin.Thanks, -Stolee
Derrick Stolee (11):
config-batch: basic boilerplate of new builtin
config-batch: create parse loop and unknown command
config-batch: implement get v1
config-batch: create 'help' command
config-batch: add NUL-terminated I/O format
docs: add design doc for config-batch
config: extract location structs from builtin
config-batch: pass prefix through commands
config-batch: add 'set' v1 command
t1312: create read/write test
config-batch: add unset v1 command.gitignore | 1 + Documentation/git-config-batch.adoc | 214 ++++++ Documentation/meson.build | 1 + Documentation/technical/config-batch.adoc | 70 ++ Makefile | 1 + builtin.h | 7 + builtin/config-batch.c | 772 ++++++++++++++++++++++ builtin/config.c | 117 +--- command-list.txt | 1 + config.c | 116 ++++ config.h | 26 + git.c | 1 + meson.build | 1 + t/meson.build | 1 + t/t1312-config-batch.sh | 372 +++++++++++ 15 files changed, 1592 insertions(+), 109 deletions(-)create mode 100644
Documentation/git-config-batch.adoccreate mode 100644Documentation/technical/config-batch.adoccreate mode 100644builtin/config-batch.c
create mode 100755t/t1312-config-batch.shbase-commit:
83a69f19359e6d9bc980563caca38b2b5729808c
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2033%2Fderrickstolee%2Fbatched-config-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2033/derrickstolee/batched-config-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2033
Quoted 5 lines · Derrick Stolee via GitGitGadget
This RFC explores a new git config-batch builtin that allows tools to interact with Git's config data with multiple queries using a single process. This is an orthogonal alternative to the effort to create a stable, linkable config API. Both approaches have different strengths. [snip]
This sounds incredibly useful. Thanks!
Re: [PATCH 09/11] config-batch: add 'set' v1 command
In reply to Derrick Stolee via GitGitGadgetReview
Quoted 13 lines · Derrick Stolee via GitGitGadget
[snip]
(quoted diff) +6
-0+git-config-batch(1) +=================== + +NAME +---- +git-config-batch - Get and set options using machine-parseableinterface
(quoted diff) +5
-0+ + +SYNOPSIS +-------- +[verse]
There’s work lead by Jean-Noël Avila to use [synopsis] instead of [verse].[1] Would it make sense to start off with that?
† 1: E.g. acffc5e9 (doc: convert git-remote to synopsis style, 2025-12-20)
Quoted 5 lines
+'git config-batch' <options>
(quoted diff) +3
-0+ +DESCRIPTION +-----------
Re: [PATCH 01/11] config-batch: basic boilerplate of new builtin
In reply to Derrick StoleeQuoted 8 lines · Derrick Stolee
Quoted 5 lines
Quoted 1 line
[snip]
We have had a bad reputation for having too many commands; would it be better to present it as a new mode of existing "git config" command at the end-user level, I wonder?
Interesting thought. I think we also have a bad reputation of commands that are overloaded with too many purposes.
I had a response to that in my head...
Quoted 4 lines
In this case, though, I do think that the modern 'git config <subcommand>' model presents some clear boundaries for how the command should behave with the 'batch' (or 'server') subcommand. Grouping all config-related operations in the same builtin may be ideal.
Which turned out to be exactly about a subcommand. :)
I find the modern subcommand model very easy to navigate. And with much less downsides compared to having dozens of options for one command (or: one particular subcommand to git(1)).
Quoted 10 lines
Quoted 4 lines
Also after reading patches for a few early steps, I do not quite see "batch"-ness in this protocol; it is strictly "a single request is met with a single response".
The batch-ness is that multiple requests can eventually go to the same process. The client could collect multiple commands in a batch and send them all without processing the responses one-by-one. This is how it works in the tests: a single input file is prepared and all responses are scanned after-the-fact.
As a user that makes sense given the existing --batch and --stdin options.
Quoted 13 lines
The back-and-forth mechanism is how the git-credential-manager tool would use it, because it dynamically explores certain config keys. For example:
it checks the deepest possible URL for a specific key then peels away the last segment of the URL to see if there is a directory-prefix match in a key. (This is the main reason that there are so many requests in this application.)I believe this is similar to how 'git cat-file --batch' or 'git cat-file --batch-check' work, which was my inspiration for this word. If we regret those names, then I'm happy to move towards a better name.
Thanks,
-Stolee
Re: [PATCH 01/11] config-batch: basic boilerplate of new builtin
In reply to Derrick Stolee via GitGitGadgetDocumentation/git-config-batch.adoc line 11
Quoted 13 lines · Derrick Stolee via GitGitGadget
[snip]
Documentation/git-config-batch.adoc new file +6
-0+git-config-batch(1) +=================== + +NAME +---- +git-config-batch - Get and set options using machine-parseableinterface
Documentation/git-config-batch.adoc new file +5
-0+ + +SYNOPSIS +-------- +[verse]
There’s work lead by Jean-Noël Avila to use [synopsis] instead of [verse].[1] Would it make sense to start off with that?
† 1: E.g. acffc5e9 (doc: convert git-remote to synopsis style, 2025-12-20)
Quoted 5 lines
+'git config-batch' <options>
Documentation/git-config-batch.adoc new file +3
-0+ +DESCRIPTION +-----------
Re: [PATCH 02/11] config-batch: create parse loop and unknown command
In reply to Derrick Stolee via GitGitGadgetDocumentation/git-config-batch.adoc SYNOPSIS · line 22
Quoted 11 lines · Derrick Stolee via GitGitGadget
[snip]
DESCRIPTION
-----------Documentation/git-config-batch.adoc +7
-1-TODO +Tools frequently need to change their behavior based on values stored in +Git's configuration files. These files may have complicated conditions +for including extra files, so it is difficult to produce an independent +parser. To avoid executing multiple processes to discover or modify +multiple configuration values, the `git config-batch` command allows a +single process to handle multiple requests using a machine-parseable +interface across `stdin` and `stdout`.
I really like that the doc itself motivates the command. Many man pages on git(1) just tells you what it does as if you would already know why you need it.
Quoted 2 lines
+
[snip]
Re: [PATCH 11/11] config-batch: add unset v1 command
In reply to Derrick Stolee via GitGitGadgetQuoted 4 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
Add a new 'unset' command with version 1 that mimics 'git config --unset' with optional regex pattern or '--fixed-value' arguments.
git config --unset is deprecated in favor of git config unset.
Quoted 23 lines
Signed-off-by Derrick Stolee <[email protected]>--- Documentation/git-config-batch.adoc | 28 ++++++++ builtin/config-batch.c | 99 +++++++++++++++++++++++++++++ t/t1312-config-batch.sh | 61 ++++++++++++++++-- 3 files changed, 181 insertions(+), 7 deletions(-)Documentation/git-config-batch.adoc +6
-0@@ -135,6 +135,34 @@ set 1 success <scope> <key> <value> set 1 failed <scope> <key> <value> ------------ +`unset` version 1:: + The `unset` command removes a single value from a config file. + It specifies which file by a `<scope>` parameter from among + `system`, `global`, `local`, and `worktree`. The `<key>` is the + next positional argument. There could be two additional + arguments used to match specific config values, where the first
Documentation/git-config-batch.adoc set 1 success <scope> <key> <value> · line 153
Quoted 10 lines
Documentation/git-config-batch.adoc +10
-0+ is either `arg:regex` or `arg:fixed-value` to specify the type + of match. ++ +------------ +unset 1 <scope> <key> +unset 1 <scope> <key> arg:regex <value-pattern> +unset 1 <scope> <key> arg:fixed-value <value> +------------ ++ +These uses will match the behavior of `git config --unset --<scope> <key>`
Same as above.
Quoted 53 lines
Documentation/git-config-batch.adoc +12
-0+with the additional arguments of `<value-pattern>` if `arg:regex` is +given or `--fixed-value <value>` if `arg:fixed-value` is given. ++ +The response of these commands will include a `success` message +if matched values are found and removed as expected or `failed` if an +unexpected failure occurs: ++ +------------ +unset 1 success <scope> <key> +unset 1 failed <scope> <key> +------------ + NUL-Terminated Format ~~~~~~~~~~~~~~~~~~~~~builtin/config-batch.c +23
-0@@ -17,6 +17,7 @@ static int zformat = 0;#define HELP_COMMAND "help"#define GET_COMMAND "get"#define SET_COMMAND "set" +#define UNSET_COMMAND "unset"#define COMMAND_PARSE_ERROR "command_parse_error"static void print_word (const char *word,int start)@@ -445,6 +446,99 @@ cleanup:return res; } +/** +* 'unset' command, version 1. +* +* Positional arguments should be of the form: +* +* [0] scope ("system", "global", "local", or "worktree") +* [1] config key +* [2] config value +* [3*] match ("regex", "fixed-value") +* [4*] value regex OR value string +* +* [N*] indicates optional parameters that are not needed. +*/ +static int unset_command_1 (struct repository *repo, +const char *prefix, +char *data, +size_t data_len) +{ +int res =0 , err =0 , flags =0 ; +enum config_scope scope =CONFIG_SCOPE_UNKNOWN ; +char *token =NULL , *key =NULL , *value_pattern =NULL ; +size_t token_len;
builtin/config-batch.c cleanup: · line 480
Quoted 10 lines
builtin/config-batch.c +10
-0+struct config_location_options locopts =CONFIG_LOCATION_OPTIONS_INIT ; + +if (!parse_token (&data, &data_len, &token, &err) || err) + goto parse_error; + +if (parse_scope (token, &scope) || + scope ==CONFIG_SCOPE_UNKNOWN || + scope ==CONFIG_SCOPE_SUBMODULE || + scope ==CONFIG_SCOPE_COMMAND ) + goto parse_error;
I think this should get braces since it has many lines? Or maybe multi-line conditionals are excempt.
Quoted 4 lines
builtin/config-batch.c +3
-0+ +if (!parse_token (&data, &data_len, &key, &err) || err) + goto parse_error;
Re: [PATCH 06/11] docs: add design doc for config-batch
In reply to Derrick Stolee via GitGitGadgetDocumentation/technical/config-batch.adoc line 36
Quoted 5 lines · Derrick Stolee via GitGitGadget
[snip]
Documentation/technical/config-batch.adoc new file +4
-0+Current commands +---------------- + +See the documentation in linkgit::config-batch[1] for the latest set of
s/linkgit::config-batch[1]/linkgit:git-config-batch[1]/
Documentation/technical/config-batch.adoc line 48
Quoted 13 lines
Documentation/technical/config-batch.adoc new file +6
-0+available commands and their protocols. + +Future commands +--------------- + +The following modes of `git config` are not currently available ascommands
Documentation/technical/config-batch.adoc new file +6
-0+in `git config-batch`, but are planned for future integration: + +`git config list [--<scope>]`:: + Getting all values, regardless of config key, would require a + multi-valued output similar to the `help` command. This tool will + likely assume advanced options such as `--show-origin`.
What does it mean to assume options?
Quoted 27 lines
Documentation/technical/config-batch.adoc new file +8
-0+ +`git config set [--<scope>] <key> <value>`:: + It will be desirable to set a config key at a given scope as a + single value, replacing the current value at that scope, if it + exists and is a single value. A `set` command could satisfy this + purpose. + +`git config set --all [<value-pattern>|--fixed-value=<fixedvalue>]<key> <value>`::
(quoted diff) +1
-0+ When replacing multiple values, it may be necessary to have adifferent
Documentation/technical/config-batch.adoc new file +7
-0+ output describing the places those values were set, so it may need to + be implemented via a `set-all` command to differentiate from a `set` + command. + +`git config unset <key>`:: + +`git config unset --all [<value-pattern>|--fixed-value=<fixedvalue>]<key>`::
Documentation/technical/config-batch.adoc new file +6
-1+ +`git config get --all --rexexp <key-pattern> [<value-options>]`:: + +`--replace-all` option:: + +`--type=<type>` option:: --gitgitgadget
Re: [PATCH 05/11] config-batch: add NUL-terminated I/O format
In reply to Derrick Stolee via GitGitGadgetDocumentation/git-config-batch.adoc multiple configuration values, the `git config-batch` command allows a · line 28
Quoted 7 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
[snip]Documentation/git-config-batch.adoc +5
-0+OPTIONS +------- + +`-z`:: + If specified, then use the NUL-terminated input and output
It seems to me that using the imperative mood for options might be preferred now. Like:
Use NUL-terminated input and output...
Documentation/git-config-batch.adoc These are the commands that are currently understood: · line 59
Quoted 11 lines
[snip]
Documentation/git-config-batch.adoc +3
-3- line provides the count of possible commands via `help count <N>`. - The next `<N>` lines are of the form `help <command> <version>` + line provides the count of possible commands via `help 1 count <N>`. + The next `<N>` lines are of the form `help 1 <command> <version>` to state that this Git version supports that `<command>` at version `<version>`. Note that the same command may have multiple available versions. + -Here is the currentl output of the help text at the latest version: +Here is the current output of the help text at the latest version:
Innocent intra-series typofix.
Documentation/git-config-batch.adoc get 1 missing <key> [<value-pattern>|<value>] · line 117
Quoted 11 lines
+
------------
help 1 count 2Documentation/git-config-batch.adoc +4
-0@@ -102,6 +111,48 @@ get 1 missing <key> [<value-pattern>|<value>] where `<value-pattern>` or `<value>` is only supplied if provided in the command. +NUL-Terminated Format +~~~~~~~~~~~~~~~~~~~~~ + +When `-z` is given, the protocol changes in some structural ways.
It might flow better with “Option -z changes the protocol...” ?
I don’t know how usual it is to say “Option <x>”.
builtin/config-batch.c static const char *const builtin_config_batch_usage[] = { · line 26
Quoted 7 lines
[snip]
builtin/config-batch.c +6
-0+static void print_word (const char *word,int start) +{ +if (zformat) { +printf ("%" PRIu32":%s" , (uint32_t )strlen (word), word); +fputc (0 , stdout); + }else if (start)
All of the arms should get braces here.
Quoted 6 lines
builtin/config-batch.c +5
-0+printf ("%s" , word); + else + printf(" %s", word); +} +
Re: [PATCH 09/11] config-batch: add 'set' v1 command
In reply to Kristoffer HaugsbakkQuoted 18 lines · Kristoffer Haugsbakk
Review
Quoted 13 lines · Derrick Stolee via GitGitGadget
[snip]
(quoted diff) +6
-0+git-config-batch(1) +=================== + +NAME +---- +git-config-batch - Get and set options using machine-parseableinterface
(quoted diff) +5
-0+ + +SYNOPSIS +-------- +[verse]There’s work lead by Jean-Noël Avila to use
[synopsis]instead of[verse].[1] Would it make sense to start off with that?
Quoted 7 lines
† 1: E.g.
acffc5e9(doc: convert git-remote to synopsis style, 2025-12-20)Review
Quoted 5 lines
+'git config-batch' <options>
(quoted diff) +3
-0+ +DESCRIPTION +-----------
(sorry for replying to the wrong email)
Re: [PATCH 09/11] config-batch: add 'set' v1 command
In reply to Derrick Stolee via GitGitGadgetDocumentation/git-config-batch.adoc get 1 missing <key> [<value-pattern>|<value>] · line 125
Quoted 13 lines · Derrick Stolee via GitGitGadget
[snip]
+setversion 1::Documentation/git-config-batch.adoc +11
-0+ The `set` command writes a single key-value pair to a config + file. It specifies which file by a `<scope>` parameter from + among `system`, `global`, `local`, and `worktree`. The `<key>` + is the next positional argument. The remaining data in the line + is provided as the `<value>` to assign the config. ++ +------------ +set 1 <scope> <key> <value> +------------ ++ +These uses will match the behavior of `git config --set --<scope> <key>
--set doesn’t exist. I think you meant set.
Quoted 14 lines
[snip]
config.c +13
-0+int location_options_set_scope (struct config_location_options *opts, +enum config_scope scope) +{ +switch (scope) { +case CONFIG_SCOPE_SYSTEM : + opts->use_system_config =1 ; +break ; + +case CONFIG_SCOPE_GLOBAL : + opts->use_global_config =1 ; +break ; + +case CONFIG_SCOPE_LOCAL :
config.c int lookup_config(const char **mapping, int nr_mapping, const char *var) · line 3619
Quoted 10 lines
config.c +10
-0+ opts->use_local_config =1 ; +break ; + +case CONFIG_SCOPE_WORKTREE : + opts->use_worktree_config =1 ; +break ; + +default : +return -1 ; + }
Is there support for a user-provided file? (git config --file=...)
Quoted 5 lines
config.c +4
-0+ +return 0 ; +} +
Re: [PATCH 01/11] config-batch: basic boilerplate of new builtin
In reply to Derrick Stolee via GitGitGadgetLe 04/02/2026 à 15:19, Derrick Stolee via GitGitGadget a écrit :
Quoted 48 lines
From: Derrick Stolee <[email protected]>
Later changes will document, implement, and test this new builtin. For now, this serves as the latest example of the minimum boilerplate to introduce a new builtin.
Recently, we updated the comment in
builtin.habout how to create a new builtin, but failed to mention the required change to meson.build files for some CI builds to pass. Fix that oversight.Signed-off-by Derrick Stolee <[email protected]>--- .gitignore | 1 + Documentation/git-config-batch.adoc | 24 +++++++++++++++++++++++ Documentation/meson.build | 1 + Makefile | 1 + builtin.h | 7 +++++++ builtin/config-batch.c | 30 +++++++++++++++++++++++++++++ command-list.txt | 1 + git.c | 1 + meson.build | 1 + t/meson.build | 1 + t/t1312-config-batch.sh | 12 ++++++++++++ 11 files changed, 80 insertions(+) create mode 100644 Documentation/git-config-batch.adoc create mode 100644 builtin/config-batch.c create mode 100755 t/t1312-config-batch.sh.gitignore +1
-0@@ -44,6 +44,7 @@ /git-commit-graph /git-commit-tree /git-config +/git-config-batch /git-count-objects /git-credential /git-credential-cacheDocumentation/git-config-batch.adoc new file +2
-0@@ -0,0 +1,24 @@ +git-config-batch(1) +===================
Documentation/git-config-batch.adoc line 12
Quoted 10 lines
Documentation/git-config-batch.adoc new file +10
-0+ +NAME +---- +git-config-batch - Get and set options using machine-parseable interface + + +SYNOPSIS +-------- +[verse] +'git config-batch' <options>
For this new manual page, please use the synopsis style:
[synopsis]
git config-batch <options>
Thanks
Re: [PATCH 02/11] config-batch: create parse loop and unknown command
In reply to Derrick Stolee via GitGitGadgetLe 04/02/2026 à 15:19, Derrick Stolee via GitGitGadget a écrit :
Quoted 28 lines
From: Derrick Stolee <[email protected]>
As we build new features in the config-batch command, we define the plaintext protocol with line-by-line output and responses. To think to the future, we make sure that the protocol has a clear way to respond to an unknown command or an unknown version of that command.
As some commands will allow the final argument to contain spaces or even be able to parse "\ " as a non-split token, we only provide the remaining line as data.
Signed-off-by Derrick Stolee <[email protected]>--- Documentation/git-config-batch.adoc | 23 ++++- builtin/config-batch.c | 133 +++++++++++++++++++++++++++- t/t1312-config-batch.sh | 19 +++- 3 files changed, 170 insertions(+), 5 deletions(-)Documentation/git-config-batch.adoc +1
-1@@ -13,7 +13,28 @@ SYNOPSIS DESCRIPTION ----------- -TODO +Tools frequently need to change their behavior based on values stored in
Documentation/git-config-batch.adoc SYNOPSIS · line 26
Quoted 10 lines
Documentation/git-config-batch.adoc +10
-0+Git's configuration files. These files may have complicated conditions +for including extra files, so it is difficult to produce an independent +parser. To avoid executing multiple processes to discover or modify +multiple configuration values, the `git config-batch` command allows a +single process to handle multiple requests using a machine-parseable +interface across `stdin` and `stdout`. + +PROTOCOL +-------- +By default, the protocol uses line feeds (`LF`) to signal the end of a
Characters are typefaced as placeholders: _LF_
Documentation/git-config-batch.adoc SYNOPSIS · line 38
Quoted 11 lines
Documentation/git-config-batch.adoc +10
-0+command over `stdin` or a response over `stdout`. + +The protocol will be extended in the future, and consumers should be +resilient to older Git versions not understanding the latest command +set. Thus, if the Git version includes the `git config-batch` builtin +but doesn't understand an input command, it will return a single line +response: + +``` +unknown_command LF> +```
This is Markdown. For Asciidoc, use code block:
----unknown_command LF
----
Re: [PATCH 03/11] config-batch: implement get v1
In reply to Derrick Stolee via GitGitGadgetLe 04/02/2026 à 15:19, Derrick Stolee via GitGitGadget a écrit :
Quoted 33 lines
From: Derrick Stolee <[email protected]>
The 'get' command for the 'git config-batch' builtin is the first command and is currently at version 1. It returns at most one value, the same as 'git config --get <key>' with optional value-based filtering.
The documentation and tests detail the specifics of how to format requests of this format and how to parse the results.
Future versions could consider multi-valued responses or regex-based key matching.
For the sake of incremental exploration of the potential in the 'git config-batch' command, this is the only implementation being presented in the first patch series.
Future extensions could include a '-z' parameter that uses NUL bytes in the command and output format to allow for spaces or newlines in the input or newlines in the output.
Signed-off-by Derrick Stolee <[email protected]>--- Documentation/git-config-batch.adoc | 53 +++++- builtin/config-batch.c | 251 +++++++++++++++++++++++++++- config.h | 3 + t/t1312-config-batch.sh | 101 +++++++++++ 4 files changed, 405 insertions(+), 3 deletions(-)Documentation/git-config-batch.adoc +0
-0
Documentation/git-config-batch.adoc set. Thus, if the Git version includes the `git config-batch` builtin · line 38
Quoted 9 lines
Documentation/git-config-batch.adoc +3
-2@@ -32,9 +32,58 @@ set. Thus, if the Git version includes the `git config-batch` builtin but doesn't understand an input command, it will return a single line response: -``` +------------ unknown_command LF -``` +------------ +
OK, the change to Asciidoc code block is done here. Would it be possible to push it up at the introduction of these lines?
Documentation/git-config-batch.adoc set. Thus, if the Git version includes the `git config-batch` builtin · line 43
Quoted 5 lines
Documentation/git-config-batch.adoc +5
-0+These are the commands that are currently understood: + +`get` version 1:: + The `get` command searches the config key-value pairs within a + given `<scope>` for values that match the fixed `<key>` and
The rendering of these is correct due to the synopsis formatter, but we usually prefer to use the direct formatting for placeholders: _<scope>_, _<key>_,…
Documentation/git-config-batch.adoc set. Thus, if the Git version includes the `git config-batch` builtin · line 53
Quoted 10 lines
Documentation/git-config-batch.adoc +10
-0+ filters the resulting value based on an optional `<value-filter>`. + This can either be a regex or a fixed value. The command format + is one of the following formats: ++ +------------ +get 1 <scope> <key> +get 1 <scope> <key> arg:regex <value-pattern> +get 1 <scope> <key> arg:fixed-value <value> +------------ ++
If you are using synopsis style in the block, with the upcoming change of synopsis style block[1], you can format it:
[synopsis]
------------
get 1 <scope> <key>
get 1 <scope> <key> arg:regex <value-pattern>
get 1 <scope> <key> arg:fixed-value <value>
------------
Documentation/git-config-batch.adoc set. Thus, if the Git version includes the `git config-batch` builtin · line 66
Quoted 13 lines
Documentation/git-config-batch.adoc +13
-0+The `<scope>` value can be one of `inherited`, `system`, `global`, +`local`, `worktree`, `submodule`, or `command`. If `inherited`, then all +config key-value pairs will be considered regardless of scope. Otherwise, +only the given scope will be considered. ++ +If no optional arguments are given, then the value will not be filtered +by any pattern matching. If `arg:regex` is specified, then the rest of +the line is considered a single string, `<value-pattern>`, and is +interpreted as a regular expression for matching against stored values, +similar to specifying a value to `get config --get <key> "<value-pattern>"`. +If `arg:fixed-value` is specified, then the rest of the line is +considered a single string, `<value>`, and is checked for an exact +match against the key-value pairs, simmilar to `git config --get <key>
similar
Documentation/git-config-batch.adoc set. Thus, if the Git version includes the `git config-batch` builtin · line 68
Quoted 2 lines
Documentation/git-config-batch.adoc +2
-0+--fixed-value "<value>"`. ++
Here I would use a sub definition list for each matching type, instead of long running description paragraph.
optional arguments can be specified:
no optional arguments;;
the value will not be filteredby any pattern matching. arg:regex <value-pattern>;;<value-pattern> is interpreted as a regular expression for matching against stored values, similar to specifying a value to get config --get <key> "<value-pattern>".arg:fixed-value <value>;;<value> is checked for an exact match against the key-value pairs, similar to git config --get <key>.
Documentation/git-config-batch.adoc set. Thus, if the Git version includes the `git config-batch` builtin · line 69
Quoted 1 line
Documentation/git-config-batch.adoc +1
-0+At mmost one key-value pair is returned, that being the last key-value
At most
Quoted 5 lines
Documentation/git-config-batch.adoc +5
-0+pair in the standard config order by scope and sequence within each scope. ++ +If a key-value pair is found, then the following output is given: ++ +------------
Documentation/git-config-batch.adoc set. Thus, if the Git version includes the `git config-batch` builtin · line 84
Quoted 10 lines
Documentation/git-config-batch.adoc +10
-0+get 1 found <key> <scope> <value> +------------ ++ +If no matching key-value pair is found, then the following output is +given: ++ +------------ +get 1 missing <key> [<value-pattern>|<value>] +------------ ++
Please also apply synopsis block style.
Documentation/git-config-batch.adoc set. Thus, if the Git version includes the `git config-batch` builtin · line 89
Quoted 5 lines
Documentation/git-config-batch.adoc +2
-0+where `<value-pattern>` or `<value>` is only supplied if provided in +the command. SEE ALSO --------
Re: [PATCH 04/11] config-batch: create 'help' command
In reply to Derrick Stolee via GitGitGadgetLe 04/02/2026 à 15:19, Derrick Stolee via GitGitGadget a écrit :
Quoted 18 lines
From: Derrick Stolee <[email protected]>
Tools that use the 'git config-batch' tool will want to know which commands are available in the current Git version. Having a 'help' command assists greatly to give a clear set of available commands and their versions.
Signed-off-by Derrick Stolee <[email protected]>--- Documentation/git-config-batch.adoc | 17 +++++++++++++++ builtin/config-batch.c | 32 +++++++++++++++++++++++++++++ t/t1312-config-batch.sh | 13 ++++++++++++ 3 files changed, 62 insertions(+)Documentation/git-config-batch.adoc +0
-0
Documentation/git-config-batch.adoc unknown_command LF · line 42
Quoted 5 lines
Documentation/git-config-batch.adoc +2
-0@@ -38,6 +38,23 @@ unknown_command LF These are the commands that are currently understood: +`help` version 1:: + The `help` command lists the currently-available commands in
The boilerplat text "The help command" is not very useful to the reader. The new usage is to directly state the command in imperative mood:
List the currently...
Documentation/git-config-batch.adoc unknown_command LF · line 48
Quoted 6 lines
Documentation/git-config-batch.adoc +6
-0+ this version of Git. The output is multi-line, but the first + line provides the count of possible commands via `help count <N>`. + The next `<N>` lines are of the form `help <command> <version>` + to state that this Git version supports that `<command>` at + version `<version>`. Note that the same command may have multiple + available versions.
Placeholder punning to keep a consistency between the command and its description. Good!
Documentation/git-config-batch.adoc unknown_command LF · line 50
Quoted 2 lines
Documentation/git-config-batch.adoc +2
-0++ +Here is the currentl output of the help text at the latest version:
current
It may not be wise to talk about the "latest version". If the manpages and the git command are out of sync (the user compiles her own git version, but does not update the man pages), this may be confusing.
Is this specification of version critical to the understanding?
Quoted 10 lines
Documentation/git-config-batch.adoc +7
-0++ +------------ +help 1 count 2 +help 1 help 1 +help 1 get 1 +------------ + `get` version 1:: The `get` command searches the config key-value pairs within a given `<scope>` for values that match the fixed `<key>` and
Re: [PATCH 05/11] config-batch: add NUL-terminated I/O format
In reply to Derrick Stolee via GitGitGadgetLe 04/02/2026 à 15:19, Derrick Stolee via GitGitGadget a écrit :
Quoted 30 lines
From: Derrick Stolee <[email protected]>
When using automated tools, it is critical to allow for input/output formats that include special characters such as spaces and newlines. While the existing protocol for 'git config-batch' is human-readable and has some capacity for some spaces in certain positions, it is not available for spaces in the config key or newlines in the config values.
Add the '-z' option to signal the use of NUL-terminated strings. To understand where commands end regardless of potential future formats, use two NUL bytes in a row to terminate a command. To allow for empty string values, each token is provided in a <length>:<value> format, making "0:" the empty string value.
Update the existing 'help' and 'get' commands to match this format. Create helper methods that make it easy to parse and print in both formats simultaneously.
Signed-off-by Derrick Stolee <[email protected]>--- Documentation/git-config-batch.adoc | 57 ++++++++- builtin/config-batch.c | 188 +++++++++++++++++++++++++--- t/t1312-config-batch.sh | 69 ++++++++++ 3 files changed, 293 insertions(+), 21 deletions(-)Documentation/git-config-batch.adoc +0
-0
Documentation/git-config-batch.adoc multiple configuration values, the `git config-batch` command allows a · line 28
Quoted 8 lines
Documentation/git-config-batch.adoc +5
-0@@ -21,6 +21,15 @@ multiple configuration values, the `git config-batch` command allows a single process to handle multiple requests using a machine-parseable interface across `stdin` and `stdout`. +OPTIONS +------- + +`-z`:: + If specified, then use the NUL-terminated input and output
This boilerplate preliminary does not convey information, it is simpler to just jump to the action performed by the option:
Use the _NUL_-terminated input and output…
Quoted 11 lines
Documentation/git-config-batch.adoc +4
-0+ format instead of the space and newline format. This format is + useful when the strings involved may include spaces or newlines. + See PROTOCOL for more details. + PROTOCOL -------- By default, the protocol uses line feeds (`LF`) to signal the end of a@@ -41,13 +50,13 @@ These are the commands that are currently understood: `help` version 1:: The `help` command lists the currently-available commands in this version of Git. The output is multi-line, but the first
Documentation/git-config-batch.adoc These are the commands that are currently understood: · line 59
Quoted 10 lines
Documentation/git-config-batch.adoc +3
-3- line provides the count of possible commands via `help count <N>`. - The next `<N>` lines are of the form `help <command> <version>` + line provides the count of possible commands via `help 1 count <N>`. + The next `<N>` lines are of the form `help 1 <command> <version>` to state that this Git version supports that `<command>` at version `<version>`. Note that the same command may have multiple available versions. + -Here is the currentl output of the help text at the latest version: +Here is the current output of the help text at the latest version:
OK, the typo was fixed here.
Quoted 11 lines
Documentation/git-config-batch.adoc +4
-0+ ------------ help 1 count 2@@ -102,6 +111,48 @@ get 1 missing <key> [<value-pattern>|<value>] where `<value-pattern>` or `<value>` is only supplied if provided in the command. +NUL-Terminated Format +~~~~~~~~~~~~~~~~~~~~~ + +When `-z` is given, the protocol changes in some structural ways.
Documentation/git-config-batch.adoc get 1 missing <key> [<value-pattern>|<value>] · line 127
Quoted 10 lines
Documentation/git-config-batch.adoc +10
-0+ +First, each command is terminated with two NUL bytes, providing a clear +boundary between commands regardless of future possibilities of new +command formats. + +Second, any time that a space _would_ be used to partition tokens in a +command, a NUL byte is used instead. Further, each token is prefixed +with `<N>:` where `<N>` is a decimal representation of the length of +the string between the `:` and the next NUL byte. Any disagreement in +these lengths is treated as a parsing error. This use of a length does
I thought this length encoding was used to allow _NUL_ in the config values. But here it is considered a parse error.
Quoted 31 lines
Documentation/git-config-batch.adoc +28
-0+imply that "`0:`" is the representation of an empty string, if relevant. + +The decimal representation must have at most five numerals, thus the +maximum length of a string token can have 99999 characters. + +For example, the `get` command, version 1, could have any of the +following forms: + +------------ +3:get NUL 1:1 NUL 5:local NUL 14:key.with space NUL NUL +3:get NUL 1:1 NUL 9:inherit NUL 8:test.key NUL 9:arg:regex NUL 6:.*\ .* NUL NUL +3:get NUL 1:1 NUL 6:global NUL 8:test.key NUL 15:arg:fixed-value NUL 3:a b NUL NUL +------------ + +The output is modified similarly, such as the following output examples, +as if the input has a parse error, a valid `help` command, a `get` +command that had a match, and a `get` command that did not match. + +------------ +15:unknown_command NUL NUL +4:help NUL 1:1 NUL 5:count NUL 1:2 NUL NUL +4:help NUL 1:1 NUL 4:help NUL 1:1 NUL NUL +4:help NUL 1:1 NUL 3:get NUL 1:1 NUL NUL +3:get NUL 1:1 NUL 5:found NUL 8:test.key NUL 5:value NUL NUL +3:get NUL 1:1 NUL 7:missing NUL 8:test.key NUL NUL +------------ + + SEE ALSO -------- linkgit:git-config[1]
Re: [PATCH 09/11] config-batch: add 'set' v1 command
In reply to Derrick Stolee via GitGitGadgetLe 04/02/2026 à 15:20, Derrick Stolee via GitGitGadget a écrit :
Quoted 20 lines
From: Derrick Stolee <[email protected]>
This new command is intended for single-value assignments to a specific chosen scope. More complicated versions of the 'git config set' command will be incorporated into future commands.
Signed-off-by Derrick Stolee <[email protected]>--- Documentation/git-config-batch.adoc | 24 ++++++++ builtin/config-batch.c | 71 ++++++++++++++++++++++ config.c | 27 +++++++++ config.h | 3 + t/t1312-config-batch.sh | 94 ++++++++++++++++++++++++++++- 5 files changed, 217 insertions(+), 2 deletions(-)Documentation/git-config-batch.adoc +0
-0
Documentation/git-config-batch.adoc get 1 missing <key> [<value-pattern>|<value>] · line 115
Quoted 5 lines
Documentation/git-config-batch.adoc +2
-0@@ -111,6 +111,30 @@ get 1 missing <key> [<value-pattern>|<value>] where `<value-pattern>` or `<value>` is only supplied if provided in the command. +`set` version 1:: + The `set` command writes a single key-value pair to a config
Please use direct imperative form.
Documentation/git-config-batch.adoc get 1 missing <key> [<value-pattern>|<value>] · line 125
Quoted 10 lines
Documentation/git-config-batch.adoc +10
-0+ file. It specifies which file by a `<scope>` parameter from + among `system`, `global`, `local`, and `worktree`. The `<key>` + is the next positional argument. The remaining data in the line + is provided as the `<value>` to assign the config. ++ +------------ +set 1 <scope> <key> <value> +------------ ++ +These uses will match the behavior of `git config --set --<scope> <key>
This "--<scope>" form is new in the synopsis grammar. Would we just cite all alternatives or use a "normal" placeholder _<scope>_ ?
Documentation/git-config-batch.adoc get 1 missing <key> [<value-pattern>|<value>] · line 137
Quoted 12 lines
Documentation/git-config-batch.adoc +12
-0+<value>`. Note that replacing all values with the `--all` option or +matching specific value patterns are not supported by this command. ++ +The response of these commands will include a `success` message if the +value is written as expected or `failed` if an unexpected failure +occurs: ++ +------------ +set 1 success <scope> <key> <value> +set 1 failed <scope> <key> <value> +------------ +
Please use synopsis style block for these too.
Quoted 3 lines
Documentation/git-config-batch.adoc +0
-0NUL-Terminated Format ~~~~~~~~~~~~~~~~~~~~~
Re: [PATCH 04/11] config-batch: create 'help' command
In reply to Jean-Noël AvilaQuoted 9 lines · Jean-Noël Avila
Le 04/02/2026 à 15:19, Derrick Stolee via GitGitGadget a écrit :
Quoted 2 lines
++
+Here is the currentl output of the help text at the latest version:It may not be wise to talk about the "latest version". If the manpages and the git command are out of sync (the user compiles her own git version, but does not update the man pages), this may be confusing.
Is this specification of version critical to the understanding?
It's important to talk about how to build tools that work against versions that don't match the current documentation.
If you build something against v2.58.0 and we deprecate them in v2.59.0 and delete them in v3.0.0, then the tool should know that the command isn't available (and maybe it was replaced with a v2).
The same holds for someone who builds against v3.0.0 but their tool is run against v2.58.0 and the feature they want to use isn't available (or not at the same version).
Thanks,
-Stolee
Re: [PATCH 06/11] docs: add design doc for config-batch
In reply to Kristoffer HaugsbakkQuoted 8 lines · Kristoffer Haugsbakk
Documentation/technical/config-batch.adoc line 48
Quoted 4 lines · Derrick Stolee via GitGitGadget
+
git config list [--<scope>]::Documentation/technical/config-batch.adoc new file +3
-0+ Getting all values, regardless of config key, would require a + multi-valued output similar to the `help` command. This tool will + likely assume advanced options such as `--show-origin`.What does it mean to assume options?
I mean that since we expect to have the output parsed by tools, then we will probably want the maximum amount of information by default. Maybe --show-origin isn't as helpful as --show-scope.
Thanks,
-Stolee
Re: [PATCH 09/11] config-batch: add 'set' v1 command
In reply to Kristoffer HaugsbakkQuoted 16 lines · Kristoffer Haugsbakk
Documentation/git-config-batch.adoc get 1 missing <key> [<value-pattern>|<value>] · line 125
Quoted 13 lines · Derrick Stolee via GitGitGadget
[snip]
+setversion 1::Documentation/git-config-batch.adoc +11
-0+ The `set` command writes a single key-value pair to a config + file. It specifies which file by a `<scope>` parameter from + among `system`, `global`, `local`, and `worktree`. The `<key>` + is the next positional argument. The remaining data in the line + is provided as the `<value>` to assign the config. ++ +------------ +set 1 <scope> <key> <value> +------------ ++ +These uses will match the behavior of `git config --set --<scope> <key>
--setdoesn’t exist. I think you meantset.
You're right. Also git config --<scope> <key> is the older mode. I'm not always catching myself using the old format. Or inventing a mixed-up one that never existed!
Quoted 1 line
Is there support for a user-provided file? (
git config --file=...)
Not at the moment. It's worth thinking about what that interface would be within this query model.
My initial feeling is that we wouldn't want to accept arbitrary filenames on a per-command basis, but instead would want to provide an alternate file in the command-line arguments as a replacement for the local config.
Thanks,
-Stolee
Quoted 5 lines · Derrick Stolee
Quoted 4 lines · brian m. carlson
Quoted 1 line · Derrick Stolee via GitGitGadget
* Is this a worthwhile feature to add to Git?
Quoted 7 lines
Quoted 5 lines
Git LFS has the same needs, but I believe it can use
git config -l -zto do that and parse the config options itself. If this is just config fetching, I'm not sure of the additional utility that such a feature would add. If that interface _almost_ meets your needs, could we add functionality there instead of a new interface?This is a good suggestion to look into as a potentially-easier solution.
After digging into this, I realized that GCM uses Git's --type=<X> option, which doesn't work with 'git config list'!
Please see a new RFC [1] that adds that feature, though it is a "breaking" change from previous behavior.
[1] https://lore.kernel.org/git/[email protected]/
There's still some awkwardness in my GCM prototype, as it can require three commands to query all the types (no type, path, and book) that are needed. I found that the slowest queries are using the path type, but only because they are the most frequent ones.
This awkwardness does make me think both of these things:
1. I can get performance boosts to GCM faster by the RFC in [1].
2. Using 'git config list' isn't sufficient to minimize multiple processes.
For now, I'll put _this_ RFC down for a little while to pursue those easier gains. I'll come back again and consider all of the big-picture considerations, including:
* Make this a subcommand of 'git config'.
* Make this a server that can serve multiple client processes.
* Ensure that all "complicated" options are accounted for.
Thanks,
-Stolee