diff options
author | Junio C Hamano <gitster@pobox.com> | 2016-09-21 15:15:23 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2016-09-21 15:15:24 -0700 |
commit | d845d727cb1935202b61a3b7b6c8cfa7c09bd204 (patch) | |
tree | 7296be39100177a980c2e3d5700371f11dd5b03d | |
parent | ac8ddd7ba36751a9f6413bb3921fc6797144717e (diff) | |
parent | 4d0efa101bbbc63d8bd1ec0477f027f23b9f573b (diff) | |
download | git-d845d727cb1935202b61a3b7b6c8cfa7c09bd204.tar.gz git-d845d727cb1935202b61a3b7b6c8cfa7c09bd204.tar.xz |
Merge branch 'jk/setup-sequence-update'
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
-rw-r--r-- | builtin/diff.c | 27 | ||||
-rw-r--r-- | builtin/hash-object.c | 13 | ||||
-rw-r--r-- | builtin/init-db.c | 17 | ||||
-rw-r--r-- | cache.h | 14 | ||||
-rw-r--r-- | config.c | 5 | ||||
-rw-r--r-- | diff-no-index.c | 3 | ||||
-rw-r--r-- | environment.c | 13 | ||||
-rw-r--r-- | git.c | 2 | ||||
-rw-r--r-- | pager.c | 93 | ||||
-rw-r--r-- | t/helper/test-config.c | 3 | ||||
-rwxr-xr-x | t/t0001-init.sh | 9 | ||||
-rwxr-xr-x | t/t1007-hash-object.sh | 43 | ||||
-rwxr-xr-x | t/t1301-shared-repo.sh | 41 | ||||
-rwxr-xr-x | t/t1302-repo-version.sh | 32 | ||||
-rwxr-xr-x | t/t4053-diff-no-index.sh | 38 | ||||
-rwxr-xr-x | t/t4204-patch-id.sh | 14 |
16 files changed, 268 insertions, 99 deletions
diff --git a/builtin/diff.c b/builtin/diff.c index b7a9405d9..7f91f6d22 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -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(); diff --git a/builtin/hash-object.c b/builtin/hash-object.c index f7d3567dd..9028e1fdc 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -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[] = { @@ -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); diff --git a/builtin/init-db.c b/builtin/init-db.c index 80192f65e..72e81447a 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -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); @@ -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); @@ -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 @@ -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); @@ -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); @@ -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)) diff --git a/diff-no-index.c b/diff-no-index.c index 1f8999b9c..f42078603 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -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); diff --git a/environment.c b/environment.c index ca72464a9..cd5aa5717 100644 --- a/environment.c +++ b/environment.c @@ -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; @@ -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) @@ -345,3 +351,8 @@ int get_shared_repository(void) } return the_shared_repository; } + +void reset_shared_repository(void) +{ + need_shared_repository_from_config = 1; +} @@ -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 }, @@ -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) { @@ -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; @@ -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) @@ -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; } diff --git a/t/helper/test-config.c b/t/helper/test-config.c index 3c6d08cd0..83a4f2ab8 100644 --- a/t/helper/test-config.c +++ b/t/helper/test-config.c @@ -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) { diff --git a/t/t0001-init.sh b/t/t0001-init.sh index a6fdd5ef3..8ffbbea4d 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -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 diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh index 7d2baa15b..acca9ac56 100755 --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh @@ -101,7 +101,7 @@ test_expect_success 'git hash-object --stdin file1 <file0 first operates on file test "$obname1" = "$obname1new" ' -test_expect_success 'check that appropriate filter is invoke when --path is used' ' +test_expect_success 'set up crlf tests' ' echo fooQ | tr Q "\\015" >file0 && cp file0 file1 && echo "file0 -crlf" >.gitattributes && @@ -109,7 +109,10 @@ test_expect_success 'check that appropriate filter is invoke when --path is used git config core.autocrlf true && file0_sha=$(git hash-object file0) && file1_sha=$(git hash-object file1) && - test "$file0_sha" != "$file1_sha" && + test "$file0_sha" != "$file1_sha" +' + +test_expect_success 'check that appropriate filter is invoke when --path is used' ' path1_sha=$(git hash-object --path=file1 file0) && path0_sha=$(git hash-object --path=file0 file1) && test "$file0_sha" = "$path0_sha" && @@ -117,38 +120,30 @@ test_expect_success 'check that appropriate filter is invoke when --path is used path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) && path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) && test "$file0_sha" = "$path0_sha" && - test "$file1_sha" = "$path1_sha" && - git config --unset core.autocrlf + test "$file1_sha" = "$path1_sha" +' + +test_expect_success 'gitattributes also work in a subdirectory' ' + mkdir subdir && + ( + cd subdir && + subdir_sha0=$(git hash-object ../file0) && + subdir_sha1=$(git hash-object ../file1) && + test "$file0_sha" = "$subdir_sha0" && + test "$file1_sha" = "$subdir_sha1" + ) ' test_expect_success 'check that --no-filters option works' ' - echo fooQ | tr Q "\\015" >file0 && - cp file0 file1 && - echo "file0 -crlf" >.gitattributes && - echo "file1 crlf" >>.gitattributes && - git config core.autocrlf true && - file0_sha=$(git hash-object file0) && - file1_sha=$(git hash-object file1) && - test "$file0_sha" != "$file1_sha" && nofilters_file1=$(git hash-object --no-filters file1) && test "$file0_sha" = "$nofilters_file1" && nofilters_file1=$(cat file1 | git hash-object --stdin) && - test "$file0_sha" = "$nofilters_file1" && - git config --unset core.autocrlf + test "$file0_sha" = "$nofilters_file1" ' test_expect_success 'check that --no-filters option works with --stdin-paths' ' - echo fooQ | tr Q "\\015" >file0 && - cp file0 file1 && - echo "file0 -crlf" >.gitattributes && - echo "file1 crlf" >>.gitattributes && - git config core.autocrlf true && - file0_sha=$(git hash-object file0) && - file1_sha=$(git hash-object file1) && - test "$file0_sha" != "$file1_sha" && nofilters_file1=$(echo "file1" | git hash-object --stdin-paths --no-filters) && - test "$file0_sha" = "$nofilters_file1" && - git config --unset core.autocrlf + test "$file0_sha" = "$nofilters_file1" ' pop_repo diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh index ac1087540..1312004f8 100755 --- a/t/t1301-shared-repo.sh +++ b/t/t1301-shared-repo.sh @@ -172,4 +172,45 @@ test_expect_success POSIXPERM 'forced modes' ' }" actual)" ' +test_expect_success POSIXPERM 'remote init does not use config from cwd' ' + git config core.sharedrepository 0666 && + umask 0022 && + git init --bare child.git && + echo "-rw-r--r--" >expect && + modebits child.git/config >actual && + test_cmp expect actual +' + +test_expect_success POSIXPERM 're-init respects core.sharedrepository (local)' ' + git config core.sharedrepository 0666 && + umask 0022 && + echo whatever >templates/foo && + git init --template=templates && + echo "-rw-rw-rw-" >expect && + modebits .git/foo >actual && + test_cmp expect actual +' + +test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)' ' + rm -rf child.git && + umask 0022 && + git init --bare --shared=0666 child.git && + test_path_is_missing child.git/foo && + git init --bare --template=../templates child.git && + echo "-rw-rw-rw-" >expect && + modebits child.git/foo >actual && + test_cmp expect actual +' + +test_expect_success POSIXPERM 'template can set core.sharedrepository' ' + rm -rf child.git && + umask 0022 && + git config core.sharedrepository 0666 && + cp .git/config templates/config && + git init --bare --template=../templates child.git && + echo "-rw-rw-rw-" >expect && + modebits child.git/HEAD >actual && + test_cmp expect actual +' + test_done diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh index 9bcd34969..ce4cff13b 100755 --- a/t/t1302-repo-version.sh +++ b/t/t1302-repo-version.sh @@ -25,46 +25,26 @@ test_expect_success 'setup' ' test_expect_success 'gitdir selection on normal repos' ' echo 0 >expect && git config core.repositoryformatversion >actual && - ( - cd test && - git config core.repositoryformatversion >../actual2 - ) && + git -C test config core.repositoryformatversion >actual2 && test_cmp expect actual && test_cmp expect actual2 ' test_expect_success 'gitdir selection on unsupported repo' ' # Make sure it would stop at test2, not trash - echo 99 >expect && - ( - cd test2 && - git config core.repositoryformatversion >../actual - ) && - test_cmp expect actual + test_expect_code 1 git -C test2 config core.repositoryformatversion >actual ' test_expect_success 'gitdir not required mode' ' git apply --stat test.patch && - ( - cd test && - git apply --stat ../test.patch - ) && - ( - cd test2 && - git apply --stat ../test.patch - ) + git -C test apply --stat ../test.patch && + git -C test2 apply --stat ../test.patch ' test_expect_success 'gitdir required mode' ' git apply --check --index test.patch && - ( - cd test && - git apply --check --index ../test.patch - ) && - ( - cd test2 && - test_must_fail git apply --check --index ../test.patch - ) + git -C test apply --check --index ../test.patch && + test_must_fail git -C test2 apply --check --index ../test.patch ' check_allow () { diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh index 6eb83211b..453e6c35e 100755 --- a/t/t4053-diff-no-index.sh +++ b/t/t4053-diff-no-index.sh @@ -89,4 +89,42 @@ test_expect_success 'turning a file into a directory' ' ) ' +test_expect_success 'diff from repo subdir shows real paths (explicit)' ' + echo "diff --git a/../../non/git/a b/../../non/git/b" >expect && + test_expect_code 1 \ + git -C repo/sub \ + diff --no-index ../../non/git/a ../../non/git/b >actual && + head -n 1 <actual >actual.head && + test_cmp expect actual.head +' + +test_expect_success 'diff from repo subdir shows real paths (implicit)' ' + echo "diff --git a/../../non/git/a b/../../non/git/b" >expect && + test_expect_code 1 \ + git -C repo/sub \ + diff ../../non/git/a ../../non/git/b >actual && + head -n 1 <actual >actual.head && + test_cmp expect actual.head +' + +test_expect_success 'diff --no-index from repo subdir respects config (explicit)' ' + echo "diff --git ../../non/git/a ../../non/git/b" >expect && + test_config -C repo diff.noprefix true && + test_expect_code 1 \ + git -C repo/sub \ + diff --no-index ../../non/git/a ../../non/git/b >actual && + head -n 1 <actual >actual.head && + test_cmp expect actual.head +' + +test_expect_success 'diff --no-index from repo subdir respects config (implicit)' ' + echo "diff --git ../../non/git/a ../../non/git/b" >expect && + test_config -C repo diff.noprefix true && + test_expect_code 1 \ + git -C repo/sub \ + diff ../../non/git/a ../../non/git/b >actual && + head -n 1 <actual >actual.head && + test_cmp expect actual.head +' + test_done diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh index 84a809690..0288c17ec 100755 --- a/t/t4204-patch-id.sh +++ b/t/t4204-patch-id.sh @@ -143,6 +143,20 @@ test_expect_success 'patch-id supports git-format-patch MIME output' ' test_cmp patch-id_master patch-id_same ' +test_expect_success 'patch-id respects config from subdir' ' + test_config patchid.stable true && + mkdir subdir && + + # copy these because test_patch_id() looks for them in + # the current directory + cp bar-then-foo foo-then-bar subdir && + + ( + cd subdir && + test_patch_id irrelevant patchid.stable=true + ) +' + cat >nonl <<\EOF diff --git i/a w/a index e69de29..2e65efe 100644 |