Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
/git-commit-graph
Copy link

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):

"Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:

> 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>
> ---

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".

Copy link

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):

On 2/4/2026 6:23 PM, Junio C Hamano wrote:
> "Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:
> 
>> 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>
>> ---
> 
> 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. 

> 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

Copy link

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):

On Thu, Feb 5, 2026, at 15:17, Derrick Stolee wrote:
>>>[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...

> 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)).

>
>> 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.

> 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

Copy link

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):

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]

Copy link

Choose a reason for hiding this comment

The 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
Expand Down
24 changes: 24 additions & 0 deletions Documentation/git-config-batch.adoc
Original file line number Diff line number Diff line change
@@ -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>

Copy link

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):

"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)))
		;

Copy link

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):

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]

Copy link

Choose a reason for hiding this comment

The 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
-----------
TODO

SEE ALSO
--------
linkgit:git-config[1]

GIT
---
Part of the linkgit:git[1] suite
1 change: 1 addition & 0 deletions Documentation/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -167,6 +173,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix, struct repositor
int 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);
Expand Down
30 changes: 30 additions & 0 deletions builtin/config-batch.c
Original file line number Diff line number Diff line change
@@ -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;
}
1 change: 1 addition & 0 deletions command-list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions git.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
1 change: 1 addition & 0 deletions t/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
12 changes: 12 additions & 0 deletions t/t1312-config-batch.sh
Original file line number Diff line number Diff line change
@@ -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