From: Johannes Schindelin <johannes.schindelin@gmx.de>
To: git@vger.kernel.org
Cc: Junio C Hamano <gitster@pobox.com>, Jeff King <peff@peff.net>,
Duy Nguyen <pclouds@gmail.com>
Subject: [PATCH v2 0/9] Fix the early config
Date: Fri, 3 Mar 2017 03:03:56 +0100 (CET) [thread overview]
Message-ID: <cover.1488506615.git.johannes.schindelin@gmx.de> (raw)
In-Reply-To: <cover.1481211338.git.johannes.schindelin@gmx.de>
These patches are an attempt to make Git's startup sequence a bit less
surprising.
The idea here is to discover the .git/ directory gently (i.e. without
changing the current working directory, or global variables), and to use
it to read the .git/config file early, before we actually called
setup_git_directory() (if we ever do that).
This also allows us to fix the early config e.g. to determine the pager
or to resolve aliases in a non-surprising manner.
The first iteration of this patch series still tried to be clever and to
avoid having to disentangle the side effects from the
setup_git_directory_gently_1() function simply by duplicating the logic.
However, Peff suggested in a very short sentence that this would not fly
well.
Little did I know that I would spend the better part of an entire week
on trying to address that innocuous comment! There are simply *so many*
side effects in that code. Who would have thought that a function
called check_repository_format() would set global variables?
But after all that work, I am actually quite a bit satisfied with the
way things turned out.
My dirty little secret is that I actually need this for something else
entirely. I need to patch an internal version of Git to gather
statistics, and to that end I need to read the config before and after
running every Git command. Hence the need for a gentle, and correct
early config.
Notes:
- I do not handle dashed invocations of `init` and `clone` correctly.
That is, even if `git-init` and `git-clone` clearly do not want to
read the local config, they do. It does not matter all that much
because they do not use a pager, but still. It is a wart.
- The read_early_config() function is still called multiple times,
re-reading all the config files and re-discovering the .git/ directory
multiple times, which is quite wasteful. I was tempted to take care of
that but I must not run the danger to spread myself even thinner these
days. If a patch adding that caching were to fly my way, I'd gladly
integrate it, of course... ;-)
Changes since v1:
- the discover_git_directory() function is no longer completely separate
from setup_git_directory(), but a callee of the latter.
- t7006 succeeds now (I removed the incorrect test case in favor of one
that verifies that setup_git_directory() was not run via the tell-tale
that the current working directory has not changed when the pager
runs).
Johannes Schindelin (9):
t7006: replace dubious test
setup_git_directory(): use is_dir_sep() helper
setup_git_directory(): avoid changing global state during discovery
Export the discover_git_directory() function
Make read_early_config() reusable
read_early_config(): special-case builtins that create a repository
read_early_config(): avoid .git/config hack when unneeded
read_early_config(): really discover .git/
Test read_early_config()
cache.h | 4 +-
config.c | 36 +++++++++
git.c | 3 +
pager.c | 31 -------
setup.c | 211 +++++++++++++++++++++++++++++++-----------------
t/helper/test-config.c | 15 ++++
t/t1309-early-config.sh | 50 ++++++++++++
t/t7006-pager.sh | 18 ++++-
8 files changed, 257 insertions(+), 111 deletions(-)
create mode 100755 t/t1309-early-config.sh
base-commit: 3bc53220cb2dcf709f7a027a3f526befd021d858
Published-As: https://github.com/dscho/git/releases/tag/early-config-v2
Fetch-It-Via: git fetch https://github.com/dscho/git early-config-v2
Interdiff vs v1:
diff --git a/builtin/am.c b/builtin/am.c
index 2b81dabddd9..f7a7a971fbe 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1791,7 +1791,7 @@ static int do_interactive(struct am_state *state)
}
strbuf_release(&msg);
} else if (*reply == 'v' || *reply == 'V') {
- const char *pager = git_pager(1, 1);
+ const char *pager = git_pager(1);
struct child_process cp = CHILD_PROCESS_INIT;
if (!pager)
diff --git a/builtin/blame.c b/builtin/blame.c
index 628ca237da6..cffc6265408 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2919,7 +2919,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
assign_blame(&sb, opt);
if (!incremental)
- setup_pager(1);
+ setup_pager();
free(final_commit_name);
diff --git a/builtin/grep.c b/builtin/grep.c
index f820e4b1c4d..9304c33e750 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -1133,7 +1133,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
}
if (show_in_pager == default_pager)
- show_in_pager = git_pager(1, 1);
+ show_in_pager = git_pager(1);
if (show_in_pager) {
opt.color = 0;
opt.name_only = 1;
@@ -1268,7 +1268,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
die(_("option not supported with --recurse-submodules."));
if (!show_in_pager && !opt.status_only)
- setup_pager(1);
+ setup_pager();
if (!use_index && (untracked || cached))
die(_("--cached or --untracked cannot be used with --no-index."));
diff --git a/builtin/log.c b/builtin/log.c
index 96618d38cbf..55d20cc2d88 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -203,7 +203,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
if (rev->line_level_traverse)
line_log_init(rev, line_cb.prefix, &line_cb.args);
- setup_pager(1);
+ setup_pager();
}
static void cmd_log_init(int argc, const char **argv, const char *prefix,
@@ -1600,7 +1600,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (!use_stdout)
output_directory = set_outdir(prefix, output_directory);
else
- setup_pager(1);
+ setup_pager();
if (output_directory) {
if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
diff --git a/builtin/var.c b/builtin/var.c
index 879867b8427..aedbb53a2da 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -19,7 +19,7 @@ static const char *editor(int flag)
static const char *pager(int flag)
{
- const char *pgm = git_pager(1, 1);
+ const char *pgm = git_pager(1);
if (!pgm)
pgm = "cat";
diff --git a/cache.h b/cache.h
index 4d89966d711..0af7141242f 100644
--- a/cache.h
+++ b/cache.h
@@ -518,6 +518,7 @@ extern void set_git_work_tree(const char *tree);
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
extern void setup_work_tree(void);
+extern const char *discover_git_directory(struct strbuf *gitdir);
extern const char *setup_git_directory_gently(int *);
extern const char *setup_git_directory(void);
extern char *prefix_path(const char *prefix, int len, const char *path);
@@ -1428,7 +1429,7 @@ extern const char *fmt_name(const char *name, const char *email);
extern const char *ident_default_name(void);
extern const char *ident_default_email(void);
extern const char *git_editor(void);
-extern const char *git_pager(int stdout_is_tty, int discover_git_dir);
+extern const char *git_pager(int stdout_is_tty);
extern int git_ident_config(const char *, const char *, void *);
extern void reset_ident_date(void);
@@ -1797,8 +1798,7 @@ extern int git_config_from_blob_sha1(config_fn_t fn, const char *name,
const unsigned char *sha1, void *data);
extern void git_config_push_parameter(const char *text);
extern int git_config_from_parameters(config_fn_t fn, void *data);
-extern void read_early_config(config_fn_t cb, void *data,
- int discover_git_dir);
+extern void read_early_config(config_fn_t cb, void *data);
extern void git_config(config_fn_t fn, void *);
extern int git_config_with_options(config_fn_t fn, void *,
struct git_config_source *config_source,
@@ -1994,12 +1994,12 @@ __attribute__((format (printf, 2, 3)))
extern void write_file(const char *path, const char *fmt, ...);
/* pager.c */
-extern void setup_pager(int discover_git_dir);
+extern void setup_pager(void);
extern int pager_in_use(void);
extern int pager_use_color;
extern int term_columns(void);
extern int decimal_width(uintmax_t);
-extern int check_pager_config(const char *cmd, int discover_git_dir);
+extern int check_pager_config(const char *cmd);
extern void prepare_pager_args(struct child_process *, const char *pager);
extern const char *editor_program;
@@ -2070,7 +2070,7 @@ const char *split_cmdline_strerror(int cmdline_errno);
/* setup.c */
struct startup_info {
- int have_repository;
+ int have_repository, creating_repository;
const char *prefix;
};
extern struct startup_info *startup_info;
diff --git a/config.c b/config.c
index c9f191e1fe3..bcda397d42e 100644
--- a/config.c
+++ b/config.c
@@ -1412,106 +1412,32 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
}
}
-/*
- * A "string_list_each_func_t" function that canonicalizes an entry
- * from GIT_CEILING_DIRECTORIES using real_path_if_valid(), or
- * discards it if unusable. The presence of an empty entry in
- * GIT_CEILING_DIRECTORIES turns off canonicalization for all
- * subsequent entries.
- */
-static int canonicalize_ceiling_entry(struct string_list_item *item,
- void *cb_data)
-{
- int *empty_entry_found = cb_data;
- char *ceil = item->string;
-
- if (!*ceil) {
- *empty_entry_found = 1;
- return 0;
- } else if (!is_absolute_path(ceil)) {
- return 0;
- } else if (*empty_entry_found) {
- /* Keep entry but do not canonicalize it */
- return 1;
- } else {
- const char *real_path = real_path_if_valid(ceil);
- if (!real_path)
- return 0;
- free(item->string);
- item->string = xstrdup(real_path);
- return 1;
- }
-}
-
-/*
- * Note that this is a really dirty hack that replicates what the
- * setup_git_directory() function does, without changing the current
- * working directory. The crux of the problem is that we cannot run
- * setup_git_directory() early on in git's setup, so we have to
- * duplicate the work that setup_git_directory() would otherwise do.
- */
-static int discover_git_directory_gently(struct strbuf *result)
-{
- const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
- int ceiling_offset = -1;
- const char *p;
-
- if (strbuf_getcwd(result) < 0)
- return -1;
- p = real_path_if_valid(result->buf);
- if (!p)
- return -1;
- strbuf_reset(result);
- strbuf_addstr(result, p);
-
- if (env_ceiling_dirs) {
- struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
- int empty_entry_found = 0;
-
- string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP,
- -1);
- filter_string_list(&ceiling_dirs, 0, canonicalize_ceiling_entry,
- &empty_entry_found);
- ceiling_offset = longest_ancestor_length(result->buf,
- &ceiling_dirs);
- string_list_clear(&ceiling_dirs, 0);
- }
-
- if (ceiling_offset < 0 && has_dos_drive_prefix(result->buf))
- ceiling_offset = 1;
-
- for (;;) {
- int len = result->len, i;
-
- strbuf_addstr(result, "/" DEFAULT_GIT_DIR_ENVIRONMENT);
- p = read_gitfile_gently(result->buf, &i);
- if (p) {
- strbuf_reset(result);
- strbuf_addstr(result, p);
- return 0;
- }
- if (is_git_directory(result->buf))
- return 0;
- strbuf_setlen(result, len);
- if (is_git_directory(result->buf))
- return 0;
- for (i = len; --i > ceiling_offset; )
- if (is_dir_sep(result->buf[i]))
- break;
- if (i <= ceiling_offset)
- return -1;
- strbuf_setlen(result, i);
- }
-}
-
-void read_early_config(config_fn_t cb, void *data, int discover_git_dir)
+void read_early_config(config_fn_t cb, void *data)
{
struct strbuf buf = STRBUF_INIT;
git_config_with_options(cb, data, NULL, 1);
- if (discover_git_dir && !have_git_dir() &&
- !discover_git_directory_gently(&buf)) {
+ /*
+ * 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->creating_repository && !have_git_dir() &&
+ discover_git_directory(&buf)) {
struct git_config_source repo_config;
memset(&repo_config, 0, sizeof(repo_config));
diff --git a/diff.c b/diff.c
index 1e1d3b85c2a..051761be405 100644
--- a/diff.c
+++ b/diff.c
@@ -5259,6 +5259,6 @@ void setup_diff_pager(struct diff_options *opt)
* --exit-code" in hooks and other scripts, we do not do so.
*/
if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
- check_pager_config("diff", 1) != 0)
- setup_pager(1);
+ check_pager_config("diff") != 0)
+ setup_pager();
}
diff --git a/git.c b/git.c
index d4712b25fee..9fb9bb90a21 100644
--- a/git.c
+++ b/git.c
@@ -61,13 +61,13 @@ static void restore_env(int external_alias)
}
}
-static void commit_pager_choice(int discover_git_dir) {
+static void commit_pager_choice(void) {
switch (use_pager) {
case 0:
setenv("GIT_PAGER", "cat", 1);
break;
case 1:
- setup_pager(discover_git_dir);
+ setup_pager();
break;
default:
break;
@@ -261,7 +261,7 @@ static int handle_alias(int *argcp, const char ***argv)
if (alias_string[0] == '!') {
struct child_process child = CHILD_PROCESS_INIT;
- commit_pager_choice(1);
+ commit_pager_choice();
restore_env(1);
child.use_shell = 1;
@@ -318,13 +318,12 @@ static int handle_alias(int *argcp, const char ***argv)
#define RUN_SETUP (1<<0)
#define RUN_SETUP_GENTLY (1<<1)
#define USE_PAGER (1<<2)
-#define CREATES_GIT_DIR (1<<3)
/*
* require working tree to be present -- anything uses this needs
* RUN_SETUP for reading from the configuration file.
*/
-#define NEED_WORK_TREE (1<<4)
-#define SUPPORT_SUPER_PREFIX (1<<5)
+#define NEED_WORK_TREE (1<<3)
+#define SUPPORT_SUPER_PREFIX (1<<4)
struct cmd_struct {
const char *cmd;
@@ -338,6 +337,9 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
struct stat st;
const char *prefix;
+ if (p->fn == cmd_init_db || p->fn == cmd_clone)
+ startup_info->creating_repository = 1;
+
prefix = NULL;
help = argc == 2 && !strcmp(argv[1], "-h");
if (!help) {
@@ -349,7 +351,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
}
if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY))
- use_pager = check_pager_config(p->cmd, !(p->option & CREATES_GIT_DIR));
+ use_pager = check_pager_config(p->cmd);
if (use_pager == -1 && p->option & USE_PAGER)
use_pager = 1;
@@ -357,7 +359,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */
trace_repo_setup(prefix);
}
- commit_pager_choice(!(p->option & CREATES_GIT_DIR));
+ commit_pager_choice();
if (!help && get_super_prefix()) {
if (!(p->option & SUPPORT_SUPER_PREFIX))
@@ -413,7 +415,7 @@ static struct cmd_struct commands[] = {
{ "cherry", cmd_cherry, RUN_SETUP },
{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
- { "clone", cmd_clone, CREATES_GIT_DIR },
+ { "clone", cmd_clone },
{ "column", cmd_column, RUN_SETUP_GENTLY },
{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
{ "commit-tree", cmd_commit_tree, RUN_SETUP },
@@ -440,7 +442,7 @@ static struct cmd_struct commands[] = {
{ "hash-object", cmd_hash_object },
{ "help", cmd_help },
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
- { "init", cmd_init_db, CREATES_GIT_DIR },
+ { "init", cmd_init_db },
{ "init-db", cmd_init_db },
{ "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
{ "log", cmd_log, RUN_SETUP },
@@ -585,8 +587,8 @@ static void execv_dashed_external(const char **argv)
die("%s doesn't support --super-prefix", argv[0]);
if (use_pager == -1)
- use_pager = check_pager_config(argv[0], 1);
- commit_pager_choice(1);
+ use_pager = check_pager_config(argv[0]);
+ commit_pager_choice();
argv_array_pushf(&cmd.args, "git-%s", argv[0]);
argv_array_pushv(&cmd.args, argv + 1);
@@ -684,7 +686,7 @@ int cmd_main(int argc, const char **argv)
skip_prefix(argv[0], "--", &argv[0]);
} else {
/* The user didn't specify a command; give them help */
- commit_pager_choice(1);
+ commit_pager_choice();
printf("usage: %s\n\n", git_usage_string);
list_common_cmds_help();
printf("\n%s\n", _(git_more_info_string));
diff --git a/pager.c b/pager.c
index 16b3cbe2320..73ca8bc3b17 100644
--- a/pager.c
+++ b/pager.c
@@ -43,7 +43,7 @@ static int core_pager_config(const char *var, const char *value, void *data)
return 0;
}
-const char *git_pager(int stdout_is_tty, int discover_git_dir)
+const char *git_pager(int stdout_is_tty)
{
const char *pager;
@@ -53,8 +53,7 @@ const char *git_pager(int stdout_is_tty, int discover_git_dir)
pager = getenv("GIT_PAGER");
if (!pager) {
if (!pager_program)
- read_early_config(core_pager_config, NULL,
- discover_git_dir);
+ read_early_config(core_pager_config, NULL);
pager = pager_program;
}
if (!pager)
@@ -101,9 +100,9 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)
setup_pager_env(&pager_process->env_array);
}
-void setup_pager(int discover_git_dir)
+void setup_pager(void)
{
- const char *pager = git_pager(isatty(1), discover_git_dir);
+ const char *pager = git_pager(isatty(1));
if (!pager)
return;
@@ -209,7 +208,7 @@ static int pager_command_config(const char *var, const char *value, void *vdata)
}
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
-int check_pager_config(const char *cmd, int discover_git_dir)
+int check_pager_config(const char *cmd)
{
struct pager_command_config_data data;
@@ -217,7 +216,7 @@ int check_pager_config(const char *cmd, int discover_git_dir)
data.want = -1;
data.value = NULL;
- read_early_config(pager_command_config, &data, discover_git_dir);
+ read_early_config(pager_command_config, &data);
if (data.value)
pager_program = data.value;
diff --git a/setup.c b/setup.c
index 967f289f1ef..7ceca6cc6ef 100644
--- a/setup.c
+++ b/setup.c
@@ -816,50 +816,49 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
}
}
+enum discovery_result {
+ GIT_DIR_NONE = 0,
+ GIT_DIR_EXPLICIT,
+ GIT_DIR_DISCOVERED,
+ GIT_DIR_BARE,
+ /* these are errors */
+ GIT_DIR_HIT_CEILING = -1,
+ GIT_DIR_HIT_MOUNT_POINT = -2
+};
+
/*
* We cannot decide in this function whether we are in the work tree or
* not, since the config can only be read _after_ this function was called.
+ *
+ * Also, we avoid changing any global state (such as the current working
+ * directory) to allow early callers.
+ *
+ * The directory where the search should start needs to be passed in via the
+ * `dir` parameter; upon return, the `dir` buffer will contain the path of
+ * the directory where the search ended, and `gitdir` will contain the path of
+ * the discovered .git/ directory, if any. This path may be relative against
+ * `dir` (i.e. *not* necessarily the cwd).
*/
-static const char *setup_git_directory_gently_1(int *nongit_ok)
+static enum discovery_result discover_git_directory_1(struct strbuf *dir,
+ struct strbuf *gitdir)
{
const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
- static struct strbuf cwd = STRBUF_INIT;
- const char *gitdirenv, *ret;
- char *gitfile;
- int offset, offset_parent, ceil_offset = -1;
+ const char *gitdirenv;
+ int ceil_offset = -1, min_offset = has_dos_drive_prefix(dir->buf) ? 3 : 1;
dev_t current_device = 0;
int one_filesystem = 1;
/*
- * We may have read an incomplete configuration before
- * setting-up the git directory. If so, clear the cache so
- * that the next queries to the configuration reload complete
- * configuration (including the per-repo config file that we
- * ignored previously).
- */
- git_config_clear();
-
- /*
- * Let's assume that we are in a git repository.
- * If it turns out later that we are somewhere else, the value will be
- * updated accordingly.
- */
- if (nongit_ok)
- *nongit_ok = 0;
-
- if (strbuf_getcwd(&cwd))
- die_errno(_("Unable to read current working directory"));
- offset = cwd.len;
-
- /*
* If GIT_DIR is set explicitly, we're not going
* to do any discovery, but we still do repository
* validation.
*/
gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
- if (gitdirenv)
- return setup_explicit_git_dir(gitdirenv, &cwd, nongit_ok);
+ if (gitdirenv) {
+ strbuf_addstr(gitdir, gitdirenv);
+ return GIT_DIR_EXPLICIT;
+ }
if (env_ceiling_dirs) {
int empty_entry_found = 0;
@@ -867,15 +866,15 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
filter_string_list(&ceiling_dirs, 0,
canonicalize_ceiling_entry, &empty_entry_found);
- ceil_offset = longest_ancestor_length(cwd.buf, &ceiling_dirs);
+ ceil_offset = longest_ancestor_length(dir->buf, &ceiling_dirs);
string_list_clear(&ceiling_dirs, 0);
}
- if (ceil_offset < 0 && has_dos_drive_prefix(cwd.buf))
- ceil_offset = 1;
+ if (ceil_offset < 0)
+ ceil_offset = min_offset - 2;
/*
- * Test in the following order (relative to the cwd):
+ * Test in the following order (relative to the dir):
* - .git (file containing "gitdir: <path>")
* - .git/
* - ./ (bare)
@@ -887,61 +886,123 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
*/
one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
if (one_filesystem)
- current_device = get_device_or_die(".", NULL, 0);
+ current_device = get_device_or_die(dir->buf, NULL, 0);
for (;;) {
- gitfile = (char*)read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
- if (gitfile)
- gitdirenv = gitfile = xstrdup(gitfile);
- else {
- if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
- gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
- }
-
+ int offset = dir->len;
+
+ if (offset > min_offset)
+ strbuf_addch(dir, '/');
+ strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT);
+ gitdirenv = read_gitfile(dir->buf);
+ if (!gitdirenv && is_git_directory(dir->buf))
+ gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
+ strbuf_setlen(dir, offset);
if (gitdirenv) {
- ret = setup_discovered_git_dir(gitdirenv,
- &cwd, offset,
- nongit_ok);
- free(gitfile);
- return ret;
+ strbuf_addstr(gitdir, gitdirenv);
+ return GIT_DIR_DISCOVERED;
}
- free(gitfile);
- if (is_git_directory("."))
- return setup_bare_git_dir(&cwd, offset, nongit_ok);
-
- offset_parent = offset;
- while (--offset_parent > ceil_offset && cwd.buf[offset_parent] != '/');
- if (offset_parent <= ceil_offset)
- return setup_nongit(cwd.buf, nongit_ok);
- if (one_filesystem) {
- dev_t parent_device = get_device_or_die("..", cwd.buf,
- offset);
- if (parent_device != current_device) {
- if (nongit_ok) {
- if (chdir(cwd.buf))
- die_errno(_("Cannot come back to cwd"));
- *nongit_ok = 1;
- return NULL;
- }
- strbuf_setlen(&cwd, offset);
- die(_("Not a git repository (or any parent up to mount point %s)\n"
- "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
- cwd.buf);
- }
- }
- if (chdir("..")) {
- strbuf_setlen(&cwd, offset);
- die_errno(_("Cannot change to '%s/..'"), cwd.buf);
+ if (is_git_directory(dir->buf)) {
+ strbuf_addstr(gitdir, ".");
+ return GIT_DIR_BARE;
}
- offset = offset_parent;
+
+ if (offset <= min_offset)
+ return GIT_DIR_HIT_CEILING;
+
+ while (--offset > ceil_offset && !is_dir_sep(dir->buf[offset]));
+ if (offset <= ceil_offset)
+ return GIT_DIR_HIT_CEILING;
+
+ strbuf_setlen(dir, offset > min_offset ? offset : min_offset);
+ if (one_filesystem &&
+ current_device != get_device_or_die(dir->buf, NULL, offset))
+ return GIT_DIR_HIT_MOUNT_POINT;
+ }
+}
+
+const char *discover_git_directory(struct strbuf *gitdir)
+{
+ struct strbuf dir = STRBUF_INIT;
+ int len;
+
+ if (strbuf_getcwd(&dir))
+ return NULL;
+
+ len = dir.len;
+ if (discover_git_directory_1(&dir, gitdir) < 0) {
+ strbuf_release(&dir);
+ return NULL;
+ }
+
+ if (dir.len < len && !is_absolute_path(gitdir->buf)) {
+ strbuf_addch(&dir, '/');
+ strbuf_insert(gitdir, 0, dir.buf, dir.len);
}
+ strbuf_release(&dir);
+
+ return gitdir->buf;
}
const char *setup_git_directory_gently(int *nongit_ok)
{
+ struct strbuf cwd = STRBUF_INIT, dir = STRBUF_INIT, gitdir = STRBUF_INIT;
const char *prefix;
- prefix = setup_git_directory_gently_1(nongit_ok);
+ /*
+ * We may have read an incomplete configuration before
+ * setting-up the git directory. If so, clear the cache so
+ * that the next queries to the configuration reload complete
+ * configuration (including the per-repo config file that we
+ * ignored previously).
+ */
+ git_config_clear();
+
+ /*
+ * Let's assume that we are in a git repository.
+ * If it turns out later that we are somewhere else, the value will be
+ * updated accordingly.
+ */
+ if (nongit_ok)
+ *nongit_ok = 0;
+
+ if (strbuf_getcwd(&cwd))
+ die_errno(_("Unable to read current working directory"));
+ strbuf_addbuf(&dir, &cwd);
+
+ switch (discover_git_directory_1(&dir, &gitdir)) {
+ case GIT_DIR_NONE:
+ prefix = NULL;
+ break;
+ case GIT_DIR_EXPLICIT:
+ prefix = setup_explicit_git_dir(gitdir.buf, &cwd, nongit_ok);
+ break;
+ case GIT_DIR_DISCOVERED:
+ if (dir.len < cwd.len && chdir(dir.buf))
+ die(_("Cannot change to '%s'"), dir.buf);
+ prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
+ nongit_ok);
+ break;
+ case GIT_DIR_BARE:
+ if (dir.len < cwd.len && chdir(dir.buf))
+ die(_("Cannot change to '%s'"), dir.buf);
+ prefix = setup_bare_git_dir(&cwd, dir.len, nongit_ok);
+ break;
+ case GIT_DIR_HIT_CEILING:
+ prefix = setup_nongit(cwd.buf, nongit_ok);
+ break;
+ case GIT_DIR_HIT_MOUNT_POINT:
+ if (nongit_ok) {
+ *nongit_ok = 1;
+ return NULL;
+ }
+ die(_("Not a git repository (or any parent up to mount point %s)\n"
+ "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
+ dir.buf);
+ default:
+ die("BUG: unhandled discover_git_directory() result");
+ }
+
if (prefix)
setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
else
diff --git a/t/helper/test-config.c b/t/helper/test-config.c
index 51050695876..8e3ed6a76cb 100644
--- a/t/helper/test-config.c
+++ b/t/helper/test-config.c
@@ -84,7 +84,7 @@ int cmd_main(int argc, const char **argv)
struct config_set cs;
if (argc == 3 && !strcmp(argv[1], "read_early_config")) {
- read_early_config(early_config_cb, (void *)argv[2], 1);
+ read_early_config(early_config_cb, (void *)argv[2]);
return 0;
}
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index c8dc665f2fd..bf89340988b 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -360,27 +360,37 @@ test_pager_choices 'git aliasedlog'
test_default_pager expect_success 'git -p aliasedlog'
test_PAGER_overrides expect_success 'git -p aliasedlog'
test_core_pager_overrides expect_success 'git -p aliasedlog'
-test_core_pager_subdir expect_failure 'git -p aliasedlog'
+test_core_pager_subdir expect_success 'git -p aliasedlog'
test_GIT_PAGER_overrides expect_success 'git -p aliasedlog'
test_default_pager expect_success 'git -p true'
test_PAGER_overrides expect_success 'git -p true'
test_core_pager_overrides expect_success 'git -p true'
-test_core_pager_subdir expect_failure 'git -p true'
+test_core_pager_subdir expect_success 'git -p true'
test_GIT_PAGER_overrides expect_success 'git -p true'
test_default_pager expect_success test_must_fail 'git -p request-pull'
test_PAGER_overrides expect_success test_must_fail 'git -p request-pull'
test_core_pager_overrides expect_success test_must_fail 'git -p request-pull'
-test_core_pager_subdir expect_failure test_must_fail 'git -p request-pull'
+test_core_pager_subdir expect_success test_must_fail 'git -p request-pull'
test_GIT_PAGER_overrides expect_success test_must_fail 'git -p request-pull'
test_default_pager expect_success test_must_fail 'git -p'
test_PAGER_overrides expect_success test_must_fail 'git -p'
test_local_config_ignored expect_failure test_must_fail 'git -p'
-test_no_local_config_subdir expect_success test_must_fail 'git -p'
test_GIT_PAGER_overrides expect_success test_must_fail 'git -p'
+test_expect_success TTY 'core.pager in repo config works and retains cwd' '
+ sane_unset GIT_PAGER &&
+ test_config core.pager "cat >cwd-retained" &&
+ (
+ cd sub &&
+ rm -f cwd-retained &&
+ test_terminal git -p rev-parse HEAD &&
+ test -e cwd-retained
+ )
+'
+
test_doesnt_paginate expect_failure test_must_fail 'git -p nonsense'
test_pager_choices 'git shortlog'
--
2.12.0.windows.1.3.g8a117c48243
next prev parent reply other threads:[~2017-03-03 2:04 UTC|newest]
Thread overview: 123+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-12-08 15:35 [PATCH/RFC 0/7] Pie-in-the-sky attempt to fix the early config Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 1/7] Make read_early_config() reusable Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 2/7] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 3/7] Mark builtins that create .git/ directories Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 4/7] read_early_config(): special-case `init` and `clone` Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 5/7] read_early_config(): really discover .git/ Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 6/7] WIP read_config_early(): respect ceiling directories Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 7/7] WIP: read_early_config(): add tests Johannes Schindelin
2016-12-08 17:26 ` [PATCH/RFC 0/7] Pie-in-the-sky attempt to fix the early config Jeff King
2016-12-09 17:28 ` Johannes Schindelin
2016-12-09 17:55 ` Jeff King
2016-12-09 12:42 ` Duy Nguyen
2016-12-09 16:52 ` Johannes Schindelin
2017-03-03 2:03 ` Johannes Schindelin [this message]
2017-03-03 2:04 ` [PATCH v2 1/9] t7006: replace dubious test Johannes Schindelin
2017-03-03 3:36 ` Jeff King
2017-03-03 11:10 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 2/9] setup_git_directory(): use is_dir_sep() helper Johannes Schindelin
2017-03-03 3:37 ` Jeff King
2017-03-03 11:16 ` Johannes Schindelin
2017-03-03 11:26 ` Jeff King
2017-03-03 15:35 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 3/9] setup_git_directory(): avoid changing global state during discovery Johannes Schindelin
2017-03-03 4:24 ` Jeff King
2017-03-03 13:54 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 4/9] Export the discover_git_directory() function Johannes Schindelin
2017-03-03 4:45 ` Jeff King
2017-03-03 14:49 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 5/9] Make read_early_config() reusable Johannes Schindelin
2017-03-03 4:46 ` Jeff King
2017-03-03 14:11 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 6/9] read_early_config(): special-case builtins that create a repository Johannes Schindelin
2017-03-03 4:51 ` Jeff King
2017-03-03 15:11 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 7/9] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2017-03-03 4:51 ` Jeff King
2017-03-03 2:04 ` [PATCH v2 8/9] read_early_config(): really discover .git/ Johannes Schindelin
2017-03-03 5:06 ` Jeff King
2017-03-03 15:26 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 9/9] Test read_early_config() Johannes Schindelin
2017-03-03 5:07 ` Jeff King
2017-03-03 15:04 ` Johannes Schindelin
2017-03-03 5:14 ` [PATCH v2 0/9] Fix the early config Jeff King
2017-03-03 15:31 ` Johannes Schindelin
2017-03-03 17:31 ` [PATCH v3 " Johannes Schindelin
2017-03-03 17:32 ` [PATCH v3 1/9] t7006: replace dubious test Johannes Schindelin
2017-03-03 17:32 ` [PATCH v3 2/9] setup_git_directory(): use is_dir_sep() helper Johannes Schindelin
2017-03-03 17:32 ` [PATCH v3 3/9] Prepare setup_discovered_git_directory() the root directory Johannes Schindelin
2017-03-03 17:32 ` [PATCH v3 4/9] setup_git_directory_1(): avoid changing global state Johannes Schindelin
2017-03-03 17:33 ` [PATCH v3 5/9] Export the discover_git_directory() function Johannes Schindelin
2017-03-03 17:33 ` [PATCH v3 6/9] Make read_early_config() reusable Johannes Schindelin
2017-03-03 17:33 ` [PATCH v3 7/9] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2017-03-03 17:33 ` [PATCH v3 8/9] read_early_config(): really discover .git/ Johannes Schindelin
2017-03-03 17:33 ` [PATCH v3 9/9] Test read_early_config() Johannes Schindelin
2017-03-03 21:35 ` [PATCH v3 0/9] Fix the early config Junio C Hamano
2017-03-07 11:55 ` Johannes Schindelin
2017-03-07 15:18 ` Johannes Schindelin
2017-03-04 7:39 ` Jeff King
2017-03-05 3:36 ` Junio C Hamano
2017-03-07 14:31 ` Johannes Schindelin
2017-03-08 7:30 ` Jeff King
2017-03-08 16:18 ` Johannes Schindelin
2017-03-08 16:29 ` Jeff King
2017-03-08 17:09 ` Junio C Hamano
2017-03-08 17:42 ` Jeff King
2017-03-08 22:43 ` Junio C Hamano
2017-03-09 11:51 ` Johannes Schindelin
2017-03-09 12:16 ` Jeff King
2017-03-10 16:39 ` Junio C Hamano
2017-03-07 14:32 ` [PATCH v4 00/10] " Johannes Schindelin
2017-03-07 14:32 ` [PATCH v4 01/10] t7006: replace dubious test Johannes Schindelin
2017-03-07 14:32 ` [PATCH v4 02/10] setup_git_directory(): use is_dir_sep() helper Johannes Schindelin
2017-03-07 14:32 ` [PATCH v4 03/10] Prepare setup_discovered_git_directory() the root directory Johannes Schindelin
2017-03-07 14:32 ` [PATCH v4 04/10] setup_git_directory_1(): avoid changing global state Johannes Schindelin
2017-03-07 23:24 ` Junio C Hamano
2017-03-07 23:35 ` Brandon Williams
2017-03-08 0:57 ` Johannes Schindelin
2017-03-08 2:10 ` Brandon Williams
2017-03-07 14:33 ` [PATCH v4 05/10] Introduce the discover_git_directory() function Johannes Schindelin
2017-03-07 14:33 ` [PATCH v4 06/10] Make read_early_config() reusable Johannes Schindelin
2017-03-07 14:33 ` [PATCH v4 07/10] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2017-03-07 14:33 ` [PATCH v4 08/10] read_early_config(): really discover .git/ Johannes Schindelin
2017-03-07 14:33 ` [PATCH v4 09/10] Test read_early_config() Johannes Schindelin
2017-03-07 14:33 ` [PATCH v4 10/10] setup_git_directory_gently_1(): avoid die()ing Johannes Schindelin
2017-03-09 22:23 ` [PATCH v5 00/11] Fix the early config Johannes Schindelin
2017-03-09 22:23 ` [PATCH v5 01/11] t7006: replace dubious test Johannes Schindelin
2017-03-09 22:23 ` [PATCH v5 02/11] setup_git_directory(): use is_dir_sep() helper Johannes Schindelin
2017-03-09 22:23 ` [PATCH v5 03/11] Prepare setup_discovered_git_directory() the root directory Johannes Schindelin
2017-03-09 22:24 ` [PATCH v5 04/11] setup_git_directory_1(): avoid changing global state Johannes Schindelin
2017-03-10 19:34 ` Junio C Hamano
2017-03-09 22:24 ` [PATCH v5 05/11] Introduce the discover_git_directory() function Johannes Schindelin
2017-03-09 22:24 ` [PATCH v5 06/11] Make read_early_config() reusable Johannes Schindelin
2017-03-09 22:24 ` [PATCH v5 07/11] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2017-03-09 22:25 ` [PATCH v5 08/11] read_early_config(): really discover .git/ Johannes Schindelin
2017-03-09 22:25 ` [PATCH v5 09/11] Test read_early_config() Johannes Schindelin
2017-03-10 19:02 ` Junio C Hamano
2017-03-13 17:19 ` Johannes Schindelin
2017-03-13 17:32 ` Junio C Hamano
2017-03-09 22:25 ` [PATCH v5 10/11] setup_git_directory_gently_1(): avoid die()ing Johannes Schindelin
2017-03-10 18:58 ` Junio C Hamano
2017-03-13 19:38 ` Johannes Schindelin
2017-03-13 19:47 ` Junio C Hamano
2017-03-13 20:20 ` Junio C Hamano
2017-03-13 21:46 ` Johannes Schindelin
2017-03-13 23:31 ` Junio C Hamano
2017-03-09 22:25 ` [PATCH v5 11/11] t1309: document cases where we would want early config not to die() Johannes Schindelin
2017-03-13 20:09 ` [PATCH v6 00/12] Fix the early config Johannes Schindelin
2017-03-13 20:09 ` [PATCH v6 01/12] t7006: replace dubious test Johannes Schindelin
2017-03-13 20:09 ` [PATCH v6 02/12] setup_git_directory(): use is_dir_sep() helper Johannes Schindelin
2017-03-13 20:09 ` [PATCH v6 03/12] Prepare setup_discovered_git_directory() the root directory Johannes Schindelin
2017-03-13 20:34 ` Junio C Hamano
2017-03-13 21:44 ` Johannes Schindelin
2017-03-13 20:10 ` [PATCH v6 04/12] setup_git_directory_1(): avoid changing global state Johannes Schindelin
2017-03-13 20:10 ` [PATCH v6 05/12] Introduce the discover_git_directory() function Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 06/12] Make read_early_config() reusable Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 07/12] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 08/12] read_early_config(): really discover .git/ Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 09/12] Add t1309 to test read_early_config() Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 10/12] setup_git_directory_gently_1(): avoid die()ing Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 11/12] t1309: document cases where we would want early config not to die() Johannes Schindelin
2017-03-13 20:12 ` [PATCH v6 12/12] setup.c: mention unresolved problems Johannes Schindelin
2017-03-13 22:31 ` [PATCH v6 00/12] Fix the early config Junio C Hamano
2017-03-14 18:01 ` Jeff King
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
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=cover.1488506615.git.johannes.schindelin@gmx.de \
--to=johannes.schindelin@gmx.de \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=pclouds@gmail.com \
--cc=peff@peff.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.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).