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

[PATCH v2 09/13] builtin/config: introduce "unset" subcommand

From
Patrick Steinhardt <ps@pks.im>
Date
Mar 11, 2024, 23:21 UTC
Message-ID
<c8a44b6189ad247d7b5a4b6e490ffa89c8461ddd.1710198711.git.ps@pks.im>
In-Reply-To
<cover.1710198711.git.ps@pks.im>

Introduce a new "unset" subcommand to git-config(1). Please refer to preceding commits regarding the motivation behind this change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/git-config.txt | 25 ++++++++++++--------
 builtin/config.c             | 38 +++++++++++++++++++++++++++++++
 t/t1300-config.sh            | 44 ++++++++++++++++++++++++------------
 3 files changed, 82 insertions(+), 25 deletions(-)
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index e540f41b56..42e659ae63 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -12,8 +12,7 @@ SYNOPSIS
 'git config list' [<file-option>] [<display-option>] [--includes]
 'git config get' [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>
 'git config set' [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>
-'git config' [<file-option>] [--fixed-value] --unset <name> [<value-pattern>]
-'git config' [<file-option>] [--fixed-value] --unset-all <name> [<value-pattern>]
+'git config unset' [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>
 'git config' [<file-option>] --rename-section <old-name> <new-name>
 'git config' [<file-option>] --remove-section <name>
 'git config' [<file-option>] --get-colorbool <name> [<stdout-is-tty>]
@@ -87,6 +86,12 @@ set::
 	`--value=` will replace all config options whose values match the given
 	pattern.
 
+unset::
+	Unset value for one or more config options. By default, this command
+	refuses to unset multi-valued keys. Passing `--all` will unset all
+	multi-valued config options, whereas `--value` will unset all config
+	options whose values match the given pattern.
+
 [[OPTIONS]]
 OPTIONS
 -------
@@ -178,12 +183,6 @@ See also <<FILES>>.
 --rename-section::
 	Rename the given section to a new name.
 
---unset::
-	Remove the line matching the key from config file.
-
---unset-all::
-	Remove all lines matching the key from config file.
-
 --fixed-value::
 	When used with the `value-pattern` argument, treat `value-pattern` as
 	an exact string instead of a regular expression. This will restrict
@@ -312,6 +311,12 @@ recommended to migrate to the new syntax.
 --add <name> <value>::
 	Replaced by `git config set --append <name> <value>`.
 
+--unset <name> [<value-pattern>]::
+	Replaced by `git config unset [--value=<pattern>] <name>`.
+
+--unset-all <name> [<value-pattern>]::
+	Replaced by `git config unset [--value=<pattern>] --all <name>`.
+
 CONFIGURATION
 -------------
 `pager.config` is only respected when listing configuration, i.e., when
@@ -359,7 +364,7 @@ values of a key from all files will be used.
 
 By default, options are only written to the repository specific
 configuration file. Note that this also affects options like `set`
-and `--unset`. *'git config' will only ever change one file at a time*.
+and `unset`. *'git config' will only ever change one file at a time*.
 
 You can limit which configuration sources are read from or written to by
 specifying the path of a file with the `--file` option, or by specifying a
@@ -510,7 +515,7 @@ This makes sure that only the key/value pair for kernel.org is replaced.
 To delete the entry for renames, do
 
 ------------
-% git config --unset diff.renames
+% git config unset diff.renames
 ------------
 
 If you want to delete an entry for a multivar (like core.gitproxy above),
diff --git a/builtin/config.c b/builtin/config.c
index 18f6dc69d5..e62a09061f 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -19,6 +19,7 @@ static const char *const builtin_config_usage[] = {
 	N_("git config list [<file-option>] [<display-option>] [--includes]"),
 	N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
 	N_("git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+	N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
 	NULL
 };
 
@@ -37,6 +38,11 @@ static const char *const builtin_config_set_usage[] = {
 	NULL
 };
 
+static const char *const builtin_config_unset_usage[] = {
+	N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+	NULL
+};
+
 static char *key;
 static regex_t *key_regexp;
 static const char *value_pattern;
@@ -903,10 +909,42 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)
 	return ret;
 }
 
+static int cmd_config_unset(int argc, const char **argv, const char *prefix)
+{
+	const char *value_pattern = NULL;
+	int flags = 0;
+	struct option opts[] = {
+		CONFIG_LOCATION_OPTIONS,
+		OPT_GROUP(N_("Filter")),
+		OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
+		OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
+		OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+	check_write();
+	check_argc(argc, 1, 1);
+
+	if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
+		die(_("--fixed-value only applies with 'value-pattern'"));
+
+	handle_config_location(prefix);
+
+	if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)
+		return git_config_set_multivar_in_file_gently(given_config_source.file,
+							      argv[0], NULL, value_pattern,
+							      flags);
+	else
+		return git_config_set_in_file_gently(given_config_source.file, argv[0], NULL);
+}
+
 static struct option builtin_subcommand_options[] = {
 	OPT_SUBCOMMAND("list", &subcommand, cmd_config_list),
 	OPT_SUBCOMMAND("get", &subcommand, cmd_config_get),
 	OPT_SUBCOMMAND("set", &subcommand, cmd_config_set),
+	OPT_SUBCOMMAND("unset", &subcommand, cmd_config_unset),
 	OPT_END(),
 };
 
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 2c2d97e0e9..06d34b1d6a 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -22,6 +22,8 @@ legacy)
 	mode_get_regexp="--get-regexp"
 	mode_set=""
 	mode_replace_all="--replace-all"
+	mode_unset="--unset"
+	mode_unset_all="--unset-all"
 	;;
 subcommands)
 	mode_prefix=""
@@ -30,6 +32,8 @@ subcommands)
 	mode_get_regexp="get --regexp --all --show-names"
 	mode_set="set"
 	mode_replace_all="set --all"
+	mode_unset="unset"
+	mode_unset_all="unset --all"
 	;;
 *)
 	echo "unknown mode $mode" >&2
@@ -150,7 +154,7 @@ foo = bar
 EOF
 
 test_expect_success 'unset with cont. lines' '
-	git config --unset beta.baz
+	git config ${mode_unset} beta.baz
 '
 
 cat > expect <<\EOF
@@ -177,7 +181,7 @@ EOF
 cp .git/config .git/config2
 
 test_expect_success 'multiple unset' '
-	git config --unset-all beta.haha
+	git config ${mode_unset_all} beta.haha
 '
 
 cat > expect << EOF
@@ -263,7 +267,7 @@ noIndent= sillyValue ; 'nother silly comment
 	nonewline = wow
 EOF
 test_expect_success 'unset' '
-	git config --unset beta.haha &&
+	git config ${mode_unset} beta.haha &&
 	test_cmp expect .git/config
 '
 
@@ -319,11 +323,11 @@ test_expect_success 'multivar replace' '
 '
 
 test_expect_success 'ambiguous unset' '
-	test_must_fail git config --unset nextsection.nonewline
+	test_must_fail git config ${mode_unset} nextsection.nonewline
 '
 
 test_expect_success 'invalid unset' '
-	test_must_fail git config --unset somesection.nonewline
+	test_must_fail git config ${mode_unset} somesection.nonewline
 '
 
 cat > expect << EOF
@@ -337,7 +341,12 @@ noIndent= sillyValue ; 'nother silly comment
 EOF
 
 test_expect_success 'multivar unset' '
-	git config --unset nextsection.nonewline "wow3$" &&
+	case "$mode" in
+	legacy)
+		git config --unset nextsection.nonewline "wow3$";;
+	subcommands)
+		git config unset --value="wow3$" nextsection.nonewline;;
+	esac &&
 	test_cmp expect .git/config
 '
 
@@ -1888,7 +1897,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
 	# please be careful when you update the above variable
 	EOF
 
-	git config --unset section.key &&
+	git config ${mode_unset} section.key &&
 	test_cmp expect .git/config &&
 
 	cat >.git/config <<-\EOF &&
@@ -1901,7 +1910,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
 	[next-section]
 	EOF
 
-	git config --unset section.key &&
+	git config ${mode_unset} section.key &&
 	test_cmp expect .git/config &&
 
 	q_to_tab >.git/config <<-\EOF &&
@@ -1911,7 +1920,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
 	[two]
 	key = true
 	EOF
-	git config --unset two.key &&
+	git config ${mode_unset} two.key &&
 	! grep two .git/config &&
 
 	q_to_tab >.git/config <<-\EOF &&
@@ -1921,7 +1930,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
 	[one]
 	key = true
 	EOF
-	git config --unset-all one.key &&
+	git config ${mode_unset_all} one.key &&
 	test_line_count = 0 .git/config &&
 
 	q_to_tab >.git/config <<-\EOF &&
@@ -1931,7 +1940,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
 	[two]
 	Qkey = true
 	EOF
-	git config --unset two.key &&
+	git config ${mode_unset} two.key &&
 	grep two .git/config &&
 
 	q_to_tab >.git/config <<-\EOF &&
@@ -1943,7 +1952,7 @@ test_expect_success '--unset last key removes section (except if commented)' '
 	[TWO "subsection"]
 	[one]
 	EOF
-	git config --unset two.subsection.key &&
+	git config ${mode_unset} two.subsection.key &&
 	test "not [two subsection]" = "$(git config ${mode_get} one.key)" &&
 	test_line_count = 3 .git/config
 '
@@ -1955,7 +1964,7 @@ test_expect_success '--unset-all removes section if empty & uncommented' '
 	key = value2
 	EOF
 
-	git config --unset-all section.key &&
+	git config ${mode_unset_all} section.key &&
 	test_line_count = 0 .git/config
 '
 
@@ -2479,8 +2488,8 @@ test_expect_success 'refuse --fixed-value for incompatible actions' '
 	test_must_fail git config ${mode_prefix}get --file=config --fixed-value dev.null &&
 	test_must_fail git config ${mode_get_all} --file=config --fixed-value dev.null &&
 	test_must_fail git config ${mode_get_regexp} --file=config --fixed-value "dev.*" &&
-	test_must_fail git config --file=config --fixed-value --unset dev.null &&
-	test_must_fail git config --file=config --fixed-value --unset-all dev.null
+	test_must_fail git config ${mode_unset} --file=config --fixed-value dev.null &&
+	test_must_fail git config ${mode_unset_all} --file=config --fixed-value dev.null
 '
 
 test_expect_success '--fixed-value uses exact string matching' '
@@ -2510,6 +2519,11 @@ test_expect_success '--fixed-value uses exact string matching' '
 	git config --file=config --fixed-value --unset fixed.test "$META" &&
 	test_must_fail git config ${mode_get} --file=config fixed.test &&
 
+	cp initial config &&
+	test_must_fail git config unset --file=config --value="$META" fixed.test &&
+	git config unset --file=config --fixed-value --value="$META" fixed.test &&
+	test_must_fail git config ${mode_get} --file=config fixed.test &&
+
 	cp initial config &&
 	test_must_fail git config --file=config --unset-all fixed.test "$META" &&
 	git config --file=config --fixed-value --unset-all fixed.test "$META" &&
-- 
2.44.0
Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 41 of 116 in “builtin/config: introduce subcommands”
  1. 0/8 builtin/config: introduce subcommandsPatrick Steinhardt, Mar 6, 2024
  2. 1/8 builtin/config: move option array aroundPatrick Steinhardt, Mar 6, 2024
  3. 2/8 builtin/config: move "fixed-value" option to correct groupPatrick Steinhardt, Mar 6, 2024
  4. 3/8 builtin/config: use `OPT_CMDMODE()` to specify modesPatrick Steinhardt, Mar 6, 2024
  5. Taylor BlauMar 6, 2024
  6. Patrick SteinhardtMar 7, 2024
  7. 4/8 builtin/config: move modes into separate functionsPatrick Steinhardt, Mar 6, 2024
  8. 5/8 builtin/config: track subcommands by actionPatrick Steinhardt, Mar 6, 2024
  9. Jean-Noël AVILAMar 6, 2024
  10. Patrick SteinhardtMar 7, 2024
  11. Taylor BlauMar 7, 2024
  12. Patrick SteinhardtMar 7, 2024
  13. 6/8 builtin/config: introduce subcommandsPatrick Steinhardt, Mar 6, 2024
  14. Karthik NayakMar 6, 2024
  15. Patrick SteinhardtMar 7, 2024
  16. 7/8 t1300: exercise both old- and new-style modesPatrick Steinhardt, Mar 6, 2024
  17. 8/8 Documentation/git-config: update to new-style syntaxPatrick Steinhardt, Mar 6, 2024
  18. Eric SunshineMar 7, 2024
  19. Patrick SteinhardtMar 7, 2024
  20. Junio C HamanoMar 6, 2024
  21. Taylor BlauMar 6, 2024
  22. Junio C HamanoMar 6, 2024
  23. Taylor BlauMar 7, 2024
  24. Dragan SimicMar 7, 2024
  25. Patrick SteinhardtMar 7, 2024
  26. Junio C HamanoMar 7, 2024
  27. Kristoffer HaugsbakkMar 6, 2024
  28. 00/13 builtin/config: introduce subcommandsPatrick Steinhardt, Mar 11, 2024
  29. 01/13 builtin/config: move option array aroundPatrick Steinhardt, Mar 11, 2024
  30. 02/13 builtin/config: move "fixed-value" option to correct groupPatrick Steinhardt, Mar 11, 2024
  31. 03/13 builtin/config: use `OPT_CMDMODE()` to specify modesPatrick Steinhardt, Mar 11, 2024
  32. 04/13 builtin/config: pull out function to handle config locationPatrick Steinhardt, Mar 11, 2024
  33. 05/13 builtin/config: pull out function to handle `--null`Patrick Steinhardt, Mar 11, 2024
  34. 06/13 builtin/config: introduce "list" subcommandPatrick Steinhardt, Mar 11, 2024
  35. Eric SunshineMar 13, 2024
  36. Patrick SteinhardtMar 27, 2024
  37. 07/13 builtin/config: introduce "get" subcommandPatrick Steinhardt, Mar 11, 2024
  38. Eric SunshineMar 13, 2024
  39. Patrick SteinhardtMar 27, 2024
  40. 08/13 builtin/config: introduce "set" subcommandPatrick Steinhardt, Mar 11, 2024
  41. 09/13 builtin/config: introduce "unset" subcommandPatrick Steinhardt, Mar 11, 2024
  42. 10/13 builtin/config: introduce "rename-section" subcommandPatrick Steinhardt, Mar 11, 2024
  43. 11/13 builtin/config: introduce "remove-section" subcommandPatrick Steinhardt, Mar 11, 2024
  44. 12/13 builtin/config: introduce "edit" subcommandPatrick Steinhardt, Mar 11, 2024
  45. 13/13 builtin/config: display subcommand helpPatrick Steinhardt, Mar 11, 2024
  46. 00/13 builtin/config: introduce subcommandsPatrick Steinhardt, Mar 27, 2024
  47. 01/13 builtin/config: move option array aroundPatrick Steinhardt, Mar 27, 2024
  48. 02/13 builtin/config: move "fixed-value" option to correct groupPatrick Steinhardt, Mar 27, 2024
  49. 03/13 builtin/config: use `OPT_CMDMODE()` to specify modesPatrick Steinhardt, Mar 27, 2024
  50. 04/13 builtin/config: pull out function to handle config locationPatrick Steinhardt, Mar 27, 2024
  51. 05/13 builtin/config: pull out function to handle `--null`Patrick Steinhardt, Mar 27, 2024
  52. 06/13 builtin/config: introduce "list" subcommandPatrick Steinhardt, Mar 27, 2024
  53. 07/13 builtin/config: introduce "get" subcommandPatrick Steinhardt, Mar 27, 2024
  54. 08/13 builtin/config: introduce "set" subcommandPatrick Steinhardt, Mar 27, 2024
  55. 09/13 builtin/config: introduce "unset" subcommandPatrick Steinhardt, Mar 27, 2024
  56. 10/13 builtin/config: introduce "rename-section" subcommandPatrick Steinhardt, Mar 27, 2024
  57. 11/13 builtin/config: introduce "remove-section" subcommandPatrick Steinhardt, Mar 27, 2024
  58. 12/13 builtin/config: introduce "edit" subcommandPatrick Steinhardt, Mar 27, 2024
  59. 13/13 builtin/config: display subcommand helpPatrick Steinhardt, Mar 27, 2024
  60. Eric SunshineMar 27, 2024
  61. Patrick SteinhardtMar 27, 2024
  62. 00/14 builtin/config: introduce subcommandsPatrick Steinhardt, May 3, 2024
  63. 01/14 config: clarify memory ownership when preparing comment stringsPatrick Steinhardt, May 3, 2024
  64. Kristoffer HaugsbakkMay 3, 2024
  65. 02/14 builtin/config: move option array aroundPatrick Steinhardt, May 3, 2024
  66. 03/14 builtin/config: move "fixed-value" option to correct groupPatrick Steinhardt, May 3, 2024
  67. Karthik NayakMay 3, 2024
  68. Patrick SteinhardtMay 6, 2024
  69. 04/14 builtin/config: use `OPT_CMDMODE()` to specify modesPatrick Steinhardt, May 3, 2024
  70. 05/14 builtin/config: pull out function to handle config locationPatrick Steinhardt, May 3, 2024
  71. 06/14 builtin/config: pull out function to handle `--null`Patrick Steinhardt, May 3, 2024
  72. 07/14 builtin/config: introduce "list" subcommandPatrick Steinhardt, May 3, 2024
  73. Karthik NayakMay 3, 2024
  74. rsbecker@nexbridge.comMay 3, 2024
  75. Junio C HamanoMay 3, 2024
  76. Patrick SteinhardtMay 6, 2024
  77. Junio C HamanoMay 6, 2024
  78. rsbecker@nexbridge.comMay 6, 2024
  79. Dragan SimicMay 6, 2024
  80. Kristoffer HaugsbakkMay 7, 2024
  81. Git 3.0?Junio C Hamano, May 6, 2024
  82. Patrick SteinhardtMay 7, 2024
  83. Patrick SteinhardtMay 7, 2024
  84. Patrick SteinhardtMay 6, 2024
  85. Karthik NayakMay 6, 2024
  86. 08/14 builtin/config: introduce "get" subcommandPatrick Steinhardt, May 3, 2024
  87. 09/14 builtin/config: introduce "set" subcommandPatrick Steinhardt, May 3, 2024
  88. 10/14 builtin/config: introduce "unset" subcommandPatrick Steinhardt, May 3, 2024
  89. 11/14 builtin/config: introduce "rename-section" subcommandPatrick Steinhardt, May 3, 2024
  90. 12/14 builtin/config: introduce "remove-section" subcommandPatrick Steinhardt, May 3, 2024
  91. 13/14 builtin/config: introduce "edit" subcommandPatrick Steinhardt, May 3, 2024
  92. 14/14 builtin/config: display subcommand helpPatrick Steinhardt, May 3, 2024
  93. Dragan SimicMay 3, 2024
  94. Junio C HamanoMay 3, 2024
  95. 00/14 builtin/config: introduce subcommandsPatrick Steinhardt, May 6, 2024
  96. 01/14 config: clarify memory ownership when preparing comment stringsPatrick Steinhardt, May 6, 2024
  97. 02/14 builtin/config: move option array aroundPatrick Steinhardt, May 6, 2024
  98. 03/14 builtin/config: move "fixed-value" option to correct groupPatrick Steinhardt, May 6, 2024
  99. 04/14 builtin/config: use `OPT_CMDMODE()` to specify modesPatrick Steinhardt, May 6, 2024
  100. 05/14 builtin/config: pull out function to handle config locationPatrick Steinhardt, May 6, 2024
  101. 06/14 builtin/config: pull out function to handle `--null`Patrick Steinhardt, May 6, 2024
  102. 07/14 builtin/config: introduce "list" subcommandPatrick Steinhardt, May 6, 2024
  103. 08/14 builtin/config: introduce "get" subcommandPatrick Steinhardt, May 6, 2024
  104. SZEDER GáborAug 3, 2025
  105. Junio C HamanoAug 3, 2025
  106. Patrick SteinhardtSep 11, 2025
  107. 09/14 builtin/config: introduce "set" subcommandPatrick Steinhardt, May 6, 2024
  108. 10/14 builtin/config: introduce "unset" subcommandPatrick Steinhardt, May 6, 2024
  109. 11/14 builtin/config: introduce "rename-section" subcommandPatrick Steinhardt, May 6, 2024
  110. 12/14 builtin/config: introduce "remove-section" subcommandPatrick Steinhardt, May 6, 2024
  111. 13/14 builtin/config: introduce "edit" subcommandPatrick Steinhardt, May 6, 2024
  112. 14/14 builtin/config: display subcommand helpPatrick Steinhardt, May 6, 2024
  113. Karthik NayakMay 6, 2024
  114. Taylor BlauMay 6, 2024
  115. rsbecker@nexbridge.comMay 6, 2024
  116. Patrick SteinhardtMay 7, 2024

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.