From: Andrew Pleeter <andrewpleeter@gmail.com>
To: git@vger.kernel.org
Cc: gitster@pobox.com, phillip.wood@dunelm.org.uk,
ben.knoble@gmail.com, peff@peff.net,
sandals@crustytoothpaste.net
Subject: [PATCH v9 2/4] var: add "-z" output mode
Date: Sat, 26 Sep 2026 12:20:46 -0400 [thread overview]
Message-ID: <20260926162048.30853-3-andrewpleeter@gmail.com> (raw)
In-Reply-To: <xmqq33va1lcg.fsf@gitster.g>
Scripts that read from "git var" have no unambiguous way to parse its
output. A value that itself contains a newline, and a variable that
has more than one value, both run together with the newline that
terminates each entry.
Teach "git var" a "-z" option that terminates each entry with a NUL
byte instead. With "-l", the variable name and its value are separated
by a newline and each entry is terminated by NUL, which is the same
convention "git config list -z" already uses. When a single variable
is asked for, its bare value is terminated by NUL.
Parsing of the command line is switched over to parse_options() in
order to accept the new option; as a side effect, options now have to
come before the variable name.
Signed-off-by: Andrew Pleeter <andrewpleeter@gmail.com>
---
Documentation/git-var.adoc | 19 ++++++++---
builtin/var.c | 67 +++++++++++++++++++++++++++-----------
t/t0007-git-var.sh | 30 +++++++++++++++++
3 files changed, 93 insertions(+), 23 deletions(-)
diff --git a/Documentation/git-var.adoc b/Documentation/git-var.adoc
index de3007732d..9052fbc549 100644
--- a/Documentation/git-var.adoc
+++ b/Documentation/git-var.adoc
@@ -9,13 +9,19 @@ git-var - Show a Git logical variable
SYNOPSIS
--------
[synopsis]
-git var (-l | <variable>)
+git var [-z] -l
+git var [-z] <variable>
DESCRIPTION
-----------
Prints a Git logical variable. Exits with code 1 if the variable has
no value.
+If `-z` is given, the value is terminated by a NUL byte instead of a
+newline. With `-l`, the variable name and its value are separated by a
+newline, and each entry is terminated by a NUL byte
+(`VARIABLE\nvalue\0`), in the same way as `git config list -z`.
+
OPTIONS
-------
`-l`::
@@ -24,6 +30,11 @@ OPTIONS
as well. (However, the configuration variables listing functionality
is deprecated in favor of `git config list`.)
+`-z`::
+ Terminate entries with NUL instead of newline. When used with
+ `-l`, the variable name and its value are separated by a
+ newline, and each entry is terminated with a NUL byte.
+
EXAMPLES
--------
$ git var GIT_AUTHOR_IDENT
@@ -86,9 +97,9 @@ endif::git-default-pager[]
This variable can have multiple values.
Most path values contain only one value. However, some can contain multiple
-values, which are separated by newlines, and are listed in order from highest to
-lowest priority. Callers should be prepared for any such path value to contain
-multiple items.
+values, which are separated by newlines (or NUL bytes if `-z` is given), and are
+listed in order from highest to lowest priority. Callers should be prepared for
+any such path value to contain multiple items.
Note that paths are printed even if they do not exist, but not if they are
disabled by other environment variables.
diff --git a/builtin/var.c b/builtin/var.c
index 9f7c8a6113..c9691070b8 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -14,13 +14,18 @@
#include "environment.h"
#include "ident.h"
#include "pager.h"
+#include "parse-options.h"
#include "path.h"
#include "refs.h"
#include "run-command.h"
#include "strbuf.h"
#include "string-list.h"
-static const char var_usage[] = "git var (-l | <variable>)";
+static const char * const var_usage[] = {
+ N_("git var [-z] -l"),
+ N_("git var [-z] <variable>"),
+ NULL
+};
static char *committer(int ident_flag)
{
@@ -164,16 +169,18 @@ static struct git_var git_vars[] = {
},
};
-static void list_vars(void)
+static void list_vars(int nul_term)
{
struct git_var *ptr;
+ char delim = nul_term ? '\n' : '=';
+ char term = nul_term ? '\0' : '\n';
for (ptr = git_vars; ptr->read || ptr->multiread; ptr++) {
if (ptr->read) {
char *val = ptr->read(0);
if (val) {
- printf("%s=%s\n", ptr->name, val);
+ printf("%s%c%s%c", ptr->name, delim, val, term);
free(val);
}
} else {
@@ -181,7 +188,8 @@ static void list_vars(void)
ptr->multiread(&list);
for (size_t i = 0; i < list.nr; i++)
- printf("%s=%s\n", ptr->name, list.items[i].string);
+ printf("%s%c%s%c", ptr->name, delim,
+ list.items[i].string, term);
string_list_clear(&list, 0);
}
}
@@ -201,34 +209,55 @@ static const struct git_var *get_git_var(const char *var)
static int show_config(const char *var, const char *value,
const struct config_context *ctx, void *cb)
{
+ int *nul_term = cb;
+ char delim = *nul_term ? '\n' : '=';
+ char term = *nul_term ? '\0' : '\n';
+
if (value)
- printf("%s=%s\n", var, value);
+ printf("%s%c%s%c", var, delim, value, term);
else
- printf("%s\n", var);
+ printf("%s%c", var, term);
return git_default_config(var, value, ctx, cb);
}
int cmd_var(int argc,
const char **argv,
- const char *prefix UNUSED,
+ const char *prefix,
struct repository *repo UNUSED)
{
const struct git_var *git_var;
-
- show_usage_if_asked(argc, argv, var_usage);
- if (argc != 2)
- usage(var_usage);
-
- if (strcmp(argv[1], "-l") == 0) {
- repo_config(the_repository, show_config, NULL);
- list_vars();
+ int list = 0;
+ int nul_term = 0;
+ char term;
+ struct option options[] = {
+ OPT_BOOL('l', NULL, &list,
+ N_("list all variables")),
+ OPT_BOOL('z', NULL, &nul_term,
+ N_("terminate entries with NUL")),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ var_usage, PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (list) {
+ if (argc)
+ usage_with_options(var_usage, options);
+ repo_config(the_repository, show_config, &nul_term);
+ list_vars(nul_term);
return 0;
}
+
+ if (argc != 1)
+ usage_with_options(var_usage, options);
+
repo_config(the_repository, git_default_config, NULL);
- git_var = get_git_var(argv[1]);
+ term = nul_term ? '\0' : '\n';
+
+ git_var = get_git_var(argv[0]);
if (!git_var)
- usage(var_usage);
+ usage_with_options(var_usage, options);
if (git_var->read) {
char *val = git_var->read(IDENT_STRICT);
@@ -236,7 +265,7 @@ int cmd_var(int argc,
if (!val)
return 1;
- printf("%s\n", val);
+ printf("%s%c", val, term);
free(val);
} else {
struct string_list list = STRING_LIST_INIT_DUP;
@@ -247,7 +276,7 @@ int cmd_var(int argc,
return 1;
}
for (size_t i = 0; i < list.nr; i++)
- printf("%s\n", list.items[i].string);
+ printf("%s%c", list.items[i].string, term);
string_list_clear(&list, 0);
}
diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh
index 2b60317758..661d0539c0 100755
--- a/t/t0007-git-var.sh
+++ b/t/t0007-git-var.sh
@@ -276,4 +276,34 @@ test_expect_success '`git var -l` works even without HOME' '
)
'
+test_expect_success 'get a variable with -z' '
+ printf "%sQ" "$(git var GIT_AUTHOR_IDENT)" >expect &&
+ git var -z GIT_AUTHOR_IDENT >actual.raw &&
+ nul_to_q <actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'get multi-valued variable with -z' '
+ TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+ HOME="$TRASHDIR" XDG_CONFIG_HOME="$TRASHDIR/foo" git var -z GIT_CONFIG_GLOBAL >actual.raw &&
+ printf "%sQ%sQ" "$TRASHDIR/foo/git/config" "$TRASHDIR/.gitconfig" >expect &&
+ nul_to_q <actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git var -l -z' '
+ git var -l -z >actual &&
+ tr "\0" "\n" <actual >actual.lines &&
+ git var GIT_AUTHOR_IDENT >expect &&
+ sed -n "/^GIT_AUTHOR_IDENT$/{n;p;}" actual.lines >actual.author &&
+ test_cmp expect actual.author &&
+ echo false >expect &&
+ sed -n "/^core\.bare$/{n;p;}" actual.lines >actual.bare &&
+ test_cmp expect actual.bare
+'
+
+test_expect_success 'options must precede variable arguments' '
+ test_must_fail git var GIT_AUTHOR_IDENT -z
+'
+
test_done
--
2.54.0 (Apple Git-157)
next prev parent reply other threads:[~2026-09-26 16:20 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-25 20:46 [PATCH] builtin/whoami: add new 'whoami' command Andrew Pleeter via GitGitGadget
2026-08-25 21:24 ` brian m. carlson
2026-08-25 21:41 ` Junio C Hamano
2026-08-31 23:59 ` [PATCH v2] builtin/ident: add new 'ident' command Andrew Pleeter via GitGitGadget
2026-09-01 4:39 ` Jeff King
2026-09-01 5:00 ` Junio C Hamano
2026-09-03 2:49 ` [PATCH v3] var: support broken-down idents, default key, multiple args, and -z Andrew Pleeter via GitGitGadget
2026-09-03 17:40 ` Junio C Hamano
2026-09-03 18:22 ` Ben Knoble
2026-09-04 9:11 ` Phillip Wood
2026-09-04 15:57 ` Junio C Hamano
2026-09-08 9:07 ` Phillip Wood
2026-09-08 4:09 ` [PATCH v4] var: support broken-down idents, signing " Andrew Pleeter via GitGitGadget
2026-09-08 13:54 ` Phillip Wood
2026-09-08 20:43 ` [PATCH v5] " Andrew Pleeter via GitGitGadget
2026-09-08 21:53 ` Junio C Hamano
2026-09-09 1:24 ` [PATCH v6] " Andrew Pleeter via GitGitGadget
2026-09-09 15:36 ` Phillip Wood
2026-09-09 16:42 ` Junio C Hamano
2026-09-10 3:09 ` [PATCH v7] " Andrew Pleeter via GitGitGadget
2026-09-14 19:34 ` Junio C Hamano
2026-09-14 22:50 ` [PATCH v8] " Andrew Pleeter via GitGitGadget
2026-09-15 17:21 ` Junio C Hamano
2026-09-15 22:02 ` Andrew Pleeter
2026-09-22 14:57 ` Phillip Wood
2026-09-26 16:19 ` Andrew Pleeter
2026-09-26 16:20 ` [PATCH v9 0/4] var: -z output, multiple variables, and broken-out idents Andrew Pleeter
2026-10-02 14:11 ` Phillip Wood
2026-10-02 17:41 ` Andrew Pleeter
2026-09-26 16:20 ` [PATCH v9 1/4] var: represent multi-valued variables with a string_list Andrew Pleeter
2026-09-26 16:20 ` Andrew Pleeter [this message]
2026-09-26 16:20 ` [PATCH v9 3/4] var: accept more than one variable Andrew Pleeter
2026-10-02 14:11 ` Phillip Wood
2026-09-26 16:20 ` [PATCH v9 4/4] var: add broken-out identity variables Andrew Pleeter
2026-10-02 14:11 ` Phillip Wood
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=20260926162048.30853-3-andrewpleeter@gmail.com \
--to=andrewpleeter@gmail.com \
--cc=ben.knoble@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=peff@peff.net \
--cc=phillip.wood@dunelm.org.uk \
--cc=sandals@crustytoothpaste.net \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.