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
This commit is contained in:
Junio C Hamano 2016-09-21 15:15:23 -07:00
Родитель ac8ddd7ba3 4d0efa101b
Коммит d845d727cb
16 изменённых файлов: 270 добавлений и 101 удалений

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

@ -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();

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

@ -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);

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

@ -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);

14
cache.h
Просмотреть файл

@ -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))

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

@ -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);

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

@ -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;
}

2
git.c
Просмотреть файл

@ -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 },

97
pager.c
Просмотреть файл

@ -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;
}
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)
{
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)
data->want = b;
else {
data->want = 1;
data->value = xstrdup(value);
}
}
return 0;
}
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
int check_pager_config(const char *cmd)
{
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);
if (b >= 0)
want = b;
else {
want = 1;
pager_program = xstrdup(value);
}
}
strbuf_release(&key);
return want;
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;
}

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

@ -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) {

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

@ -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

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

@ -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

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

@ -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

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

@ -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 () {

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

@ -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

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

@ -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