git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 09/11] config-batch: add 'set' v1 command

From
Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
Date
Feb 4, 2026, 14:20 UTC
Message-ID
<fdeef536f649bec811e8335d1c7151be8e352ff0.1770214803.git.gitgitgadget@gmail.com>
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
Previous: Derrick Stolee via GitGitGadgetNext: Kristoffer Haugsbakk
Message 25 of 40 in “[RFC] config-batch: a new builtin for tools querying config”
  1. 00/11 [RFC] config-batch: a new builtin for tools querying configDerrick Stolee via GitGitGadget, Feb 4, 2026
  2. 01/11 config-batch: basic boilerplate of new builtinDerrick Stolee via GitGitGadget, Feb 4, 2026
  3. Junio C HamanoFeb 4, 2026
  4. Derrick StoleeFeb 5, 2026
  5. Kristoffer HaugsbakkFeb 5, 2026
  6. Kristoffer HaugsbakkFeb 5, 2026
  7. Jean-Noël AvilaFeb 6, 2026
  8. 02/11 config-batch: create parse loop and unknown commandDerrick Stolee via GitGitGadget, Feb 4, 2026
  9. Junio C HamanoFeb 4, 2026
  10. Kristoffer HaugsbakkFeb 5, 2026
  11. Jean-Noël AvilaFeb 6, 2026
  12. 03/11 config-batch: implement get v1Derrick Stolee via GitGitGadget, Feb 4, 2026
  13. Jean-Noël AvilaFeb 6, 2026
  14. 04/11 config-batch: create 'help' commandDerrick Stolee via GitGitGadget, Feb 4, 2026
  15. Jean-Noël AvilaFeb 6, 2026
  16. Derrick StoleeFeb 10, 2026
  17. 05/11 config-batch: add NUL-terminated I/O formatDerrick Stolee via GitGitGadget, Feb 4, 2026
  18. Kristoffer HaugsbakkFeb 5, 2026
  19. Jean-Noël AvilaFeb 6, 2026
  20. 06/11 docs: add design doc for config-batchDerrick Stolee via GitGitGadget, Feb 4, 2026
  21. Kristoffer HaugsbakkFeb 5, 2026
  22. Derrick StoleeFeb 10, 2026
  23. 07/11 config: extract location structs from builtinDerrick Stolee via GitGitGadget, Feb 4, 2026
  24. 08/11 config-batch: pass prefix through commandsDerrick Stolee via GitGitGadget, Feb 4, 2026
  25. 09/11 config-batch: add 'set' v1 commandDerrick Stolee via GitGitGadget, Feb 4, 2026
  26. Kristoffer HaugsbakkFeb 5, 2026
  27. Kristoffer HaugsbakkFeb 5, 2026
  28. Kristoffer HaugsbakkFeb 5, 2026
  29. Derrick StoleeFeb 10, 2026
  30. Jean-Noël AvilaFeb 6, 2026
  31. 10/11 t1312: create read/write testDerrick Stolee via GitGitGadget, Feb 4, 2026
  32. 11/11 config-batch: add unset v1 commandDerrick Stolee via GitGitGadget, Feb 4, 2026
  33. Kristoffer HaugsbakkFeb 5, 2026
  34. Junio C HamanoFeb 4, 2026
  35. Derrick StoleeFeb 5, 2026
  36. brian m. carlsonFeb 5, 2026
  37. Derrick StoleeFeb 5, 2026
  38. Derrick StoleeFeb 10, 2026
  39. Phillip WoodFeb 5, 2026
  40. Kristoffer HaugsbakkFeb 5, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.