From: "Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: gitster@pobox.com, Derrick Stolee <stolee@gmail.com>,
Derrick Stolee <stolee@gmail.com>
Subject: [PATCH 09/11] config-batch: add 'set' v1 command
Date: Wed, 04 Feb 2026 14:20:01 +0000 [thread overview]
Message-ID: <fdeef536f649bec811e8335d1c7151be8e352ff0.1770214803.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2033.git.1770214803.gitgitgadget@gmail.com>
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
+ 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
~~~~~~~~~~~~~~~~~~~~~
diff --git a/builtin/config-batch.c b/builtin/config-batch.c
index 9829b16c6f..373b0cad47 100644
--- a/builtin/config-batch.c
+++ b/builtin/config-batch.c
@@ -16,6 +16,7 @@ static int zformat = 0;
#define UNKNOWN_COMMAND "unknown_command"
#define HELP_COMMAND "help"
#define GET_COMMAND "get"
+#define SET_COMMAND "set"
#define COMMAND_PARSE_ERROR "command_parse_error"
static void print_word(const char *word, int start)
@@ -379,6 +380,71 @@ cleanup:
return res;
}
+
+/**
+ * 'set' command, version 1.
+ *
+ * Positional arguments should be of the form:
+ *
+ * [0] scope ("system", "global", "local", or "worktree")
+ * [1] config key
+ * [2] config value
+ */
+static int set_command_1(struct repository *repo,
+ const char *prefix,
+ char *data,
+ size_t data_len)
+{
+ int res = 0, err = 0;
+ enum config_scope scope = CONFIG_SCOPE_UNKNOWN;
+ char *token = NULL, *key = NULL, *value = NULL;
+ 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;
+
+ if (!parse_token(&data, &data_len, &key, &err) || err)
+ goto parse_error;
+
+ /* Use the remaining data as the value string. */
+ if (!zformat)
+ value = data;
+ else {
+ parse_token(&data, &data_len, &value, &err);
+ if (err)
+ goto parse_error;
+ }
+
+ if (location_options_set_scope(&locopts, scope))
+ goto parse_error;
+ location_options_init(repo, &locopts, prefix);
+
+ res = repo_config_set_in_file_gently(repo, locopts.source.file,
+ key, NULL, value);
+
+ if (res)
+ res = emit_response(SET_COMMAND, "1", "failure",
+ scope_str(scope), key, value, NULL);
+ else
+ res = emit_response(SET_COMMAND, "1", "success",
+ scope_str(scope), key, value, NULL);
+
+ goto cleanup;
+
+parse_error:
+ res = command_parse_error(SET_COMMAND);
+
+cleanup:
+ location_options_release(&locopts);
+ return res;
+}
+
struct command {
const char *name;
command_fn fn;
@@ -396,6 +462,11 @@ static struct command commands[] = {
.fn = get_command_1,
.version = 1,
},
+ {
+ .name = SET_COMMAND,
+ .fn = set_command_1,
+ .version = 1,
+ },
/* unknown_command must be last. */
{
.name = "",
diff --git a/config.c b/config.c
index 9f1a7b45cf..fa72234750 100644
--- a/config.c
+++ b/config.c
@@ -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)
diff --git a/config.h b/config.h
index 6663964977..f6432c1ec2 100644
--- a/config.h
+++ b/config.h
@@ -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);
diff --git a/t/t1312-config-batch.sh b/t/t1312-config-batch.sh
index f7a74ddc2c..40f6f90ef2 100755
--- a/t/t1312-config-batch.sh
+++ b/t/t1312-config-batch.sh
@@ -47,9 +47,10 @@ test_expect_success 'help command' '
echo "help 1" >in &&
cat >expect <<-\EOF &&
- help 1 count 2
+ help 1 count 3
help 1 help 1
help 1 get 1
+ help 1 set 1
EOF
git config-batch >out <in &&
@@ -63,9 +64,10 @@ test_expect_success 'help -z' '
EOF
cat >expect <<-\EOF &&
- 4:help 1:1 5:count 1:2
+ 4:help 1:1 5:count 1:3
4:help 1:1 4:help 1:1
4:help 1:1 3:get 1:1
+ 4:help 1:1 3:set 1:1
15:unknown_command
EOF
@@ -205,4 +207,92 @@ test_expect_success 'get config with -z' '
test_cmp expect out
'
+test_expect_success 'set config by scope' '
+ test_when_finished git config remove-section test.set &&
+ GIT_CONFIG_SYSTEM=system-config-file &&
+ GIT_CONFIG_NOSYSTEM=0 &&
+ GIT_CONFIG_GLOBAL=global-config-file &&
+ export GIT_CONFIG_SYSTEM &&
+ export GIT_CONFIG_NOSYSTEM &&
+ export GIT_CONFIG_GLOBAL &&
+
+ cat >in <<-\EOF &&
+ set 1 system test.set.system system
+ set 1 global test.set.global global
+ set 1 local test.set.local local with spaces
+ set 1 worktree test.set.worktree worktree
+ set 1 submodule test.set.submodule submodule
+ set 1 command test.set.command command
+ set 1 inherited test.set.inherited inherited
+ EOF
+
+ cat >expect <<-\EOF &&
+ set 1 success system test.set.system system
+ set 1 success global test.set.global global
+ set 1 success local test.set.local local with spaces
+ set 1 success worktree test.set.worktree worktree
+ command_parse_error set
+ command_parse_error set
+ command_parse_error set
+ EOF
+
+ git config-batch <in >out 2>err &&
+
+ test_must_be_empty err &&
+ test_cmp expect out &&
+
+ cat >expect-values <<-EOF &&
+ file:system-config-file system
+ file:global-config-file global
+ file:.git/config local with spaces
+ file:.git/config.worktree worktree
+ EOF
+
+ git config get --show-origin --regexp --all test.set.* >values &&
+ test_cmp expect-values values
+'
+
+test_expect_success 'set config by scope with -z' '
+ test_when_finished git config remove-section test.set &&
+ GIT_CONFIG_SYSTEM=system-config-file &&
+ GIT_CONFIG_NOSYSTEM=0 &&
+ GIT_CONFIG_GLOBAL=global-config-file &&
+ export GIT_CONFIG_SYSTEM &&
+ export GIT_CONFIG_NOSYSTEM &&
+ export GIT_CONFIG_GLOBAL &&
+
+ cat >in <<-\EOF &&
+ 3:set NUL 1:1 NUL 6:system NUL 15:test.set.system NUL 6:system NUL NUL
+ 3:set NUL 1:1 NUL 6:global NUL 15:test.set.global NUL 6:global NUL NUL
+ 3:set NUL 1:1 NUL 5:local NUL 14:test.set.local NUL 17:local with spaces NUL NUL
+ 3:set NUL 1:1 NUL 8:worktree NUL 17:test.set.worktree NUL 8:worktree NUL NUL
+ 3:set NUL 1:1 NUL 9:submodule NUL 18:test.set.submodule NUL 9:submodule NUL NUL
+ 3:set NUL 1:1 NUL 7:command NUL 16:test.set.command NUL 7:command NUL NUL
+ 3:set NUL 1:1 NUL 9:inherited NUL 18:test.set.inherited NUL 9:inherited NUL NUL
+ EOF
+
+ cat >expect <<-\EOF &&
+ 3:set 1:1 7:success 6:system 15:test.set.system 6:system
+ 3:set 1:1 7:success 6:global 15:test.set.global 6:global
+ 3:set 1:1 7:success 5:local 14:test.set.local 17:local with spaces
+ 3:set 1:1 7:success 8:worktree 17:test.set.worktree 8:worktree
+ 19:command_parse_error 3:set
+ 19:command_parse_error 3:set
+ 19:command_parse_error 3:set
+ EOF
+
+ test_zformat git config-batch -z >out <in &&
+ test_cmp expect out &&
+
+ cat >expect-values <<-EOF &&
+ file:system-config-file system
+ file:global-config-file global
+ file:.git/config local with spaces
+ file:.git/config.worktree worktree
+ EOF
+
+ git config get --show-origin --regexp --all test.set.* >values &&
+ test_cmp expect-values values
+'
+
test_done
--
gitgitgadget
next prev parent reply other threads:[~2026-02-04 14:20 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-04 14:19 [PATCH 00/11] [RFC] config-batch: a new builtin for tools querying config Derrick Stolee via GitGitGadget
2026-02-04 14:19 ` [PATCH 01/11] config-batch: basic boilerplate of new builtin Derrick Stolee via GitGitGadget
2026-02-04 23:23 ` Junio C Hamano
2026-02-05 14:17 ` Derrick Stolee
2026-02-05 17:26 ` Kristoffer Haugsbakk
2026-02-05 17:29 ` Kristoffer Haugsbakk
2026-02-06 4:11 ` Jean-Noël Avila
2026-02-04 14:19 ` [PATCH 02/11] config-batch: create parse loop and unknown command Derrick Stolee via GitGitGadget
2026-02-04 23:26 ` Junio C Hamano
2026-02-05 17:30 ` Kristoffer Haugsbakk
2026-02-06 4:15 ` Jean-Noël Avila
2026-02-04 14:19 ` [PATCH 03/11] config-batch: implement get v1 Derrick Stolee via GitGitGadget
2026-02-06 4:41 ` Jean-Noël Avila
2026-02-04 14:19 ` [PATCH 04/11] config-batch: create 'help' command Derrick Stolee via GitGitGadget
2026-02-06 4:49 ` Jean-Noël Avila
2026-02-10 4:20 ` Derrick Stolee
2026-02-04 14:19 ` [PATCH 05/11] config-batch: add NUL-terminated I/O format Derrick Stolee via GitGitGadget
2026-02-05 17:44 ` Kristoffer Haugsbakk
2026-02-06 4:58 ` Jean-Noël Avila
2026-02-04 14:19 ` [PATCH 06/11] docs: add design doc for config-batch Derrick Stolee via GitGitGadget
2026-02-05 17:38 ` Kristoffer Haugsbakk
2026-02-10 4:22 ` Derrick Stolee
2026-02-04 14:19 ` [PATCH 07/11] config: extract location structs from builtin Derrick Stolee via GitGitGadget
2026-02-04 14:20 ` [PATCH 08/11] config-batch: pass prefix through commands Derrick Stolee via GitGitGadget
2026-02-04 14:20 ` Derrick Stolee via GitGitGadget [this message]
2026-02-05 17:21 ` [PATCH 09/11] config-batch: add 'set' v1 command Kristoffer Haugsbakk
2026-02-05 18:58 ` Kristoffer Haugsbakk
2026-02-05 19:01 ` Kristoffer Haugsbakk
2026-02-10 4:25 ` Derrick Stolee
2026-02-06 5:04 ` Jean-Noël Avila
2026-02-04 14:20 ` [PATCH 10/11] t1312: create read/write test Derrick Stolee via GitGitGadget
2026-02-04 14:20 ` [PATCH 11/11] config-batch: add unset v1 command Derrick Stolee via GitGitGadget
2026-02-05 17:36 ` Kristoffer Haugsbakk
2026-02-04 23:04 ` [PATCH 00/11] [RFC] config-batch: a new builtin for tools querying config Junio C Hamano
2026-02-05 14:10 ` Derrick Stolee
2026-02-05 0:04 ` brian m. carlson
2026-02-05 13:52 ` Derrick Stolee
2026-02-10 4:49 ` Derrick Stolee
2026-02-05 14:45 ` Phillip Wood
2026-02-05 17:20 ` Kristoffer Haugsbakk
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=fdeef536f649bec811e8335d1c7151be8e352ff0.1770214803.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=stolee@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox