diff --git a/builtin/config.c b/builtin/config.c
index ad6c695737..6ab5a0d68f 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -626,6 +626,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 {
 	int nongit = !startup_info->have_repository;
 	char *value;
+	int flags = 0;
 
 	given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
 
@@ -761,6 +762,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 		error(_("--literal only applies with 'value_regex'"));
 		usage_builtin_config();
 	}
+	if (literal)
+		flags = CONFIG_FLAGS_LITERAL_VALUE;
 
 	if (actions & PAGING_ACTIONS)
 		setup_auto_pager("config", 1);
@@ -823,7 +826,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 		value = normalize_value(argv[0], argv[1]);
 		UNLEAK(value);
 		return git_config_set_multivar_in_file_gently(given_config_source.file,
-							      argv[0], value, argv[2], 0);
+							      argv[0], value, argv[2],
+							      flags);
 	}
 	else if (actions == ACTION_ADD) {
 		check_write();
@@ -832,7 +836,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 		UNLEAK(value);
 		return git_config_set_multivar_in_file_gently(given_config_source.file,
 							      argv[0], value,
-							      CONFIG_REGEX_NONE, 0);
+							      CONFIG_REGEX_NONE,
+							      flags);
 	}
 	else if (actions == ACTION_REPLACE_ALL) {
 		check_write();
@@ -841,7 +846,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 		UNLEAK(value);
 		return git_config_set_multivar_in_file_gently(given_config_source.file,
 							      argv[0], value, argv[2],
-							      CONFIG_FLAGS_MULTI_REPLACE);
+							      flags | CONFIG_FLAGS_MULTI_REPLACE);
 	}
 	else if (actions == ACTION_GET) {
 		check_argc(argc, 1, 2);
@@ -868,7 +873,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 		check_argc(argc, 1, 2);
 		if (argc == 2)
 			return git_config_set_multivar_in_file_gently(given_config_source.file,
-								      argv[0], NULL, argv[1], 0);
+								      argv[0], NULL, argv[1],
+								      flags);
 		else
 			return git_config_set_in_file_gently(given_config_source.file,
 							     argv[0], NULL);
@@ -878,7 +884,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 		check_argc(argc, 1, 2);
 		return git_config_set_multivar_in_file_gently(given_config_source.file,
 							      argv[0], NULL, argv[1],
-							      CONFIG_FLAGS_MULTI_REPLACE);
+							      flags | CONFIG_FLAGS_MULTI_REPLACE);
 	}
 	else if (actions == ACTION_RENAME_SECTION) {
 		int ret;
diff --git a/config.c b/config.c
index 4841c68a91..5028c1e736 100644
--- a/config.c
+++ b/config.c
@@ -2402,6 +2402,7 @@ struct config_store_data {
 	size_t baselen;
 	char *key;
 	int do_not_match;
+	const char *literal_value;
 	regex_t *value_regex;
 	int multi_replace;
 	struct {
@@ -2431,6 +2432,8 @@ static int matches(const char *key, const char *value,
 {
 	if (strcmp(key, store->key))
 		return 0; /* not ours */
+	if (store->literal_value)
+		return !strcmp(store->literal_value, value);
 	if (!store->value_regex)
 		return 1; /* always matches */
 	if (store->value_regex == CONFIG_REGEX_NONE)
@@ -2803,6 +2806,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
 			store.value_regex = NULL;
 		else if (value_regex == CONFIG_REGEX_NONE)
 			store.value_regex = CONFIG_REGEX_NONE;
+		else if (flags & CONFIG_FLAGS_LITERAL_VALUE)
+			store.literal_value = value_regex;
 		else {
 			if (value_regex[0] == '!') {
 				store.do_not_match = 1;
diff --git a/config.h b/config.h
index 266eb22e46..ddb81e1e32 100644
--- a/config.h
+++ b/config.h
@@ -77,6 +77,13 @@ enum config_event_t {
  */
 #define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)
 
+/*
+ * When CONFIG_FLAGS_LITERAL_VALUE is specified, match key/value pairs
+ * by string comparison (not regex match) to the provided value_regex
+ * parameter.
+ */
+#define CONFIG_FLAGS_LITERAL_VALUE (1 << 1)
+
 /*
  * The parser event function (if not NULL) is called with the event type and
  * the begin/end offsets of the parsed elements.
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 73f5ca4361..ed12b4f7a0 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -1978,4 +1978,52 @@ test_expect_success 'refuse --literal-value for incompatible actions' '
 	test_must_fail git config --literal-value --edit
 '
 
+test_expect_success '--literal-value uses exact string matching' '
+	GLOB="a+b*c?d[e]f.g" &&
+	q_to_tab >initial <<-EOF &&
+	[literal]
+	Qtest = $GLOB
+	EOF
+
+	cp initial .git/config &&
+	git config literal.test bogus "$GLOB" &&
+	q_to_tab >expect <<-EOF &&
+	[literal]
+	Qtest = $GLOB
+	Qtest = bogus
+	EOF
+	test_cmp expect .git/config &&
+	cp initial .git/config &&
+	git config --literal-value literal.test bogus "$GLOB" &&
+	q_to_tab >expect <<-EOF &&
+	[literal]
+	Qtest = bogus
+	EOF
+	test_cmp expect .git/config &&
+
+	cp initial .git/config &&
+	test_must_fail git config --unset literal.test "$GLOB" &&
+	git config --literal-value --unset literal.test "$GLOB" &&
+	test_must_fail git config literal.test &&
+
+	cp initial .git/config &&
+	test_must_fail git config --unset-all literal.test "$GLOB" &&
+	git config --literal-value --unset-all literal.test "$GLOB" &&
+	test_must_fail git config literal.test &&
+
+	cp initial .git/config &&
+	git config --replace-all literal.test bogus "$GLOB" &&
+	q_to_tab >expect <<-EOF &&
+	[literal]
+	Qtest = $GLOB
+	Qtest = bogus
+	EOF
+	test_cmp expect .git/config &&
+
+	cp initial .git/config &&
+	git config --replace-all literal.test bogus "$GLOB" &&
+	git config --literal-value --replace-all literal.test bogus "$GLOB" &&
+	test_cmp_config bogus literal.test
+'
+
 test_done
