Skip to content

Commit

Permalink
Merge branch 'jk/setup-sequence-update'
Browse files Browse the repository at this point in the history
There were numerous corner cases in which the configuration files
are read and used or not read at all depending on the directory a
Git command was run, leading to inconsistent behaviour.  The code
to set-up repository access at the beginning of a Git process has
been updated to fix them.

* jk/setup-sequence-update:
  t1007: factor out repeated setup
  init: reset cached config when entering new repo
  init: expand comments explaining config trickery
  config: only read .git/config from configured repos
  test-config: setup git directory
  t1302: use "git -C"
  pager: handle early config
  pager: use callbacks instead of configset
  pager: make pager_program a file-local static
  pager: stop loading git_default_config()
  pager: remove obsolete comment
  diff: always try to set up the repository
  diff: handle --no-index prefixes consistently
  diff: skip implicit no-index check when given --no-index
  patch-id: use RUN_SETUP_GENTLY
  hash-object: always try to set up the git repository
  • Loading branch information
gitster committed Sep 21, 2016
2 parents ac8ddd7 + 4d0efa1 commit d845d72
Show file tree
Hide file tree
Showing 16 changed files with 268 additions and 99 deletions.
27 changes: 14 additions & 13 deletions builtin/diff.c
Expand Up @@ -301,20 +301,21 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
break;
}

if (!no_index)
prefix = setup_git_directory_gently(&nongit);
prefix = setup_git_directory_gently(&nongit);

/*
* Treat git diff with at least one path outside of the
* repo the same as if the command would have been executed
* outside of a git repository. In this case it behaves
* the same way as "git diff --no-index <a> <b>", which acts
* as a colourful "diff" replacement.
*/
if (nongit || ((argc == i + 2) &&
(!path_inside_repo(prefix, argv[i]) ||
!path_inside_repo(prefix, argv[i + 1]))))
no_index = DIFF_NO_INDEX_IMPLICIT;
if (!no_index) {
/*
* Treat git diff with at least one path outside of the
* repo the same as if the command would have been executed
* outside of a git repository. In this case it behaves
* the same way as "git diff --no-index <a> <b>", which acts
* as a colourful "diff" replacement.
*/
if (nongit || ((argc == i + 2) &&
(!path_inside_repo(prefix, argv[i]) ||
!path_inside_repo(prefix, argv[i + 1]))))
no_index = DIFF_NO_INDEX_IMPLICIT;
}

if (!no_index)
gitmodules_config();
Expand Down
13 changes: 8 additions & 5 deletions builtin/hash-object.c
Expand Up @@ -87,6 +87,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
int stdin_paths = 0;
int no_filters = 0;
int literally = 0;
int nongit = 0;
unsigned flags = HASH_FORMAT_CHECK;
const char *vpath = NULL;
const struct option hash_object_options[] = {
Expand All @@ -107,12 +108,14 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, NULL, hash_object_options,
hash_object_usage, 0);

if (flags & HASH_WRITE_OBJECT) {
if (flags & HASH_WRITE_OBJECT)
prefix = setup_git_directory();
prefix_length = prefix ? strlen(prefix) : 0;
if (vpath && prefix)
vpath = prefix_filename(prefix, prefix_length, vpath);
}
else
prefix = setup_git_directory_gently(&nongit);

prefix_length = prefix ? strlen(prefix) : 0;
if (vpath && prefix)
vpath = prefix_filename(prefix, prefix_length, vpath);

git_config(git_default_config, NULL);

Expand Down
17 changes: 13 additions & 4 deletions builtin/init-db.c
Expand Up @@ -185,16 +185,25 @@ static int create_default_files(const char *template_path)
/* Just look for `init.templatedir` */
git_config(git_init_db_config, NULL);

/* First copy the templates -- we might have the default
/*
* First copy the templates -- we might have the default
* config file there, in which case we would want to read
* from it after installing.
*
* Before reading that config, we also need to clear out any cached
* values (since we've just potentially changed what's available on
* disk).
*/
copy_templates(template_path);

git_config_clear();
reset_shared_repository();
git_config(git_default_config, NULL);
is_bare_repository_cfg = init_is_bare_repository;

/* reading existing config may have overwrote it */
/*
* We must make sure command-line options continue to override any
* values we might have just re-read from the config.
*/
is_bare_repository_cfg = init_is_bare_repository;
if (init_shared_repository != -1)
set_shared_repository(init_shared_repository);

Expand Down
14 changes: 13 additions & 1 deletion cache.h
Expand Up @@ -453,6 +453,12 @@ static inline enum object_type object_type(unsigned int mode)
*/
extern const char * const local_repo_env[];

/*
* Returns true iff we have a configured git repository (either via
* setup_git_directory, or in the environment via $GIT_DIR).
*/
int have_git_dir(void);

extern int is_bare_repository_cfg;
extern int is_bare_repository(void);
extern int is_inside_git_dir(void);
Expand Down Expand Up @@ -665,8 +671,15 @@ extern size_t delta_base_cache_limit;
extern unsigned long big_file_threshold;
extern unsigned long pack_size_limit_cfg;

/*
* Accessors for the core.sharedrepository config which lazy-load the value
* from the config (if not already set). The "reset" function can be
* used to unset "set" or cached value, meaning that the value will be loaded
* fresh from the config file on the next call to get_shared_repository().
*/
void set_shared_repository(int value);
int get_shared_repository(void);
void reset_shared_repository(void);

/*
* Do replace refs need to be checked this run? This variable is
Expand Down Expand Up @@ -1813,7 +1826,6 @@ extern void write_file(const char *path, const char *fmt, ...);

/* pager.c */
extern void setup_pager(void);
extern const char *pager_program;
extern int pager_in_use(void);
extern int pager_use_color;
extern int term_columns(void);
Expand Down
5 changes: 1 addition & 4 deletions config.c
Expand Up @@ -927,9 +927,6 @@ static int git_default_core_config(const char *var, const char *value)
return 0;
}

if (!strcmp(var, "core.pager"))
return git_config_string(&pager_program, var, value);

if (!strcmp(var, "core.editor"))
return git_config_string(&editor_program, var, value);

Expand Down Expand Up @@ -1289,7 +1286,7 @@ static int do_git_config_sequence(config_fn_t fn, void *data)
int ret = 0;
char *xdg_config = xdg_config_home("config");
char *user_config = expand_user_path("~/.gitconfig");
char *repo_config = git_pathdup("config");
char *repo_config = have_git_dir() ? git_pathdup("config") : NULL;

current_parsing_scope = CONFIG_SCOPE_SYSTEM;
if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
Expand Down
3 changes: 3 additions & 0 deletions diff-no-index.c
Expand Up @@ -281,6 +281,9 @@ void diff_no_index(struct rev_info *revs,

DIFF_OPT_SET(&revs->diffopt, NO_INDEX);

DIFF_OPT_SET(&revs->diffopt, RELATIVE_NAME);
revs->diffopt.prefix = prefix;

revs->max_count = -2;
diff_setup_done(&revs->diffopt);

Expand Down
13 changes: 12 additions & 1 deletion environment.c
Expand Up @@ -40,7 +40,6 @@ size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
size_t delta_base_cache_limit = 96 * 1024 * 1024;
unsigned long big_file_threshold = 512 * 1024 * 1024;
const char *pager_program;
int pager_use_color = 1;
const char *editor_program;
const char *askpass_program;
Expand Down Expand Up @@ -196,6 +195,13 @@ int is_bare_repository(void)
return is_bare_repository_cfg && !get_git_work_tree();
}

int have_git_dir(void)
{
return startup_info->have_repository
|| git_dir
|| getenv(GIT_DIR_ENVIRONMENT);
}

const char *get_git_dir(void)
{
if (!git_dir)
Expand Down Expand Up @@ -345,3 +351,8 @@ int get_shared_repository(void)
}
return the_shared_repository;
}

void reset_shared_repository(void)
{
need_shared_repository_from_config = 1;
}
2 changes: 1 addition & 1 deletion git.c
Expand Up @@ -444,7 +444,7 @@ static struct cmd_struct commands[] = {
{ "pack-objects", cmd_pack_objects, RUN_SETUP },
{ "pack-redundant", cmd_pack_redundant, RUN_SETUP },
{ "pack-refs", cmd_pack_refs, RUN_SETUP },
{ "patch-id", cmd_patch_id },
{ "patch-id", cmd_patch_id, RUN_SETUP_GENTLY },
{ "pickaxe", cmd_blame, RUN_SETUP },
{ "prune", cmd_prune, RUN_SETUP },
{ "prune-packed", cmd_prune_packed, RUN_SETUP },
Expand Down
93 changes: 73 additions & 20 deletions pager.c
Expand Up @@ -6,12 +6,8 @@
#define DEFAULT_PAGER "less"
#endif

/*
* This is split up from the rest of git so that we can do
* something different on Windows.
*/

static struct child_process pager_process = CHILD_PROCESS_INIT;
static const char *pager_program;

static void wait_for_pager(int in_signal)
{
Expand Down Expand Up @@ -40,6 +36,44 @@ static void wait_for_pager_signal(int signo)
raise(signo);
}

static int core_pager_config(const char *var, const char *value, void *data)
{
if (!strcmp(var, "core.pager"))
return git_config_string(&pager_program, var, value);
return 0;
}

static void read_early_config(config_fn_t cb, void *data)
{
git_config_with_options(cb, data, NULL, 1);

/*
* Note that this is a really dirty hack that does the wrong thing in
* many cases. The crux of the problem is that we cannot run
* setup_git_directory() early on in git's setup, so we have no idea if
* we are in a repository or not, and therefore are not sure whether
* and how to read repository-local config.
*
* So if we _aren't_ in a repository (or we are but we would reject its
* core.repositoryformatversion), we'll read whatever is in .git/config
* blindly. Similarly, if we _are_ in a repository, but not at the
* root, we'll fail to find .git/config (because it's really
* ../.git/config, etc). See t7006 for a complete set of failures.
*
* However, we have historically provided this hack because it does
* work some of the time (namely when you are at the top-level of a
* valid repository), and would rarely make things worse (i.e., you do
* not generally have a .git/config file sitting around).
*/
if (!startup_info->have_repository) {
struct git_config_source repo_config;

memset(&repo_config, 0, sizeof(repo_config));
repo_config.file = ".git/config";
git_config_with_options(cb, data, &repo_config, 1);
}
}

const char *git_pager(int stdout_is_tty)
{
const char *pager;
Expand All @@ -50,7 +84,7 @@ const char *git_pager(int stdout_is_tty)
pager = getenv("GIT_PAGER");
if (!pager) {
if (!pager_program)
git_config(git_default_config, NULL);
read_early_config(core_pager_config, NULL);
pager = pager_program;
}
if (!pager)
Expand Down Expand Up @@ -180,23 +214,42 @@ int decimal_width(uintmax_t number)
return width;
}

/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
int check_pager_config(const char *cmd)
struct pager_command_config_data {
const char *cmd;
int want;
char *value;
};

static int pager_command_config(const char *var, const char *value, void *vdata)
{
int want = -1;
struct strbuf key = STRBUF_INIT;
const char *value = NULL;
strbuf_addf(&key, "pager.%s", cmd);
if (git_config_key_is_valid(key.buf) &&
!git_config_get_value(key.buf, &value)) {
int b = git_config_maybe_bool(key.buf, value);
struct pager_command_config_data *data = vdata;
const char *cmd;

if (skip_prefix(var, "pager.", &cmd) && !strcmp(cmd, data->cmd)) {
int b = git_config_maybe_bool(var, value);
if (b >= 0)
want = b;
data->want = b;
else {
want = 1;
pager_program = xstrdup(value);
data->want = 1;
data->value = xstrdup(value);
}
}
strbuf_release(&key);
return want;

return 0;
}

/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
int check_pager_config(const char *cmd)
{
struct pager_command_config_data data;

data.cmd = cmd;
data.want = -1;
data.value = NULL;

read_early_config(pager_command_config, &data);

if (data.value)
pager_program = data.value;
return data.want;
}
3 changes: 3 additions & 0 deletions t/helper/test-config.c
Expand Up @@ -72,6 +72,9 @@ int cmd_main(int argc, const char **argv)
const char *v;
const struct string_list *strptr;
struct config_set cs;

setup_git_directory();

git_configset_init(&cs);

if (argc < 2) {
Expand Down
9 changes: 9 additions & 0 deletions t/t0001-init.sh
Expand Up @@ -384,4 +384,13 @@ test_expect_success MINGW 'bare git dir not hidden' '
! is_hidden newdir
'

test_expect_success 'remote init from does not use config from cwd' '
rm -rf newdir &&
test_config core.logallrefupdates true &&
git init newdir &&
echo true >expect &&
git -C newdir config --bool core.logallrefupdates >actual &&
test_cmp expect actual
'

test_done

0 comments on commit d845d72

Please sign in to comment.