Merge branch 'bc/more-git-var'

Add more "git var" for toolsmiths to learn various locations Git is
configured with either via the configuration or hardcoded defaults.

* bc/more-git-var:
  var: add config file locations
  var: add attributes files locations
  attr: expose and rename accessor functions
  var: adjust memory allocation for strings
  var: format variable structure with C99 initializers
  var: add support for listing the shell
  t: add a function to check executable bit
  var: mark unused parameters in git_var callbacks
This commit is contained in:
Junio C Hamano 2023-07-04 16:08:18 -07:00
Родитель 812907d16f ed773a18c6
Коммит 89d62d5e8e
7 изменённых файлов: 303 добавлений и 27 удалений

Просмотреть файл

@ -71,6 +71,29 @@ endif::git-default-pager[]
GIT_DEFAULT_BRANCH::
The name of the first branch created in newly initialized repositories.
GIT_SHELL_PATH::
The path of the binary providing the POSIX shell for commands which use the shell.
GIT_ATTR_SYSTEM::
The path to the system linkgit:gitattributes[5] file, if one is enabled.
GIT_ATTR_GLOBAL::
The path to the global (per-user) linkgit:gitattributes[5] file.
GIT_CONFIG_SYSTEM::
The path to the system configuration file, if one is enabled.
GIT_CONFIG_GLOBAL::
The path to the global (per-user) configuration files, if any.
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.
Note that paths are printed even if they do not exist, but not if they are
disabled by other environment variables.
SEE ALSO
--------
linkgit:git-commit-tree[1]

14
attr.c
Просмотреть файл

@ -872,7 +872,7 @@ static struct attr_stack *read_attr(struct index_state *istate,
return res;
}
static const char *git_etc_gitattributes(void)
const char *git_attr_system_file(void)
{
static const char *system_wide;
if (!system_wide)
@ -880,7 +880,7 @@ static const char *git_etc_gitattributes(void)
return system_wide;
}
static const char *get_home_gitattributes(void)
const char *git_attr_global_file(void)
{
if (!git_attributes_file)
git_attributes_file = xdg_config_home("attributes");
@ -888,7 +888,7 @@ static const char *get_home_gitattributes(void)
return git_attributes_file;
}
static int git_attr_system(void)
int git_attr_system_is_enabled(void)
{
return !git_env_bool("GIT_ATTR_NOSYSTEM", 0);
}
@ -922,14 +922,14 @@ static void bootstrap_attr_stack(struct index_state *istate,
push_stack(stack, e, NULL, 0);
/* system-wide frame */
if (git_attr_system()) {
e = read_attr_from_file(git_etc_gitattributes(), flags);
if (git_attr_system_is_enabled()) {
e = read_attr_from_file(git_attr_system_file(), flags);
push_stack(stack, e, NULL, 0);
}
/* home directory */
if (get_home_gitattributes()) {
e = read_attr_from_file(get_home_gitattributes(), flags);
if (git_attr_global_file()) {
e = read_attr_from_file(git_attr_global_file(), flags);
push_stack(stack, e, NULL, 0);
}

9
attr.h
Просмотреть файл

@ -227,4 +227,13 @@ void git_attr_set_direction(enum git_attr_direction new_direction);
void attr_start(void);
/* Return the system gitattributes file. */
const char *git_attr_system_file(void);
/* Return the global gitattributes file, if any. */
const char *git_attr_global_file(void);
/* Return whether the system gitattributes file is enabled and should be used. */
int git_attr_system_is_enabled(void);
#endif /* ATTR_H */

Просмотреть файл

@ -4,60 +4,188 @@
* Copyright (C) Eric Biederman, 2005
*/
#include "builtin.h"
#include "attr.h"
#include "config.h"
#include "editor.h"
#include "ident.h"
#include "pager.h"
#include "refs.h"
#include "path.h"
#include "strbuf.h"
static const char var_usage[] = "git var (-l | <variable>)";
static const char *editor(int flag)
static char *committer(int ident_flag)
{
return git_editor();
return xstrdup_or_null(git_committer_info(ident_flag));
}
static const char *sequence_editor(int flag)
static char *author(int ident_flag)
{
return git_sequence_editor();
return xstrdup_or_null(git_author_info(ident_flag));
}
static const char *pager(int flag)
static char *editor(int ident_flag UNUSED)
{
return xstrdup_or_null(git_editor());
}
static char *sequence_editor(int ident_flag UNUSED)
{
return xstrdup_or_null(git_sequence_editor());
}
static char *pager(int ident_flag UNUSED)
{
const char *pgm = git_pager(1);
if (!pgm)
pgm = "cat";
return pgm;
return xstrdup(pgm);
}
static const char *default_branch(int flag)
static char *default_branch(int ident_flag UNUSED)
{
return git_default_branch_name(1);
return xstrdup_or_null(git_default_branch_name(1));
}
static char *shell_path(int ident_flag UNUSED)
{
return xstrdup(SHELL_PATH);
}
static char *git_attr_val_system(int ident_flag UNUSED)
{
if (git_attr_system_is_enabled()) {
char *file = xstrdup(git_attr_system_file());
normalize_path_copy(file, file);
return file;
}
return NULL;
}
static char *git_attr_val_global(int ident_flag UNUSED)
{
char *file = xstrdup(git_attr_global_file());
if (file) {
normalize_path_copy(file, file);
return file;
}
return NULL;
}
static char *git_config_val_system(int ident_flag UNUSED)
{
if (git_config_system()) {
char *file = git_system_config();
normalize_path_copy(file, file);
return file;
}
return NULL;
}
static char *git_config_val_global(int ident_flag UNUSED)
{
struct strbuf buf = STRBUF_INIT;
char *user, *xdg;
size_t unused;
git_global_config(&user, &xdg);
if (xdg && *xdg) {
normalize_path_copy(xdg, xdg);
strbuf_addf(&buf, "%s\n", xdg);
}
if (user && *user) {
normalize_path_copy(user, user);
strbuf_addf(&buf, "%s\n", 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;
const char *(*read)(int);
char *(*read)(int);
int multivalued;
};
static struct git_var git_vars[] = {
{ "GIT_COMMITTER_IDENT", git_committer_info },
{ "GIT_AUTHOR_IDENT", git_author_info },
{ "GIT_EDITOR", editor },
{ "GIT_SEQUENCE_EDITOR", sequence_editor },
{ "GIT_PAGER", pager },
{ "GIT_DEFAULT_BRANCH", default_branch },
{ "", NULL },
{
.name = "GIT_COMMITTER_IDENT",
.read = committer,
},
{
.name = "GIT_AUTHOR_IDENT",
.read = author,
},
{
.name = "GIT_EDITOR",
.read = editor,
},
{
.name = "GIT_SEQUENCE_EDITOR",
.read = sequence_editor,
},
{
.name = "GIT_PAGER",
.read = pager,
},
{
.name = "GIT_DEFAULT_BRANCH",
.read = default_branch,
},
{
.name = "GIT_SHELL_PATH",
.read = shell_path,
},
{
.name = "GIT_ATTR_SYSTEM",
.read = git_attr_val_system,
},
{
.name = "GIT_ATTR_GLOBAL",
.read = git_attr_val_global,
},
{
.name = "GIT_CONFIG_SYSTEM",
.read = git_config_val_system,
},
{
.name = "GIT_CONFIG_GLOBAL",
.read = git_config_val_global,
.multivalued = 1,
},
{
.name = "",
.read = NULL,
},
};
static void list_vars(void)
{
struct git_var *ptr;
const char *val;
char *val;
for (ptr = git_vars; ptr->read; ptr++)
if ((val = ptr->read(0)))
printf("%s=%s\n", ptr->name, val);
if ((val = ptr->read(0))) {
if (ptr->multivalued && *val) {
struct string_list list = STRING_LIST_INIT_DUP;
int i;
string_list_split(&list, val, '\n', -1);
for (i = 0; i < list.nr; i++)
printf("%s=%s\n", ptr->name, list.items[i].string);
string_list_clear(&list, 0);
} else {
printf("%s=%s\n", ptr->name, val);
}
free(val);
}
}
static const struct git_var *get_git_var(const char *var)
@ -83,7 +211,7 @@ static int show_config(const char *var, const char *value, void *cb)
int cmd_var(int argc, const char **argv, const char *prefix UNUSED)
{
const struct git_var *git_var;
const char *val;
char *val;
if (argc != 2)
usage(var_usage);
@ -104,6 +232,7 @@ int cmd_var(int argc, const char **argv, const char *prefix UNUSED)
return 1;
printf("%s\n", val);
free(val);
return 0;
}

Просмотреть файл

@ -1102,6 +1102,12 @@ see test-lib-functions.sh for the full list and their options.
the symbolic link in the file system and a part that does; then only
the latter part need be protected by a SYMLINKS prerequisite (see below).
- test_path_is_executable
This tests whether a file is executable and prints an error message
if not. This must be used only under the POSIXPERM prerequisite
(see below).
- test_oid_init
This function loads facts and useful object IDs related to the hash

Просмотреть файл

@ -147,6 +147,84 @@ test_expect_success 'get GIT_SEQUENCE_EDITOR with configuration and environment
)
'
test_expect_success POSIXPERM 'GIT_SHELL_PATH points to a valid executable' '
shellpath=$(git var GIT_SHELL_PATH) &&
test_path_is_executable "$shellpath"
'
# We know in this environment that our shell will be one of a few fixed values
# that all end in "sh".
test_expect_success MINGW 'GIT_SHELL_PATH points to a suitable shell' '
shellpath=$(git var GIT_SHELL_PATH) &&
case "$shellpath" in
*sh) ;;
*) return 1;;
esac
'
test_expect_success 'GIT_ATTR_SYSTEM produces expected output' '
test_must_fail env GIT_ATTR_NOSYSTEM=1 git var GIT_ATTR_SYSTEM &&
(
sane_unset GIT_ATTR_NOSYSTEM &&
systempath=$(git var GIT_ATTR_SYSTEM) &&
test "$systempath" != ""
)
'
test_expect_success 'GIT_ATTR_GLOBAL points to the correct location' '
TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
globalpath=$(XDG_CONFIG_HOME="$TRASHDIR/.config" git var GIT_ATTR_GLOBAL) &&
test "$globalpath" = "$TRASHDIR/.config/git/attributes" &&
(
sane_unset XDG_CONFIG_HOME &&
globalpath=$(HOME="$TRASHDIR" git var GIT_ATTR_GLOBAL) &&
test "$globalpath" = "$TRASHDIR/.config/git/attributes"
)
'
test_expect_success 'GIT_CONFIG_SYSTEM points to the correct location' '
TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
test_must_fail env GIT_CONFIG_NOSYSTEM=1 git var GIT_CONFIG_SYSTEM &&
(
sane_unset GIT_CONFIG_NOSYSTEM &&
systempath=$(git var GIT_CONFIG_SYSTEM) &&
test "$systempath" != "" &&
systempath=$(GIT_CONFIG_SYSTEM=/dev/null git var GIT_CONFIG_SYSTEM) &&
if test_have_prereq MINGW
then
test "$systempath" = "nul"
else
test "$systempath" = "/dev/null"
fi &&
systempath=$(GIT_CONFIG_SYSTEM="$TRASHDIR/gitconfig" git var GIT_CONFIG_SYSTEM) &&
test "$systempath" = "$TRASHDIR/gitconfig"
)
'
test_expect_success 'GIT_CONFIG_GLOBAL points to the correct location' '
TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
HOME="$TRASHDIR" XDG_CONFIG_HOME="$TRASHDIR/foo" git var GIT_CONFIG_GLOBAL >actual &&
echo "$TRASHDIR/foo/git/config" >expected &&
echo "$TRASHDIR/.gitconfig" >>expected &&
test_cmp expected actual &&
(
sane_unset XDG_CONFIG_HOME &&
HOME="$TRASHDIR" git var GIT_CONFIG_GLOBAL >actual &&
echo "$TRASHDIR/.config/git/config" >expected &&
echo "$TRASHDIR/.gitconfig" >>expected &&
test_cmp expected actual &&
globalpath=$(GIT_CONFIG_GLOBAL=/dev/null git var GIT_CONFIG_GLOBAL) &&
if test_have_prereq MINGW
then
test "$globalpath" = "nul"
else
test "$globalpath" = "/dev/null"
fi &&
globalpath=$(GIT_CONFIG_GLOBAL="$TRASHDIR/gitconfig" git var GIT_CONFIG_GLOBAL) &&
test "$globalpath" = "$TRASHDIR/gitconfig"
)
'
# For git var -l, we check only a representative variable;
# testing the whole output would make our test too brittle with
# respect to unrelated changes in the test suite's environment.
@ -164,6 +242,28 @@ test_expect_success 'git var -l lists config' '
test_cmp expect actual.bare
'
test_expect_success 'git var -l lists multiple global configs' '
TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
HOME="$TRASHDIR" XDG_CONFIG_HOME="$TRASHDIR/foo" git var -l >actual &&
grep "^GIT_CONFIG_GLOBAL=" actual >filtered &&
echo "GIT_CONFIG_GLOBAL=$TRASHDIR/foo/git/config" >expected &&
echo "GIT_CONFIG_GLOBAL=$TRASHDIR/.gitconfig" >>expected &&
test_cmp expected filtered
'
test_expect_success 'git var -l does not split multiline editors' '
(
GIT_EDITOR="!f() {
echo Hello!
}; f" &&
export GIT_EDITOR &&
echo "GIT_EDITOR=$GIT_EDITOR" >expected &&
git var -l >var &&
sed -n -e "/^GIT_EDITOR/,\$p" var | head -n 3 >actual &&
test_cmp expected actual
)
'
test_expect_success 'listing and asking for variables are exclusive' '
test_must_fail git var -l GIT_COMMITTER_IDENT
'

Просмотреть файл

@ -910,6 +910,15 @@ test_path_is_symlink () {
fi
}
test_path_is_executable () {
test "$#" -ne 1 && BUG "1 param"
if ! test -x "$1"
then
echo "$1 is not executable"
false
fi
}
# Check if the directory exists and is empty as expected, barf otherwise.
test_dir_is_empty () {
test "$#" -ne 1 && BUG "1 param"