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, nor any 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. My own use case: in the GVFS Git fork, we need to execute pre-command and post-command hooks before and after *every* Git command. A previous version of the pre-command/post-command hook support was broken, as it used run_hook() which implicitly called setup_git_directory() too early. The discover_git_directory() function (and due to core.hooksPath also the read_early_config() function) helped me fix this. Notable notes: - Even if it can cause surprising problems, `init` and `clone` are not special-cased. Rationale: it would introduce technical debt and violate the Principle Of Least Astonishment. - The read_early_config() function does not cache Git directory discovery nor read values. This is left for another patch series, if it ever becomes necessary. - The alias handling in git.c could possibly benefit from this work, but again, this is a separate topic from the current patch series. Changes since v4: - plugged memory leaks in setup_git_directory() (thanks, Brandon) - added some tests that demonstrate how early config currently can fail in undesired ways - fixed indentation (this change was snuck into gitster/git's js/early-config branch, and I happened to notice the difference by pure random chance) Johannes Schindelin (11): t7006: replace dubious test setup_git_directory(): use is_dir_sep() helper Prepare setup_discovered_git_directory() the root directory setup_git_directory_1(): avoid changing global state Introduce the discover_git_directory() function Make read_early_config() reusable read_early_config(): avoid .git/config hack when unneeded read_early_config(): really discover .git/ Test read_early_config() setup_git_directory_gently_1(): avoid die()ing t1309: document cases where we would want early config not to die() cache.h | 8 ++ config.c | 25 +++++ pager.c | 31 ------ setup.c | 252 +++++++++++++++++++++++++++++++++--------------- t/helper/test-config.c | 15 +++ t/t1309-early-config.sh | 75 ++++++++++++++ t/t7006-pager.sh | 18 +++- 7 files changed, 313 insertions(+), 111 deletions(-) create mode 100755 t/t1309-early-config.sh base-commit: e0688e9b28f2c5ff711460ee8b62077be5df2360 Published-As: https://github.com/dscho/git/releases/tag/early-config-v5 Fetch-It-Via: git fetch https://github.com/dscho/git early-config-v5 Interdiff vs v4: diff --git a/setup.c b/setup.c index 9118b48590a..b0a28f609e2 100644 --- a/setup.c +++ b/setup.c @@ -903,7 +903,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, if (die_on_error || error_code == READ_GITFILE_ERR_NOT_A_FILE) { if (is_git_directory(dir->buf)) - gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT; + gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT; } else if (error_code && error_code != READ_GITFILE_ERR_STAT_FAILED) return GIT_DIR_INVALID_GITFILE; @@ -979,7 +979,8 @@ const char *discover_git_directory(struct strbuf *gitdir) const char *setup_git_directory_gently(int *nongit_ok) { - struct strbuf cwd = STRBUF_INIT, dir = STRBUF_INIT, gitdir = STRBUF_INIT; + static struct strbuf cwd = STRBUF_INIT; + struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT; const char *prefix; /* @@ -1027,6 +1028,8 @@ const char *setup_git_directory_gently(int *nongit_ok) case GIT_DIR_HIT_MOUNT_POINT: if (nongit_ok) { *nongit_ok = 1; + strbuf_release(&cwd); + strbuf_release(&dir); return NULL; } die(_("Not a git repository (or any parent up to mount point %s)\n" @@ -1044,6 +1047,9 @@ const char *setup_git_directory_gently(int *nongit_ok) startup_info->have_repository = !nongit_ok || !*nongit_ok; startup_info->prefix = prefix; + strbuf_release(&dir); + strbuf_release(&gitdir); + return prefix; } diff --git a/t/t1309-early-config.sh b/t/t1309-early-config.sh index 0c55dee514c..027eca63a3c 100755 --- a/t/t1309-early-config.sh +++ b/t/t1309-early-config.sh @@ -47,4 +47,29 @@ test_expect_success 'ceiling #2' ' test xdg = "$(cat output)" ' +test_with_config () +{ + rm -rf throwaway && + git init throwaway && + ( + cd throwaway && + echo "$*" >.git/config && + test-config read_early_config early.config + ) +} + +test_expect_success 'ignore .git/ with incompatible repository version' ' + test_with_config "[core]repositoryformatversion = 999999" 2>err && + grep "warning:.* Expected git repo version <= [1-9]" err +' + +test_expect_failure 'ignore .git/ with invalid repository version' ' + test_with_config "[core]repositoryformatversion = invalid" +' + + +test_expect_failure 'ignore .git/ with invalid config' ' + test_with_config "[" +' + test_done -- 2.12.0.windows.1.7.g94dafc3b124