Conversation
Welcome to GitGitGadgetHi @anpl1623, and welcome to GitGitGadget, the GitHub App to send patch series to the Git mailing list from GitHub Pull Requests. Please make sure that either:
You can CC potential reviewers by adding a footer to the PR description with the following syntax: NOTE: DO NOT copy/paste your CC list from a previous GGG PR's description, Also, it is a good idea to review the commit messages one last time, as the Git project expects them in a quite specific form:
It is in general a good idea to await the automated test ("Checks") in this Pull Request before contributing the patches, e.g. to avoid trivial issues such as unportable code. Contributing the patchesBefore you can contribute the patches, your GitHub username needs to be added to the list of permitted users. Any already-permitted user can do that, by adding a comment to your PR of the form Both the person who commented An alternative is the channel Once on the list of permitted usernames, you can contribute the patches to the Git mailing list by adding a PR comment If you want to see what email(s) would be sent for a After you submit, GitGitGadget will respond with another comment that contains the link to the cover letter mail in the Git mailing list archive. Please make sure to monitor the discussion in that thread and to address comments and suggestions (while the comments and suggestions will be mirrored into the PR by GitGitGadget, you will still want to reply via mail). If you do not want to subscribe to the Git mailing list just to be able to respond to a mail, you can download the mbox from the Git mailing list archive (click the curl -g --user "<EMailAddress>:<Password>" \
--url "imaps://imap.gmail.com/INBOX" -T /path/to/raw.txtTo iterate on your change, i.e. send a revised patch or patch series, you will first want to (force-)push to the same branch. You probably also want to modify your Pull Request description (or title). It is a good idea to summarize the revision by adding something like this to the cover letter (read: by editing the first comment on the PR, i.e. the PR description): To send a new iteration, just add another PR comment with the contents: Need help?New contributors who want advice are encouraged to join [email protected], where volunteers who regularly contribute to Git are willing to answer newbie questions, give advice, or otherwise provide mentoring to interested contributors. You must join in order to post or view messages, but anyone can join. You may also be able to find help in real time in the developer IRC channel, |
|
There is an issue in commit d12cc4a:
|
|
There is an issue in commit e1bb825:
|
8c4bcfb to
263aebc
Compare
|
/allow |
|
User anpl1623 is now allowed to use GitGitGadget. |
|
Sleepwalk |
|
/submit |
|
Submitted as [email protected] To fetch this version into To fetch this version to local tag |
|
There is a merge commit in this Pull Request: Please rebase the branch and force-push. |
|
There is a merge commit in this Pull Request: Please rebase the branch and force-push. |
|
"brian m. carlson" wrote on the Git mailing list (how to reply to this email): On 2026-08-25 at 20:46:42, Andrew Pleeter via GitGitGadget wrote:
> From: anpl1623 <[email protected]>
You will probably want this to match your real name since you're using
it in the email. We prefer that people use their real names, but don't
require it, but I assume that it's not a problem since you just CC'd
yourself using it (and it's also in your email address).
> Add a builtin 'whoami' command to inspect and display the resolved
> author and committer identity along with the commit signing
> configuration (GPG/SSH key ID and commit.gpgsign status) used when
> creating Git commits.
>
> Support optional flags (--author, --committer, --name, --email,
> --signing-key, and --verbose) for targeted querying and scripting.
I suspect users will want a way to get _all_ of the output in a
machine-readable way, so you'd probably want to provide some method of
doing that. Note that because your existing endpoints provide
translated strings, they are not suitable for this. That doesn't mean
that they should not be translated (because they should) but we'd
probably want a format like the following:
user.author.name=A U Thor
user.author.email=[email protected]
Other formats are possible, though.
Possibly a `-z` option for NUL-terminated instead of LF-terminated
output might be warranted as well unless we're certain that our output
will never contain a newline (hint: config options can).
> Include documentation in Documentation/git-whoami.adoc and regression
> tests in t/t0015-whoami.sh.
>
> Signed-off-by: anpl1623 <[email protected]>
Again, you'll want to sign this off with your real name.
> MOTIVATION
>
> Users often work across multiple environments, profiles, or repositories
> with different global/local configs and signing keys. Currently,
> verifying what identity and signing key will be attached to a new commit
> requires checking several individual git config and git var settings.
> git whoami provides a simple, direct porcelain command to verify this in
> one step.
I think this should go in the commit message. I thought to myself,
"Well, there are already ways to get this information, so why add a new
one?" Telling us why your patch is compelling and solves an important
purpose is appropriate for the commit message.
I might also like to see an explanation as to why this wouldn't work
better in `git var` or elsewhere instead, since much of the information
is already there. Since that's an alternative you've rejected, tell us
why and sell us on your vision.
> + repo_config(the_repository, git_default_config, NULL);
Let's not add more uses of `the_repository`. Use the `repo` argument to
the main function above, taking care to handle the NULL case.
> + repo_config_get_bool(the_repository, "commit.gpgsign", &gpgsign);
> + repo_config_get_string(the_repository, "user.signingkey", &signing_key);
> + repo_config_get_string(the_repository, "gpg.format", &gpg_format);
> + repo_config_get_string(the_repository, "gpg.ssh.defaultkeycommand", &ssh_default_key_cmd);
> +
> + is_ssh = gpg_format && !strcmp(gpg_format, "ssh");
> +
> + if (signing_key && *signing_key) {
> + resolved_key = xstrdup(signing_key);
> + } else if (is_ssh) {
> + if (ssh_default_key_cmd && *ssh_default_key_cmd)
> + resolved_key = get_signing_key_id();
> + } else if (gpgsign) {
> + resolved_key = get_signing_key_id();
> + }
Should this also do something useful for X.509 keys?
Overall, I don't have a strong need for this and I'm fine using the
existing functionality. However, I see how it could be useful and if it
were merged and available in the versions of Git I use, then I might
make use of it.
Perhaps others think this is compelling, though, so I'm interested to
hear other opinions about the utility of the command.
--
brian m. carlson (they/them)
Toronto, Ontario, CA |
|
User |
Hi Brian, Thank you for the detailed feedback! I have updated the patch to address all of your points:
All regression tests in t/t0015-whoami.sh and documentation linters pass. |
|
Junio C Hamano wrote on the Git mailing list (how to reply to this email): "Andrew Pleeter via GitGitGadget" <[email protected]> writes:
> builtin/whoami: add new 'whoami' command
In general, I would really really want to see us refrain from adding
any more random subcommands.
> +`-a`::
> +`--author`::
> + Show author identity.
> +
> +`-c`::
> +`--committer`::
> + Show committer identity.
This pair hints the users they are equals.
But the code tells us otherwise:
> + if (show_name) {
> + if (show_author)
> + puts(author_name.buf);
> + else
> + puts(committer_name.buf);
> + goto cleanup;
> + }
> +
So when "-n" is in effect, "-c" is completely ignored. Lack of "-a"
means "-c" instead. The same story holds for "-e".
Yet later in the code that is executed when neither "-n" or "-e" is
in effect:
> + if (show_author) {
> + puts(author_info.buf);
> + goto cleanup;
> + }
> +
> + if (show_committer) {
> + puts(committer_info.buf);
> + goto cleanup;
> + }
> +
Here, lack of "-a" is not sufficient to view committer information
and you'd explicitly need to pass "-c" if you want to view committer
information.
So confusing.
> +`-n`::
> +`--name`::
> + Show name only.
> +
> +`-e`::
> +`--email`::
> + Show email only.
Why not make -a/-c/-n/-e more additive instead? Something along the
lines of ...
$ git ident -a -e -n
Andrew Pleeter <[email protected]>
$ git ident -a -n -v
Author: Andrew Pleeter
$ git ident -a -c -e
<[email protected]>
<[email protected]>
$ git ident -a -c -e -v
Author: <[email protected]>
Committer: <[email protected]>
|
dae4823 to
f322e7f
Compare
|
There is a merge commit in this Pull Request: Please rebase the branch and force-push. |
|
There is a merge commit in this Pull Request: Please rebase the branch and force-push. |
|
/submit |
|
Submitted as [email protected] To fetch this version into To fetch this version to local tag |
Hi Junio, Thank you for the review and guidance!
My apologies for not adopting your grouped suggestion sooner. In v7, we have replaced the individual entries with your grouped definitions in Documentation/git-var.adoc:
Following up on your due diligence suggestion: we audited In v7, we added explicit defensive assertions:
Regarding the multi-variable design and delimiters:
Thanks, |
|
/submit |
|
Submitted as [email protected] To fetch this version into To fetch this version to local tag |
|
@anpl1623 please note that none of your replies in this PR will ever reach the intended recipient, as the welcome message said:
Your PR comments are not mirrored to the Git mailing list. Neither Phillip nor Junio got your messages. |
|
This patch series was integrated into seen via a6f2bea. |
|
This branch is now known as |
|
There was a status update in the "Cooking" section about the branch Needs review. source: <[email protected]> |
|
Junio C Hamano wrote on the Git mailing list (how to reply to this email): "Andrew Pleeter via GitGitGadget" <[email protected]> writes:
> DESCRIPTION
> -----------
> +Prints Git logical variables. Exits with code 1 if any requested
> +variable has no value. When multiple variables are requested, an empty
> +record (a blank line, or an empty NUL-terminated record when `-z` is given)
> +is printed for any variable that has no value, and the command continues
> +processing the remaining variables.
Very clearly described. Although it makes it sound as if the
command always notices a variable without any value and reports
failure with its exit value, no matter in what mode, but I do not
think that matches what the code does (below).
> int cmd_var(int argc,
> ...
> + term = nul_term ? '\0' : '\n';
> +
> + for (i = 0; i < argc; i++) {
> + const struct git_var *git_var = get_git_var(argv[i]);
>
> + if (!git_var)
> + usage_with_options(var_usage, options);
>
> + if (git_var->read) {
> + char *val = git_var->read(IDENT_STRICT);
> +
> + if (!val) {
> + if (argc == 1)
> + return 1;
> + putc(term, stdout);
> + continue;
> + }
> + printf("%s%c", val, term);
> + free(val);
> + } else {
> + struct string_list list = STRING_LIST_INIT_DUP;
> + size_t j;
> +
> + git_var->multiread(&list);
> + if (argc == 1 && !list.nr) {
> + string_list_clear(&list, 0);
> + return 1;
> + }
> + for (j = 0; j < list.nr; j++)
> + printf("%s%c", list.items[j].string, term);
> + if (argc > 1)
> + putc(term, stdout);
> + string_list_clear(&list, 0);
> + }
> + }
>
> return 0;
> }
When we ask for a single variable, 'argc' is 1 (and we never update
'argc' in the loop, which is good), and we return 1 upon seeing a
missing value. We also do the same when we receive a 0-element list
back for a multi-valued variable. Otherwise, nobody in the loop
remembers that we had any such failure; the loop continues, and we
return 0 unconditionally. A "missing value" anomaly noticed during
the loop gets forgotten.
Either the documentation or the code needs to be updated, I
think.
I am still not convinced this output format is easy for scripts to
handle when multi-valued variables are involved. It is also a bit
unclear what exactly "variable has no value" means. A variable
whose value is an empty string is not such a variable, right? If a
multi-valued variable has an empty string and the string "hello" as
its value, would the output from the command confuse the reading
script into thinking that the first blank line signals that the
variable has no value, for example? Having to know which variables
are multi-valued and which are not before parsing the output format
does not help, either.
We could, of course, disambiguate by prefixing these lines with
variable names followed by '=' (or NUL), which would likely
eliminate the ambiguity. But I understand that you are trying to
allow the parsers to proceed without having to strip prefixes from
each input, which is why the format tries to rely solely on the
correspondence between command-line arguments and output lines. I,
however, doubt you succeeded in doing so without making the output
ambiguous.
Thanks. |
|
There was a status update in the "Cooking" section about the branch The 'git var' command has been extended to expose individual
identity components ('GIT_AUTHOR_NAME', etc.) and the commit
signing key, and can now accept multiple variables to query at
once, safely formatting the output with a new '-z' option.
Needs review.
source: <[email protected]>
|
While 'git var' exposes GIT_AUTHOR_IDENT and GIT_COMMITTER_IDENT, extracting individual components (name, email, or date) currently requires callers to manually parse the composite string. Furthermore, there is no way to query the resolved commit signing key through 'git var', and the command only accepts a single variable at a time. Teach 'git var' to expose individual identity components and commit signing configuration, and allow querying multiple variables with optional NUL-termination: - Add GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and GIT_AUTHOR_DATE. - Add GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL, and GIT_COMMITTER_DATE. - Add GIT_SIGNING_KEY to resolve the key that would be used to sign the resulting commit if you were to run 'git commit' right now. - Allow passing multiple variable arguments (e.g., 'git var GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL'). - When a single variable is requested, print its bare value for backward compatibility. - When multiple variables are requested, model the output after 'git var -l' by printing 'VARIABLE=value' pairs (or 'VARIABLE\nvalue\0' when '-z' is given). - Format multi-valued variables in multi-variable mode as repeated 'VARIABLE=value' entries (or 'VARIABLE\nvalue\0' with '-z'), eliminating stream ambiguity without extra trailing delimiters. - When querying multiple variables, omit any variable that has no value, continue processing remaining variables, and exit with code 1. - Support '-z' to terminate variable outputs with NUL bytes. - Format 'git var -l -z' using the same convention as 'git config list -z' (newline separating key and value, NUL separating entries). - Use parse_options() to strictly require options before arguments. - Update Documentation/git-var.adoc and t/t0007-git-var.sh. Signed-off-by: Andrew Pleeter <[email protected]>
|
/submit |
|
Submitted as [email protected] To fetch this version into To fetch this version to local tag |
|
Junio C Hamano wrote on the Git mailing list (how to reply to this email): "Andrew Pleeter via GitGitGadget" <[email protected]> writes:
> From: Andrew Pleeter <[email protected]>
>
> While 'git var' exposes GIT_AUTHOR_IDENT and GIT_COMMITTER_IDENT,
> extracting individual components (name, email, or date) currently
> requires callers to manually parse the composite string. Furthermore,
> there is no way to query the resolved commit signing key through
> 'git var', and the command only accepts a single variable at a time.
>
> Teach 'git var' to expose individual identity components and commit
> signing configuration, and allow querying multiple variables with
> optional NUL-termination:
The huge laundary list (below) strongly tells us that this single
patch is doing too many things at once and it is better done as a
multi-patch series. Also, some descriptions seem to hint how the
implementation evolved during the development of this patch, which
nobody is interested when they read "git log" output (which is the
ultimate target audience we write our commit log messages for).
I would say this should be split into at least 3 patches.
(1) Add "-z" output mode.
To allow reading scripts to unambiguously parse output from
"git var (-l | <var>)" command, implement a NUL terminated
output mode, similar to how "git config -l -z" shows list of
configuration variables and their values. When showing the
value of a single variable this only makes difference for
variable with multiple values, but in the next step in this
series, we will introduce a mode where multiple variables are
queried.
(2) Add (2 <= argc) mode that displays like "var -l" mode on top.
To allow reading values for multiple variables with a single
command invocation, teach "var" to take more than one variable,
and show output the same way as "git var -l [-z]", giving list
of "var=value" but only for variables requested by the user.
(3) Add new variables.
Scripts reading from "git var GIT_AUTHOR_IDENT" needs to parse
the output if they want to extract only the author name.
To allow scripts to easily access broken-out fields of
GIT_{AUTHOR,COMMITTER}_IDENT, add a GIT_AUTHOR_NAME variable
and its friends, as well as GIT_SIGNING_KEY.
After you receive a review, you should respond and try to engage in
a dialog with reviewers, before sending a new iteration of a patch.
When your new iteration is different from what reviewer suggested,
without such an exchange beforehand, reviewers cannot tell if that
is merely due to miscommunication, or you had a good reason to do
things differently. Don't make reviewers feel as if they are
talking to silent machine that takes an earlier iteration of the
patch with their input and spits out a new iteration.
Thanks.
> - Add GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and GIT_AUTHOR_DATE.
> - Add GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL, and GIT_COMMITTER_DATE.
> - Add GIT_SIGNING_KEY to resolve the key that would be used to sign
> the resulting commit if you were to run 'git commit' right now.
> - Allow passing multiple variable arguments (e.g., 'git var
> GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL').
> - When a single variable is requested, print its bare value for backward
> compatibility.
> - When multiple variables are requested, model the output after
> 'git var -l' by printing 'VARIABLE=value' pairs (or 'VARIABLE\nvalue\0'
> when '-z' is given).
> - Format multi-valued variables in multi-variable mode as repeated
> 'VARIABLE=value' entries (or 'VARIABLE\nvalue\0' with '-z'),
> eliminating stream ambiguity without extra trailing delimiters.
> - When querying multiple variables, omit any variable that has no value,
> continue processing remaining variables, and exit with code 1.
> - Support '-z' to terminate variable outputs with NUL bytes.
> - Format 'git var -l -z' using the same convention as 'git config
> list -z' (newline separating key and value, NUL separating entries).
> - Use parse_options() to strictly require options before arguments.
> - Update Documentation/git-var.adoc and t/t0007-git-var.sh.
|
|
This patch series is no longer integrated into seen. |
|
This patch series was integrated into seen via 94eff80. |
|
Andrew Pleeter wrote on the Git mailing list (how to reply to this email): Hi Junio,
Thank you for the guidance, and I sincerely apologize for the lack of
communication. I had posted replies on the GitHub pull request and did
not realize until now that GitGitGadget does not mirror PR comments back
to the mailing list. I certainly did not intend to be silent or ignore
your reviews.
I completely agree with your feedback. Packing all of these features into
a single commit makes the patch difficult to review and overcomplicates
the history.
I will restructure the series for v9 into three separate patches as you
suggested:
1. Add the -z output mode to git var (-l and single-variable).
2. Teach git var to accept multiple variables (argc >= 2) with var=value
output.
3. Expose the broken-out identity components and GIT_SIGNING_KEY.
I will send v9 shortly. Thank you again for your patience and direction.
Best regards,
Andrew |
|
There was a status update in the "Cooking" section about the branch The 'git var' command has been extended to expose individual
identity components ('GIT_AUTHOR_NAME', etc.) and the commit
signing key, and can now accept multiple variables to query at
once, safely formatting the output with a new '-z' option.
Expecting a reroll.
cf. <[email protected]>
source: <[email protected]>
|
|
There was a status update in the "Cooking" section about the branch The 'git var' command has been extended to expose individual
identity components ('GIT_AUTHOR_NAME', etc.) and the commit
signing key, and can now accept multiple variables to query at
once, safely formatting the output with a new '-z' option.
Expecting a reroll.
cf. <[email protected]>
source: <[email protected]>
|
|
Phillip Wood wrote on the Git mailing list (how to reply to this email): On 15/09/2026 23:02, Andrew Pleeter wrote:
> Hi Junio,
> > Thank you for the guidance, and I sincerely apologize for the lack of
> communication. I had posted replies on the GitHub pull request and did
> not realize until now that GitGitGadget does not mirror PR comments back
> to the mailing list.
Ah, that explains the silence! I had a quick look at the PR comments but did not notice any response to my question about the use of GIT_SIGNING_KEY.
> I certainly did not intend to be silent or ignore
> your reviews.
> > I completely agree with your feedback. Packing all of these features into
> a single commit makes the patch difficult to review and overcomplicates
> the history.
> > I will restructure the series for v9 into three separate patches as you
> suggested:
> > 1. Add the -z output mode to git var (-l and single-variable).
> 2. Teach git var to accept multiple variables (argc >= 2) with var=value
> output.
> 3. Expose the broken-out identity components and GIT_SIGNING_KEY.
That sounds like a good direction
Thanks
Phillip
> > I will send v9 shortly. Thank you again for your patience and direction.
> > Best regards,
> Andrew |
|
There was a status update in the "Cooking" section about the branch The 'git var' command has been extended to expose individual
identity components ('GIT_AUTHOR_NAME', etc.) and the commit
signing key, and can now accept multiple variables to query at
once, safely formatting the output with a new '-z' option.
Expecting a reroll.
cf. <[email protected]>
source: <[email protected]>
|
|
Andrew Pleeter wrote on the Git mailing list (how to reply to this email): Hi Phillip,
Sorry for the silence. I was replying on the GitGitGadget PR and did not
realise those comments never reach the list. That is also why v6 switched
to get_signing_key() without a word from me about your question.
You were right to keep asking about GIT_SIGNING_KEY. I am dropping it
from the series. get_signing_key() returns a string whose meaning depends
on gpg.format, and the variable gives the caller no way to tell which
kind it got:
gpg.format=openpgp, user.signingkey unset
$ git var GIT_SIGNING_KEY
C O Mitter <[email protected]>
gpg.format=openpgp, user.signingkey=DEADBEEF
$ git var GIT_SIGNING_KEY
DEADBEEF
gpg.format=ssh, user.signingkey=/path/to/key.pub
$ git var GIT_SIGNING_KEY
/path/to/key.pub
So no, the key alone does not tell a caller whether to use gpg or ssh;
they would have to read gpg.format as well. And in the default case the
value is a committer ident rather than a key, because that is what
get_signing_key() hands to gpg to look up. Better to drop it than ship a
GIT_SIGNING_KEY that sometimes isn't one. I will revisit it when I can
name a caller it serves.
On the exit code, your GIT_CONFIG_NOSYSTEM example convinced me. In v9,
asking for several variables omits the ones with no value and exits 0.
Non-zero is reserved for real errors such as an unknown variable name, so
callers can detect those from the exit code alone. A single variable
still exits 1 when it has no value.
The string_list conversion is split out as you suggested:
1/4 var: represent multi-valued variables with a string_list
2/4 var: add "-z" output mode
3/4 var: accept more than one variable
4/4 var: add broken-out identity variables
parse_options() sits in 2/4, since "-z" is what requires it.
GIT_CONFIG_GLOBAL is now documented as multi-valued, and the tests use
nul_to_q instead of test_cmp over files containing NUL.
One place I did not follow you: instead of a trailing delimiter for a
multi-valued variable when argc > 1, each value gets its own entry:
$ git var GIT_AUTHOR_IDENT GIT_CONFIG_GLOBAL
GIT_AUTHOR_IDENT=A U Thor <[email protected]> 1112912053 -0700
GIT_CONFIG_GLOBAL=/home/me/.config/git/config
GIT_CONFIG_GLOBAL=/home/me/.gitconfig
Same shape as "git var -l", so one parser handles both. Happy to switch
if you prefer the trailing delimiter. I should have raised it last time
instead of just changing it.
Thanks,
Andrew |
|
Andrew Pleeter wrote on the Git mailing list (how to reply to this email): This is a reroll of the single patch in v8, split up as Junio asked
for, with the changes Phillip asked for on the list.
v8 was one commit doing four things at once. It is now four patches:
1/4 converts the internal representation of a multi-valued variable
from a newline-joined string to a string_list, with no change in
output. Phillip suggested doing this as its own preparatory step.
2/4 adds "-z", which is what forces the switch to parse_options(), so
that conversion lives here rather than in 1/4.
3/4 teaches "git var" to take more than one variable.
4/4 adds GIT_AUTHOR_NAME and friends.
Changes since v8:
* GIT_SIGNING_KEY is dropped. Phillip asked three times how it was
meant to be used, and once I looked properly the answer was that the
value cannot be interpreted without also reading gpg.format, and that
in the default configuration it is a committer ident rather than a
key at all. I would rather leave it out than define a variable I
cannot describe. Details are in my reply to him on this thread.
* Asking for several variables no longer exits non-zero just because
one of them has no value; such a variable is left out of the output
and the rest are still shown. A non-zero status is now reserved for
real errors, such as naming a variable that does not exist, so
callers can detect those from the exit code. A single variable still
exits 1 when it has no value, as before. This is Phillip's
GIT_CONFIG_NOSYSTEM point.
* GIT_CONFIG_GLOBAL is documented as a variable that can have more than
one value.
* The commit messages are prose rather than a list of bullet points,
and no longer narrate how the patch was developed.
The tests use nul_to_q rather than running test_cmp over files
containing NUL bytes, which Phillip pointed out in v6. Each patch builds
and passes t0007 on its own.
One thing I did not do, and would like an opinion on: for a multi-valued
variable in multi-variable mode, rather than emitting a trailing
delimiter, each value is shown as its own "VARIABLE=value" entry, which
matches "git var -l". Phillip suggested the trailing delimiter and I am
happy to switch.
Andrew Pleeter (4):
var: represent multi-valued variables with a string_list
var: add "-z" output mode
var: accept more than one variable
var: add broken-out identity variables
Documentation/git-var.adoc | 68 +++++++++--
builtin/var.c | 242 +++++++++++++++++++++++++++++--------
t/t0007-git-var.sh | 153 +++++++++++++++++++++++
3 files changed, 403 insertions(+), 60 deletions(-)
--
2.54.0 (Apple Git-157)
|
|
Andrew Pleeter wrote on the Git mailing list (how to reply to this email): A variable whose value is a list, of which GIT_CONFIG_GLOBAL is
currently the only one, is read into a single string whose elements are
joined with newlines, and "git var -l" splits that string apart again
before showing each element. Round-tripping through a delimiter that
can legitimately appear in a value is fragile, and it forces every
future caller to know which variables need splitting.
Give "struct git_var" a separate "multiread" function that appends the
elements to a string_list, and let the single "read" function stay for
the variables that have exactly one value. A variable uses one or the
other, never both, so which one is set also says whether the variable
is multi-valued.
The output is unchanged. While at it, say in the documentation that
GIT_CONFIG_GLOBAL can have multiple values, so that callers know when
to expect a list.
Signed-off-by: Andrew Pleeter <[email protected]>
---
Documentation/git-var.adoc | 1 +
builtin/var.c | 79 +++++++++++++++++++++-----------------
2 files changed, 44 insertions(+), 36 deletions(-)
diff --git a/Documentation/git-var.adoc b/Documentation/git-var.adoc
index 697c10aded..de3007732d 100644
--- a/Documentation/git-var.adoc
+++ b/Documentation/git-var.adoc
@@ -83,6 +83,7 @@ endif::git-default-pager[]
`GIT_CONFIG_GLOBAL`::
The path to the global (per-user) configuration files, if any.
+ 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
diff --git a/builtin/var.c b/builtin/var.c
index cc3a43cde2..9f7c8a6113 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -14,10 +14,11 @@
#include "environment.h"
#include "ident.h"
#include "pager.h"
-#include "refs.h"
#include "path.h"
-#include "strbuf.h"
+#include "refs.h"
#include "run-command.h"
+#include "strbuf.h"
+#include "string-list.h"
static const char var_usage[] = "git var (-l | <variable>)";
@@ -90,35 +91,27 @@ static char *git_config_val_system(int ident_flag UNUSED)
return NULL;
}
-static char *git_config_val_global(int ident_flag UNUSED)
+static void git_config_val_global(struct string_list *list)
{
- struct strbuf buf = STRBUF_INIT;
char *user, *xdg;
- size_t unused;
git_global_config_paths(&user, &xdg);
if (xdg && *xdg) {
normalize_path_copy(xdg, xdg);
- strbuf_addf(&buf, "%s\n", xdg);
+ string_list_append(list, xdg);
}
if (user && *user) {
normalize_path_copy(user, user);
- strbuf_addf(&buf, "%s\n", user);
+ string_list_append(list, user);
}
free(xdg);
free(user);
- strbuf_trim_trailing_newline(&buf);
- if (buf.len == 0) {
- strbuf_release(&buf);
- return NULL;
- }
- return strbuf_detach(&buf, &unused);
}
struct git_var {
const char *name;
char *(*read)(int);
- int multivalued;
+ void (*multiread)(struct string_list *);
};
static struct git_var git_vars[] = {
{
@@ -163,8 +156,7 @@ static struct git_var git_vars[] = {
},
{
.name = "GIT_CONFIG_GLOBAL",
- .read = git_config_val_global,
- .multivalued = 1,
+ .multiread = git_config_val_global,
},
{
.name = "",
@@ -175,28 +167,30 @@ static struct git_var git_vars[] = {
static void list_vars(void)
{
struct git_var *ptr;
- char *val;
-
- for (ptr = git_vars; ptr->read; ptr++)
- if ((val = ptr->read(0))) {
- if (ptr->multivalued && *val) {
- struct string_list list = STRING_LIST_INIT_DUP;
-
- string_list_split(&list, val, "\n", -1);
- for (size_t i = 0; i < list.nr; i++)
- printf("%s=%s\n", ptr->name, list.items[i].string);
- string_list_clear(&list, 0);
- } else {
+
+ 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);
+ free(val);
}
- free(val);
+ } else {
+ struct string_list list = STRING_LIST_INIT_DUP;
+
+ ptr->multiread(&list);
+ for (size_t i = 0; i < list.nr; i++)
+ printf("%s=%s\n", ptr->name, list.items[i].string);
+ string_list_clear(&list, 0);
}
+ }
}
static const struct git_var *get_git_var(const char *var)
{
struct git_var *ptr;
- for (ptr = git_vars; ptr->read; ptr++) {
+ for (ptr = git_vars; ptr->read || ptr->multiread; ptr++) {
if (strcmp(var, ptr->name) == 0) {
return ptr;
}
@@ -220,7 +214,6 @@ int cmd_var(int argc,
struct repository *repo UNUSED)
{
const struct git_var *git_var;
- char *val;
show_usage_if_asked(argc, argv, var_usage);
if (argc != 2)
@@ -237,12 +230,26 @@ int cmd_var(int argc,
if (!git_var)
usage(var_usage);
- val = git_var->read(IDENT_STRICT);
- if (!val)
- return 1;
+ if (git_var->read) {
+ char *val = git_var->read(IDENT_STRICT);
+
+ if (!val)
+ return 1;
- printf("%s\n", val);
- free(val);
+ printf("%s\n", val);
+ free(val);
+ } else {
+ struct string_list list = STRING_LIST_INIT_DUP;
+
+ git_var->multiread(&list);
+ if (!list.nr) {
+ string_list_clear(&list, 0);
+ return 1;
+ }
+ for (size_t i = 0; i < list.nr; i++)
+ printf("%s\n", list.items[i].string);
+ string_list_clear(&list, 0);
+ }
return 0;
}
--
2.54.0 (Apple Git-157)
|
|
Andrew Pleeter wrote on the Git mailing list (how to reply to this email): "git var" takes exactly one variable and shows its value, so a script
that needs several of them has to run the command once per variable,
paying process startup and configuration parsing each time.
Teach "git var" to take any number of variables and show them the way
"git var -l" does, i.e. as "VARIABLE=value" (or "VARIABLE\nvalue\0"
under "-z"), but limited to the variables the user asked for. A single
variable still shows its bare value, and still exits with status 1 when
it has no value, so existing users are unaffected.
When more than one variable is asked for, a variable that has no value
is simply left out of the output and the rest are still shown. That a
variable is unset is not an error in itself: a caller asking for both
GIT_CONFIG_SYSTEM and GIT_CONFIG_GLOBAL should not be told that
something went wrong merely because the user set GIT_CONFIG_NOSYSTEM.
Keeping a non-zero status for genuine errors, such as naming a variable
that does not exist, also lets callers detect those by looking at the
exit code alone.
Signed-off-by: Andrew Pleeter <[email protected]>
---
Documentation/git-var.adoc | 32 +++++++++++-------
builtin/var.c | 56 +++++++++++++++++++------------
t/t0007-git-var.sh | 67 ++++++++++++++++++++++++++++++++++++++
3 files changed, 123 insertions(+), 32 deletions(-)
diff --git a/Documentation/git-var.adoc b/Documentation/git-var.adoc
index 9052fbc549..d0772d9743 100644
--- a/Documentation/git-var.adoc
+++ b/Documentation/git-var.adoc
@@ -10,17 +10,22 @@ SYNOPSIS
--------
[synopsis]
git var [-z] -l
-git var [-z] <variable>
+git var [-z] <variable>...
DESCRIPTION
-----------
-Prints a Git logical variable. Exits with code 1 if the variable has
-no value.
+Prints Git logical variables. When a single variable is requested, its
+bare value is printed, and the command exits with code 1 if it has no
+value. When multiple variables are requested, they are printed as
+`VARIABLE=value` pairs, separated by newlines; a variable that has no
+value is omitted from the output, which is not by itself an error.
-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`.
+If `-z` is given, the output format changes depending on the mode:
+
+* With a single variable, the bare value is terminated by a NUL byte.
+* With multiple variables or with `-l`, the variable name and 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
-------
@@ -32,8 +37,9 @@ OPTIONS
`-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.
+ `-l` or when multiple variables are requested, the variable name
+ and its value are separated by a newline, and each entry is
+ terminated with a NUL byte.
EXAMPLES
--------
@@ -97,9 +103,11 @@ 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 (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.
+values, which are separated by newlines (or NUL bytes if `-z` is given),
+and are listed in order from highest to lowest priority. When querying
+multiple variables (or using `-l`), each value is output as a separate
+`VARIABLE=value` entry (or `VARIABLE\nvalue\0` with `-z`). 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 c9691070b8..5fbebc62e2 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -23,7 +23,7 @@
static const char * const var_usage[] = {
N_("git var [-z] -l"),
- N_("git var [-z] <variable>"),
+ N_("git var [-z] <variable>..."),
NULL
};
@@ -225,9 +225,9 @@ int cmd_var(int argc,
const char *prefix,
struct repository *repo UNUSED)
{
- const struct git_var *git_var;
int list = 0;
int nul_term = 0;
+ char delim;
char term;
struct option options[] = {
OPT_BOOL('l', NULL, &list,
@@ -248,36 +248,52 @@ int cmd_var(int argc,
return 0;
}
- if (argc != 1)
+ if (!argc)
usage_with_options(var_usage, options);
repo_config(the_repository, git_default_config, NULL);
+ delim = nul_term ? '\n' : '=';
term = nul_term ? '\0' : '\n';
- git_var = get_git_var(argv[0]);
- if (!git_var)
- usage_with_options(var_usage, options);
+ for (int i = 0; i < argc; i++) {
+ const struct git_var *git_var = get_git_var(argv[i]);
- if (git_var->read) {
- char *val = git_var->read(IDENT_STRICT);
+ if (!git_var)
+ usage_with_options(var_usage, options);
- if (!val)
- return 1;
+ if (git_var->read) {
+ char *val = git_var->read(IDENT_STRICT);
- printf("%s%c", val, term);
- free(val);
- } else {
- struct string_list list = STRING_LIST_INIT_DUP;
+ if (!val) {
+ if (argc == 1)
+ return 1;
+ continue;
+ }
+ if (argc == 1)
+ printf("%s%c", val, term);
+ else
+ printf("%s%c%s%c", git_var->name, delim,
+ val, term);
+ free(val);
+ } else {
+ struct string_list list = STRING_LIST_INIT_DUP;
- git_var->multiread(&list);
- if (!list.nr) {
+ git_var->multiread(&list);
+ if (argc == 1 && !list.nr) {
+ string_list_clear(&list, 0);
+ return 1;
+ }
+ for (size_t j = 0; j < list.nr; j++) {
+ if (argc == 1)
+ printf("%s%c", list.items[j].string,
+ term);
+ else
+ printf("%s%c%s%c", git_var->name, delim,
+ list.items[j].string, term);
+ }
string_list_clear(&list, 0);
- return 1;
}
- for (size_t i = 0; i < list.nr; i++)
- printf("%s%c", list.items[i].string, term);
- string_list_clear(&list, 0);
}
return 0;
diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh
index 661d0539c0..593610300a 100755
--- a/t/t0007-git-var.sh
+++ b/t/t0007-git-var.sh
@@ -306,4 +306,71 @@ test_expect_success 'options must precede variable arguments' '
test_must_fail git var GIT_AUTHOR_IDENT -z
'
+test_expect_success 'get multiple variables' '
+ test_tick &&
+ cat >expect <<-EOF &&
+ GIT_AUTHOR_IDENT=$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+ GIT_COMMITTER_IDENT=$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ EOF
+ git var GIT_AUTHOR_IDENT GIT_COMMITTER_IDENT >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'get multiple variables with -z' '
+ test_tick &&
+ printf "GIT_AUTHOR_IDENT\n%sQGIT_COMMITTER_IDENT\n%sQ" \
+ "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" \
+ "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE" >expect &&
+ git var -z GIT_AUTHOR_IDENT GIT_COMMITTER_IDENT >actual.raw &&
+ nul_to_q <actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'get multiple variables including multi-valued variable' '
+ test_tick &&
+ TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+ cat >expect <<-EOF &&
+ GIT_AUTHOR_IDENT=$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+ GIT_CONFIG_GLOBAL=$TRASHDIR/foo/git/config
+ GIT_CONFIG_GLOBAL=$TRASHDIR/.gitconfig
+ GIT_COMMITTER_IDENT=$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ EOF
+ HOME="$TRASHDIR" XDG_CONFIG_HOME="$TRASHDIR/foo" \
+ git var GIT_AUTHOR_IDENT GIT_CONFIG_GLOBAL GIT_COMMITTER_IDENT >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'get multiple variables including multi-valued variable with -z' '
+ test_tick &&
+ TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+ printf "GIT_AUTHOR_IDENT\n%sQGIT_CONFIG_GLOBAL\n%sQGIT_CONFIG_GLOBAL\n%sQ" \
+ "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" \
+ "$TRASHDIR/foo/git/config" "$TRASHDIR/.gitconfig" >expect &&
+ HOME="$TRASHDIR" XDG_CONFIG_HOME="$TRASHDIR/foo" \
+ git var -z GIT_AUTHOR_IDENT GIT_CONFIG_GLOBAL >actual.raw &&
+ nul_to_q <actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'variable without a value is omitted but is not an error' '
+ test_tick &&
+ cat >expect <<-EOF &&
+ GIT_AUTHOR_IDENT=$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+ GIT_COMMITTER_IDENT=$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ EOF
+ test_env GIT_CONFIG_GLOBAL= \
+ git var GIT_AUTHOR_IDENT GIT_CONFIG_GLOBAL GIT_COMMITTER_IDENT >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'a single variable without a value still exits with 1' '
+ test_env GIT_CONFIG_GLOBAL= test_expect_code 1 git var GIT_CONFIG_GLOBAL >out &&
+ test_must_be_empty out
+'
+
+test_expect_success 'unknown variable is a usage error' '
+ test_must_fail git var GIT_AUTHOR_IDENT NO_SUCH_VARIABLE 2>err &&
+ test_grep usage err
+'
+
test_done
--
2.54.0 (Apple Git-157)
|
|
Andrew Pleeter wrote on the Git mailing list (how to reply to this email): 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 <[email protected]>
---
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)
|
|
Andrew Pleeter wrote on the Git mailing list (how to reply to this email): A script that wants only the author's name has to ask for
GIT_AUTHOR_IDENT and take the result apart itself, which means
reimplementing the rules split_ident_line() already knows, and getting
them subtly wrong for names that contain an e-mail address or other
awkward characters.
Add GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL and GIT_AUTHOR_DATE, together
with their GIT_COMMITTER_* counterparts, which give the individual
pieces of the corresponding *_IDENT variable. They are derived from
the same ident string, so they are strict in the same way: asking for
one of them fails if the identity cannot be determined.
Signed-off-by: Andrew Pleeter <[email protected]>
---
Documentation/git-var.adoc | 36 +++++++++++++--
builtin/var.c | 90 ++++++++++++++++++++++++++++++++++++++
t/t0007-git-var.sh | 56 ++++++++++++++++++++++++
3 files changed, 178 insertions(+), 4 deletions(-)
diff --git a/Documentation/git-var.adoc b/Documentation/git-var.adoc
index d0772d9743..6982228f97 100644
--- a/Documentation/git-var.adoc
+++ b/Documentation/git-var.adoc
@@ -43,17 +43,45 @@ OPTIONS
EXAMPLES
--------
- $ git var GIT_AUTHOR_IDENT
- Eric W. Biederman <[email protected]> 1121223278 -0600
+* Get the author identity:
++
+------------
+$ git var GIT_AUTHOR_IDENT
+Eric W. Biederman <[email protected]> 1121223278 -0600
+------------
+
+* Get the author name and email:
++
+------------
+$ git var GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+GIT_AUTHOR_NAME=Eric W. Biederman
+GIT_AUTHOR_EMAIL=[email protected]
+------------
VARIABLES
---------
`GIT_AUTHOR_IDENT`::
- The author of a piece of code.
+`GIT_AUTHOR_NAME`::
+`GIT_AUTHOR_EMAIL`::
+`GIT_AUTHOR_DATE`::
+ The authorship information that would be recorded in the
+ resulting commit object if you ran `git commit` right now.
+ `GIT_AUTHOR_IDENT` consists of the author's name, e-mail
+ address, and timestamp+timezone. These three pieces of
+ information are available separately as `GIT_AUTHOR_NAME`,
+ `GIT_AUTHOR_EMAIL`, and `GIT_AUTHOR_DATE`.
`GIT_COMMITTER_IDENT`::
- The person who put a piece of code into Git.
+`GIT_COMMITTER_NAME`::
+`GIT_COMMITTER_EMAIL`::
+`GIT_COMMITTER_DATE`::
+ The committer information that would be recorded in the
+ resulting commit object if you ran `git commit` right now.
+ `GIT_COMMITTER_IDENT` consists of the committer's name, e-mail
+ address, and timestamp+timezone. These three pieces of
+ information are available separately as `GIT_COMMITTER_NAME`,
+ `GIT_COMMITTER_EMAIL`, and `GIT_COMMITTER_DATE`.
`GIT_EDITOR`::
Text editor for use by Git commands. The value is meant to be
diff --git a/builtin/var.c b/builtin/var.c
index 5fbebc62e2..dd4325e5b5 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -27,16 +27,82 @@ static const char * const var_usage[] = {
NULL
};
+enum ident_part {
+ IDENT_NAME,
+ IDENT_MAIL,
+ IDENT_DATE,
+};
+
+static char *ident_part(const char *ident, enum ident_part part)
+{
+ struct ident_split split;
+
+ if (!ident)
+ return NULL;
+ if (split_ident_line(&split, ident, strlen(ident)))
+ return NULL;
+
+ switch (part) {
+ case IDENT_NAME:
+ if (!split.name_begin || !split.name_end)
+ BUG("split_ident_line() gave NULL names???");
+ return xmemdupz(split.name_begin,
+ split.name_end - split.name_begin);
+ case IDENT_MAIL:
+ if (!split.mail_begin || !split.mail_end)
+ BUG("split_ident_line() gave NULL mail???");
+ return xmemdupz(split.mail_begin,
+ split.mail_end - split.mail_begin);
+ case IDENT_DATE:
+ if (!split.date_begin || !split.tz_end)
+ BUG("split_ident_line() gave NULL date/tz???");
+ return xmemdupz(split.date_begin,
+ split.tz_end - split.date_begin);
+ default:
+ BUG("unknown ident_part %d", part);
+ }
+}
+
static char *committer(int ident_flag)
{
return xstrdup_or_null(git_committer_info(ident_flag));
}
+static char *committer_name(int ident_flag)
+{
+ return ident_part(git_committer_info(ident_flag), IDENT_NAME);
+}
+
+static char *committer_email(int ident_flag)
+{
+ return ident_part(git_committer_info(ident_flag), IDENT_MAIL);
+}
+
+static char *committer_date(int ident_flag)
+{
+ return ident_part(git_committer_info(ident_flag), IDENT_DATE);
+}
+
static char *author(int ident_flag)
{
return xstrdup_or_null(git_author_info(ident_flag));
}
+static char *author_name(int ident_flag)
+{
+ return ident_part(git_author_info(ident_flag), IDENT_NAME);
+}
+
+static char *author_email(int ident_flag)
+{
+ return ident_part(git_author_info(ident_flag), IDENT_MAIL);
+}
+
+static char *author_date(int ident_flag)
+{
+ return ident_part(git_author_info(ident_flag), IDENT_DATE);
+}
+
static char *editor(int ident_flag UNUSED)
{
return xstrdup_or_null(git_editor());
@@ -123,10 +189,34 @@ static struct git_var git_vars[] = {
.name = "GIT_COMMITTER_IDENT",
.read = committer,
},
+ {
+ .name = "GIT_COMMITTER_NAME",
+ .read = committer_name,
+ },
+ {
+ .name = "GIT_COMMITTER_EMAIL",
+ .read = committer_email,
+ },
+ {
+ .name = "GIT_COMMITTER_DATE",
+ .read = committer_date,
+ },
{
.name = "GIT_AUTHOR_IDENT",
.read = author,
},
+ {
+ .name = "GIT_AUTHOR_NAME",
+ .read = author_name,
+ },
+ {
+ .name = "GIT_AUTHOR_EMAIL",
+ .read = author_email,
+ },
+ {
+ .name = "GIT_AUTHOR_DATE",
+ .read = author_date,
+ },
{
.name = "GIT_EDITOR",
.read = editor,
diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh
index 593610300a..e43becff1c 100755
--- a/t/t0007-git-var.sh
+++ b/t/t0007-git-var.sh
@@ -373,4 +373,60 @@ test_expect_success 'unknown variable is a usage error' '
test_grep usage err
'
+test_expect_success 'get author identity components' '
+ test_tick &&
+ echo "$GIT_AUTHOR_NAME" >expect.name &&
+ echo "$GIT_AUTHOR_EMAIL" >expect.email &&
+ echo "$GIT_AUTHOR_DATE" >expect.date &&
+ git var GIT_AUTHOR_NAME >actual.name &&
+ git var GIT_AUTHOR_EMAIL >actual.email &&
+ git var GIT_AUTHOR_DATE >actual.date &&
+ test_cmp expect.name actual.name &&
+ test_cmp expect.email actual.email &&
+ test_cmp expect.date actual.date
+'
+
+test_expect_success 'get committer identity components' '
+ test_tick &&
+ echo "$GIT_COMMITTER_NAME" >expect.name &&
+ echo "$GIT_COMMITTER_EMAIL" >expect.email &&
+ echo "$GIT_COMMITTER_DATE" >expect.date &&
+ git var GIT_COMMITTER_NAME >actual.name &&
+ git var GIT_COMMITTER_EMAIL >actual.email &&
+ git var GIT_COMMITTER_DATE >actual.date &&
+ test_cmp expect.name actual.name &&
+ test_cmp expect.email actual.email &&
+ test_cmp expect.date actual.date
+'
+
+test_expect_success !FAIL_PREREQS,!AUTOIDENT 'identity components are strict' '
+ (
+ sane_unset GIT_COMMITTER_NAME &&
+ sane_unset GIT_COMMITTER_EMAIL &&
+ test_must_fail git var GIT_COMMITTER_NAME
+ )
+'
+
+test_expect_success 'get several identity components at once' '
+ test_tick &&
+ cat >expect <<-EOF &&
+ GIT_AUTHOR_NAME=$GIT_AUTHOR_NAME
+ GIT_AUTHOR_EMAIL=$GIT_AUTHOR_EMAIL
+ GIT_COMMITTER_NAME=$GIT_COMMITTER_NAME
+ GIT_COMMITTER_EMAIL=$GIT_COMMITTER_EMAIL
+ EOF
+ git var GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git var -l lists the identity components' '
+ git var -l >actual &&
+ test_grep "^GIT_AUTHOR_NAME=" actual &&
+ test_grep "^GIT_AUTHOR_EMAIL=" actual &&
+ test_grep "^GIT_AUTHOR_DATE=" actual &&
+ test_grep "^GIT_COMMITTER_NAME=" actual &&
+ test_grep "^GIT_COMMITTER_EMAIL=" actual &&
+ test_grep "^GIT_COMMITTER_DATE=" actual
+'
+
test_done
--
2.54.0 (Apple Git-157)
|
Teach
git varto expose individual identity components and commit signing configuration, and allow querying multiple variables with optional NUL-termination.Changes since v7:
git var -l, printingVARIABLE=valuepairs (orVARIABLE\nvalue\0when-zis given) instead of bare values, per feedback from Junio C. Hamano.VARIABLE=valueentries (orVARIABLE\nvalue\0with-z), eliminating stream ambiguity and the need for extra trailing delimiters.Changes since v6:
GIT_AUTHOR_*andGIT_COMMITTER_*entries together into concise definitions inDocumentation/git-var.adocto avoid repetitive descriptions, per feedback from Junio C. Hamano.BUG()checks inident_part()for NULL name/email pointers and date/timezone to protect against unforeseen changes insplit_ident_line().struct git_varmembermultireadto returnvoid(void (*multiread)(struct string_list *)) sincelist->nrcommunicates length.list_vars()to use an unconditionalelseblock forptr->multireadinstead of redundantelse if.cmd_var(), do not set a non-zero exit status for missing variables in multi-variable queries; callers can see the empty record (e.g.GIT_CONFIG_SYSTEMunderGIT_CONFIG_NOSYSTEM), matching Phillip Wood's recommendation.putc(term, stdout)incmd_var()when emitting delimiters.t/t0007-git-var.sh, converted-ztests to pipe output throughnul_to_qsotest_cmpproduces clean diffs rather than binary comparison errors.Changes since v5:
get_git_var()to keep the diff minimal and focused on addingptr->multiread.BUG("unknown ident_part %d", part)to default case inident_part()and removed redundant NULL checks onsplit.name_beginandsplit.mail_begin.int (*multiread)(struct string_list *)callback instruct git_varfor multi-valued variables likeGIT_CONFIG_GLOBAL, cleanly populating astruct string_listinstead of relying on embedded NUL buffers.GIT_AUTHOR_*andGIT_COMMITTER_*documentation inDocumentation/git-var.adocto describe the values that would be used if you were to rungit commitright now.git var -ldo not print an extra delimiter after multi-valued variables.Changes since v4:
git_signing_key()to directly callget_signing_key()as used throughout Git (intag,send-pack, andsign_buffer()).null_termtonul_termacrossbuiltin/var.c, and simplifiedshow_config()callback handling.cmd_var()by validating arguments directly in the main execution loop.\0with-z) for any variable that has no value, and continue printing remaining variables instead of terminating prematurely.git_config_val_global()) to internal\0delimiters, iterating directly through string sequences without allocating a temporarystring_list.\nor\0) after multi-valued variables to clearly mark the end of their list.git var -l -zformat and multi-variable handling inDocumentation/git-var.adoc.t/t0007-git-var.sh.Changes since v3:
GIT_DEFAULT_KEYtoGIT_SIGNING_KEYper feedback from Phillip Wood and Junio C Hamano; dropped the alias mechanism andcommit.gpgsigncheck.parse_options()withPARSE_OPT_STOP_AT_NON_OPTIONinbuiltin/var.c, strictly enforcing that options precede variable arguments.git config list -zformat (key\nvalue\0) forgit var -l -zto prevent ambiguity with=in config keys.GIT_CONFIG_GLOBAL) with NUL bytes under-z.char partinident_part()withenum ident_part.Documentation/git-var.adocinto separate lines for-land<variable>..., and removed awkward legacy phrasing ("of a piece of code").t/t0007-git-var.shcovering the new-zformat, multi-valued-z, and argument ordering.Changes since v2:
git ident/git whoamisubcommand entirely.GIT_AUTHOR_NAME,GIT_AUTHOR_EMAIL, andGIT_AUTHOR_DATE.GIT_COMMITTER_NAME,GIT_COMMITTER_EMAIL, andGIT_COMMITTER_DATE.GIT_SIGNING_KEYto resolve commit signing keys.git varto accept multiple variable arguments (git var <var1> <var2> ...).-zoption to terminate outputs with NUL bytes (includinggit var -l -z).Documentation/git-var.adocandt/t0007-git-var.sh.CC: "brian m. carlson" [email protected], Jeff King [email protected], Junio C Hamano [email protected]
cc: Ben Knoble [email protected]
cc: Phillip Wood [email protected]