-
Notifications
You must be signed in to change notification settings - Fork 171
[RFC] config-batch: a new builtin for tools querying config #2033
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
c4dab06
ecd26a0
3de1bba
d5e0c32
33faa3f
014e959
4be089a
60443c5
fdeef53
cf4f054
59d19fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,6 +44,7 @@ | |
| /git-commit-graph | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email): On Wed, Feb 4, 2026, at 15:19, Derrick Stolee via GitGitGadget wrote:
>[snip]
> +git-config-batch(1)
> +===================
> +
> +NAME
> +----
> +git-config-batch - Get and set options using machine-parseable
> interface
> +
> +
> +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)
> +'git config-batch' <options>
> +
> +DESCRIPTION
> +-----------
>[snip]There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jean-Noël Avila wrote on the Git mailing list (how to reply to this email): Le 04/02/2026 à 15:19, Derrick Stolee via GitGitGadget a écrit :
> From: Derrick Stolee <stolee@gmail.com>
>
> 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.
>
> Signed-off-by: Derrick Stolee <stolee@gmail.com>
> ---
> .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
>
> diff --git a/.gitignore b/.gitignore
> index 78a45cb5be..42640b5e24 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -44,6 +44,7 @@
> /git-commit-graph
> /git-commit-tree
> /git-config
> +/git-config-batch
> /git-count-objects
> /git-credential
> /git-credential-cache
> diff --git a/Documentation/git-config-batch.adoc b/Documentation/git-config-batch.adoc
> new file mode 100644
> index 0000000000..dfa0bd83e2
> --- /dev/null
> +++ b/Documentation/git-config-batch.adoc
> @@ -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>
For this new manual page, please use the synopsis style:
[synopsis]
git config-batch <options>
Thanks |
||
| /git-commit-tree | ||
| /git-config | ||
| /git-config-batch | ||
| /git-count-objects | ||
| /git-credential | ||
| /git-credential-cache | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,214 @@ | ||
| git-config-batch(1) | ||
| =================== | ||
|
|
||
| NAME | ||
| ---- | ||
| git-config-batch - Get and set options using machine-parseable interface | ||
|
|
||
|
|
||
| SYNOPSIS | ||
| -------- | ||
| [verse] | ||
| 'git config-batch' <options> | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Junio C Hamano wrote on the Git mailing list (how to reply to this email): "Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:
> +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.
> +#define COMMAND_COUNT ((size_t)(sizeof(commands) / sizeof(*commands)))
Isn't this ARRAY_SIZE(commands)?
> + while (!(res = process_command(repo)));
Please write an empty statement on its own line, i.e.
while (!(res = process_command(repo)))
;
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email): On Wed, Feb 4, 2026, at 15:19, Derrick Stolee via GitGitGadget wrote:
>[snip]
> 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`.
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.
> +
>[snip]There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jean-Noël Avila wrote on the Git mailing list (how to reply to this email): Le 04/02/2026 à 15:19, Derrick Stolee via GitGitGadget a écrit :
> From: Derrick Stolee <stolee@gmail.com>
>
> 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 <stolee@gmail.com>
> ---
> Documentation/git-config-batch.adoc | 23 ++++-
> builtin/config-batch.c | 133 +++++++++++++++++++++++++++-
> t/t1312-config-batch.sh | 19 +++-
> 3 files changed, 170 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/git-config-batch.adoc b/Documentation/git-config-batch.adoc
> index dfa0bd83e2..9ca04b0c1e 100644
> --- a/Documentation/git-config-batch.adoc
> +++ b/Documentation/git-config-batch.adoc
> @@ -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
Characters are typefaced as placeholders: _LF_
> +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
----
|
||
| DESCRIPTION | ||
| ----------- | ||
| 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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email): On Wed, Feb 4, 2026, at 15:19, Derrick Stolee via GitGitGadget wrote:
> From: Derrick Stolee <stolee@gmail.com>
>[snip]
> +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...
See: https://lore.kernel.org/git/bcd6fcd1190fe21c667b5253a4a33b833e658609.1769462744.git.gitgitgadget@gmail.com/
>[snip]
> - 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.
> +
> ------------
> 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.
It might flow better with “Option `-z` changes the protocol...” ?
I don’t know how usual it is to say “Option <x>”.
>[snip]
> +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.
> + printf("%s", word);
> + else
> + printf(" %s", word);
> +}
> +
>[snip]There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jean-Noël Avila wrote on the Git mailing list (how to reply to this email): Le 04/02/2026 à 15:19, Derrick Stolee via GitGitGadget a écrit :
> From: Derrick Stolee <stolee@gmail.com>
>
> 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 <stolee@gmail.com>
> ---
> Documentation/git-config-batch.adoc | 57 ++++++++-
> builtin/config-batch.c | 188 +++++++++++++++++++++++++---
> t/t1312-config-batch.sh | 69 ++++++++++
> 3 files changed, 293 insertions(+), 21 deletions(-)
>
> diff --git a/Documentation/git-config-batch.adoc b/Documentation/git-config-batch.adoc
> index 1fff68a13c..3c9a3bb763 100644
> --- a/Documentation/git-config-batch.adoc
> +++ b/Documentation/git-config-batch.adoc
> @@ -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…
> + 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:
OK, the typo was fixed here.
> +
> ------------
> 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
I thought this length encoding was used to allow _NUL_ in the config
values. But here it is considered a parse error.
> +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] |
||
| 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 | ||
| 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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jean-Noël Avila wrote on the Git mailing list (how to reply to this email): Le 04/02/2026 à 15:19, Derrick Stolee via GitGitGadget a écrit :
> From: Derrick Stolee <stolee@gmail.com>
>
> 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 <stolee@gmail.com>
> ---
> 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(-)
>
> diff --git a/Documentation/git-config-batch.adoc b/Documentation/git-config-batch.adoc
> index 9ca04b0c1e..31dd42f481 100644
> --- a/Documentation/git-config-batch.adoc
> +++ b/Documentation/git-config-batch.adoc
> @@ -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?
> +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>_,…
> + 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>
------------
> +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
> +--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>`.
> +At mmost one key-value pair is returned, that being the last key-value
At most
> +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>]
> +------------
> ++
Please also apply synopsis block style.
> +where `<value-pattern>` or `<value>` is only supplied if provided in
> +the command.
>
> SEE ALSO
> --------
[1]:
https://lore.kernel.org/git/6a2b94e720862fa07fe9463ebf7f7beaa9a1ccd4.1770351146.git.gitgitgadget@gmail.com/T/#u |
||
| response: | ||
|
|
||
| ------------ | ||
| unknown_command LF | ||
| ------------ | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jean-Noël Avila wrote on the Git mailing list (how to reply to this email): Le 04/02/2026 à 15:19, Derrick Stolee via GitGitGadget a écrit :
> From: Derrick Stolee <stolee@gmail.com>
>
> 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 <stolee@gmail.com>
> ---
> Documentation/git-config-batch.adoc | 17 +++++++++++++++
> builtin/config-batch.c | 32 +++++++++++++++++++++++++++++
> t/t1312-config-batch.sh | 13 ++++++++++++
> 3 files changed, 62 insertions(+)
>
> diff --git a/Documentation/git-config-batch.adoc b/Documentation/git-config-batch.adoc
> index 31dd42f481..1fff68a13c 100644
> --- a/Documentation/git-config-batch.adoc
> +++ b/Documentation/git-config-batch.adoc
> @@ -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...
> + 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!
> ++
> +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?
> ++
> +------------
> +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 |
||
| 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 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 current 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 | ||
| 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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email): On Wed, Feb 4, 2026, at 15:19, Derrick Stolee via GitGitGadget wrote:
>[snip]
> +git-config-batch(1)
> +===================
> +
> +NAME
> +----
> +git-config-batch - Get and set options using machine-parseable
> interface
> +
> +
> +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)
> +'git config-batch' <options>
> +
> +DESCRIPTION
> +-----------
>[snip]There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email): On Thu, Feb 5, 2026, at 18:21, Kristoffer Haugsbakk wrote:
> On Wed, Feb 4, 2026, at 15:19, Derrick Stolee via GitGitGadget wrote:
>>[snip]
>> +git-config-batch(1)
>> +===================
>> +
>> +NAME
>> +----
>> +git-config-batch - Get and set options using machine-parseable
>> interface
>> +
>> +
>> +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)
>
>> +'git config-batch' <options>
>> +
>> +DESCRIPTION
>> +-----------
>>[snip]
(sorry for replying to the wrong email)There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email): On Wed, Feb 4, 2026, at 15:20, Derrick Stolee via GitGitGadget wrote:
>[snip]
> +`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>
`--set` doesn’t exist. I think you meant `set`.
>[snip]
> +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;
> + }
Is there support for a user-provided file? (`git config --file=...`)
> +
> + return 0;
> +}
> +
>[snip]There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jean-Noël Avila wrote on the Git mailing list (how to reply to this email): Le 04/02/2026 à 15:20, Derrick Stolee via GitGitGadget a écrit :
> From: Derrick Stolee <stolee@gmail.com>
>
> 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 <stolee@gmail.com>
> ---
> 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(-)
>
> diff --git a/Documentation/git-config-batch.adoc b/Documentation/git-config-batch.adoc
> index 3c9a3bb763..feec85c4ef 100644
> --- a/Documentation/git-config-batch.adoc
> +++ b/Documentation/git-config-batch.adoc
> @@ -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.
> + 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>_ ?
> +<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.
> NUL-Terminated Format
> ~~~~~~~~~~~~~~~~~~~~~
> |
||
| 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> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email): On Wed, Feb 4, 2026, at 15:20, Derrick Stolee via GitGitGadget wrote:
> From: Derrick Stolee <stolee@gmail.com>
>
> 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`.
>
> Signed-off-by: Derrick Stolee <stolee@gmail.com>
> ---
> Documentation/git-config-batch.adoc | 28 ++++++++
> builtin/config-batch.c | 99 +++++++++++++++++++++++++++++
> t/t1312-config-batch.sh | 61 ++++++++++++++++--
> 3 files changed, 181 insertions(+), 7 deletions(-)
>
> diff --git a/Documentation/git-config-batch.adoc
> b/Documentation/git-config-batch.adoc
> index feec85c4ef..bdfd872d65 100644
> --- a/Documentation/git-config-batch.adoc
> +++ b/Documentation/git-config-batch.adoc
> @@ -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>`
Same as above.
> +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
> ~~~~~~~~~~~~~~~~~~~~~
>
> diff --git a/builtin/config-batch.c b/builtin/config-batch.c
> index 373b0cad47..25a942ba61 100644
> --- a/builtin/config-batch.c
> +++ b/builtin/config-batch.c
> @@ -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;
> + 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.
> +
> + if (!parse_token(&data, &data_len, &key, &err) || err)
> + goto parse_error;
>[snip] |
||
| ------------ | ||
|
|
||
| `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 | ||
| ~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| 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] | ||
|
|
||
| GIT | ||
| --- | ||
| Part of the linkgit:git[1] suite | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| Git Config-Batch Design Notes | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email): On Wed, Feb 4, 2026, at 15:19, Derrick Stolee via GitGitGadget wrote:
>[snip]
> +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]/
> +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`.
What does it mean to assume options?
> +
> +`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::
> --
> gitgitgadget |
||
| ============================= | ||
|
|
||
| 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:: | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Junio C Hamano wrote on the Git mailing list (how to reply to this email):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Derrick Stolee wrote on the Git mailing list (how to reply to this email):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email):