{"thread":{"id":"49407","subject":"[PATCH] worktree: add per-worktree config files","startedAt":"2018-09-23T17:04:47Z","lastAt":"2018-10-25T09:16:31Z","messageCount":34,"participants":["Nguyễn Thái Ngọc Duy","Eric Sunshine","Taylor Blau","Duy Nguyen","Junio C Hamano","Ævar Arnfjörð Bjarmason","Marc Branchaud","SZEDER Gábor"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"358728","messageId":"20180923170438.23610-1-pclouds@gmail.com","threadId":"49407","inReplyTo":null,"subject":"[PATCH] worktree: add per-worktree config files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-23T17:04:38Z","receivedAt":"2018-09-23T17:04:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A new repo extension is added, worktreeConfig. When it is present:\n\n - Repository config reading by default includes $GIT_DIR/config _and_\n   $GIT_DIR/config.worktree. \"config\" file remains shared in multiple\n   worktree setup.\n\n - The special treatment for core.bare and core.worktree, to stay\n   effective only in main worktree, is gone. These config files are\n   supposed to be in config.worktree.\n\nThis extension is most useful in multiple worktree setup because you\nnow have an option to store per-worktree config (which is either\n.git/config.worktree for main worktree, or\n.git/worktrees/xx/config.worktree for linked ones).\n\nThis extension can be used in single worktree mode, even though it's\npretty much useless (but this can happen after you remove all linked\nworktrees and move back to single worktree).\n\n\"git config\" reads from both \"config\" and \"config.worktree\" by default\n(i.e. without either --user, --file...) when this extension is\npresent. Default writes still go to \"config\", not \"config.worktree\". A\nnew option --worktree is added for that (*).\n\nSince a new repo extension is introduced, existing git binaries should\nrefuse to access to the repo (both from main and linked worktrees). So\nthey will not misread the config file (i.e. skip the config.worktree\npart). They may still accidentally write to the config file anyway if\nthey use with \"git config --file <path>\".\n\nThis design places a bet on the assumption that the majority of config\nvariables are shared so it is the default mode. A safer move would be\ndefault writes go to per-worktree file, so that accidental changes are\nisolated.\n\n(*) \"git config --worktree\" points back to \"config\" file when this\n    extension is not present so that it works in any setup.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt               | 11 +++-\n Documentation/git-config.txt           | 26 +++++---\n Documentation/git-worktree.txt         | 37 ++++++++++++\n Documentation/gitrepository-layout.txt |  8 +++\n builtin/config.c                       | 16 ++++-\n cache.h                                |  2 +\n config.c                               |  7 +++\n environment.c                          |  1 +\n setup.c                                |  5 +-\n t/t2029-worktree-config.sh             | 82 ++++++++++++++++++++++++++\n worktree.c                             | 41 +++++++++++++\n worktree.h                             |  6 ++\n 12 files changed, 231 insertions(+), 11 deletions(-)\n create mode 100755 t/t2029-worktree-config.sh\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 8d85d1a324..c24abf5871 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2,8 +2,9 @@ CONFIGURATION FILE\n ------------------\n \n The Git configuration file contains a number of variables that affect\n-the Git commands' behavior. The `.git/config` file in each repository\n-is used to store the configuration for that repository, and\n+the Git commands' behavior. The files `.git/config` and optionally\n+`config.worktree` (see `extensions.worktreeConfig` below) are each\n+repository is used to store the configuration for that repository, and\n `$HOME/.gitconfig` is used to store a per-user configuration as\n fallback values for the `.git/config` file. The file `/etc/gitconfig`\n can be used to store a system-wide default configuration.\n@@ -371,6 +372,12 @@ advice.*::\n \t\teditor input from the user.\n --\n \n+extensions.worktreeConfig::\n+\tIf set, by default \"git config\" reads from both \"config\" and\n+\t\"config.worktree\" file in that order. In multiple working\n+\tdirectory mode, \"config\" file is shared while\n+\t\"config.worktree\" is per-working directory.\n+\n core.fileMode::\n \tTells Git if the executable bit of files in the working tree\n \tis to be honored.\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 8e240435be..4870e00b89 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -45,13 +45,15 @@ unset an existing `--type` specifier with `--no-type`.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n-`--system`, `--global`, `--local` and `--file <filename>` can be\n-used to tell the command to read from only that location (see <<FILES>>).\n+`--system`, `--global`, `--local`, `--worktree` and\n+`--file <filename>` can be used to tell the command to read from only\n+that location (see <<FILES>>).\n \n When writing, the new value is written to the repository local\n configuration file by default, and options `--system`, `--global`,\n-`--file <filename>` can be used to tell the command to write to\n-that location (you can say `--local` but that is the default).\n+`--worktree`, `--file <filename>` can be used to tell the command to\n+write to that location (you can say `--local` but that is the\n+default).\n \n This command will fail with non-zero status upon error.  Some exit\n codes are:\n@@ -131,6 +133,11 @@ from all available files.\n +\n See also <<FILES>>.\n \n+--worktree::\n+\tSimilar to `--local` except that `.git/config.worktree` is\n+\tread from or written to if `extensions.worktreeConfig` is\n+\tpresent. If not it's the same as `--local`.\n+\n -f config-file::\n --file config-file::\n \tUse the given config file instead of the one specified by GIT_CONFIG.\n@@ -281,6 +288,10 @@ $XDG_CONFIG_HOME/git/config::\n $GIT_DIR/config::\n \tRepository specific configuration file.\n \n+$GIT_DIR/config.worktree::\n+\tThis is optional and is only searched when\n+\t`extensions.worktreeConfig` is present in $GIT_DIR/config.\n+\n If no further options are given, all reading options will read all of these\n files that are available. If the global or the system-wide configuration\n file are not available they will be ignored. If the repository configuration\n@@ -299,9 +310,10 @@ configuration file. Note that this also affects options like `--replace-all`\n and `--unset`. *'git config' will only ever change one file at a time*.\n \n You can override these rules either by command-line options or by environment\n-variables. The `--global` and the `--system` options will limit the file used\n-to the global or system-wide file respectively. The `GIT_CONFIG` environment\n-variable has a similar effect, but you can specify any filename you want.\n+variables. The `--global`, `--system` and `--worktree` options will limit\n+the file used to the global, system-wide or per-worktree file respectively.\n+The `GIT_CONFIG` environment variable has a similar effect, but you\n+can specify any filename you want.\n \n \n ENVIRONMENT\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex e2ee9fc21b..3f9112db56 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -204,6 +204,43 @@ working trees, it can be used to identify worktrees. For example if\n you only have two working trees, at \"/abc/def/ghi\" and \"/abc/def/ggg\",\n then \"ghi\" or \"def/ghi\" is enough to point to the former working tree.\n \n+CONFIGURATION FILE\n+------------------\n+By default, the repository \"config\" file is shared across all working\n+directories. If the config variables `core.bare` or `core.worktree`\n+are already present in the config file, they will be applied to the\n+main working directory only.\n+\n+In order to have configuration specific to working directories, you\n+can turn on \"worktreeConfig\" extension, e.g.:\n+\n+------------\n+$ git config extensions.worktreeConfig true\n+------------\n+\n+In this mode, specific configuration stays in the path pointed by `git\n+rev-parse --git-path config.worktree`. You can add or update\n+configuration in this file with `git config --worktree`. Git before\n+version 2.20.0 will refuse to access repositories with this extension.\n+\n+Note that in this file, the exception for `core.bare` and\n+`core.worktree` is gone. If you have them before, you need to move\n+them to the `config.worktree` of the main working directory. You may\n+also take this opportunity to move other configuration that you do not\n+want to share to all working directories:\n+\n+ - `core.worktree` and `core.bare` should never be shared\n+\n+ - `core.sparseCheckout` is recommended per working directory, unless\n+   you are sure you always use sparse checkout for all working\n+   directories.\n+\n+When `git config --worktree` is used to set a configuration variable\n+in multiple working directory setup, `extensions.worktreeConfig` will\n+be automatically set. The two variables `core.worktree` and\n+`core.bare` if present will be moved to `config.worktree` of the main\n+working tree.\n+\n DETAILS\n -------\n Each linked working tree has a private sub-directory in the repository's\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex e85148f05e..36fcca8087 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -143,6 +143,11 @@ config::\n \tif $GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/config\" will be\n \tused instead.\n \n+config.worktree::\n+\tWorking directory specific configuration file for the main\n+\tworking directory in multiple working directory setup (see\n+\tlinkgit:git-worktree[1]).\n+\n branches::\n \tA slightly deprecated way to store shorthands to be used\n \tto specify a URL to 'git fetch', 'git pull' and 'git push'.\n@@ -275,6 +280,9 @@ worktrees/<id>/locked::\n \tor manually by `git worktree prune`. The file may contain a string\n \texplaining why the repository is locked.\n \n+worktrees/<id>/config.worktree::\n+\tWorking directory specific configuration file.\n+\n SEE ALSO\n --------\n linkgit:git-init[1],\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 97b58c4aea..337db7b552 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -5,6 +5,7 @@\n #include \"parse-options.h\"\n #include \"urlmatch.h\"\n #include \"quote.h\"\n+#include \"worktree.h\"\n \n static const char *const builtin_config_usage[] = {\n \tN_(\"git config [<options>]\"),\n@@ -24,6 +25,7 @@ static char key_delim = ' ';\n static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n+static int use_worktree_config;\n static struct git_config_source given_config_source;\n static int actions, type;\n static char *default_value;\n@@ -123,6 +125,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n \tOPT_BOOL(0, \"system\", &use_system_config, N_(\"use system config file\")),\n \tOPT_BOOL(0, \"local\", &use_local_config, N_(\"use repository config file\")),\n+\tOPT_BOOL(0, \"worktree\", &use_worktree_config, N_(\"use per-worktree config file\")),\n \tOPT_STRING('f', \"file\", &given_config_source.file, N_(\"file\"), N_(\"use given config file\")),\n \tOPT_STRING(0, \"blob\", &given_config_source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\")),\n \tOPT_GROUP(N_(\"Action\")),\n@@ -602,6 +605,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n \tif (use_global_config + use_system_config + use_local_config +\n+\t    use_worktree_config +\n \t    !!given_config_source.file + !!given_config_source.blob > 1) {\n \t\terror(_(\"only one config file at a time\"));\n \t\tusage_builtin_config();\n@@ -645,7 +649,17 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tgiven_config_source.file = git_etc_gitconfig();\n \telse if (use_local_config)\n \t\tgiven_config_source.file = git_pathdup(\"config\");\n-\telse if (given_config_source.file) {\n+\telse if (use_worktree_config) {\n+\t\tstruct worktree **worktrees = get_worktrees(0);\n+\t\tif (repository_format_worktree_config)\n+\t\t\tgiven_config_source.file = git_pathdup(\"config.worktree\");\n+\t\telse if (worktrees[0] && worktrees[1]) {\n+\t\t\tmigrate_worktree_config(the_repository);\n+\t\t\tgiven_config_source.file = git_pathdup(\"config.worktree\");\n+\t\t} else\n+\t\t\tgiven_config_source.file = git_pathdup(\"config\");\n+\t\tfree_worktrees(worktrees);\n+\t} else if (given_config_source.file) {\n \t\tif (!is_absolute_path(given_config_source.file) && prefix)\n \t\t\tgiven_config_source.file =\n \t\t\t\tprefix_filename(prefix, given_config_source.file);\ndiff --git a/cache.h b/cache.h\nindex d508f3d4f8..9e9b917e99 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -957,11 +957,13 @@ extern int grafts_replace_parents;\n extern int repository_format_precious_objects;\n extern char *repository_format_partial_clone;\n extern const char *core_partial_clone_filter_default;\n+extern int repository_format_worktree_config;\n \n struct repository_format {\n \tint version;\n \tint precious_objects;\n \tchar *partial_clone; /* value of extensions.partialclone */\n+\tint worktree_config;\n \tint is_bare;\n \tint hash_algo;\n \tchar *work_tree;\ndiff --git a/config.c b/config.c\nindex 3461993f0a..24f68a5208 100644\n--- a/config.c\n+++ b/config.c\n@@ -1695,6 +1695,13 @@ static int do_git_config_sequence(const struct config_options *opts,\n \tif (repo_config && !access_or_die(repo_config, R_OK, 0))\n \t\tret += git_config_from_file(fn, repo_config, data);\n \n+\tif (repository_format_worktree_config) {\n+\t\tchar *path = git_pathdup(\"config.worktree\");\n+\t\tif (!access_or_die(path, R_OK, 0))\n+\t\t\tret += git_config_from_file(fn, path, data);\n+\t\tfree(path);\n+\t}\n+\n \tcurrent_parsing_scope = CONFIG_SCOPE_CMDLINE;\n \tif (git_config_from_parameters(fn, data) < 0)\n \t\tdie(_(\"unable to parse command-line config\"));\ndiff --git a/environment.c b/environment.c\nindex 3f3c8746c2..268310b3dc 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -33,6 +33,7 @@ int ref_paranoia = -1;\n int repository_format_precious_objects;\n char *repository_format_partial_clone;\n const char *core_partial_clone_filter_default;\n+int repository_format_worktree_config;\n const char *git_commit_encoding;\n const char *git_log_output_encoding;\n const char *apply_default_whitespace;\ndiff --git a/setup.c b/setup.c\nindex b24c811c1c..6169193946 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -423,7 +423,9 @@ static int check_repo_format(const char *var, const char *value, void *vdata)\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(var);\n \t\t\tdata->partial_clone = xstrdup(value);\n-\t\t} else\n+\t\t} else if (!strcmp(ext, \"worktreeconfig\"))\n+\t\t\tdata->worktree_config = git_config_bool(var, value);\n+\t\telse\n \t\t\tstring_list_append(&data->unknown_extensions, ext);\n \t} else if (strcmp(var, \"core.bare\") == 0) {\n \t\tdata->is_bare = git_config_bool(var, value);\n@@ -466,6 +468,7 @@ static int check_repository_format_gently(const char *gitdir, struct repository_\n \n \trepository_format_precious_objects = candidate->precious_objects;\n \trepository_format_partial_clone = candidate->partial_clone;\n+\trepository_format_worktree_config = candidate->worktree_config;\n \tstring_list_clear(&candidate->unknown_extensions, 0);\n \tif (!has_common) {\n \t\tif (candidate->is_bare != -1) {\ndiff --git a/t/t2029-worktree-config.sh b/t/t2029-worktree-config.sh\nnew file mode 100755\nindex 0000000000..4ebdf13cf9\n--- /dev/null\n+++ b/t/t2029-worktree-config.sh\n@@ -0,0 +1,82 @@\n+#!/bin/sh\n+\n+test_description=\"config file in multi worktree\"\n+\n+. ./test-lib.sh\n+\n+cmp_config() {\n+\tif [ \"$1\" = \"-C\" ]; then\n+\t\tshift &&\n+\t\tGD=\"-C $1\" &&\n+\t\tshift\n+\telse\n+\t\tGD=\n+\tfi &&\n+\techo \"$1\" >expected &&\n+\tshift &&\n+\tgit $GD config \"$@\" >actual &&\n+\ttest_cmp expected actual\n+}\n+\n+test_expect_success 'setup' '\n+\ttest_commit start &&\n+\tgit config --worktree per.worktree is-ok &&\n+\tgit worktree add wt1 &&\n+\tgit worktree add wt2 &&\n+\tgit config --worktree per.worktree is-ok &&\n+\tcmp_config true extensions.worktreeConfig\n+'\n+\n+test_expect_success 'config is shared as before' '\n+\tgit config this.is shared &&\n+\tcmp_config shared this.is &&\n+\tcmp_config -C wt1 shared this.is &&\n+\tcmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'config is shared (set from another worktree)' '\n+\tgit -C wt1 config that.is also-shared &&\n+\tcmp_config also-shared that.is &&\n+\tcmp_config -C wt1 also-shared that.is &&\n+\tcmp_config -C wt2 also-shared that.is\n+'\n+\n+test_expect_success 'config private to main worktree' '\n+\tgit config --worktree this.is for-main &&\n+\tcmp_config for-main this.is &&\n+\tcmp_config -C wt1 shared this.is &&\n+\tcmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'config private to linked worktree' '\n+\tgit -C wt1 config --worktree this.is for-wt1 &&\n+\tcmp_config for-main this.is &&\n+\tcmp_config -C wt1 for-wt1 this.is &&\n+\tcmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'core.bare no longer for main only' '\n+\tgit config core.bare true &&\n+\tcmp_config true core.bare &&\n+\tcmp_config -C wt1 true core.bare &&\n+\tcmp_config -C wt2 true core.bare &&\n+\tgit config --unset core.bare\n+'\n+\n+test_expect_success 'config.worktree no longer read without extension' '\n+\tgit config --unset extensions.worktreeConfig &&\n+\tcmp_config shared this.is &&\n+\tcmp_config -C wt1 shared this.is &&\n+\tcmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'config --worktree migrate core.bare and core.worktree' '\n+\tgit config core.bare true &&\n+\tgit config --worktree foo.bar true &&\n+\tcmp_config true extensions.worktreeConfig &&\n+\tcmp_config true foo.bar &&\n+\tcmp_config true core.bare &&\n+\t! git -C wt1 config core.bare\n+'\n+\n+test_done\ndiff --git a/worktree.c b/worktree.c\nindex b0d0b5426d..a239f99bdc 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -5,6 +5,7 @@\n #include \"worktree.h\"\n #include \"dir.h\"\n #include \"wt-status.h\"\n+#include \"config.h\"\n \n void free_worktrees(struct worktree **worktrees)\n {\n@@ -508,3 +509,43 @@ int other_head_refs(each_ref_fn fn, void *cb_data)\n \tfree_worktrees(worktrees);\n \treturn ret;\n }\n+\n+void migrate_worktree_config(struct repository *r)\n+{\n+\tstruct strbuf worktree_path = STRBUF_INIT;\n+\tstruct strbuf main_path = STRBUF_INIT;\n+\tstruct repository_format format;\n+\n+\tassert(repository_format_worktree_config == 0);\n+\n+\tstrbuf_git_common_path(&worktree_path, r, \"config.worktree\");\n+\tstrbuf_git_path(&main_path, \"config\");\n+\n+\tread_repository_format(&format, main_path.buf);\n+\tassert(format.worktree_config == 0);\n+\n+\tif (format.is_bare >= 0) {\n+\t\tgit_config_set_in_file(worktree_path.buf,\n+\t\t\t\t       \"core.bare\", \"true\");\n+\t\tgit_config_set_in_file(main_path.buf,\n+\t\t\t\t       \"core.bare\", NULL);\n+\t}\n+\tif (format.work_tree) {\n+\t\tgit_config_set_in_file(worktree_path.buf,\n+\t\t\t\t       \"core.worktree\",\n+\t\t\t\t       format.work_tree);\n+\t\tgit_config_set_in_file(main_path.buf,\n+\t\t\t\t       \"core.worktree\", NULL);\n+\t}\n+\n+\tgit_config_set_in_file(main_path.buf,\n+\t\t\t       \"extensions.worktreeConfig\", \"true\");\n+\tif (format.version == 0)\n+\t\tgit_config_set_in_file(main_path.buf,\n+\t\t\t\t       \"core.repositoryFormatVersion\", \"1\");\n+\n+\trepository_format_worktree_config = 1;\n+\n+\tstrbuf_release(&main_path);\n+\tstrbuf_release(&worktree_path);\n+}\ndiff --git a/worktree.h b/worktree.h\nindex df3fc30f73..b27b407d1b 100644\n--- a/worktree.h\n+++ b/worktree.h\n@@ -108,4 +108,10 @@ extern const char *worktree_git_path(const struct worktree *wt,\n \t\t\t\t     const char *fmt, ...)\n \t__attribute__((format (printf, 2, 3)));\n \n+/*\n+ * Called to add extensions.worktreeConfig to $GIT_DIR/config and move\n+ * main worktree specific config variables to $GIT_DIR/config.worktree.\n+ */\n+void migrate_worktree_config(struct repository *r);\n+\n #endif\n-- \n2.19.0.647.gb9a6049235\n\n"},{"id":"358736","messageId":"CAPig+cR4d3p=PoLAC=0Pss64tBa1eVq3Zz2a70spmc6Vb=cUdw@mail.gmail.com","threadId":"49407","inReplyTo":"20180923170438.23610-1-pclouds@gmail.com","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-09-23T20:47:44Z","receivedAt":"2018-09-23T20:47:59Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Sep 23, 2018 at 1:04 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> A new repo extension is added, worktreeConfig. When it is present:\n>\n>  - Repository config reading by default includes $GIT_DIR/config _and_\n>    $GIT_DIR/config.worktree. \"config\" file remains shared in multiple\n>    worktree setup.\n>\n>  - The special treatment for core.bare and core.worktree, to stay\n>    effective only in main worktree, is gone. These config files are\n>    supposed to be in config.worktree.\n\n\"These config _settings_ are supposed to be...\"\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> @@ -2,8 +2,9 @@ CONFIGURATION FILE\n>  The Git configuration file contains a number of variables that affect\n> -the Git commands' behavior. The `.git/config` file in each repository\n> -is used to store the configuration for that repository, and\n> +the Git commands' behavior. The files `.git/config` and optionally\n> +`config.worktree` (see `extensions.worktreeConfig` below) are each\n\ns/are/in/\n\n> +repository is used to store the configuration for that repository, and\n>  `$HOME/.gitconfig` is used to store a per-user configuration as\n> diff --git a/t/t2029-worktree-config.sh b/t/t2029-worktree-config.sh\n> @@ -0,0 +1,82 @@\n> +cmp_config() {\n> +       if [ \"$1\" = \"-C\" ]; then\n\nStyle:\n\n    if test \"$1\" = \"-C\"\n    then\n        ...\n\n> +}\n> +\n> +test_expect_success 'setup' '\n> +       test_commit start &&\n> +       git config --worktree per.worktree is-ok &&\n\nDid you want:\n\n    cmp_config false extensions.worktreeConfig\n\nat this point in the test or something? It's not clear what the\nintention is here.\n\n> +       git worktree add wt1 &&\n> +       git worktree add wt2 &&\n> +       git config --worktree per.worktree is-ok &&\n> +       cmp_config true extensions.worktreeConfig\n> +'\n> +test_expect_success 'core.bare no longer for main only' '\n> +       git config core.bare true &&\n> +       cmp_config true core.bare &&\n> +       cmp_config -C wt1 true core.bare &&\n> +       cmp_config -C wt2 true core.bare &&\n> +       git config --unset core.bare\n\nShould this be?\n\n    test_when_finished \"git config --unset core.bare\" &&\n\nor perhaps test_config()?\n\n> +'\n"},{"id":"358756","messageId":"20180924142143.GD68796@syl","threadId":"49407","inReplyTo":"20180923170438.23610-1-pclouds@gmail.com","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-09-24T14:21:43Z","receivedAt":"2018-09-24T14:21:49Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Sun, Sep 23, 2018 at 07:04:38PM +0200, Nguyễn Thái Ngọc Duy wrote:\n> A new repo extension is added, worktreeConfig. When it is present:\n\nI was initially scratching my head at why this should be a repository\nextension, but...\n\n>  - The special treatment for core.bare and core.worktree, to stay\n>    effective only in main worktree, is gone. These config files are\n>    supposed to be in config.worktree.\n\nThis seems to be sufficient justification for it. A destructive action\n(such as migrating configuration from one location to another, as you\nhave implemented in the patch below) should be something that the user\nopts into, rather than having happen automatically.\n\n> This extension is most useful in multiple worktree setup because you\n> now have an option to store per-worktree config (which is either\n> .git/config.worktree for main worktree, or\n> .git/worktrees/xx/config.worktree for linked ones).\n\nThis sounds quite useful for these situations.\n\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 8d85d1a324..c24abf5871 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -2,8 +2,9 @@ CONFIGURATION FILE\n>  ------------------\n>\n>  The Git configuration file contains a number of variables that affect\n> -the Git commands' behavior. The `.git/config` file in each repository\n> -is used to store the configuration for that repository, and\n> +the Git commands' behavior. The files `.git/config` and optionally\n> +`config.worktree` (see `extensions.worktreeConfig` below) are each\n> +repository is used to store the configuration for that repository, and\n\nTypo: 'are each'.\n\n>  ENVIRONMENT\n> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\n> index e2ee9fc21b..3f9112db56 100644\n> --- a/Documentation/git-worktree.txt\n> +++ b/Documentation/git-worktree.txt\n> @@ -204,6 +204,43 @@ working trees, it can be used to identify worktrees. For example if\n>  you only have two working trees, at \"/abc/def/ghi\" and \"/abc/def/ggg\",\n>  then \"ghi\" or \"def/ghi\" is enough to point to the former working tree.\n>\n> +CONFIGURATION FILE\n> +------------------\n> +By default, the repository \"config\" file is shared across all working\n> +directories. If the config variables `core.bare` or `core.worktree`\n> +are already present in the config file, they will be applied to the\n> +main working directory only.\n> +\n> +In order to have configuration specific to working directories, you\n> +can turn on \"worktreeConfig\" extension, e.g.:\n> +\n> +------------\n> +$ git config extensions.worktreeConfig true\n> +------------\n\nGood, this matches my expectation from above.\n\n> @@ -24,6 +25,7 @@ static char key_delim = ' ';\n>  static char term = '\\n';\n>\n>  static int use_global_config, use_system_config, use_local_config;\n> +static int use_worktree_config;\n>  static struct git_config_source given_config_source;\n>  static int actions, type;\n>  static char *default_value;\n> @@ -123,6 +125,7 @@ static struct option builtin_config_options[] = {\n>  \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n>  \tOPT_BOOL(0, \"system\", &use_system_config, N_(\"use system config file\")),\n>  \tOPT_BOOL(0, \"local\", &use_local_config, N_(\"use repository config file\")),\n> +\tOPT_BOOL(0, \"worktree\", &use_worktree_config, N_(\"use per-worktree config file\")),\n>  \tOPT_STRING('f', \"file\", &given_config_source.file, N_(\"file\"), N_(\"use given config file\")),\n>  \tOPT_STRING(0, \"blob\", &given_config_source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\")),\n>  \tOPT_GROUP(N_(\"Action\")),\n> @@ -602,6 +605,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n>  \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n>\n>  \tif (use_global_config + use_system_config + use_local_config +\n> +\t    use_worktree_config +\n>  \t    !!given_config_source.file + !!given_config_source.blob > 1) {\n\nI feel like we're getting into \"let's extract a function\" territory,\nhere, since this line is growing in width. Perhaps:\n\n  static int config_files_count()\n  {\n    return use_global_config + use_system_config + use_local_config +\n  \t\tuse_worktree_config +\n  \t\t!!given_config_source.file +\n  \t\t!!given_config_source.blob;\n  }\n\nSimplifying the call to:\n\n> diff --git a/t/t2029-worktree-config.sh b/t/t2029-worktree-config.sh\n> new file mode 100755\n> index 0000000000..4ebdf13cf9\n> --- /dev/null\n> +++ b/t/t2029-worktree-config.sh\n> @@ -0,0 +1,82 @@\n> +#!/bin/sh\n> +\n> +test_description=\"config file in multi worktree\"\n> +\n> +. ./test-lib.sh\n> +\n> +cmp_config() {\n> +\tif [ \"$1\" = \"-C\" ]; then\n> +\t\tshift &&\n> +\t\tGD=\"-C $1\" &&\n> +\t\tshift\n> +\telse\n> +\t\tGD=\n> +\tfi &&\n> +\techo \"$1\" >expected &&\n> +\tshift &&\n> +\tgit $GD config \"$@\" >actual &&\n> +\ttest_cmp expected actual\n> +}\n\nThis cmp_config seems generally useful, perhaps beyond t2029. What do\nyou think about putting it in t/test-lib-functions.sh instead?\n\nThanks,\nTaylor\n"},{"id":"358840","messageId":"CACsJy8BFydQorJveGirFG4v3KLsF9bd3fALddJVU62BTF9UR2Q@mail.gmail.com","threadId":"49407","inReplyTo":"20180924142143.GD68796@syl","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-25T15:57:55Z","receivedAt":"2018-09-25T15:58:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Sep 24, 2018 at 4:21 PM Taylor Blau <me@ttaylorr.com> wrote:\n> > +cmp_config() {\n> > +     if [ \"$1\" = \"-C\" ]; then\n> > +             shift &&\n> > +             GD=\"-C $1\" &&\n> > +             shift\n> > +     else\n> > +             GD=\n> > +     fi &&\n> > +     echo \"$1\" >expected &&\n> > +     shift &&\n> > +     git $GD config \"$@\" >actual &&\n> > +     test_cmp expected actual\n> > +}\n>\n> This cmp_config seems generally useful, perhaps beyond t2029. What do\n> you think about putting it in t/test-lib-functions.sh instead?\n\nGood point. t1300 (and I think some t13xx) does the same. Other tests also do\n\n    test \"$(git config...)\" = blah\n\nwhich can also use this function to have better error reporting when\nthings go wrong.\n-- \nDuy\n"},{"id":"358875","messageId":"xmqqtvmdnuab.fsf@gitster-ct.c.googlers.com","threadId":"49407","inReplyTo":"20180923170438.23610-1-pclouds@gmail.com","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-09-25T21:26:04Z","receivedAt":"2018-09-25T21:26:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This extension is most useful in multiple worktree setup because you\n> now have an option to store per-worktree config (which is either\n> .git/config.worktree for main worktree, or\n> .git/worktrees/xx/config.worktree for linked ones).\n\nHeh.  \"This is useful if you have multiple\" is true without saying,\nbecause this is totally useless if you have only a single worktree.\nI'd suggest writing the above without \"most useful\".\n\n> This design places a bet on the assumption that the majority of config\n> variables are shared so it is the default mode. A safer move would be\n> default writes go to per-worktree file, so that accidental changes are\n> isolated.\n\nWarning: devil's advocate mode on.\n\nDone in either way, this will confuse the users.  What is the reason\nwhy people are led to think it is a good idea to use multiple\nworktrees, even when they need different settings?  What do they\nwant out of \"multiple worktrees linked to a single repository\" as\nopposed to just a simple \"as many clones as necessary\"?  Reduced\ndisk footprint?  Is there a better way to achieve that without the\ndownside of multiple worktrees (e.g. configuration need to be\nuniform)?\n\n> (*) \"git config --worktree\" points back to \"config\" file when this\n>     extension is not present so that it works in any setup.\n\nShouldn't it barf and error out instead?  A user who hasn't enabled\nthe extension uses --worktree option and misled to believe that the\nsetting affects only a single worktree, even though the change is\nmade globally---that does not sound like a great end-user experience.\n"},{"id":"358951","messageId":"CACsJy8Bux0iiOp+zjELM4DuZwiQMA6EDaL0M71Jkp_qPACD8og@mail.gmail.com","threadId":"49407","inReplyTo":"xmqqtvmdnuab.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-26T15:48:48Z","receivedAt":"2018-09-26T15:49:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Sep 25, 2018 at 11:26 PM Junio C Hamano <gitster@pobox.com> wrote:\n> Warning: devil's advocate mode on.\n\nOh good. I need to a good dose of sanity anyway.\n\n> Done in either way, this will confuse the users.  What is the reason\n> why people are led to think it is a good idea to use multiple\n> worktrees, even when they need different settings?  What do they\n> want out of \"multiple worktrees linked to a single repository\" as\n> opposed to just a simple \"as many clones as necessary\"?  Reduced\n> disk footprint?\n\nI believe the main selling point of multiple worktrees is sharing\nrefs. You could easily avoid expensive clones with --local, but\nsynchronizing between different clones is not very convenient. Other\nthan that, different worktrees tend to behave like separate clones.\n\nThis leaves a gray area where other things should be shared or not. I\nthink the preference (or default mode) is still _not_ shared (*).\nSharing more things (besides refs and object database) is just a new\nopportunity popping up when we implement multiple worktrees. Since\nmultiple worktrees (or clones before its time) are grouped together,\nsometimes you would like to share common configuration. We could sort\nof achieve this already with includeIf but again not as convenient.\n\n(*) real life is not that simple. Since refs are shared, including\n_remotes_ refs, so configuration related to remotes should also be\nshared, or it will be a maintenance nightmare. Which is why\n$GIT_DIR/config so far has been shared besides the two exceptions that\nare core.bare and core.worktree. And I really like to get rid of these\nexceptions.\n\n> Is there a better way to achieve that without the\n> downside of multiple worktrees (e.g. configuration need to be\n> uniform)?\n\nIs there a better way to achieve sharing refs between clones? I gave\nit a minute but couldn't come up with a good answer :(\n\n> > (*) \"git config --worktree\" points back to \"config\" file when this\n> >     extension is not present so that it works in any setup.\n>\n> Shouldn't it barf and error out instead?\n\nThe intention is a uniform interface/api that works with both single\nand multiple worktrees configurations. Otherwise in your scripts you\nwould need to write \"if single worktree, do this, else do that\". If\n\"git config --worktree\" works with both, the existing scripts can be\naudited and updated just a bit, adding \"--worktree\" where the config\nshould not be shared, and we're done.\n\n> A user who hasn't enabled\n> the extension uses --worktree option and misled to believe that the\n> setting affects only a single worktree, even though the change is\n> made globally---that does not sound like a great end-user experience.\n\nI was talking about a single worktree. But I think here you meant the\nuser has multiple worktrees, but the extension is not enabled. I'm\nprobably not clear in the commit message, but \"git config\" can detect\nthat the extension has not been enabled, automatically enable it (and\nmove core.bare and core.worktree (if present) to the main worktree's\nprivate config), so \"git config --worktree\" will never share the\nchange.\n\nBut perhaps the user should be made aware of this situation and asked\nto explicitly enable the extension instead? It's certainly a more\nconservative approach.\n-- \nDuy\n"},{"id":"358961","messageId":"xmqq4lecma32.fsf@gitster-ct.c.googlers.com","threadId":"49407","inReplyTo":"CACsJy8Bux0iiOp+zjELM4DuZwiQMA6EDaL0M71Jkp_qPACD8og@mail.gmail.com","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-09-26T17:40:01Z","receivedAt":"2018-09-26T17:40:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> I believe the main selling point of multiple worktrees is sharing\n> refs. You could easily avoid expensive clones with --local, but\n> synchronizing between different clones is not very convenient. Other\n> than that, different worktrees tend to behave like separate clones.\n\nOK.  Even with the enforced limitation that no single branch can be\nchecked out in multiple worktrees at the same time, it is more\nconvenient as you can \"merge\" other branch and trust that the result\non the checked-out branch in your worktree is immediately visible to\nother worktrees.\n\n> user has multiple worktrees, but the extension is not enabled. I'm\n\nExactly.  \"config --worktree\" in your script will silently break\nother worktrees; it wanted to affect only the current worktree, but\nit changed settings to all the others.\n\n> probably not clear in the commit message, but \"git config\" can detect\n> that the extension has not been enabled, automatically enable it (and\n> move core.bare and core.worktree (if present) to the main worktree's\n> private config), so \"git config --worktree\" will never share the\n> change.\n\nI wonder if that is good enough.  The user in one worktree did\n\"config --worktree\" and all the worktrees now start diverging in\ntheir config---but that is true _only_ when they use \"--worktree\"\noption to say \"I want this to be set differently from others\".  All\nthe other calls to \"git config\" will stil be shared.\n\nSo from a cursory thinking, it sounds OK to me, but somebody else\nmay think of bad ramifications after a good night sleep.\n\n\n\n"},{"id":"358970","messageId":"87bm8kjeu5.fsf@evledraar.gmail.com","threadId":"49407","inReplyTo":"20180923170438.23610-1-pclouds@gmail.com","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-09-26T18:25:38Z","receivedAt":"2018-09-26T18:25:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Sep 23 2018, Nguyễn Thái Ngọc Duy wrote:\n\n> +extensions.worktreeConfig::\n> +\tIf set, by default \"git config\" reads from both \"config\" and\n> +\t\"config.worktree\" file in that order.\n\nHow does this interact with config that's now only used if it's in\n.git/config? E.g. you can't set remote.<remote>.<url> in ~/.gitconfig,\nwill that be inherited across the two of these?\n\n> In multiple working\n> +\tdirectory mode, \"config\" file is shared while\n> +\t\"config.worktree\" is per-working directory.\n\n\"But then how will it work with more than one?\" I found myself thinking\nbefore reading some more and remembering .git/worktree. Shouldn't we\nconsistently say:\n\n    [...]\"config\" and \"worktrees/<worktree name>/config\"[...]\n\nOr something like that?\n"},{"id":"359059","messageId":"e7f63c0f-90dd-0e53-9721-35d2b827e101@xiplink.com","threadId":"49407","inReplyTo":"CACsJy8Bux0iiOp+zjELM4DuZwiQMA6EDaL0M71Jkp_qPACD8og@mail.gmail.com","subject":"Wherefor worktrees?","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2018-09-27T15:24:21Z","receivedAt":"2018-09-27T15:31:46Z","isPatch":false,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"On 2018-09-26 11:48 AM, Duy Nguyen wrote:\n> \n> I believe the main selling point of multiple worktrees is sharing\n> refs. You could easily avoid expensive clones with --local, but\n> synchronizing between different clones is not very convenient. Other\n> than that, different worktrees tend to behave like separate clones.\n\nSharing hooks is also useful, but yes mainly the refs.\n\nI love being able to work in more than one branch at a time.  I often \nhave a couple of ongoing big, messy topics, and being able to easily \njump onto some release branch for a quick bugfix, without having to \nfirst stash things or finish an interactive rebase or fix a conflicting \nmerge, is a godsend.\n\nAnd the reason I use worktrees for this, instead of clones, is for the \nshared refs.  It makes sense to me that I'm working with different \ncheckouts from a single repo, with all my local branches and local tags. \n  \"git fetch\" updates the remote refs regardless of which worktree I'm \nin when I run it.  The setup is lightweight and efficient; it's just how \nI want to work.\n\nHaving used git-new-workdir for a long time, it's main deficiency for me \nis submodules (the shared bisection state didn't bother me much).  It \nwould be nice if all my worktrees' submodules also shared refs.  That's \n\"nice\", but not \"essential\".  Mainly it would be convenient if a \nrecursive-submodule fetch performed in one worktree updated the \nsubmodule refs in my other worktrees.  Similarly, if I create a local \nbranch in a submodule in one worktree, it would be nice to see that \nbranch in the submodule in other worktrees.  Again, \"nice\", but probably \njust because I've lived with git-new-workdir's limitations for so long \nthat I'm used to them.\n\nThat said, I really appreciate Duy's work here -- thanks!  Git deserves \nto have a cool feature like worktrees be part of its standard toolkit.\n\n\t\tM.\n\n\n> This leaves a gray area where other things should be shared or not. I\n> think the preference (or default mode) is still _not_ shared (*).\n> Sharing more things (besides refs and object database) is just a new\n> opportunity popping up when we implement multiple worktrees. Since\n> multiple worktrees (or clones before its time) are grouped together,\n> sometimes you would like to share common configuration. We could sort\n> of achieve this already with includeIf but again not as convenient.\n> \n> (*) real life is not that simple. Since refs are shared, including\n> _remotes_ refs, so configuration related to remotes should also be\n> shared, or it will be a maintenance nightmare. Which is why\n> $GIT_DIR/config so far has been shared besides the two exceptions that\n> are core.bare and core.worktree. And I really like to get rid of these\n> exceptions.\n> \n>> Is there a better way to achieve that without the\n>> downside of multiple worktrees (e.g. configuration need to be\n>> uniform)?\n> \n> Is there a better way to achieve sharing refs between clones? I gave\n> it a minute but couldn't come up with a good answer :(\n> \n>>> (*) \"git config --worktree\" points back to \"config\" file when this\n>>>      extension is not present so that it works in any setup.\n>>\n>> Shouldn't it barf and error out instead?\n> \n> The intention is a uniform interface/api that works with both single\n> and multiple worktrees configurations. Otherwise in your scripts you\n> would need to write \"if single worktree, do this, else do that\". If\n> \"git config --worktree\" works with both, the existing scripts can be\n> audited and updated just a bit, adding \"--worktree\" where the config\n> should not be shared, and we're done.\n> \n>> A user who hasn't enabled\n>> the extension uses --worktree option and misled to believe that the\n>> setting affects only a single worktree, even though the change is\n>> made globally---that does not sound like a great end-user experience.\n> \n> I was talking about a single worktree. But I think here you meant the\n> user has multiple worktrees, but the extension is not enabled. I'm\n> probably not clear in the commit message, but \"git config\" can detect\n> that the extension has not been enabled, automatically enable it (and\n> move core.bare and core.worktree (if present) to the main worktree's\n> private config), so \"git config --worktree\" will never share the\n> change.\n> \n> But perhaps the user should be made aware of this situation and asked\n> to explicitly enable the extension instead? It's certainly a more\n> conservative approach.\n> \n"},{"id":"359068","messageId":"CACsJy8B_xDUQjC7uGRpR6FhFVuToUwim3sjUTk9tWUfLY2LkBA@mail.gmail.com","threadId":"49407","inReplyTo":"e7f63c0f-90dd-0e53-9721-35d2b827e101@xiplink.com","subject":"Re: Wherefor worktrees?","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-27T16:36:55Z","receivedAt":"2018-09-27T16:37:24Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Sep 27, 2018 at 5:24 PM Marc Branchaud <marcnarc@xiplink.com> wrote:\n>\n> On 2018-09-26 11:48 AM, Duy Nguyen wrote:\n> >\n> > I believe the main selling point of multiple worktrees is sharing\n> > refs. You could easily avoid expensive clones with --local, but\n> > synchronizing between different clones is not very convenient. Other\n> > than that, different worktrees tend to behave like separate clones.\n>\n> Sharing hooks is also useful\n\nWell yes, but for hooks I think a better way is moving hook management\nback to config files. I think we have a rough idea what to do, and\nAEvar highlighted the mand roadblocks elsewhere. Hopefully it will\nmaterialize someday.\n\n> Having used git-new-workdir for a long time, it's main deficiency for me\n> is submodules (the shared bisection state didn't bother me much).  It\n> would be nice if all my worktrees' submodules also shared refs.  That's\n> \"nice\", but not \"essential\".\n\nHeh I've been thinking about this a bit too. I thought separate refs\nwas a requirement for submodules, but if you don't actively use\nbranches in submodules and go with detached HEAD, it might work.\n-- \nDuy\n"},{"id":"359072","messageId":"CACsJy8Dwo_cBEQeqttj3G=RGaJT7bmVb=ABnV2916swgYQnyrw@mail.gmail.com","threadId":"49407","inReplyTo":"87bm8kjeu5.fsf@evledraar.gmail.com","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-27T17:24:42Z","receivedAt":"2018-09-27T17:25:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Sep 26, 2018 at 8:25 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> On Sun, Sep 23 2018, Nguyễn Thái Ngọc Duy wrote:\n>\n> > +extensions.worktreeConfig::\n> > +     If set, by default \"git config\" reads from both \"config\" and\n> > +     \"config.worktree\" file in that order.\n>\n> How does this interact with config that's now only used if it's in\n> .git/config? E.g. you can't set remote.<remote>.<url> in ~/.gitconfig,\n> will that be inherited across the two of these?\n\nEr... we can't? If I remember correctly we don't have any enforcement\non where what config vars must or must not go. The only exception is\ncore.bare and core.worktree which is only read from $GIT_DIR/config\nbecause of the way they are involved in .git directory discovery. If I\nput remote \"foo\" in ~/.gitconfig, \"git remote\" happily reports remote\n\"foo\" to me.\n\nTo sum up, we always inherit config from higher levels, with\n/etc/gitconfig being the highest and $GIT_DIR/config the lowest. It's\nup to the user to share config between repos responsibly. This patch\nonly adds one more level, $GIT_DIR/config.worktree which is now the\nlowest level.\n\n> > In multiple working\n> > +     directory mode, \"config\" file is shared while\n> > +     \"config.worktree\" is per-working directory.\n>\n> \"But then how will it work with more than one?\" I found myself thinking\n> before reading some more and remembering .git/worktree. Shouldn't we\n> consistently say:\n>\n>     [...]\"config\" and \"worktrees/<worktree name>/config\"[...]\n>\n> Or something like that?\n\nPoint taken. Maybe I'm trying to hide implementation details too much.\n-- \nDuy\n"},{"id":"359086","messageId":"87r2heiybu.fsf@evledraar.gmail.com","threadId":"49407","inReplyTo":"CACsJy8Dwo_cBEQeqttj3G=RGaJT7bmVb=ABnV2916swgYQnyrw@mail.gmail.com","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-09-27T18:34:29Z","receivedAt":"2018-09-27T18:34:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Sep 27 2018, Duy Nguyen wrote:\n\n> On Wed, Sep 26, 2018 at 8:25 PM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>> On Sun, Sep 23 2018, Nguyễn Thái Ngọc Duy wrote:\n>>\n>> > +extensions.worktreeConfig::\n>> > +     If set, by default \"git config\" reads from both \"config\" and\n>> > +     \"config.worktree\" file in that order.\n>>\n>> How does this interact with config that's now only used if it's in\n>> .git/config? E.g. you can't set remote.<remote>.<url> in ~/.gitconfig,\n>> will that be inherited across the two of these?\n>\n> Er... we can't? If I remember correctly we don't have any enforcement\n> on where what config vars must or must not go. The only exception is\n> core.bare and core.worktree which is only read from $GIT_DIR/config\n> because of the way they are involved in .git directory discovery. If I\n> put remote \"foo\" in ~/.gitconfig, \"git remote\" happily reports remote\n> \"foo\" to me.\n>\n> To sum up, we always inherit config from higher levels, with\n> /etc/gitconfig being the highest and $GIT_DIR/config the lowest. It's\n> up to the user to share config between repos responsibly. This patch\n> only adds one more level, $GIT_DIR/config.worktree which is now the\n> lowest level.\n\nI see I'm misremembering most of the details here. I thought that if I put:\n\n    [remote \"whatever]\n    url = ...\n\nInto my ~/.gitconfig that it wouldn't work, but it does, e.g. here in my\n~/g/git:\n\n    $ grep -A1 whatever .git/config\n    $\n    $ grep -A1 whatever ~/.gitconfig\n    [remote \"whatever\"]\n        url = git@github.com:test/git.git\n\nBut there's still some special casing for .git/config going on,\ne.g. here:\n\n    $ git config remote.origin.url\n    git@github.com:git/git.git\n    $ git config remote.whatever.url\n    git@github.com:test/git.git\n    $ git remote get-url origin\n    git@github.com:git/git.git\n    $ git remote get-url whatever\n    fatal: No such remote 'whatever'\n\nAnd:\n\n    $ git remote set-url whatever git@github.com:test2/git.git\n    fatal: No such remote 'whatever'\n\nSo there is some special casing of .git/config somewhere. I looked into\nthis ages ago, and don't remember where that's done.\n\nI was wondering if these patches introduced any unwanted edge cases in\nthis regard, e.g. if you're using the per-worktree config, and you have\nremotes in .git/config, does \"git remote get/set-url\" do the right\nthing?\n\nThen if we have remotes in .git/config, should we add new remotes to\n.git/config or the per-worktree file? I'd lean towards .git/config,\nsince remotes are orthagonal to worktrees, and closely tied with refs\nwhich are shared no matter what this extension says, but maybe there's a\ngood argument for doing it the other way around.\n\n\n\n>> > In multiple working\n>> > +     directory mode, \"config\" file is shared while\n>> > +     \"config.worktree\" is per-working directory.\n>>\n>> \"But then how will it work with more than one?\" I found myself thinking\n>> before reading some more and remembering .git/worktree. Shouldn't we\n>> consistently say:\n>>\n>>     [...]\"config\" and \"worktrees/<worktree name>/config\"[...]\n>>\n>> Or something like that?\n>\n> Point taken. Maybe I'm trying to hide implementation details too much.\n"},{"id":"359091","messageId":"CACsJy8AxUC7MjTnwG8LL+-WEQzTCj7pya8s-6eE=NG3HaWWunw@mail.gmail.com","threadId":"49407","inReplyTo":"87r2heiybu.fsf@evledraar.gmail.com","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-27T18:49:26Z","receivedAt":"2018-09-27T18:49:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Sep 27, 2018 at 8:34 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> ...\n>\n> So there is some special casing of .git/config somewhere. I looked into\n> this ages ago, and don't remember where that's done.\n\nThanks! At least know I have some clues to look into (and will do).\n-- \nDuy\n"},{"id":"359270","messageId":"CACsJy8DKG3dh-TxzdJPUJxZRMuV8+UpYoSTp7hyk8koZ0+Raeg@mail.gmail.com","threadId":"49407","inReplyTo":"87r2heiybu.fsf@evledraar.gmail.com","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T06:36:14Z","receivedAt":"2018-09-29T06:36:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Sep 27, 2018 at 8:34 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> I see I'm misremembering most of the details here. I thought that if I put:\n>\n>     [remote \"whatever]\n>     url = ...\n>\n> Into my ~/.gitconfig that it wouldn't work, but it does, e.g. here in my\n> ~/g/git:\n>\n>     $ grep -A1 whatever .git/config\n>     $\n>     $ grep -A1 whatever ~/.gitconfig\n>     [remote \"whatever\"]\n>         url = git@github.com:test/git.git\n>\n> But there's still some special casing for .git/config going on,\n> e.g. here:\n>\n>     $ git config remote.origin.url\n>     git@github.com:git/git.git\n>     $ git config remote.whatever.url\n>     git@github.com:test/git.git\n>     $ git remote get-url origin\n>     git@github.com:git/git.git\n>     $ git remote get-url whatever\n>     fatal: No such remote 'whatever'\n>\n> And:\n>\n>     $ git remote set-url whatever git@github.com:test2/git.git\n>     fatal: No such remote 'whatever'\n>\n> So there is some special casing of .git/config somewhere. I looked into\n> this ages ago, and don't remember where that's done.\n\nTo conclude this thread. Yes some code does know about where the\nconfig variable is from and it looks like only \"git remote\" and \"git\nupload-pack\" takes advantage of it [1] [2].\n\nI considered documentation improvement for the git-remote part, but I\ndon't see anywhere I can fit \"some remote attributes can be shared\nfrom ~/.gitconfig, but a remote can only be added from repo-level\nconfig\" in. Jeff already documented it well. I found one minor problem\nthere in the documentation, which I will send separately.\n\nThanks again for making me aware of this.\n\n[1] e459b073fb (remote rename: more carefully determine whether a\nremote is configured - 2017-01-19)\n[2] 20b20a22f8 (upload-pack: provide a hook for running pack-objects -\n2016-05-18)\n-- \nDuy\n"},{"id":"359286","messageId":"CACsJy8AqX+ckqxLpf5HtpfSJi=gkfodTNQP_mQb=LC-Dgvih=Q@mail.gmail.com","threadId":"49407","inReplyTo":"20180924142143.GD68796@syl","subject":"Re: [PATCH] worktree: add per-worktree config files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T13:53:38Z","receivedAt":"2018-09-29T13:54:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Sep 24, 2018 at 4:21 PM Taylor Blau <me@ttaylorr.com> wrote:\n> > @@ -602,6 +605,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n> >                            PARSE_OPT_STOP_AT_NON_OPTION);\n> >\n> >       if (use_global_config + use_system_config + use_local_config +\n> > +         use_worktree_config +\n> >           !!given_config_source.file + !!given_config_source.blob > 1) {\n>\n> I feel like we're getting into \"let's extract a function\" territory,\n> here, since this line is growing in width. Perhaps:\n>\n>   static int config_files_count()\n>   {\n>     return use_global_config + use_system_config + use_local_config +\n>                 use_worktree_config +\n>                 !!given_config_source.file +\n>                 !!given_config_source.blob;\n>   }\n>\n> Simplifying the call to:\n\nI think there's a bigger problem here because we have no good way to\ndescribe that a bunch of options are mutually exclusive. We get by ok\nif the number of options is two, but when it gets bigger (and this is\nnot the only place), it gets messier. Besides the long \"if\" condition,\nI consider the error message \"only one config file at a time\"\ninadequate. We should tell the user what exact options are\nconflicting.\n\nSo, I'm going to bury my head in the sand this time and ignore the\nproblem, including adding config_files_count(). It could be a\ninteresting mini project if someone wants to jump in. Meanwhile it'll\ngo to the very bottom of my long long backlog.\n-- \nDuy\n"},{"id":"359294","messageId":"20180929153005.10599-1-pclouds@gmail.com","threadId":"49407","inReplyTo":"20180923170438.23610-1-pclouds@gmail.com","subject":"[PATCH v2 0/2] Per-worktree config files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T15:30:03Z","receivedAt":"2018-09-29T15:30:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Besides various doc/test changes. v2 has two code changes\n\n- \"git config --worktree\" on a multiple worktree setup _without_ the\n  new extension will now barf. The user is supposed to RTFM and enable\n  the extension manually.\n\n- I failed to update setup code to pick up config from per-worktree\n  files. Granted, core.bare and core.worktree don't have much use in\n  this context (except perhaps submodules). But let's make sure we\n  have proper code path to handle it.\n\nconfig.worktree should have a new scope, CONFIG_SCOPE_WORKTREE, on top\nof CONFIG_SCOPE_REPO. But we would need to go over scope-sensitive\ncode (upload-pack and remote) to update if necessary. I don't think\nit's worth getting into that just yet.\n\nRange diff\n\n-:  ---------- > 1:  8f334e0020 t1300: extract and use test_cmp_config()\n1:  12a999e517 ! 2:  99337a52f2 worktree: add per-worktree config files\n    @@ -9,7 +9,7 @@\n            worktree setup.\n     \n          - The special treatment for core.bare and core.worktree, to stay\n    -       effective only in main worktree, is gone. These config files are\n    +       effective only in main worktree, is gone. These config settings are\n            supposed to be in config.worktree.\n     \n         This extension is most useful in multiple worktree setup because you\n    @@ -38,7 +38,8 @@\n         isolated.\n     \n         (*) \"git config --worktree\" points back to \"config\" file when this\n    -        extension is not present so that it works in any setup.\n    +        extension is not present and there is only one worktree so that it\n    +        works in any both single and multiple worktree setups.\n     \n      diff --git a/Documentation/config.txt b/Documentation/config.txt\n      --- a/Documentation/config.txt\n    @@ -50,7 +51,7 @@\n     -the Git commands' behavior. The `.git/config` file in each repository\n     -is used to store the configuration for that repository, and\n     +the Git commands' behavior. The files `.git/config` and optionally\n    -+`config.worktree` (see `extensions.worktreeConfig` below) are each\n    ++`config.worktree` (see `extensions.worktreeConfig` below) in each\n     +repository is used to store the configuration for that repository, and\n      `$HOME/.gitconfig` is used to store a per-user configuration as\n      fallback values for the `.git/config` file. The file `/etc/gitconfig`\n    @@ -61,9 +62,10 @@\n      \n     +extensions.worktreeConfig::\n     +\tIf set, by default \"git config\" reads from both \"config\" and\n    -+\t\"config.worktree\" file in that order. In multiple working\n    -+\tdirectory mode, \"config\" file is shared while\n    -+\t\"config.worktree\" is per-working directory.\n    ++\t\"config.worktree\" file from GIT_DIR in that order. In\n    ++\tmultiple working directory mode, \"config\" file is shared while\n    ++\t\"config.worktree\" is per-working directory (i.e., it's in\n    ++\tGIT_COMMON_DIR/worktrees/<id>/config.worktree)\n     +\n      core.fileMode::\n      \tTells Git if the executable bit of files in the working tree\n    @@ -140,12 +142,12 @@\n     +CONFIGURATION FILE\n     +------------------\n     +By default, the repository \"config\" file is shared across all working\n    -+directories. If the config variables `core.bare` or `core.worktree`\n    -+are already present in the config file, they will be applied to the\n    -+main working directory only.\n    ++trees. If the config variables `core.bare` or `core.worktree` are\n    ++already present in the config file, they will be applied to the main\n    ++working trees only.\n     +\n    -+In order to have configuration specific to working directories, you\n    -+can turn on \"worktreeConfig\" extension, e.g.:\n    ++In order to have configuration specific to working trees, you can turn\n    ++on \"worktreeConfig\" extension, e.g.:\n     +\n     +------------\n     +$ git config extensions.worktreeConfig true\n    @@ -153,26 +155,19 @@\n     +\n     +In this mode, specific configuration stays in the path pointed by `git\n     +rev-parse --git-path config.worktree`. You can add or update\n    -+configuration in this file with `git config --worktree`. Git before\n    -+version 2.20.0 will refuse to access repositories with this extension.\n    ++configuration in this file with `git config --worktree`. Older Git\n    ++versions may will refuse to access repositories with this extension.\n     +\n    -+Note that in this file, the exception for `core.bare` and\n    -+`core.worktree` is gone. If you have them before, you need to move\n    -+them to the `config.worktree` of the main working directory. You may\n    -+also take this opportunity to move other configuration that you do not\n    -+want to share to all working directories:\n    ++Note that in this file, the exception for `core.bare` and `core.worktree`\n    ++is gone. If you have them in $GIT_DIR/config before, you must move\n    ++them to the `config.worktree` of the main working tree. You may also\n    ++take this opportunity to review and move other configuration that you\n    ++do not want to share to all working trees:\n     +\n     + - `core.worktree` and `core.bare` should never be shared\n     +\n    -+ - `core.sparseCheckout` is recommended per working directory, unless\n    -+   you are sure you always use sparse checkout for all working\n    -+   directories.\n    -+\n    -+When `git config --worktree` is used to set a configuration variable\n    -+in multiple working directory setup, `extensions.worktreeConfig` will\n    -+be automatically set. The two variables `core.worktree` and\n    -+`core.bare` if present will be moved to `config.worktree` of the main\n    -+working tree.\n    ++ - `core.sparseCheckout` is recommended per working tree, unless you\n    ++   are sure you always use sparse checkout for all working trees.\n     +\n      DETAILS\n      -------\n    @@ -248,10 +243,13 @@\n     +\t\tstruct worktree **worktrees = get_worktrees(0);\n     +\t\tif (repository_format_worktree_config)\n     +\t\t\tgiven_config_source.file = git_pathdup(\"config.worktree\");\n    -+\t\telse if (worktrees[0] && worktrees[1]) {\n    -+\t\t\tmigrate_worktree_config(the_repository);\n    -+\t\t\tgiven_config_source.file = git_pathdup(\"config.worktree\");\n    -+\t\t} else\n    ++\t\telse if (worktrees[0] && worktrees[1])\n    ++\t\t\tdie(_(\"--worktree cannot be used with multiple \"\n    ++\t\t\t      \"working trees unless the config\\n\"\n    ++\t\t\t      \"extension worktreeConfig is enabled. \"\n    ++\t\t\t      \"Please read \\\"CONFIGURATION FILE\\\"\\n\"\n    ++\t\t\t      \"section in \\\"git help worktree\\\" for details\"));\n    ++\t\telse\n     +\t\t\tgiven_config_source.file = git_pathdup(\"config\");\n     +\t\tfree_worktrees(worktrees);\n     +\t} else if (given_config_source.file) {\n    @@ -284,6 +282,10 @@\n      \tif (repo_config && !access_or_die(repo_config, R_OK, 0))\n      \t\tret += git_config_from_file(fn, repo_config, data);\n      \n    ++\t/*\n    ++\t * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE.\n    ++\t * But let's not complicate things before it's actually needed.\n    ++\t */\n     +\tif (repository_format_worktree_config) {\n     +\t\tchar *path = git_pathdup(\"config.worktree\");\n     +\t\tif (!access_or_die(path, R_OK, 0))\n    @@ -310,6 +312,27 @@\n      diff --git a/setup.c b/setup.c\n      --- a/setup.c\n      +++ b/setup.c\n    +@@\n    + \tinitialized = 1;\n    + }\n    + \n    ++static int read_worktree_config(const char *var, const char *value, void *vdata)\n    ++{\n    ++\tstruct repository_format *data = vdata;\n    ++\n    ++\tif (strcmp(var, \"core.bare\") == 0) {\n    ++\t\tdata->is_bare = git_config_bool(var, value);\n    ++\t} else if (strcmp(var, \"core.worktree\") == 0) {\n    ++\t\tif (!value)\n    ++\t\t\treturn config_error_nonbool(var);\n    ++\t\tdata->work_tree = xstrdup(value);\n    ++\t}\n    ++\treturn 0;\n    ++}\n    ++\n    + static int check_repo_format(const char *var, const char *value, void *vdata)\n    + {\n    + \tstruct repository_format *data = vdata;\n     @@\n      \t\t\tif (!value)\n      \t\t\t\treturn config_error_nonbool(var);\n    @@ -319,16 +342,40 @@\n     +\t\t\tdata->worktree_config = git_config_bool(var, value);\n     +\t\telse\n      \t\t\tstring_list_append(&data->unknown_extensions, ext);\n    - \t} else if (strcmp(var, \"core.bare\") == 0) {\n    - \t\tdata->is_bare = git_config_bool(var, value);\n    +-\t} else if (strcmp(var, \"core.bare\") == 0) {\n    +-\t\tdata->is_bare = git_config_bool(var, value);\n    +-\t} else if (strcmp(var, \"core.worktree\") == 0) {\n    +-\t\tif (!value)\n    +-\t\t\treturn config_error_nonbool(var);\n    +-\t\tdata->work_tree = xstrdup(value);\n    + \t}\n    +-\treturn 0;\n    ++\n    ++\treturn read_worktree_config(var, value, vdata);\n    + }\n    + \n    + static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)\n     @@\n      \n      \trepository_format_precious_objects = candidate->precious_objects;\n      \trepository_format_partial_clone = candidate->partial_clone;\n     +\trepository_format_worktree_config = candidate->worktree_config;\n      \tstring_list_clear(&candidate->unknown_extensions, 0);\n    ++\n    ++\tif (repository_format_worktree_config) {\n    ++\t\t/*\n    ++\t\t * pick up core.bare and core.worktree from per-worktree\n    ++\t\t * config if present\n    ++\t\t */\n    ++\t\tstrbuf_addf(&sb, \"%s/config.worktree\", gitdir);\n    ++\t\tgit_config_from_file(read_worktree_config, sb.buf, candidate);\n    ++\t\tstrbuf_release(&sb);\n    ++\t\thas_common = 0;\n    ++\t}\n    ++\n      \tif (!has_common) {\n      \t\tif (candidate->is_bare != -1) {\n    + \t\t\tis_bare_repository_cfg = candidate->is_bare;\n     \n      diff --git a/t/t2029-worktree-config.sh b/t/t2029-worktree-config.sh\n      new file mode 100755\n    @@ -341,150 +388,76 @@\n     +\n     +. ./test-lib.sh\n     +\n    -+cmp_config() {\n    -+\tif [ \"$1\" = \"-C\" ]; then\n    -+\t\tshift &&\n    -+\t\tGD=\"-C $1\" &&\n    -+\t\tshift\n    -+\telse\n    -+\t\tGD=\n    -+\tfi &&\n    -+\techo \"$1\" >expected &&\n    -+\tshift &&\n    -+\tgit $GD config \"$@\" >actual &&\n    -+\ttest_cmp expected actual\n    -+}\n    -+\n     +test_expect_success 'setup' '\n    -+\ttest_commit start &&\n    -+\tgit config --worktree per.worktree is-ok &&\n    ++\ttest_commit start\n    ++'\n    ++\n    ++test_expect_success 'config --worktree in single worktree' '\n    ++\tgit config --worktree foo.bar true &&\n    ++\ttest_cmp_config true foo.bar\n    ++'\n    ++\n    ++test_expect_success 'add worktrees' '\n     +\tgit worktree add wt1 &&\n    -+\tgit worktree add wt2 &&\n    -+\tgit config --worktree per.worktree is-ok &&\n    -+\tcmp_config true extensions.worktreeConfig\n    ++\tgit worktree add wt2\n    ++'\n    ++\n    ++test_expect_success 'config --worktree without extension' '\n    ++\ttest_must_fail git config --worktree foo.bar false\n    ++'\n    ++\n    ++test_expect_success 'enable worktreeConfig extension' '\n    ++\tgit config extensions.worktreeConfig true &&\n    ++\ttest_cmp_config true extensions.worktreeConfig\n     +'\n     +\n     +test_expect_success 'config is shared as before' '\n     +\tgit config this.is shared &&\n    -+\tcmp_config shared this.is &&\n    -+\tcmp_config -C wt1 shared this.is &&\n    -+\tcmp_config -C wt2 shared this.is\n    ++\ttest_cmp_config shared this.is &&\n    ++\ttest_cmp_config -C wt1 shared this.is &&\n    ++\ttest_cmp_config -C wt2 shared this.is\n     +'\n     +\n     +test_expect_success 'config is shared (set from another worktree)' '\n     +\tgit -C wt1 config that.is also-shared &&\n    -+\tcmp_config also-shared that.is &&\n    -+\tcmp_config -C wt1 also-shared that.is &&\n    -+\tcmp_config -C wt2 also-shared that.is\n    ++\ttest_cmp_config also-shared that.is &&\n    ++\ttest_cmp_config -C wt1 also-shared that.is &&\n    ++\ttest_cmp_config -C wt2 also-shared that.is\n     +'\n     +\n     +test_expect_success 'config private to main worktree' '\n     +\tgit config --worktree this.is for-main &&\n    -+\tcmp_config for-main this.is &&\n    -+\tcmp_config -C wt1 shared this.is &&\n    -+\tcmp_config -C wt2 shared this.is\n    ++\ttest_cmp_config for-main this.is &&\n    ++\ttest_cmp_config -C wt1 shared this.is &&\n    ++\ttest_cmp_config -C wt2 shared this.is\n     +'\n     +\n     +test_expect_success 'config private to linked worktree' '\n     +\tgit -C wt1 config --worktree this.is for-wt1 &&\n    -+\tcmp_config for-main this.is &&\n    -+\tcmp_config -C wt1 for-wt1 this.is &&\n    -+\tcmp_config -C wt2 shared this.is\n    ++\ttest_cmp_config for-main this.is &&\n    ++\ttest_cmp_config -C wt1 for-wt1 this.is &&\n    ++\ttest_cmp_config -C wt2 shared this.is\n     +'\n     +\n     +test_expect_success 'core.bare no longer for main only' '\n    -+\tgit config core.bare true &&\n    -+\tcmp_config true core.bare &&\n    -+\tcmp_config -C wt1 true core.bare &&\n    -+\tcmp_config -C wt2 true core.bare &&\n    -+\tgit config --unset core.bare\n    ++\ttest_config core.bare true &&\n    ++\ttest \"$(git rev-parse --is-bare-repository)\" = true &&\n    ++\ttest \"$(git -C wt1 rev-parse --is-bare-repository)\" = true &&\n    ++\ttest \"$(git -C wt2 rev-parse --is-bare-repository)\" = true\n     +'\n     +\n    -+test_expect_success 'config.worktree no longer read without extension' '\n    -+\tgit config --unset extensions.worktreeConfig &&\n    -+\tcmp_config shared this.is &&\n    -+\tcmp_config -C wt1 shared this.is &&\n    -+\tcmp_config -C wt2 shared this.is\n    ++test_expect_success 'per-worktree core.bare is picked up' '\n    ++\tgit -C wt1 config --worktree core.bare true &&\n    ++\ttest \"$(git rev-parse --is-bare-repository)\" = false &&\n    ++\ttest \"$(git -C wt1 rev-parse --is-bare-repository)\" = true &&\n    ++\ttest \"$(git -C wt2 rev-parse --is-bare-repository)\" = false\n     +'\n     +\n    -+test_expect_success 'config --worktree migrate core.bare and core.worktree' '\n    -+\tgit config core.bare true &&\n    -+\tgit config --worktree foo.bar true &&\n    -+\tcmp_config true extensions.worktreeConfig &&\n    -+\tcmp_config true foo.bar &&\n    -+\tcmp_config true core.bare &&\n    -+\t! git -C wt1 config core.bare\n    ++test_expect_success 'config.worktree no longer read without extension' '\n    ++\tgit config --unset extensions.worktreeConfig &&\n    ++\ttest_cmp_config shared this.is &&\n    ++\ttest_cmp_config -C wt1 shared this.is &&\n    ++\ttest_cmp_config -C wt2 shared this.is\n     +'\n     +\n     +test_done\n    -\n    - diff --git a/worktree.c b/worktree.c\n    - --- a/worktree.c\n    - +++ b/worktree.c\n    -@@\n    - #include \"worktree.h\"\n    - #include \"dir.h\"\n    - #include \"wt-status.h\"\n    -+#include \"config.h\"\n    - \n    - void free_worktrees(struct worktree **worktrees)\n    - {\n    -@@\n    - \tfree_worktrees(worktrees);\n    - \treturn ret;\n    - }\n    -+\n    -+void migrate_worktree_config(struct repository *r)\n    -+{\n    -+\tstruct strbuf worktree_path = STRBUF_INIT;\n    -+\tstruct strbuf main_path = STRBUF_INIT;\n    -+\tstruct repository_format format;\n    -+\n    -+\tassert(repository_format_worktree_config == 0);\n    -+\n    -+\tstrbuf_git_common_path(&worktree_path, r, \"config.worktree\");\n    -+\tstrbuf_git_path(&main_path, \"config\");\n    -+\n    -+\tread_repository_format(&format, main_path.buf);\n    -+\tassert(format.worktree_config == 0);\n    -+\n    -+\tif (format.is_bare >= 0) {\n    -+\t\tgit_config_set_in_file(worktree_path.buf,\n    -+\t\t\t\t       \"core.bare\", \"true\");\n    -+\t\tgit_config_set_in_file(main_path.buf,\n    -+\t\t\t\t       \"core.bare\", NULL);\n    -+\t}\n    -+\tif (format.work_tree) {\n    -+\t\tgit_config_set_in_file(worktree_path.buf,\n    -+\t\t\t\t       \"core.worktree\",\n    -+\t\t\t\t       format.work_tree);\n    -+\t\tgit_config_set_in_file(main_path.buf,\n    -+\t\t\t\t       \"core.worktree\", NULL);\n    -+\t}\n    -+\n    -+\tgit_config_set_in_file(main_path.buf,\n    -+\t\t\t       \"extensions.worktreeConfig\", \"true\");\n    -+\tif (format.version == 0)\n    -+\t\tgit_config_set_in_file(main_path.buf,\n    -+\t\t\t\t       \"core.repositoryFormatVersion\", \"1\");\n    -+\n    -+\trepository_format_worktree_config = 1;\n    -+\n    -+\tstrbuf_release(&main_path);\n    -+\tstrbuf_release(&worktree_path);\n    -+}\n    -\n    - diff --git a/worktree.h b/worktree.h\n    - --- a/worktree.h\n    - +++ b/worktree.h\n    -@@\n    - \t\t\t\t     const char *fmt, ...)\n    - \t__attribute__((format (printf, 2, 3)));\n    - \n    -+/*\n    -+ * Called to add extensions.worktreeConfig to $GIT_DIR/config and move\n    -+ * main worktree specific config variables to $GIT_DIR/config.worktree.\n    -+ */\n    -+void migrate_worktree_config(struct repository *r);\n    -+\n    - #endif\n\n\nNguyễn Thái Ngọc Duy (2):\n  t1300: extract and use test_cmp_config()\n  worktree: add per-worktree config files\n\n Documentation/config.txt               | 12 +++-\n Documentation/git-config.txt           | 26 ++++++---\n Documentation/git-worktree.txt         | 30 ++++++++++\n Documentation/gitrepository-layout.txt |  8 +++\n builtin/config.c                       | 19 ++++++-\n cache.h                                |  2 +\n config.c                               | 11 ++++\n environment.c                          |  1 +\n setup.c                                | 40 ++++++++++---\n t/t1300-config.sh                      | 79 +++++++-------------------\n t/t2029-worktree-config.sh             | 79 ++++++++++++++++++++++++++\n t/test-lib-functions.sh                | 24 ++++++++\n 12 files changed, 253 insertions(+), 78 deletions(-)\n create mode 100755 t/t2029-worktree-config.sh\n\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359295","messageId":"20180929153005.10599-2-pclouds@gmail.com","threadId":"49407","inReplyTo":"20180929153005.10599-1-pclouds@gmail.com","subject":"[PATCH v2 1/2] t1300: extract and use test_cmp_config()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T15:30:04Z","receivedAt":"2018-09-29T15:30:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In many config-related tests it's common to check if a config variable\nhas expected value and we want to print the differences when the test\nfails. Doing it the normal way is three lines of shell code. Let's add\na function do to all this (and a little more).\n\nThis function has uses outside t1300 as well but I'm not going to\nconvert them all. And it will be used in the next commit where\nper-worktree config feature is introduced.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t1300-config.sh       | 79 ++++++++++-------------------------------\n t/test-lib-functions.sh | 24 +++++++++++++\n 2 files changed, 43 insertions(+), 60 deletions(-)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex cdf1fed5d1..00c2b0f0eb 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -76,15 +76,11 @@ EOF\n test_expect_success 'non-match result' 'test_cmp expect .git/config'\n \n test_expect_success 'find mixed-case key by canonical name' '\n-\techo Second >expect &&\n-\tgit config cores.whatever >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config Second cores.whatever\n '\n \n test_expect_success 'find mixed-case key by non-canonical name' '\n-\techo Second >expect &&\n-\tgit config CoReS.WhAtEvEr >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config Second CoReS.WhAtEvEr\n '\n \n test_expect_success 'subsections are not canonicalized by git-config' '\n@@ -94,12 +90,8 @@ test_expect_success 'subsections are not canonicalized by git-config' '\n \t[section \"SubSection\"]\n \tkey = two\n \tEOF\n-\techo one >expect &&\n-\tgit config section.subsection.key >actual &&\n-\ttest_cmp expect actual &&\n-\techo two >expect &&\n-\tgit config section.SubSection.key >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config one section.subsection.key &&\n+\ttest_cmp_config two section.SubSection.key\n '\n \n cat > .git/config <<\\EOF\n@@ -212,9 +204,7 @@ test_expect_success 'really really mean test' '\n '\n \n test_expect_success 'get value' '\n-\techo alpha >expect &&\n-\tgit config beta.haha >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config alpha beta.haha\n '\n \n cat > expect << EOF\n@@ -251,15 +241,11 @@ test_expect_success 'non-match' '\n '\n \n test_expect_success 'non-match value' '\n-\techo wow >expect &&\n-\tgit config --get nextsection.nonewline !for >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config wow --get nextsection.nonewline !for\n '\n \n test_expect_success 'multi-valued get returns final one' '\n-\techo \"wow2 for me\" >expect &&\n-\tgit config --get nextsection.nonewline >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config \"wow2 for me\" --get nextsection.nonewline\n '\n \n test_expect_success 'multi-valued get-all returns all' '\n@@ -520,21 +506,11 @@ test_expect_success 'editing stdin is an error' '\n \n test_expect_success 'refer config from subdirectory' '\n \tmkdir x &&\n-\t(\n-\t\tcd x &&\n-\t\techo strasse >expect &&\n-\t\tgit config --get --file ../other-config ein.bahn >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-\n+\ttest_cmp_config -C x strasse --get --file ../other-config ein.bahn\n '\n \n test_expect_success 'refer config from subdirectory via --file' '\n-\t(\n-\t\tcd x &&\n-\t\tgit config --file=../other-config --get ein.bahn >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n+\ttest_cmp_config -C x strasse --file=../other-config --get ein.bahn\n '\n \n cat > expect << EOF\n@@ -688,16 +664,13 @@ test_expect_success numbers '\n \n test_expect_success '--int is at least 64 bits' '\n \tgit config giga.watts 121g &&\n-\techo 129922760704 >expect &&\n-\tgit config --int --get giga.watts >actual &&\n-\ttest_cmp expect actual\n+\techo  >expect &&\n+\ttest_cmp_config 129922760704 --int --get giga.watts\n '\n \n test_expect_success 'invalid unit' '\n \tgit config aninvalid.unit \"1auto\" &&\n-\techo 1auto >expect &&\n-\tgit config aninvalid.unit >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_cmp_config 1auto aninvalid.unit &&\n \ttest_must_fail git config --int --get aninvalid.unit 2>actual &&\n \ttest_i18ngrep \"bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit\" actual\n '\n@@ -1039,9 +1012,7 @@ test_expect_success '--null --get-regexp' '\n \n test_expect_success 'inner whitespace kept verbatim' '\n \tgit config section.val \"foo \t  bar\" &&\n-\techo \"foo \t  bar\" >expect &&\n-\tgit config section.val >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config \"foo \t  bar\" section.val\n '\n \n test_expect_success SYMLINKS 'symlinked configuration' '\n@@ -1808,21 +1779,15 @@ big = 1M\n EOF\n \n test_expect_success 'identical modern --type specifiers are allowed' '\n-\tgit config --type=int --type=int core.big >actual &&\n-\techo 1048576 >expect &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --type=int --type=int core.big\n '\n \n test_expect_success 'identical legacy --type specifiers are allowed' '\n-\tgit config --int --int core.big >actual &&\n-\techo 1048576 >expect &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --int --int core.big\n '\n \n test_expect_success 'identical mixed --type specifiers are allowed' '\n-\tgit config --int --type=int core.big >actual &&\n-\techo 1048576 >expect &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --int --type=int core.big\n '\n \n test_expect_success 'non-identical modern --type specifiers are not allowed' '\n@@ -1841,21 +1806,15 @@ test_expect_success 'non-identical mixed --type specifiers are not allowed' '\n '\n \n test_expect_success '--type allows valid type specifiers' '\n-\techo \"true\" >expect &&\n-\tgit config --type=bool core.foo >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config true  --type=bool core.foo\n '\n \n test_expect_success '--no-type unsets type specifiers' '\n-\techo \"10\" >expect &&\n-\tgit config --type=bool --no-type core.number >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 10 --type=bool --no-type core.number\n '\n \n test_expect_success 'unset type specifiers may be reset to conflicting ones' '\n-\techo 1048576 >expect &&\n-\tgit config --type=bool --no-type --type=int core.big >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --type=bool --no-type --type=int core.big\n '\n \n test_expect_success '--type rejects unknown specifiers' '\ndiff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\nindex d82fac9d79..4cd7fb8fdf 100644\n--- a/t/test-lib-functions.sh\n+++ b/t/test-lib-functions.sh\n@@ -747,6 +747,30 @@ test_cmp() {\n \t$GIT_TEST_CMP \"$@\"\n }\n \n+# similar to test_cmp but $2 is a config key instead of actual value\n+# it can also accept -C to read from a different repo, e.g.\n+#\n+#     test_cmp_config -C xyz foo core.bar\n+#\n+# is sort of equivalent of\n+#\n+#     test \"foo\" = \"$(git -C xyz core.bar)\"\n+\n+test_cmp_config() {\n+\tif [ \"$1\" = \"-C\" ]\n+\tthen\n+\t\tshift &&\n+\t\tGD=\"-C $1\" &&\n+\t\tshift\n+\telse\n+\t\tGD=\n+\tfi &&\n+\techo \"$1\" >expected &&\n+\tshift &&\n+\tgit $GD config \"$@\" >actual &&\n+\ttest_cmp expected actual\n+}\n+\n # test_cmp_bin - helper to compare binary files\n \n test_cmp_bin() {\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359296","messageId":"20180929153005.10599-3-pclouds@gmail.com","threadId":"49407","inReplyTo":"20180929153005.10599-1-pclouds@gmail.com","subject":"[PATCH v2 2/2] worktree: add per-worktree config files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T15:30:05Z","receivedAt":"2018-09-29T15:30:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A new repo extension is added, worktreeConfig. When it is present:\n\n - Repository config reading by default includes $GIT_DIR/config _and_\n   $GIT_DIR/config.worktree. \"config\" file remains shared in multiple\n   worktree setup.\n\n - The special treatment for core.bare and core.worktree, to stay\n   effective only in main worktree, is gone. These config settings are\n   supposed to be in config.worktree.\n\nThis extension is most useful in multiple worktree setup because you\nnow have an option to store per-worktree config (which is either\n.git/config.worktree for main worktree, or\n.git/worktrees/xx/config.worktree for linked ones).\n\nThis extension can be used in single worktree mode, even though it's\npretty much useless (but this can happen after you remove all linked\nworktrees and move back to single worktree).\n\n\"git config\" reads from both \"config\" and \"config.worktree\" by default\n(i.e. without either --user, --file...) when this extension is\npresent. Default writes still go to \"config\", not \"config.worktree\". A\nnew option --worktree is added for that (*).\n\nSince a new repo extension is introduced, existing git binaries should\nrefuse to access to the repo (both from main and linked worktrees). So\nthey will not misread the config file (i.e. skip the config.worktree\npart). They may still accidentally write to the config file anyway if\nthey use with \"git config --file <path>\".\n\nThis design places a bet on the assumption that the majority of config\nvariables are shared so it is the default mode. A safer move would be\ndefault writes go to per-worktree file, so that accidental changes are\nisolated.\n\n(*) \"git config --worktree\" points back to \"config\" file when this\n    extension is not present and there is only one worktree so that it\n    works in any both single and multiple worktree setups.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt               | 12 +++-\n Documentation/git-config.txt           | 26 ++++++---\n Documentation/git-worktree.txt         | 30 ++++++++++\n Documentation/gitrepository-layout.txt |  8 +++\n builtin/config.c                       | 19 ++++++-\n cache.h                                |  2 +\n config.c                               | 11 ++++\n environment.c                          |  1 +\n setup.c                                | 40 ++++++++++---\n t/t2029-worktree-config.sh             | 79 ++++++++++++++++++++++++++\n 10 files changed, 210 insertions(+), 18 deletions(-)\n create mode 100755 t/t2029-worktree-config.sh\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 8d85d1a324..44407e69db 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2,8 +2,9 @@ CONFIGURATION FILE\n ------------------\n \n The Git configuration file contains a number of variables that affect\n-the Git commands' behavior. The `.git/config` file in each repository\n-is used to store the configuration for that repository, and\n+the Git commands' behavior. The files `.git/config` and optionally\n+`config.worktree` (see `extensions.worktreeConfig` below) in each\n+repository is used to store the configuration for that repository, and\n `$HOME/.gitconfig` is used to store a per-user configuration as\n fallback values for the `.git/config` file. The file `/etc/gitconfig`\n can be used to store a system-wide default configuration.\n@@ -371,6 +372,13 @@ advice.*::\n \t\teditor input from the user.\n --\n \n+extensions.worktreeConfig::\n+\tIf set, by default \"git config\" reads from both \"config\" and\n+\t\"config.worktree\" file from GIT_DIR in that order. In\n+\tmultiple working directory mode, \"config\" file is shared while\n+\t\"config.worktree\" is per-working directory (i.e., it's in\n+\tGIT_COMMON_DIR/worktrees/<id>/config.worktree)\n+\n core.fileMode::\n \tTells Git if the executable bit of files in the working tree\n \tis to be honored.\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 8e240435be..4870e00b89 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -45,13 +45,15 @@ unset an existing `--type` specifier with `--no-type`.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n-`--system`, `--global`, `--local` and `--file <filename>` can be\n-used to tell the command to read from only that location (see <<FILES>>).\n+`--system`, `--global`, `--local`, `--worktree` and\n+`--file <filename>` can be used to tell the command to read from only\n+that location (see <<FILES>>).\n \n When writing, the new value is written to the repository local\n configuration file by default, and options `--system`, `--global`,\n-`--file <filename>` can be used to tell the command to write to\n-that location (you can say `--local` but that is the default).\n+`--worktree`, `--file <filename>` can be used to tell the command to\n+write to that location (you can say `--local` but that is the\n+default).\n \n This command will fail with non-zero status upon error.  Some exit\n codes are:\n@@ -131,6 +133,11 @@ from all available files.\n +\n See also <<FILES>>.\n \n+--worktree::\n+\tSimilar to `--local` except that `.git/config.worktree` is\n+\tread from or written to if `extensions.worktreeConfig` is\n+\tpresent. If not it's the same as `--local`.\n+\n -f config-file::\n --file config-file::\n \tUse the given config file instead of the one specified by GIT_CONFIG.\n@@ -281,6 +288,10 @@ $XDG_CONFIG_HOME/git/config::\n $GIT_DIR/config::\n \tRepository specific configuration file.\n \n+$GIT_DIR/config.worktree::\n+\tThis is optional and is only searched when\n+\t`extensions.worktreeConfig` is present in $GIT_DIR/config.\n+\n If no further options are given, all reading options will read all of these\n files that are available. If the global or the system-wide configuration\n file are not available they will be ignored. If the repository configuration\n@@ -299,9 +310,10 @@ configuration file. Note that this also affects options like `--replace-all`\n and `--unset`. *'git config' will only ever change one file at a time*.\n \n You can override these rules either by command-line options or by environment\n-variables. The `--global` and the `--system` options will limit the file used\n-to the global or system-wide file respectively. The `GIT_CONFIG` environment\n-variable has a similar effect, but you can specify any filename you want.\n+variables. The `--global`, `--system` and `--worktree` options will limit\n+the file used to the global, system-wide or per-worktree file respectively.\n+The `GIT_CONFIG` environment variable has a similar effect, but you\n+can specify any filename you want.\n \n \n ENVIRONMENT\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex e2ee9fc21b..aa88278dde 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -204,6 +204,36 @@ working trees, it can be used to identify worktrees. For example if\n you only have two working trees, at \"/abc/def/ghi\" and \"/abc/def/ggg\",\n then \"ghi\" or \"def/ghi\" is enough to point to the former working tree.\n \n+CONFIGURATION FILE\n+------------------\n+By default, the repository \"config\" file is shared across all working\n+trees. If the config variables `core.bare` or `core.worktree` are\n+already present in the config file, they will be applied to the main\n+working trees only.\n+\n+In order to have configuration specific to working trees, you can turn\n+on \"worktreeConfig\" extension, e.g.:\n+\n+------------\n+$ git config extensions.worktreeConfig true\n+------------\n+\n+In this mode, specific configuration stays in the path pointed by `git\n+rev-parse --git-path config.worktree`. You can add or update\n+configuration in this file with `git config --worktree`. Older Git\n+versions may will refuse to access repositories with this extension.\n+\n+Note that in this file, the exception for `core.bare` and `core.worktree`\n+is gone. If you have them in $GIT_DIR/config before, you must move\n+them to the `config.worktree` of the main working tree. You may also\n+take this opportunity to review and move other configuration that you\n+do not want to share to all working trees:\n+\n+ - `core.worktree` and `core.bare` should never be shared\n+\n+ - `core.sparseCheckout` is recommended per working tree, unless you\n+   are sure you always use sparse checkout for all working trees.\n+\n DETAILS\n -------\n Each linked working tree has a private sub-directory in the repository's\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex e85148f05e..36fcca8087 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -143,6 +143,11 @@ config::\n \tif $GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/config\" will be\n \tused instead.\n \n+config.worktree::\n+\tWorking directory specific configuration file for the main\n+\tworking directory in multiple working directory setup (see\n+\tlinkgit:git-worktree[1]).\n+\n branches::\n \tA slightly deprecated way to store shorthands to be used\n \tto specify a URL to 'git fetch', 'git pull' and 'git push'.\n@@ -275,6 +280,9 @@ worktrees/<id>/locked::\n \tor manually by `git worktree prune`. The file may contain a string\n \texplaining why the repository is locked.\n \n+worktrees/<id>/config.worktree::\n+\tWorking directory specific configuration file.\n+\n SEE ALSO\n --------\n linkgit:git-init[1],\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 97b58c4aea..84385ef165 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -5,6 +5,7 @@\n #include \"parse-options.h\"\n #include \"urlmatch.h\"\n #include \"quote.h\"\n+#include \"worktree.h\"\n \n static const char *const builtin_config_usage[] = {\n \tN_(\"git config [<options>]\"),\n@@ -24,6 +25,7 @@ static char key_delim = ' ';\n static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n+static int use_worktree_config;\n static struct git_config_source given_config_source;\n static int actions, type;\n static char *default_value;\n@@ -123,6 +125,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n \tOPT_BOOL(0, \"system\", &use_system_config, N_(\"use system config file\")),\n \tOPT_BOOL(0, \"local\", &use_local_config, N_(\"use repository config file\")),\n+\tOPT_BOOL(0, \"worktree\", &use_worktree_config, N_(\"use per-worktree config file\")),\n \tOPT_STRING('f', \"file\", &given_config_source.file, N_(\"file\"), N_(\"use given config file\")),\n \tOPT_STRING(0, \"blob\", &given_config_source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\")),\n \tOPT_GROUP(N_(\"Action\")),\n@@ -602,6 +605,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n \tif (use_global_config + use_system_config + use_local_config +\n+\t    use_worktree_config +\n \t    !!given_config_source.file + !!given_config_source.blob > 1) {\n \t\terror(_(\"only one config file at a time\"));\n \t\tusage_builtin_config();\n@@ -645,7 +649,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tgiven_config_source.file = git_etc_gitconfig();\n \telse if (use_local_config)\n \t\tgiven_config_source.file = git_pathdup(\"config\");\n-\telse if (given_config_source.file) {\n+\telse if (use_worktree_config) {\n+\t\tstruct worktree **worktrees = get_worktrees(0);\n+\t\tif (repository_format_worktree_config)\n+\t\t\tgiven_config_source.file = git_pathdup(\"config.worktree\");\n+\t\telse if (worktrees[0] && worktrees[1])\n+\t\t\tdie(_(\"--worktree cannot be used with multiple \"\n+\t\t\t      \"working trees unless the config\\n\"\n+\t\t\t      \"extension worktreeConfig is enabled. \"\n+\t\t\t      \"Please read \\\"CONFIGURATION FILE\\\"\\n\"\n+\t\t\t      \"section in \\\"git help worktree\\\" for details\"));\n+\t\telse\n+\t\t\tgiven_config_source.file = git_pathdup(\"config\");\n+\t\tfree_worktrees(worktrees);\n+\t} else if (given_config_source.file) {\n \t\tif (!is_absolute_path(given_config_source.file) && prefix)\n \t\t\tgiven_config_source.file =\n \t\t\t\tprefix_filename(prefix, given_config_source.file);\ndiff --git a/cache.h b/cache.h\nindex d508f3d4f8..9e9b917e99 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -957,11 +957,13 @@ extern int grafts_replace_parents;\n extern int repository_format_precious_objects;\n extern char *repository_format_partial_clone;\n extern const char *core_partial_clone_filter_default;\n+extern int repository_format_worktree_config;\n \n struct repository_format {\n \tint version;\n \tint precious_objects;\n \tchar *partial_clone; /* value of extensions.partialclone */\n+\tint worktree_config;\n \tint is_bare;\n \tint hash_algo;\n \tchar *work_tree;\ndiff --git a/config.c b/config.c\nindex 3461993f0a..b3025164d2 100644\n--- a/config.c\n+++ b/config.c\n@@ -1695,6 +1695,17 @@ static int do_git_config_sequence(const struct config_options *opts,\n \tif (repo_config && !access_or_die(repo_config, R_OK, 0))\n \t\tret += git_config_from_file(fn, repo_config, data);\n \n+\t/*\n+\t * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE.\n+\t * But let's not complicate things before it's actually needed.\n+\t */\n+\tif (repository_format_worktree_config) {\n+\t\tchar *path = git_pathdup(\"config.worktree\");\n+\t\tif (!access_or_die(path, R_OK, 0))\n+\t\t\tret += git_config_from_file(fn, path, data);\n+\t\tfree(path);\n+\t}\n+\n \tcurrent_parsing_scope = CONFIG_SCOPE_CMDLINE;\n \tif (git_config_from_parameters(fn, data) < 0)\n \t\tdie(_(\"unable to parse command-line config\"));\ndiff --git a/environment.c b/environment.c\nindex 3f3c8746c2..268310b3dc 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -33,6 +33,7 @@ int ref_paranoia = -1;\n int repository_format_precious_objects;\n char *repository_format_partial_clone;\n const char *core_partial_clone_filter_default;\n+int repository_format_worktree_config;\n const char *git_commit_encoding;\n const char *git_log_output_encoding;\n const char *apply_default_whitespace;\ndiff --git a/setup.c b/setup.c\nindex b24c811c1c..1be5037f12 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -402,6 +402,20 @@ void setup_work_tree(void)\n \tinitialized = 1;\n }\n \n+static int read_worktree_config(const char *var, const char *value, void *vdata)\n+{\n+\tstruct repository_format *data = vdata;\n+\n+\tif (strcmp(var, \"core.bare\") == 0) {\n+\t\tdata->is_bare = git_config_bool(var, value);\n+\t} else if (strcmp(var, \"core.worktree\") == 0) {\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\tdata->work_tree = xstrdup(value);\n+\t}\n+\treturn 0;\n+}\n+\n static int check_repo_format(const char *var, const char *value, void *vdata)\n {\n \tstruct repository_format *data = vdata;\n@@ -423,16 +437,13 @@ static int check_repo_format(const char *var, const char *value, void *vdata)\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(var);\n \t\t\tdata->partial_clone = xstrdup(value);\n-\t\t} else\n+\t\t} else if (!strcmp(ext, \"worktreeconfig\"))\n+\t\t\tdata->worktree_config = git_config_bool(var, value);\n+\t\telse\n \t\t\tstring_list_append(&data->unknown_extensions, ext);\n-\t} else if (strcmp(var, \"core.bare\") == 0) {\n-\t\tdata->is_bare = git_config_bool(var, value);\n-\t} else if (strcmp(var, \"core.worktree\") == 0) {\n-\t\tif (!value)\n-\t\t\treturn config_error_nonbool(var);\n-\t\tdata->work_tree = xstrdup(value);\n \t}\n-\treturn 0;\n+\n+\treturn read_worktree_config(var, value, vdata);\n }\n \n static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)\n@@ -466,7 +477,20 @@ static int check_repository_format_gently(const char *gitdir, struct repository_\n \n \trepository_format_precious_objects = candidate->precious_objects;\n \trepository_format_partial_clone = candidate->partial_clone;\n+\trepository_format_worktree_config = candidate->worktree_config;\n \tstring_list_clear(&candidate->unknown_extensions, 0);\n+\n+\tif (repository_format_worktree_config) {\n+\t\t/*\n+\t\t * pick up core.bare and core.worktree from per-worktree\n+\t\t * config if present\n+\t\t */\n+\t\tstrbuf_addf(&sb, \"%s/config.worktree\", gitdir);\n+\t\tgit_config_from_file(read_worktree_config, sb.buf, candidate);\n+\t\tstrbuf_release(&sb);\n+\t\thas_common = 0;\n+\t}\n+\n \tif (!has_common) {\n \t\tif (candidate->is_bare != -1) {\n \t\t\tis_bare_repository_cfg = candidate->is_bare;\ndiff --git a/t/t2029-worktree-config.sh b/t/t2029-worktree-config.sh\nnew file mode 100755\nindex 0000000000..286121d8de\n--- /dev/null\n+++ b/t/t2029-worktree-config.sh\n@@ -0,0 +1,79 @@\n+#!/bin/sh\n+\n+test_description=\"config file in multi worktree\"\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit start\n+'\n+\n+test_expect_success 'config --worktree in single worktree' '\n+\tgit config --worktree foo.bar true &&\n+\ttest_cmp_config true foo.bar\n+'\n+\n+test_expect_success 'add worktrees' '\n+\tgit worktree add wt1 &&\n+\tgit worktree add wt2\n+'\n+\n+test_expect_success 'config --worktree without extension' '\n+\ttest_must_fail git config --worktree foo.bar false\n+'\n+\n+test_expect_success 'enable worktreeConfig extension' '\n+\tgit config extensions.worktreeConfig true &&\n+\ttest_cmp_config true extensions.worktreeConfig\n+'\n+\n+test_expect_success 'config is shared as before' '\n+\tgit config this.is shared &&\n+\ttest_cmp_config shared this.is &&\n+\ttest_cmp_config -C wt1 shared this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'config is shared (set from another worktree)' '\n+\tgit -C wt1 config that.is also-shared &&\n+\ttest_cmp_config also-shared that.is &&\n+\ttest_cmp_config -C wt1 also-shared that.is &&\n+\ttest_cmp_config -C wt2 also-shared that.is\n+'\n+\n+test_expect_success 'config private to main worktree' '\n+\tgit config --worktree this.is for-main &&\n+\ttest_cmp_config for-main this.is &&\n+\ttest_cmp_config -C wt1 shared this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'config private to linked worktree' '\n+\tgit -C wt1 config --worktree this.is for-wt1 &&\n+\ttest_cmp_config for-main this.is &&\n+\ttest_cmp_config -C wt1 for-wt1 this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'core.bare no longer for main only' '\n+\ttest_config core.bare true &&\n+\ttest \"$(git rev-parse --is-bare-repository)\" = true &&\n+\ttest \"$(git -C wt1 rev-parse --is-bare-repository)\" = true &&\n+\ttest \"$(git -C wt2 rev-parse --is-bare-repository)\" = true\n+'\n+\n+test_expect_success 'per-worktree core.bare is picked up' '\n+\tgit -C wt1 config --worktree core.bare true &&\n+\ttest \"$(git rev-parse --is-bare-repository)\" = false &&\n+\ttest \"$(git -C wt1 rev-parse --is-bare-repository)\" = true &&\n+\ttest \"$(git -C wt2 rev-parse --is-bare-repository)\" = false\n+'\n+\n+test_expect_success 'config.worktree no longer read without extension' '\n+\tgit config --unset extensions.worktreeConfig &&\n+\ttest_cmp_config shared this.is &&\n+\ttest_cmp_config -C wt1 shared this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_done\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359324","messageId":"CAPig+cSTvewR51Vj-4MKGJKVzqFMhf4nz5yy_PdFP+EF6qMKrA@mail.gmail.com","threadId":"49407","inReplyTo":"20180929153005.10599-2-pclouds@gmail.com","subject":"Re: [PATCH v2 1/2] t1300: extract and use test_cmp_config()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-09-30T04:05:59Z","receivedAt":"2018-09-30T04:12:34Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Sep 29, 2018 at 11:30 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> In many config-related tests it's common to check if a config variable\n> has expected value and we want to print the differences when the test\n> fails. Doing it the normal way is three lines of shell code. Let's add\n> a function do to all this (and a little more).\n> [...]\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\n> @@ -747,6 +747,30 @@ test_cmp() {\n> +# similar to test_cmp but $2 is a config key instead of actual value\n> +# it can also accept -C to read from a different repo, e.g.\n\nMinor: maybe say that \"-C <dir>\" changes to <dir> for the git-config invocation.\n\n> +#     test_cmp_config -C xyz foo core.bar\n> +#\n> +# is sort of equivalent of\n> +#\n> +#     test \"foo\" = \"$(git -C xyz core.bar)\"\n\nShould be: $(git -C xyz config core.bar)\n\n> +test_cmp_config() {\n> +       if [ \"$1\" = \"-C\" ]\n> +       then\n\nStyle:\n\n    if test \"$1\" = \"-C\"\n    then\n        ...\n\n> +               shift &&\n> +               GD=\"-C $1\" &&\n> +               shift\n> +       else\n> +               GD=\n> +       fi &&\n> +       echo \"$1\" >expected &&\n\nIf $1 starts with a hyphen, 'echo' might try interpreting it as an\noption. Use printf instead:\n\n    printf \"%s\\n\" \"$1\" &&\n\n> +       shift &&\n> +       git $GD config \"$@\" >actual &&\n> +       test_cmp expected actual\n\nPlease choose names other than \"actual\" and \"expected\" since those are\nlikely to clobber files of the same name which the test has set up\nitself. (Or, at the very least, document that this function clobbers\nthose files -- but using better filenames is preferable.)\n\n> +}\n"},{"id":"359326","messageId":"CAPig+cSv4P99ZJ=CtCC7HctfSg5W6sadiknhk9yXSaBDZ81ctw@mail.gmail.com","threadId":"49407","inReplyTo":"20180929153005.10599-3-pclouds@gmail.com","subject":"Re: [PATCH v2 2/2] worktree: add per-worktree config files","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-09-30T04:32:55Z","receivedAt":"2018-09-30T04:33:09Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Sep 29, 2018 at 11:30 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> A new repo extension is added, worktreeConfig. When it is present:\n> [...]\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> @@ -2,8 +2,9 @@ CONFIGURATION FILE\n>  The Git configuration file contains a number of variables that affect\n> +the Git commands' behavior. The files `.git/config` and optionally\n> +`config.worktree` (see `extensions.worktreeConfig` below) in each\n> +repository is used to store the configuration for that repository, and\n\ns/is used/are/used/\n\n>  `$HOME/.gitconfig` is used to store a per-user configuration as\n>  fallback values for the `.git/config` file. The file `/etc/gitconfig`\n>  can be used to store a system-wide default configuration.\n> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\n> @@ -204,6 +204,36 @@ working trees, it can be used to identify worktrees. For example if\n> +CONFIGURATION FILE\n> +------------------\n> +In this mode, specific configuration stays in the path pointed by `git\n> +rev-parse --git-path config.worktree`. You can add or update\n> +configuration in this file with `git config --worktree`. Older Git\n> +versions may will refuse to access repositories with this extension.\n\ns/may will/will/\n\n> diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\n> @@ -275,6 +280,9 @@ worktrees/<id>/locked::\n> +worktrees/<id>/config.worktree::\n> +       Working directory specific configuration file.\n\nI wonder if this deserves a quick mention in\nDocumentation/git-worktree.txt since other worktree-related files,\nsuch as \"worktrees/<id>/locked\", are mentioned there.\n\n> diff --git a/builtin/config.c b/builtin/config.c\n> @@ -645,7 +649,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n> +       else if (use_worktree_config) {\n> +               struct worktree **worktrees = get_worktrees(0);\n> +               if (repository_format_worktree_config)\n> +                       given_config_source.file = git_pathdup(\"config.worktree\");\n> +               else if (worktrees[0] && worktrees[1])\n> +                       die(_(\"--worktree cannot be used with multiple \"\n> +                             \"working trees unless the config\\n\"\n> +                             \"extension worktreeConfig is enabled. \"\n> +                             \"Please read \\\"CONFIGURATION FILE\\\"\\n\"\n> +                             \"section in \\\"git help worktree\\\" for details\"));\n> +               else\n> +                       given_config_source.file = git_pathdup(\"config\");\n\nI'm not sure I understand the purpose of allowing --worktree when only\na single worktree is present and extensions.worktreeConfig is not set.\nCan you talk about it a bit more?\n"},{"id":"359336","messageId":"CACsJy8AVowvXDkOh_fjdRpNtcXWchxFULbE8m9ZPQN9BsJx5ag@mail.gmail.com","threadId":"49407","inReplyTo":"CAPig+cSv4P99ZJ=CtCC7HctfSg5W6sadiknhk9yXSaBDZ81ctw@mail.gmail.com","subject":"Re: [PATCH v2 2/2] worktree: add per-worktree config files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-30T07:15:29Z","receivedAt":"2018-09-30T07:15:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 30, 2018 at 6:33 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > diff --git a/builtin/config.c b/builtin/config.c\n> > @@ -645,7 +649,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n> > +       else if (use_worktree_config) {\n> > +               struct worktree **worktrees = get_worktrees(0);\n> > +               if (repository_format_worktree_config)\n> > +                       given_config_source.file = git_pathdup(\"config.worktree\");\n> > +               else if (worktrees[0] && worktrees[1])\n> > +                       die(_(\"--worktree cannot be used with multiple \"\n> > +                             \"working trees unless the config\\n\"\n> > +                             \"extension worktreeConfig is enabled. \"\n> > +                             \"Please read \\\"CONFIGURATION FILE\\\"\\n\"\n> > +                             \"section in \\\"git help worktree\\\" for details\"));\n> > +               else\n> > +                       given_config_source.file = git_pathdup(\"config\");\n>\n> I'm not sure I understand the purpose of allowing --worktree when only\n> a single worktree is present and extensions.worktreeConfig is not set.\n> Can you talk about it a bit more?\n\nUnified API. If I write a script to do stuff and want it to work in\nmultiple worktrees as well, I should not need to do \"if single\nworktree, use \"git config --local\", if multiple use \"git config\n--worktree\"\". By using \"git config --worktree\" I tell git \"this config\nis per-worktree\" and git should do the right thing, single worktree or\nnot.\n-- \nDuy\n"},{"id":"359337","messageId":"CAPig+cTJKuEkb5AVi=xyztfEo84NzaduNnX3mhec3xrYf4PGUg@mail.gmail.com","threadId":"49407","inReplyTo":"CACsJy8AVowvXDkOh_fjdRpNtcXWchxFULbE8m9ZPQN9BsJx5ag@mail.gmail.com","subject":"Re: [PATCH v2 2/2] worktree: add per-worktree config files","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-09-30T07:24:15Z","receivedAt":"2018-09-30T07:24:29Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Sep 30, 2018 at 3:16 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> On Sun, Sep 30, 2018 at 6:33 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > > diff --git a/builtin/config.c b/builtin/config.c\n> > > @@ -645,7 +649,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n> > > +       else if (use_worktree_config) {\n> > > +               struct worktree **worktrees = get_worktrees(0);\n> > > +               if (repository_format_worktree_config)\n> > > +                       given_config_source.file = git_pathdup(\"config.worktree\");\n> > > +               else if (worktrees[0] && worktrees[1])\n> > > +                       die(_(\"--worktree cannot be used with multiple \"\n> > > +                             \"working trees unless the config\\n\"\n> > > +                             \"extension worktreeConfig is enabled. \"\n> > > +                             \"Please read \\\"CONFIGURATION FILE\\\"\\n\"\n> > > +                             \"section in \\\"git help worktree\\\" for details\"));\n> > > +               else\n> > > +                       given_config_source.file = git_pathdup(\"config\");\n> >\n> > I'm not sure I understand the purpose of allowing --worktree when only\n> > a single worktree is present and extensions.worktreeConfig is not set.\n> > Can you talk about it a bit more?\n>\n> Unified API. If I write a script to do stuff and want it to work in\n> multiple worktrees as well, I should not need to do \"if single\n> worktree, use \"git config --local\", if multiple use \"git config\n> --worktree\"\". By using \"git config --worktree\" I tell git \"this config\n> is per-worktree\" and git should do the right thing, single worktree or\n> not.\n\nThat's what I thought, but I still don't understand how that unified\nAPI is going to help if the script writer happens to have multiple\nworktrees but doesn't have extensions.worktreeConfig set, in which\ncase the command will error out. I don't see how that simplifies\nthings, but perhaps I'm missing something obvious.\n"},{"id":"359338","messageId":"CACsJy8BrdXZx3+exwL-xDfdTxMa4Ez1y051CaKA3z2e7OC2FZQ@mail.gmail.com","threadId":"49407","inReplyTo":"CAPig+cTJKuEkb5AVi=xyztfEo84NzaduNnX3mhec3xrYf4PGUg@mail.gmail.com","subject":"Re: [PATCH v2 2/2] worktree: add per-worktree config files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-30T07:36:22Z","receivedAt":"2018-09-30T07:36:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 30, 2018 at 9:24 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Sun, Sep 30, 2018 at 3:16 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> > On Sun, Sep 30, 2018 at 6:33 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > > > diff --git a/builtin/config.c b/builtin/config.c\n> > > > @@ -645,7 +649,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n> > > > +       else if (use_worktree_config) {\n> > > > +               struct worktree **worktrees = get_worktrees(0);\n> > > > +               if (repository_format_worktree_config)\n> > > > +                       given_config_source.file = git_pathdup(\"config.worktree\");\n> > > > +               else if (worktrees[0] && worktrees[1])\n> > > > +                       die(_(\"--worktree cannot be used with multiple \"\n> > > > +                             \"working trees unless the config\\n\"\n> > > > +                             \"extension worktreeConfig is enabled. \"\n> > > > +                             \"Please read \\\"CONFIGURATION FILE\\\"\\n\"\n> > > > +                             \"section in \\\"git help worktree\\\" for details\"));\n> > > > +               else\n> > > > +                       given_config_source.file = git_pathdup(\"config\");\n> > >\n> > > I'm not sure I understand the purpose of allowing --worktree when only\n> > > a single worktree is present and extensions.worktreeConfig is not set.\n> > > Can you talk about it a bit more?\n> >\n> > Unified API. If I write a script to do stuff and want it to work in\n> > multiple worktrees as well, I should not need to do \"if single\n> > worktree, use \"git config --local\", if multiple use \"git config\n> > --worktree\"\". By using \"git config --worktree\" I tell git \"this config\n> > is per-worktree\" and git should do the right thing, single worktree or\n> > not.\n>\n> That's what I thought, but I still don't understand how that unified\n> API is going to help if the script writer happens to have multiple\n> worktrees but doesn't have extensions.worktreeConfig set, in which\n> case the command will error out. I don't see how that simplifies\n> things, but perhaps I'm missing something obvious.\n\nNo it does not simplify that step. The user has to enable it manually\n(at least for now). v1 tried to enable it automatically, but I think\nit's safer to go one step at a time.\n-- \nDuy\n"},{"id":"359339","messageId":"20180930123116.GI23446@localhost","threadId":"49407","inReplyTo":"20180929153005.10599-2-pclouds@gmail.com","subject":"Re: [PATCH v2 1/2] t1300: extract and use test_cmp_config()","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-09-30T12:31:16Z","receivedAt":"2018-09-30T12:31:23Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Sat, Sep 29, 2018 at 05:30:04PM +0200, Nguyễn Thái Ngọc Duy wrote:\n> In many config-related tests it's common to check if a config variable\n> has expected value and we want to print the differences when the test\n> fails. Doing it the normal way is three lines of shell code. Let's add\n> a function do to all this (and a little more).\n> \n> This function has uses outside t1300 as well but I'm not going to\n> convert them all. And it will be used in the next commit where\n> per-worktree config feature is introduced.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  t/t1300-config.sh       | 79 ++++++++++-------------------------------\n>  t/test-lib-functions.sh | 24 +++++++++++++\n>  2 files changed, 43 insertions(+), 60 deletions(-)\n> \n> diff --git a/t/t1300-config.sh b/t/t1300-config.sh\n> index cdf1fed5d1..00c2b0f0eb 100755\n> --- a/t/t1300-config.sh\n> +++ b/t/t1300-config.sh\n> @@ -76,15 +76,11 @@ EOF\n\n>  test_expect_success 'unset type specifiers may be reset to conflicting ones' '\n> -\techo 1048576 >expect &&\n> -\tgit config --type=bool --no-type --type=int core.big >actual &&\n> -\ttest_cmp expect actual\n> +\ttest_cmp_config 1048576 --type=bool --no-type --type=int core.big\n>  '\n>  \n>  test_expect_success '--type rejects unknown specifiers' '\n> diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\n> index d82fac9d79..4cd7fb8fdf 100644\n> --- a/t/test-lib-functions.sh\n> +++ b/t/test-lib-functions.sh\n> @@ -747,6 +747,30 @@ test_cmp() {\n>  \t$GIT_TEST_CMP \"$@\"\n>  }\n>  \n> +# similar to test_cmp but $2 is a config key instead of actual value\n\nThis doesn't describe very well the function's parameters: $1\nspecifies the expected value (as opposed to the file containing the\nexpected value), and the rest can be all kinds of 'git config'\noptions, not just a second argument with the config key; e.g. see call\nin the above hunk.\n\nPerheps only a brief description and a usage string like this would\nsufficiently cover everything?\n\n  # Check that the given config key has the expected value.\n  #\n  #   test_cmp_config [-C <dir>] <expected-value>\n  #                   [<git-config-options>...] <config-key>\n\n> +# it can also accept -C to read from a different repo, e.g.\n> +#\n> +#     test_cmp_config -C xyz foo core.bar\n> +#\n> +# is sort of equivalent of\n\nI think this comment should outright say that \"is a better alternative\nto\", because it provides useful output on failure, and because it\ndoesn't hide 'git config's exit code.\n\nNote that this second point does make a bit of a difference:\n\n  test_cmp_config \"\" nonexisting.variable\n\nwould fail, because\n\n  $ git config nonexisting.variable ; echo $?\n  1\n\nand the &&-chain.  However,\n\n  test \"\" = \"$(git config nonexisting.variable)\"\n\nwould still succeed, because the non-zero exit code is ignored.\n\nI consider this a benefit, as it will protect us from a typo in the\nname of a set but empty variable, even though we won't get any error\nmessage.\n\n> +#\n> +#     test \"foo\" = \"$(git -C xyz core.bar)\"\n> +\n\nNit: unnecessary empty line.\n\n> +test_cmp_config() {\n> +\tif [ \"$1\" = \"-C\" ]\n> +\tthen\n> +\t\tshift &&\n> +\t\tGD=\"-C $1\" &&\n> +\t\tshift\n> +\telse\n> +\t\tGD=\n> +\tfi &&\n\nI think you could now safely declare GD as local.  The test balloon\n01d3a526ad (t0000: check whether the shell supports the \"local\"\nkeyword, 2017-10-26) has been out there for 4 releases / almost a\nyear, and we haven't heard about any issues, and the upcoming hash\ntranslation test helpers use 'local' as well.\n\n> +\techo \"$1\" >expected &&\n> +\tshift &&\n> +\tgit $GD config \"$@\" >actual &&\n> +\ttest_cmp expected actual\n> +}\n> +\n>  # test_cmp_bin - helper to compare binary files\n>  \n>  test_cmp_bin() {\n> -- \n> 2.19.0.341.g3acb95d729\n>\n"},{"id":"359430","messageId":"20181002160658.15891-1-pclouds@gmail.com","threadId":"49407","inReplyTo":"20180929153005.10599-1-pclouds@gmail.com","subject":"[PATCH v3 0/2] Per-worktree config files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-02T16:06:56Z","receivedAt":"2018-10-02T16:07:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v3 changes are minor (besides test_cmp_config), mostly document\ncleanup. Diff\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 44407e69db..e036ff7b86 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -4,7 +4,7 @@ CONFIGURATION FILE\n The Git configuration file contains a number of variables that affect\n the Git commands' behavior. The files `.git/config` and optionally\n `config.worktree` (see `extensions.worktreeConfig` below) in each\n-repository is used to store the configuration for that repository, and\n+repository are used to store the configuration for that repository, and\n `$HOME/.gitconfig` is used to store a per-user configuration as\n fallback values for the `.git/config` file. The file `/etc/gitconfig`\n can be used to store a system-wide default configuration.\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex aa88278dde..408c87c9ef 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -221,7 +221,7 @@ $ git config extensions.worktreeConfig true\n In this mode, specific configuration stays in the path pointed by `git\n rev-parse --git-path config.worktree`. You can add or update\n configuration in this file with `git config --worktree`. Older Git\n-versions may will refuse to access repositories with this extension.\n+versions will refuse to access repositories with this extension.\n \n Note that in this file, the exception for `core.bare` and `core.worktree`\n is gone. If you have them in $GIT_DIR/config before, you must move\n@@ -283,6 +283,9 @@ to `/path/main/.git/worktrees/test-next` then a file named\n `test-next` entry from being pruned.  See\n linkgit:gitrepository-layout[5] for details.\n \n+When extensions.worktreeConfig is enabled, the config file\n+`.git/worktrees/<id>/config.worktree` is read after `.git/config` is.\n+\n LIST OUTPUT FORMAT\n ------------------\n The worktree list command has two output formats.  The default format shows the\ndiff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\nindex 4cd7fb8fdf..2149b88392 100644\n--- a/t/test-lib-functions.sh\n+++ b/t/test-lib-functions.sh\n@@ -747,28 +747,27 @@ test_cmp() {\n \t$GIT_TEST_CMP \"$@\"\n }\n \n-# similar to test_cmp but $2 is a config key instead of actual value\n-# it can also accept -C to read from a different repo, e.g.\n+# Check that the given config key has the expected value.\n #\n-#     test_cmp_config -C xyz foo core.bar\n+#    test_cmp_config [-C <dir>] <expected-value>\n+#                    [<git-config-options>...] <config-key>\n #\n-# is sort of equivalent of\n+# for example to check that the value of core.bar is foo\n+#\n+#    test_cmp_config foo core.bar\n #\n-#     test \"foo\" = \"$(git -C xyz core.bar)\"\n-\n test_cmp_config() {\n-\tif [ \"$1\" = \"-C\" ]\n+\tlocal GD\n+\tif test \"$1\" = \"-C\"\n \tthen\n \t\tshift &&\n \t\tGD=\"-C $1\" &&\n \t\tshift\n-\telse\n-\t\tGD=\n \tfi &&\n-\techo \"$1\" >expected &&\n+\tprintf \"%s\\n\" \"$1\" >expect.config &&\n \tshift &&\n-\tgit $GD config \"$@\" >actual &&\n-\ttest_cmp expected actual\n+\tgit $GD config \"$@\" >actual.config &&\n+\ttest_cmp expect.config actual.config\n }\n \n # test_cmp_bin - helper to compare binary files\n\nNguyễn Thái Ngọc Duy (2):\n  t1300: extract and use test_cmp_config()\n  worktree: add per-worktree config files\n\n Documentation/config.txt               | 12 +++-\n Documentation/git-config.txt           | 26 ++++++---\n Documentation/git-worktree.txt         | 33 +++++++++++\n Documentation/gitrepository-layout.txt |  8 +++\n builtin/config.c                       | 19 ++++++-\n cache.h                                |  2 +\n config.c                               | 11 ++++\n environment.c                          |  1 +\n setup.c                                | 40 ++++++++++---\n t/t1300-config.sh                      | 79 +++++++-------------------\n t/t2029-worktree-config.sh             | 79 ++++++++++++++++++++++++++\n t/test-lib-functions.sh                | 23 ++++++++\n 12 files changed, 255 insertions(+), 78 deletions(-)\n create mode 100755 t/t2029-worktree-config.sh\n\n-- \n2.19.0.342.gc057aaf40a.dirty\n\n"},{"id":"359431","messageId":"20181002160658.15891-2-pclouds@gmail.com","threadId":"49407","inReplyTo":"20181002160658.15891-1-pclouds@gmail.com","subject":"[PATCH v3 1/2] t1300: extract and use test_cmp_config()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-02T16:06:57Z","receivedAt":"2018-10-02T16:07:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In many config-related tests it's common to check if a config variable\nhas expected value and we want to print the differences when the test\nfails. Doing it the normal way is three lines of shell code. Let's add\na function do to all this (and a little more).\n\nThis function has uses outside t1300 as well but I'm not going to\nconvert them all. And it will be used in the next commit where\nper-worktree config feature is introduced.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t1300-config.sh       | 79 ++++++++++-------------------------------\n t/test-lib-functions.sh | 23 ++++++++++++\n 2 files changed, 42 insertions(+), 60 deletions(-)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex cdf1fed5d1..00c2b0f0eb 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -76,15 +76,11 @@ EOF\n test_expect_success 'non-match result' 'test_cmp expect .git/config'\n \n test_expect_success 'find mixed-case key by canonical name' '\n-\techo Second >expect &&\n-\tgit config cores.whatever >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config Second cores.whatever\n '\n \n test_expect_success 'find mixed-case key by non-canonical name' '\n-\techo Second >expect &&\n-\tgit config CoReS.WhAtEvEr >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config Second CoReS.WhAtEvEr\n '\n \n test_expect_success 'subsections are not canonicalized by git-config' '\n@@ -94,12 +90,8 @@ test_expect_success 'subsections are not canonicalized by git-config' '\n \t[section \"SubSection\"]\n \tkey = two\n \tEOF\n-\techo one >expect &&\n-\tgit config section.subsection.key >actual &&\n-\ttest_cmp expect actual &&\n-\techo two >expect &&\n-\tgit config section.SubSection.key >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config one section.subsection.key &&\n+\ttest_cmp_config two section.SubSection.key\n '\n \n cat > .git/config <<\\EOF\n@@ -212,9 +204,7 @@ test_expect_success 'really really mean test' '\n '\n \n test_expect_success 'get value' '\n-\techo alpha >expect &&\n-\tgit config beta.haha >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config alpha beta.haha\n '\n \n cat > expect << EOF\n@@ -251,15 +241,11 @@ test_expect_success 'non-match' '\n '\n \n test_expect_success 'non-match value' '\n-\techo wow >expect &&\n-\tgit config --get nextsection.nonewline !for >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config wow --get nextsection.nonewline !for\n '\n \n test_expect_success 'multi-valued get returns final one' '\n-\techo \"wow2 for me\" >expect &&\n-\tgit config --get nextsection.nonewline >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config \"wow2 for me\" --get nextsection.nonewline\n '\n \n test_expect_success 'multi-valued get-all returns all' '\n@@ -520,21 +506,11 @@ test_expect_success 'editing stdin is an error' '\n \n test_expect_success 'refer config from subdirectory' '\n \tmkdir x &&\n-\t(\n-\t\tcd x &&\n-\t\techo strasse >expect &&\n-\t\tgit config --get --file ../other-config ein.bahn >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-\n+\ttest_cmp_config -C x strasse --get --file ../other-config ein.bahn\n '\n \n test_expect_success 'refer config from subdirectory via --file' '\n-\t(\n-\t\tcd x &&\n-\t\tgit config --file=../other-config --get ein.bahn >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n+\ttest_cmp_config -C x strasse --file=../other-config --get ein.bahn\n '\n \n cat > expect << EOF\n@@ -688,16 +664,13 @@ test_expect_success numbers '\n \n test_expect_success '--int is at least 64 bits' '\n \tgit config giga.watts 121g &&\n-\techo 129922760704 >expect &&\n-\tgit config --int --get giga.watts >actual &&\n-\ttest_cmp expect actual\n+\techo  >expect &&\n+\ttest_cmp_config 129922760704 --int --get giga.watts\n '\n \n test_expect_success 'invalid unit' '\n \tgit config aninvalid.unit \"1auto\" &&\n-\techo 1auto >expect &&\n-\tgit config aninvalid.unit >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_cmp_config 1auto aninvalid.unit &&\n \ttest_must_fail git config --int --get aninvalid.unit 2>actual &&\n \ttest_i18ngrep \"bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit\" actual\n '\n@@ -1039,9 +1012,7 @@ test_expect_success '--null --get-regexp' '\n \n test_expect_success 'inner whitespace kept verbatim' '\n \tgit config section.val \"foo \t  bar\" &&\n-\techo \"foo \t  bar\" >expect &&\n-\tgit config section.val >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config \"foo \t  bar\" section.val\n '\n \n test_expect_success SYMLINKS 'symlinked configuration' '\n@@ -1808,21 +1779,15 @@ big = 1M\n EOF\n \n test_expect_success 'identical modern --type specifiers are allowed' '\n-\tgit config --type=int --type=int core.big >actual &&\n-\techo 1048576 >expect &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --type=int --type=int core.big\n '\n \n test_expect_success 'identical legacy --type specifiers are allowed' '\n-\tgit config --int --int core.big >actual &&\n-\techo 1048576 >expect &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --int --int core.big\n '\n \n test_expect_success 'identical mixed --type specifiers are allowed' '\n-\tgit config --int --type=int core.big >actual &&\n-\techo 1048576 >expect &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --int --type=int core.big\n '\n \n test_expect_success 'non-identical modern --type specifiers are not allowed' '\n@@ -1841,21 +1806,15 @@ test_expect_success 'non-identical mixed --type specifiers are not allowed' '\n '\n \n test_expect_success '--type allows valid type specifiers' '\n-\techo \"true\" >expect &&\n-\tgit config --type=bool core.foo >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config true  --type=bool core.foo\n '\n \n test_expect_success '--no-type unsets type specifiers' '\n-\techo \"10\" >expect &&\n-\tgit config --type=bool --no-type core.number >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 10 --type=bool --no-type core.number\n '\n \n test_expect_success 'unset type specifiers may be reset to conflicting ones' '\n-\techo 1048576 >expect &&\n-\tgit config --type=bool --no-type --type=int core.big >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --type=bool --no-type --type=int core.big\n '\n \n test_expect_success '--type rejects unknown specifiers' '\ndiff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\nindex d82fac9d79..2149b88392 100644\n--- a/t/test-lib-functions.sh\n+++ b/t/test-lib-functions.sh\n@@ -747,6 +747,29 @@ test_cmp() {\n \t$GIT_TEST_CMP \"$@\"\n }\n \n+# Check that the given config key has the expected value.\n+#\n+#    test_cmp_config [-C <dir>] <expected-value>\n+#                    [<git-config-options>...] <config-key>\n+#\n+# for example to check that the value of core.bar is foo\n+#\n+#    test_cmp_config foo core.bar\n+#\n+test_cmp_config() {\n+\tlocal GD\n+\tif test \"$1\" = \"-C\"\n+\tthen\n+\t\tshift &&\n+\t\tGD=\"-C $1\" &&\n+\t\tshift\n+\tfi &&\n+\tprintf \"%s\\n\" \"$1\" >expect.config &&\n+\tshift &&\n+\tgit $GD config \"$@\" >actual.config &&\n+\ttest_cmp expect.config actual.config\n+}\n+\n # test_cmp_bin - helper to compare binary files\n \n test_cmp_bin() {\n-- \n2.19.0.342.gc057aaf40a.dirty\n\n"},{"id":"359432","messageId":"20181002160658.15891-3-pclouds@gmail.com","threadId":"49407","inReplyTo":"20181002160658.15891-1-pclouds@gmail.com","subject":"[PATCH v3 2/2] worktree: add per-worktree config files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-02T16:06:58Z","receivedAt":"2018-10-02T16:07:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A new repo extension is added, worktreeConfig. When it is present:\n\n - Repository config reading by default includes $GIT_DIR/config _and_\n   $GIT_DIR/config.worktree. \"config\" file remains shared in multiple\n   worktree setup.\n\n - The special treatment for core.bare and core.worktree, to stay\n   effective only in main worktree, is gone. These config settings are\n   supposed to be in config.worktree.\n\nThis extension is most useful in multiple worktree setup because you\nnow have an option to store per-worktree config (which is either\n.git/config.worktree for main worktree, or\n.git/worktrees/xx/config.worktree for linked ones).\n\nThis extension can be used in single worktree mode, even though it's\npretty much useless (but this can happen after you remove all linked\nworktrees and move back to single worktree).\n\n\"git config\" reads from both \"config\" and \"config.worktree\" by default\n(i.e. without either --user, --file...) when this extension is\npresent. Default writes still go to \"config\", not \"config.worktree\". A\nnew option --worktree is added for that (*).\n\nSince a new repo extension is introduced, existing git binaries should\nrefuse to access to the repo (both from main and linked worktrees). So\nthey will not misread the config file (i.e. skip the config.worktree\npart). They may still accidentally write to the config file anyway if\nthey use with \"git config --file <path>\".\n\nThis design places a bet on the assumption that the majority of config\nvariables are shared so it is the default mode. A safer move would be\ndefault writes go to per-worktree file, so that accidental changes are\nisolated.\n\n(*) \"git config --worktree\" points back to \"config\" file when this\n    extension is not present and there is only one worktree so that it\n    works in any both single and multiple worktree setups.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt               | 12 +++-\n Documentation/git-config.txt           | 26 ++++++---\n Documentation/git-worktree.txt         | 33 +++++++++++\n Documentation/gitrepository-layout.txt |  8 +++\n builtin/config.c                       | 19 ++++++-\n cache.h                                |  2 +\n config.c                               | 11 ++++\n environment.c                          |  1 +\n setup.c                                | 40 ++++++++++---\n t/t2029-worktree-config.sh             | 79 ++++++++++++++++++++++++++\n 10 files changed, 213 insertions(+), 18 deletions(-)\n create mode 100755 t/t2029-worktree-config.sh\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 8d85d1a324..e036ff7b86 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2,8 +2,9 @@ CONFIGURATION FILE\n ------------------\n \n The Git configuration file contains a number of variables that affect\n-the Git commands' behavior. The `.git/config` file in each repository\n-is used to store the configuration for that repository, and\n+the Git commands' behavior. The files `.git/config` and optionally\n+`config.worktree` (see `extensions.worktreeConfig` below) in each\n+repository are used to store the configuration for that repository, and\n `$HOME/.gitconfig` is used to store a per-user configuration as\n fallback values for the `.git/config` file. The file `/etc/gitconfig`\n can be used to store a system-wide default configuration.\n@@ -371,6 +372,13 @@ advice.*::\n \t\teditor input from the user.\n --\n \n+extensions.worktreeConfig::\n+\tIf set, by default \"git config\" reads from both \"config\" and\n+\t\"config.worktree\" file from GIT_DIR in that order. In\n+\tmultiple working directory mode, \"config\" file is shared while\n+\t\"config.worktree\" is per-working directory (i.e., it's in\n+\tGIT_COMMON_DIR/worktrees/<id>/config.worktree)\n+\n core.fileMode::\n \tTells Git if the executable bit of files in the working tree\n \tis to be honored.\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 8e240435be..4870e00b89 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -45,13 +45,15 @@ unset an existing `--type` specifier with `--no-type`.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n-`--system`, `--global`, `--local` and `--file <filename>` can be\n-used to tell the command to read from only that location (see <<FILES>>).\n+`--system`, `--global`, `--local`, `--worktree` and\n+`--file <filename>` can be used to tell the command to read from only\n+that location (see <<FILES>>).\n \n When writing, the new value is written to the repository local\n configuration file by default, and options `--system`, `--global`,\n-`--file <filename>` can be used to tell the command to write to\n-that location (you can say `--local` but that is the default).\n+`--worktree`, `--file <filename>` can be used to tell the command to\n+write to that location (you can say `--local` but that is the\n+default).\n \n This command will fail with non-zero status upon error.  Some exit\n codes are:\n@@ -131,6 +133,11 @@ from all available files.\n +\n See also <<FILES>>.\n \n+--worktree::\n+\tSimilar to `--local` except that `.git/config.worktree` is\n+\tread from or written to if `extensions.worktreeConfig` is\n+\tpresent. If not it's the same as `--local`.\n+\n -f config-file::\n --file config-file::\n \tUse the given config file instead of the one specified by GIT_CONFIG.\n@@ -281,6 +288,10 @@ $XDG_CONFIG_HOME/git/config::\n $GIT_DIR/config::\n \tRepository specific configuration file.\n \n+$GIT_DIR/config.worktree::\n+\tThis is optional and is only searched when\n+\t`extensions.worktreeConfig` is present in $GIT_DIR/config.\n+\n If no further options are given, all reading options will read all of these\n files that are available. If the global or the system-wide configuration\n file are not available they will be ignored. If the repository configuration\n@@ -299,9 +310,10 @@ configuration file. Note that this also affects options like `--replace-all`\n and `--unset`. *'git config' will only ever change one file at a time*.\n \n You can override these rules either by command-line options or by environment\n-variables. The `--global` and the `--system` options will limit the file used\n-to the global or system-wide file respectively. The `GIT_CONFIG` environment\n-variable has a similar effect, but you can specify any filename you want.\n+variables. The `--global`, `--system` and `--worktree` options will limit\n+the file used to the global, system-wide or per-worktree file respectively.\n+The `GIT_CONFIG` environment variable has a similar effect, but you\n+can specify any filename you want.\n \n \n ENVIRONMENT\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex e2ee9fc21b..408c87c9ef 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -204,6 +204,36 @@ working trees, it can be used to identify worktrees. For example if\n you only have two working trees, at \"/abc/def/ghi\" and \"/abc/def/ggg\",\n then \"ghi\" or \"def/ghi\" is enough to point to the former working tree.\n \n+CONFIGURATION FILE\n+------------------\n+By default, the repository \"config\" file is shared across all working\n+trees. If the config variables `core.bare` or `core.worktree` are\n+already present in the config file, they will be applied to the main\n+working trees only.\n+\n+In order to have configuration specific to working trees, you can turn\n+on \"worktreeConfig\" extension, e.g.:\n+\n+------------\n+$ git config extensions.worktreeConfig true\n+------------\n+\n+In this mode, specific configuration stays in the path pointed by `git\n+rev-parse --git-path config.worktree`. You can add or update\n+configuration in this file with `git config --worktree`. Older Git\n+versions will refuse to access repositories with this extension.\n+\n+Note that in this file, the exception for `core.bare` and `core.worktree`\n+is gone. If you have them in $GIT_DIR/config before, you must move\n+them to the `config.worktree` of the main working tree. You may also\n+take this opportunity to review and move other configuration that you\n+do not want to share to all working trees:\n+\n+ - `core.worktree` and `core.bare` should never be shared\n+\n+ - `core.sparseCheckout` is recommended per working tree, unless you\n+   are sure you always use sparse checkout for all working trees.\n+\n DETAILS\n -------\n Each linked working tree has a private sub-directory in the repository's\n@@ -253,6 +283,9 @@ to `/path/main/.git/worktrees/test-next` then a file named\n `test-next` entry from being pruned.  See\n linkgit:gitrepository-layout[5] for details.\n \n+When extensions.worktreeConfig is enabled, the config file\n+`.git/worktrees/<id>/config.worktree` is read after `.git/config` is.\n+\n LIST OUTPUT FORMAT\n ------------------\n The worktree list command has two output formats.  The default format shows the\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex e85148f05e..36fcca8087 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -143,6 +143,11 @@ config::\n \tif $GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/config\" will be\n \tused instead.\n \n+config.worktree::\n+\tWorking directory specific configuration file for the main\n+\tworking directory in multiple working directory setup (see\n+\tlinkgit:git-worktree[1]).\n+\n branches::\n \tA slightly deprecated way to store shorthands to be used\n \tto specify a URL to 'git fetch', 'git pull' and 'git push'.\n@@ -275,6 +280,9 @@ worktrees/<id>/locked::\n \tor manually by `git worktree prune`. The file may contain a string\n \texplaining why the repository is locked.\n \n+worktrees/<id>/config.worktree::\n+\tWorking directory specific configuration file.\n+\n SEE ALSO\n --------\n linkgit:git-init[1],\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 97b58c4aea..84385ef165 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -5,6 +5,7 @@\n #include \"parse-options.h\"\n #include \"urlmatch.h\"\n #include \"quote.h\"\n+#include \"worktree.h\"\n \n static const char *const builtin_config_usage[] = {\n \tN_(\"git config [<options>]\"),\n@@ -24,6 +25,7 @@ static char key_delim = ' ';\n static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n+static int use_worktree_config;\n static struct git_config_source given_config_source;\n static int actions, type;\n static char *default_value;\n@@ -123,6 +125,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n \tOPT_BOOL(0, \"system\", &use_system_config, N_(\"use system config file\")),\n \tOPT_BOOL(0, \"local\", &use_local_config, N_(\"use repository config file\")),\n+\tOPT_BOOL(0, \"worktree\", &use_worktree_config, N_(\"use per-worktree config file\")),\n \tOPT_STRING('f', \"file\", &given_config_source.file, N_(\"file\"), N_(\"use given config file\")),\n \tOPT_STRING(0, \"blob\", &given_config_source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\")),\n \tOPT_GROUP(N_(\"Action\")),\n@@ -602,6 +605,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n \tif (use_global_config + use_system_config + use_local_config +\n+\t    use_worktree_config +\n \t    !!given_config_source.file + !!given_config_source.blob > 1) {\n \t\terror(_(\"only one config file at a time\"));\n \t\tusage_builtin_config();\n@@ -645,7 +649,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tgiven_config_source.file = git_etc_gitconfig();\n \telse if (use_local_config)\n \t\tgiven_config_source.file = git_pathdup(\"config\");\n-\telse if (given_config_source.file) {\n+\telse if (use_worktree_config) {\n+\t\tstruct worktree **worktrees = get_worktrees(0);\n+\t\tif (repository_format_worktree_config)\n+\t\t\tgiven_config_source.file = git_pathdup(\"config.worktree\");\n+\t\telse if (worktrees[0] && worktrees[1])\n+\t\t\tdie(_(\"--worktree cannot be used with multiple \"\n+\t\t\t      \"working trees unless the config\\n\"\n+\t\t\t      \"extension worktreeConfig is enabled. \"\n+\t\t\t      \"Please read \\\"CONFIGURATION FILE\\\"\\n\"\n+\t\t\t      \"section in \\\"git help worktree\\\" for details\"));\n+\t\telse\n+\t\t\tgiven_config_source.file = git_pathdup(\"config\");\n+\t\tfree_worktrees(worktrees);\n+\t} else if (given_config_source.file) {\n \t\tif (!is_absolute_path(given_config_source.file) && prefix)\n \t\t\tgiven_config_source.file =\n \t\t\t\tprefix_filename(prefix, given_config_source.file);\ndiff --git a/cache.h b/cache.h\nindex d508f3d4f8..9e9b917e99 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -957,11 +957,13 @@ extern int grafts_replace_parents;\n extern int repository_format_precious_objects;\n extern char *repository_format_partial_clone;\n extern const char *core_partial_clone_filter_default;\n+extern int repository_format_worktree_config;\n \n struct repository_format {\n \tint version;\n \tint precious_objects;\n \tchar *partial_clone; /* value of extensions.partialclone */\n+\tint worktree_config;\n \tint is_bare;\n \tint hash_algo;\n \tchar *work_tree;\ndiff --git a/config.c b/config.c\nindex 3461993f0a..b3025164d2 100644\n--- a/config.c\n+++ b/config.c\n@@ -1695,6 +1695,17 @@ static int do_git_config_sequence(const struct config_options *opts,\n \tif (repo_config && !access_or_die(repo_config, R_OK, 0))\n \t\tret += git_config_from_file(fn, repo_config, data);\n \n+\t/*\n+\t * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE.\n+\t * But let's not complicate things before it's actually needed.\n+\t */\n+\tif (repository_format_worktree_config) {\n+\t\tchar *path = git_pathdup(\"config.worktree\");\n+\t\tif (!access_or_die(path, R_OK, 0))\n+\t\t\tret += git_config_from_file(fn, path, data);\n+\t\tfree(path);\n+\t}\n+\n \tcurrent_parsing_scope = CONFIG_SCOPE_CMDLINE;\n \tif (git_config_from_parameters(fn, data) < 0)\n \t\tdie(_(\"unable to parse command-line config\"));\ndiff --git a/environment.c b/environment.c\nindex 3f3c8746c2..268310b3dc 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -33,6 +33,7 @@ int ref_paranoia = -1;\n int repository_format_precious_objects;\n char *repository_format_partial_clone;\n const char *core_partial_clone_filter_default;\n+int repository_format_worktree_config;\n const char *git_commit_encoding;\n const char *git_log_output_encoding;\n const char *apply_default_whitespace;\ndiff --git a/setup.c b/setup.c\nindex b24c811c1c..1be5037f12 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -402,6 +402,20 @@ void setup_work_tree(void)\n \tinitialized = 1;\n }\n \n+static int read_worktree_config(const char *var, const char *value, void *vdata)\n+{\n+\tstruct repository_format *data = vdata;\n+\n+\tif (strcmp(var, \"core.bare\") == 0) {\n+\t\tdata->is_bare = git_config_bool(var, value);\n+\t} else if (strcmp(var, \"core.worktree\") == 0) {\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\tdata->work_tree = xstrdup(value);\n+\t}\n+\treturn 0;\n+}\n+\n static int check_repo_format(const char *var, const char *value, void *vdata)\n {\n \tstruct repository_format *data = vdata;\n@@ -423,16 +437,13 @@ static int check_repo_format(const char *var, const char *value, void *vdata)\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(var);\n \t\t\tdata->partial_clone = xstrdup(value);\n-\t\t} else\n+\t\t} else if (!strcmp(ext, \"worktreeconfig\"))\n+\t\t\tdata->worktree_config = git_config_bool(var, value);\n+\t\telse\n \t\t\tstring_list_append(&data->unknown_extensions, ext);\n-\t} else if (strcmp(var, \"core.bare\") == 0) {\n-\t\tdata->is_bare = git_config_bool(var, value);\n-\t} else if (strcmp(var, \"core.worktree\") == 0) {\n-\t\tif (!value)\n-\t\t\treturn config_error_nonbool(var);\n-\t\tdata->work_tree = xstrdup(value);\n \t}\n-\treturn 0;\n+\n+\treturn read_worktree_config(var, value, vdata);\n }\n \n static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)\n@@ -466,7 +477,20 @@ static int check_repository_format_gently(const char *gitdir, struct repository_\n \n \trepository_format_precious_objects = candidate->precious_objects;\n \trepository_format_partial_clone = candidate->partial_clone;\n+\trepository_format_worktree_config = candidate->worktree_config;\n \tstring_list_clear(&candidate->unknown_extensions, 0);\n+\n+\tif (repository_format_worktree_config) {\n+\t\t/*\n+\t\t * pick up core.bare and core.worktree from per-worktree\n+\t\t * config if present\n+\t\t */\n+\t\tstrbuf_addf(&sb, \"%s/config.worktree\", gitdir);\n+\t\tgit_config_from_file(read_worktree_config, sb.buf, candidate);\n+\t\tstrbuf_release(&sb);\n+\t\thas_common = 0;\n+\t}\n+\n \tif (!has_common) {\n \t\tif (candidate->is_bare != -1) {\n \t\t\tis_bare_repository_cfg = candidate->is_bare;\ndiff --git a/t/t2029-worktree-config.sh b/t/t2029-worktree-config.sh\nnew file mode 100755\nindex 0000000000..286121d8de\n--- /dev/null\n+++ b/t/t2029-worktree-config.sh\n@@ -0,0 +1,79 @@\n+#!/bin/sh\n+\n+test_description=\"config file in multi worktree\"\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit start\n+'\n+\n+test_expect_success 'config --worktree in single worktree' '\n+\tgit config --worktree foo.bar true &&\n+\ttest_cmp_config true foo.bar\n+'\n+\n+test_expect_success 'add worktrees' '\n+\tgit worktree add wt1 &&\n+\tgit worktree add wt2\n+'\n+\n+test_expect_success 'config --worktree without extension' '\n+\ttest_must_fail git config --worktree foo.bar false\n+'\n+\n+test_expect_success 'enable worktreeConfig extension' '\n+\tgit config extensions.worktreeConfig true &&\n+\ttest_cmp_config true extensions.worktreeConfig\n+'\n+\n+test_expect_success 'config is shared as before' '\n+\tgit config this.is shared &&\n+\ttest_cmp_config shared this.is &&\n+\ttest_cmp_config -C wt1 shared this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'config is shared (set from another worktree)' '\n+\tgit -C wt1 config that.is also-shared &&\n+\ttest_cmp_config also-shared that.is &&\n+\ttest_cmp_config -C wt1 also-shared that.is &&\n+\ttest_cmp_config -C wt2 also-shared that.is\n+'\n+\n+test_expect_success 'config private to main worktree' '\n+\tgit config --worktree this.is for-main &&\n+\ttest_cmp_config for-main this.is &&\n+\ttest_cmp_config -C wt1 shared this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'config private to linked worktree' '\n+\tgit -C wt1 config --worktree this.is for-wt1 &&\n+\ttest_cmp_config for-main this.is &&\n+\ttest_cmp_config -C wt1 for-wt1 this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'core.bare no longer for main only' '\n+\ttest_config core.bare true &&\n+\ttest \"$(git rev-parse --is-bare-repository)\" = true &&\n+\ttest \"$(git -C wt1 rev-parse --is-bare-repository)\" = true &&\n+\ttest \"$(git -C wt2 rev-parse --is-bare-repository)\" = true\n+'\n+\n+test_expect_success 'per-worktree core.bare is picked up' '\n+\tgit -C wt1 config --worktree core.bare true &&\n+\ttest \"$(git rev-parse --is-bare-repository)\" = false &&\n+\ttest \"$(git -C wt1 rev-parse --is-bare-repository)\" = true &&\n+\ttest \"$(git -C wt2 rev-parse --is-bare-repository)\" = false\n+'\n+\n+test_expect_success 'config.worktree no longer read without extension' '\n+\tgit config --unset extensions.worktreeConfig &&\n+\ttest_cmp_config shared this.is &&\n+\ttest_cmp_config -C wt1 shared this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_done\n-- \n2.19.0.342.gc057aaf40a.dirty\n\n"},{"id":"359499","messageId":"CAPig+cRTty2BBs_udpYxHESBMx_y0NTM0gdkgX=z3EbEhvJDdg@mail.gmail.com","threadId":"49407","inReplyTo":"20181002160658.15891-2-pclouds@gmail.com","subject":"Re: [PATCH v3 1/2] t1300: extract and use test_cmp_config()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-10-03T07:46:19Z","receivedAt":"2018-10-03T07:46:32Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Oct 2, 2018 at 12:07 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> In many config-related tests it's common to check if a config variable\n> has expected value and we want to print the differences when the test\n> fails. Doing it the normal way is three lines of shell code. Let's add\n> a function do to all this (and a little more).\n> [...]\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\n> @@ -747,6 +747,29 @@ test_cmp() {\n> +test_cmp_config() {\n> +       local GD\n\nIf you re-roll, maybe make this part of the &&-chain to protect\nagainst someone coming along and inserting code above this line\nwithout realizing that the &&-chain is broken.\n\n> +       if test \"$1\" = \"-C\"\n> +       then\n> +               shift &&\n> +               GD=\"-C $1\" &&\n> +               shift\n> +       fi &&\n> +       printf \"%s\\n\" \"$1\" >expect.config &&\n> +       shift &&\n> +       git $GD config \"$@\" >actual.config &&\n> +       test_cmp expect.config actual.config\n> +}\n"},{"id":"361092","messageId":"20181021140228.24941-1-pclouds@gmail.com","threadId":"49407","inReplyTo":"20181002160658.15891-1-pclouds@gmail.com","subject":"[PATCH v4 0/2] Per-worktree config files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T14:02:26Z","receivedAt":"2018-10-21T14:02:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v4 has just one minor change to unbreak a && chain in test_cmp_config.\n\nNguyễn Thái Ngọc Duy (2):\n  t1300: extract and use test_cmp_config()\n  worktree: add per-worktree config files\n\n Documentation/config.txt               | 12 +++-\n Documentation/git-config.txt           | 26 ++++++---\n Documentation/git-worktree.txt         | 33 +++++++++++\n Documentation/gitrepository-layout.txt |  8 +++\n builtin/config.c                       | 19 ++++++-\n cache.h                                |  2 +\n config.c                               | 11 ++++\n environment.c                          |  1 +\n setup.c                                | 40 ++++++++++---\n t/t1300-config.sh                      | 79 +++++++-------------------\n t/t2029-worktree-config.sh             | 79 ++++++++++++++++++++++++++\n t/test-lib-functions.sh                | 23 ++++++++\n 12 files changed, 255 insertions(+), 78 deletions(-)\n create mode 100755 t/t2029-worktree-config.sh\n\nRange-diff against v3:\n1:  445f985619 ! 1:  23c2ee8844 t1300: extract and use test_cmp_config()\n    @@ -201,7 +201,7 @@\n     +#    test_cmp_config foo core.bar\n     +#\n     +test_cmp_config() {\n    -+\tlocal GD\n    ++\tlocal GD &&\n     +\tif test \"$1\" = \"-C\"\n     +\tthen\n     +\t\tshift &&\n2:  4c2d1bb37d = 2:  0d4dc8c6b0 worktree: add per-worktree config files\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361093","messageId":"20181021140228.24941-2-pclouds@gmail.com","threadId":"49407","inReplyTo":"20181021140228.24941-1-pclouds@gmail.com","subject":"[PATCH v4 1/2] t1300: extract and use test_cmp_config()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T14:02:27Z","receivedAt":"2018-10-21T14:02:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In many config-related tests it's common to check if a config variable\nhas expected value and we want to print the differences when the test\nfails. Doing it the normal way is three lines of shell code. Let's add\na function do to all this (and a little more).\n\nThis function has uses outside t1300 as well but I'm not going to\nconvert them all. And it will be used in the next commit where\nper-worktree config feature is introduced.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t1300-config.sh       | 79 ++++++++++-------------------------------\n t/test-lib-functions.sh | 23 ++++++++++++\n 2 files changed, 42 insertions(+), 60 deletions(-)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex e2cd50ecfc..9652b241c7 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -76,15 +76,11 @@ EOF\n test_expect_success 'non-match result' 'test_cmp expect .git/config'\n \n test_expect_success 'find mixed-case key by canonical name' '\n-\techo Second >expect &&\n-\tgit config cores.whatever >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config Second cores.whatever\n '\n \n test_expect_success 'find mixed-case key by non-canonical name' '\n-\techo Second >expect &&\n-\tgit config CoReS.WhAtEvEr >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config Second CoReS.WhAtEvEr\n '\n \n test_expect_success 'subsections are not canonicalized by git-config' '\n@@ -94,12 +90,8 @@ test_expect_success 'subsections are not canonicalized by git-config' '\n \t[section \"SubSection\"]\n \tkey = two\n \tEOF\n-\techo one >expect &&\n-\tgit config section.subsection.key >actual &&\n-\ttest_cmp expect actual &&\n-\techo two >expect &&\n-\tgit config section.SubSection.key >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config one section.subsection.key &&\n+\ttest_cmp_config two section.SubSection.key\n '\n \n cat > .git/config <<\\EOF\n@@ -212,9 +204,7 @@ test_expect_success 'really really mean test' '\n '\n \n test_expect_success 'get value' '\n-\techo alpha >expect &&\n-\tgit config beta.haha >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config alpha beta.haha\n '\n \n cat > expect << EOF\n@@ -251,15 +241,11 @@ test_expect_success 'non-match' '\n '\n \n test_expect_success 'non-match value' '\n-\techo wow >expect &&\n-\tgit config --get nextsection.nonewline !for >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config wow --get nextsection.nonewline !for\n '\n \n test_expect_success 'multi-valued get returns final one' '\n-\techo \"wow2 for me\" >expect &&\n-\tgit config --get nextsection.nonewline >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config \"wow2 for me\" --get nextsection.nonewline\n '\n \n test_expect_success 'multi-valued get-all returns all' '\n@@ -520,21 +506,11 @@ test_expect_success 'editing stdin is an error' '\n \n test_expect_success 'refer config from subdirectory' '\n \tmkdir x &&\n-\t(\n-\t\tcd x &&\n-\t\techo strasse >expect &&\n-\t\tgit config --get --file ../other-config ein.bahn >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-\n+\ttest_cmp_config -C x strasse --get --file ../other-config ein.bahn\n '\n \n test_expect_success 'refer config from subdirectory via --file' '\n-\t(\n-\t\tcd x &&\n-\t\tgit config --file=../other-config --get ein.bahn >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n+\ttest_cmp_config -C x strasse --file=../other-config --get ein.bahn\n '\n \n cat > expect << EOF\n@@ -688,16 +664,13 @@ test_expect_success numbers '\n \n test_expect_success '--int is at least 64 bits' '\n \tgit config giga.watts 121g &&\n-\techo 129922760704 >expect &&\n-\tgit config --int --get giga.watts >actual &&\n-\ttest_cmp expect actual\n+\techo  >expect &&\n+\ttest_cmp_config 129922760704 --int --get giga.watts\n '\n \n test_expect_success 'invalid unit' '\n \tgit config aninvalid.unit \"1auto\" &&\n-\techo 1auto >expect &&\n-\tgit config aninvalid.unit >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_cmp_config 1auto aninvalid.unit &&\n \ttest_must_fail git config --int --get aninvalid.unit 2>actual &&\n \ttest_i18ngrep \"bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit\" actual\n '\n@@ -1039,9 +1012,7 @@ test_expect_success '--null --get-regexp' '\n \n test_expect_success 'inner whitespace kept verbatim' '\n \tgit config section.val \"foo \t  bar\" &&\n-\techo \"foo \t  bar\" >expect &&\n-\tgit config section.val >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config \"foo \t  bar\" section.val\n '\n \n test_expect_success SYMLINKS 'symlinked configuration' '\n@@ -1809,21 +1780,15 @@ big = 1M\n EOF\n \n test_expect_success 'identical modern --type specifiers are allowed' '\n-\tgit config --type=int --type=int core.big >actual &&\n-\techo 1048576 >expect &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --type=int --type=int core.big\n '\n \n test_expect_success 'identical legacy --type specifiers are allowed' '\n-\tgit config --int --int core.big >actual &&\n-\techo 1048576 >expect &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --int --int core.big\n '\n \n test_expect_success 'identical mixed --type specifiers are allowed' '\n-\tgit config --int --type=int core.big >actual &&\n-\techo 1048576 >expect &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --int --type=int core.big\n '\n \n test_expect_success 'non-identical modern --type specifiers are not allowed' '\n@@ -1842,21 +1807,15 @@ test_expect_success 'non-identical mixed --type specifiers are not allowed' '\n '\n \n test_expect_success '--type allows valid type specifiers' '\n-\techo \"true\" >expect &&\n-\tgit config --type=bool core.foo >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config true  --type=bool core.foo\n '\n \n test_expect_success '--no-type unsets type specifiers' '\n-\techo \"10\" >expect &&\n-\tgit config --type=bool --no-type core.number >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 10 --type=bool --no-type core.number\n '\n \n test_expect_success 'unset type specifiers may be reset to conflicting ones' '\n-\techo 1048576 >expect &&\n-\tgit config --type=bool --no-type --type=int core.big >actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp_config 1048576 --type=bool --no-type --type=int core.big\n '\n \n test_expect_success '--type rejects unknown specifiers' '\ndiff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\nindex 78d8c3783b..d158c8d0bf 100644\n--- a/t/test-lib-functions.sh\n+++ b/t/test-lib-functions.sh\n@@ -747,6 +747,29 @@ test_cmp() {\n \t$GIT_TEST_CMP \"$@\"\n }\n \n+# Check that the given config key has the expected value.\n+#\n+#    test_cmp_config [-C <dir>] <expected-value>\n+#                    [<git-config-options>...] <config-key>\n+#\n+# for example to check that the value of core.bar is foo\n+#\n+#    test_cmp_config foo core.bar\n+#\n+test_cmp_config() {\n+\tlocal GD &&\n+\tif test \"$1\" = \"-C\"\n+\tthen\n+\t\tshift &&\n+\t\tGD=\"-C $1\" &&\n+\t\tshift\n+\tfi &&\n+\tprintf \"%s\\n\" \"$1\" >expect.config &&\n+\tshift &&\n+\tgit $GD config \"$@\" >actual.config &&\n+\ttest_cmp expect.config actual.config\n+}\n+\n # test_cmp_bin - helper to compare binary files\n \n test_cmp_bin() {\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361094","messageId":"20181021140228.24941-3-pclouds@gmail.com","threadId":"49407","inReplyTo":"20181021140228.24941-1-pclouds@gmail.com","subject":"[PATCH v4 2/2] worktree: add per-worktree config files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T14:02:28Z","receivedAt":"2018-10-21T14:02:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A new repo extension is added, worktreeConfig. When it is present:\n\n - Repository config reading by default includes $GIT_DIR/config _and_\n   $GIT_DIR/config.worktree. \"config\" file remains shared in multiple\n   worktree setup.\n\n - The special treatment for core.bare and core.worktree, to stay\n   effective only in main worktree, is gone. These config settings are\n   supposed to be in config.worktree.\n\nThis extension is most useful in multiple worktree setup because you\nnow have an option to store per-worktree config (which is either\n.git/config.worktree for main worktree, or\n.git/worktrees/xx/config.worktree for linked ones).\n\nThis extension can be used in single worktree mode, even though it's\npretty much useless (but this can happen after you remove all linked\nworktrees and move back to single worktree).\n\n\"git config\" reads from both \"config\" and \"config.worktree\" by default\n(i.e. without either --user, --file...) when this extension is\npresent. Default writes still go to \"config\", not \"config.worktree\". A\nnew option --worktree is added for that (*).\n\nSince a new repo extension is introduced, existing git binaries should\nrefuse to access to the repo (both from main and linked worktrees). So\nthey will not misread the config file (i.e. skip the config.worktree\npart). They may still accidentally write to the config file anyway if\nthey use with \"git config --file <path>\".\n\nThis design places a bet on the assumption that the majority of config\nvariables are shared so it is the default mode. A safer move would be\ndefault writes go to per-worktree file, so that accidental changes are\nisolated.\n\n(*) \"git config --worktree\" points back to \"config\" file when this\n    extension is not present and there is only one worktree so that it\n    works in any both single and multiple worktree setups.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt               | 12 +++-\n Documentation/git-config.txt           | 26 ++++++---\n Documentation/git-worktree.txt         | 33 +++++++++++\n Documentation/gitrepository-layout.txt |  8 +++\n builtin/config.c                       | 19 ++++++-\n cache.h                                |  2 +\n config.c                               | 11 ++++\n environment.c                          |  1 +\n setup.c                                | 40 ++++++++++---\n t/t2029-worktree-config.sh             | 79 ++++++++++++++++++++++++++\n 10 files changed, 213 insertions(+), 18 deletions(-)\n create mode 100755 t/t2029-worktree-config.sh\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 552827935a..244560a35e 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2,8 +2,9 @@ CONFIGURATION FILE\n ------------------\n \n The Git configuration file contains a number of variables that affect\n-the Git commands' behavior. The `.git/config` file in each repository\n-is used to store the configuration for that repository, and\n+the Git commands' behavior. The files `.git/config` and optionally\n+`config.worktree` (see `extensions.worktreeConfig` below) in each\n+repository are used to store the configuration for that repository, and\n `$HOME/.gitconfig` is used to store a per-user configuration as\n fallback values for the `.git/config` file. The file `/etc/gitconfig`\n can be used to store a system-wide default configuration.\n@@ -371,6 +372,13 @@ advice.*::\n \t\teditor input from the user.\n --\n \n+extensions.worktreeConfig::\n+\tIf set, by default \"git config\" reads from both \"config\" and\n+\t\"config.worktree\" file from GIT_DIR in that order. In\n+\tmultiple working directory mode, \"config\" file is shared while\n+\t\"config.worktree\" is per-working directory (i.e., it's in\n+\tGIT_COMMON_DIR/worktrees/<id>/config.worktree)\n+\n core.fileMode::\n \tTells Git if the executable bit of files in the working tree\n \tis to be honored.\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 5e87d82933..1bfe9f56a7 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -45,13 +45,15 @@ unset an existing `--type` specifier with `--no-type`.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n-`--system`, `--global`, `--local` and `--file <filename>` can be\n-used to tell the command to read from only that location (see <<FILES>>).\n+`--system`, `--global`, `--local`, `--worktree` and\n+`--file <filename>` can be used to tell the command to read from only\n+that location (see <<FILES>>).\n \n When writing, the new value is written to the repository local\n configuration file by default, and options `--system`, `--global`,\n-`--file <filename>` can be used to tell the command to write to\n-that location (you can say `--local` but that is the default).\n+`--worktree`, `--file <filename>` can be used to tell the command to\n+write to that location (you can say `--local` but that is the\n+default).\n \n This command will fail with non-zero status upon error.  Some exit\n codes are:\n@@ -131,6 +133,11 @@ from all available files.\n +\n See also <<FILES>>.\n \n+--worktree::\n+\tSimilar to `--local` except that `.git/config.worktree` is\n+\tread from or written to if `extensions.worktreeConfig` is\n+\tpresent. If not it's the same as `--local`.\n+\n -f config-file::\n --file config-file::\n \tUse the given config file instead of the one specified by GIT_CONFIG.\n@@ -281,6 +288,10 @@ $XDG_CONFIG_HOME/git/config::\n $GIT_DIR/config::\n \tRepository specific configuration file.\n \n+$GIT_DIR/config.worktree::\n+\tThis is optional and is only searched when\n+\t`extensions.worktreeConfig` is present in $GIT_DIR/config.\n+\n If no further options are given, all reading options will read all of these\n files that are available. If the global or the system-wide configuration\n file are not available they will be ignored. If the repository configuration\n@@ -299,9 +310,10 @@ configuration file. Note that this also affects options like `--replace-all`\n and `--unset`. *'git config' will only ever change one file at a time*.\n \n You can override these rules either by command-line options or by environment\n-variables. The `--global` and the `--system` options will limit the file used\n-to the global or system-wide file respectively. The `GIT_CONFIG` environment\n-variable has a similar effect, but you can specify any filename you want.\n+variables. The `--global`, `--system` and `--worktree` options will limit\n+the file used to the global, system-wide or per-worktree file respectively.\n+The `GIT_CONFIG` environment variable has a similar effect, but you\n+can specify any filename you want.\n \n \n ENVIRONMENT\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex e2ee9fc21b..408c87c9ef 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -204,6 +204,36 @@ working trees, it can be used to identify worktrees. For example if\n you only have two working trees, at \"/abc/def/ghi\" and \"/abc/def/ggg\",\n then \"ghi\" or \"def/ghi\" is enough to point to the former working tree.\n \n+CONFIGURATION FILE\n+------------------\n+By default, the repository \"config\" file is shared across all working\n+trees. If the config variables `core.bare` or `core.worktree` are\n+already present in the config file, they will be applied to the main\n+working trees only.\n+\n+In order to have configuration specific to working trees, you can turn\n+on \"worktreeConfig\" extension, e.g.:\n+\n+------------\n+$ git config extensions.worktreeConfig true\n+------------\n+\n+In this mode, specific configuration stays in the path pointed by `git\n+rev-parse --git-path config.worktree`. You can add or update\n+configuration in this file with `git config --worktree`. Older Git\n+versions will refuse to access repositories with this extension.\n+\n+Note that in this file, the exception for `core.bare` and `core.worktree`\n+is gone. If you have them in $GIT_DIR/config before, you must move\n+them to the `config.worktree` of the main working tree. You may also\n+take this opportunity to review and move other configuration that you\n+do not want to share to all working trees:\n+\n+ - `core.worktree` and `core.bare` should never be shared\n+\n+ - `core.sparseCheckout` is recommended per working tree, unless you\n+   are sure you always use sparse checkout for all working trees.\n+\n DETAILS\n -------\n Each linked working tree has a private sub-directory in the repository's\n@@ -253,6 +283,9 @@ to `/path/main/.git/worktrees/test-next` then a file named\n `test-next` entry from being pruned.  See\n linkgit:gitrepository-layout[5] for details.\n \n+When extensions.worktreeConfig is enabled, the config file\n+`.git/worktrees/<id>/config.worktree` is read after `.git/config` is.\n+\n LIST OUTPUT FORMAT\n ------------------\n The worktree list command has two output formats.  The default format shows the\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex e85148f05e..36fcca8087 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -143,6 +143,11 @@ config::\n \tif $GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/config\" will be\n \tused instead.\n \n+config.worktree::\n+\tWorking directory specific configuration file for the main\n+\tworking directory in multiple working directory setup (see\n+\tlinkgit:git-worktree[1]).\n+\n branches::\n \tA slightly deprecated way to store shorthands to be used\n \tto specify a URL to 'git fetch', 'git pull' and 'git push'.\n@@ -275,6 +280,9 @@ worktrees/<id>/locked::\n \tor manually by `git worktree prune`. The file may contain a string\n \texplaining why the repository is locked.\n \n+worktrees/<id>/config.worktree::\n+\tWorking directory specific configuration file.\n+\n SEE ALSO\n --------\n linkgit:git-init[1],\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 97b58c4aea..84385ef165 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -5,6 +5,7 @@\n #include \"parse-options.h\"\n #include \"urlmatch.h\"\n #include \"quote.h\"\n+#include \"worktree.h\"\n \n static const char *const builtin_config_usage[] = {\n \tN_(\"git config [<options>]\"),\n@@ -24,6 +25,7 @@ static char key_delim = ' ';\n static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n+static int use_worktree_config;\n static struct git_config_source given_config_source;\n static int actions, type;\n static char *default_value;\n@@ -123,6 +125,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n \tOPT_BOOL(0, \"system\", &use_system_config, N_(\"use system config file\")),\n \tOPT_BOOL(0, \"local\", &use_local_config, N_(\"use repository config file\")),\n+\tOPT_BOOL(0, \"worktree\", &use_worktree_config, N_(\"use per-worktree config file\")),\n \tOPT_STRING('f', \"file\", &given_config_source.file, N_(\"file\"), N_(\"use given config file\")),\n \tOPT_STRING(0, \"blob\", &given_config_source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\")),\n \tOPT_GROUP(N_(\"Action\")),\n@@ -602,6 +605,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n \tif (use_global_config + use_system_config + use_local_config +\n+\t    use_worktree_config +\n \t    !!given_config_source.file + !!given_config_source.blob > 1) {\n \t\terror(_(\"only one config file at a time\"));\n \t\tusage_builtin_config();\n@@ -645,7 +649,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tgiven_config_source.file = git_etc_gitconfig();\n \telse if (use_local_config)\n \t\tgiven_config_source.file = git_pathdup(\"config\");\n-\telse if (given_config_source.file) {\n+\telse if (use_worktree_config) {\n+\t\tstruct worktree **worktrees = get_worktrees(0);\n+\t\tif (repository_format_worktree_config)\n+\t\t\tgiven_config_source.file = git_pathdup(\"config.worktree\");\n+\t\telse if (worktrees[0] && worktrees[1])\n+\t\t\tdie(_(\"--worktree cannot be used with multiple \"\n+\t\t\t      \"working trees unless the config\\n\"\n+\t\t\t      \"extension worktreeConfig is enabled. \"\n+\t\t\t      \"Please read \\\"CONFIGURATION FILE\\\"\\n\"\n+\t\t\t      \"section in \\\"git help worktree\\\" for details\"));\n+\t\telse\n+\t\t\tgiven_config_source.file = git_pathdup(\"config\");\n+\t\tfree_worktrees(worktrees);\n+\t} else if (given_config_source.file) {\n \t\tif (!is_absolute_path(given_config_source.file) && prefix)\n \t\t\tgiven_config_source.file =\n \t\t\t\tprefix_filename(prefix, given_config_source.file);\ndiff --git a/cache.h b/cache.h\nindex 59c8a93046..8fd1bb2a57 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -960,11 +960,13 @@ extern int grafts_replace_parents;\n extern int repository_format_precious_objects;\n extern char *repository_format_partial_clone;\n extern const char *core_partial_clone_filter_default;\n+extern int repository_format_worktree_config;\n \n struct repository_format {\n \tint version;\n \tint precious_objects;\n \tchar *partial_clone; /* value of extensions.partialclone */\n+\tint worktree_config;\n \tint is_bare;\n \tint hash_algo;\n \tchar *work_tree;\ndiff --git a/config.c b/config.c\nindex 4051e38823..aa0ed854f6 100644\n--- a/config.c\n+++ b/config.c\n@@ -1695,6 +1695,17 @@ static int do_git_config_sequence(const struct config_options *opts,\n \tif (repo_config && !access_or_die(repo_config, R_OK, 0))\n \t\tret += git_config_from_file(fn, repo_config, data);\n \n+\t/*\n+\t * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE.\n+\t * But let's not complicate things before it's actually needed.\n+\t */\n+\tif (repository_format_worktree_config) {\n+\t\tchar *path = git_pathdup(\"config.worktree\");\n+\t\tif (!access_or_die(path, R_OK, 0))\n+\t\t\tret += git_config_from_file(fn, path, data);\n+\t\tfree(path);\n+\t}\n+\n \tcurrent_parsing_scope = CONFIG_SCOPE_CMDLINE;\n \tif (git_config_from_parameters(fn, data) < 0)\n \t\tdie(_(\"unable to parse command-line config\"));\ndiff --git a/environment.c b/environment.c\nindex 3f3c8746c2..268310b3dc 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -33,6 +33,7 @@ int ref_paranoia = -1;\n int repository_format_precious_objects;\n char *repository_format_partial_clone;\n const char *core_partial_clone_filter_default;\n+int repository_format_worktree_config;\n const char *git_commit_encoding;\n const char *git_log_output_encoding;\n const char *apply_default_whitespace;\ndiff --git a/setup.c b/setup.c\nindex b24c811c1c..1be5037f12 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -402,6 +402,20 @@ void setup_work_tree(void)\n \tinitialized = 1;\n }\n \n+static int read_worktree_config(const char *var, const char *value, void *vdata)\n+{\n+\tstruct repository_format *data = vdata;\n+\n+\tif (strcmp(var, \"core.bare\") == 0) {\n+\t\tdata->is_bare = git_config_bool(var, value);\n+\t} else if (strcmp(var, \"core.worktree\") == 0) {\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\tdata->work_tree = xstrdup(value);\n+\t}\n+\treturn 0;\n+}\n+\n static int check_repo_format(const char *var, const char *value, void *vdata)\n {\n \tstruct repository_format *data = vdata;\n@@ -423,16 +437,13 @@ static int check_repo_format(const char *var, const char *value, void *vdata)\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(var);\n \t\t\tdata->partial_clone = xstrdup(value);\n-\t\t} else\n+\t\t} else if (!strcmp(ext, \"worktreeconfig\"))\n+\t\t\tdata->worktree_config = git_config_bool(var, value);\n+\t\telse\n \t\t\tstring_list_append(&data->unknown_extensions, ext);\n-\t} else if (strcmp(var, \"core.bare\") == 0) {\n-\t\tdata->is_bare = git_config_bool(var, value);\n-\t} else if (strcmp(var, \"core.worktree\") == 0) {\n-\t\tif (!value)\n-\t\t\treturn config_error_nonbool(var);\n-\t\tdata->work_tree = xstrdup(value);\n \t}\n-\treturn 0;\n+\n+\treturn read_worktree_config(var, value, vdata);\n }\n \n static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)\n@@ -466,7 +477,20 @@ static int check_repository_format_gently(const char *gitdir, struct repository_\n \n \trepository_format_precious_objects = candidate->precious_objects;\n \trepository_format_partial_clone = candidate->partial_clone;\n+\trepository_format_worktree_config = candidate->worktree_config;\n \tstring_list_clear(&candidate->unknown_extensions, 0);\n+\n+\tif (repository_format_worktree_config) {\n+\t\t/*\n+\t\t * pick up core.bare and core.worktree from per-worktree\n+\t\t * config if present\n+\t\t */\n+\t\tstrbuf_addf(&sb, \"%s/config.worktree\", gitdir);\n+\t\tgit_config_from_file(read_worktree_config, sb.buf, candidate);\n+\t\tstrbuf_release(&sb);\n+\t\thas_common = 0;\n+\t}\n+\n \tif (!has_common) {\n \t\tif (candidate->is_bare != -1) {\n \t\t\tis_bare_repository_cfg = candidate->is_bare;\ndiff --git a/t/t2029-worktree-config.sh b/t/t2029-worktree-config.sh\nnew file mode 100755\nindex 0000000000..286121d8de\n--- /dev/null\n+++ b/t/t2029-worktree-config.sh\n@@ -0,0 +1,79 @@\n+#!/bin/sh\n+\n+test_description=\"config file in multi worktree\"\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit start\n+'\n+\n+test_expect_success 'config --worktree in single worktree' '\n+\tgit config --worktree foo.bar true &&\n+\ttest_cmp_config true foo.bar\n+'\n+\n+test_expect_success 'add worktrees' '\n+\tgit worktree add wt1 &&\n+\tgit worktree add wt2\n+'\n+\n+test_expect_success 'config --worktree without extension' '\n+\ttest_must_fail git config --worktree foo.bar false\n+'\n+\n+test_expect_success 'enable worktreeConfig extension' '\n+\tgit config extensions.worktreeConfig true &&\n+\ttest_cmp_config true extensions.worktreeConfig\n+'\n+\n+test_expect_success 'config is shared as before' '\n+\tgit config this.is shared &&\n+\ttest_cmp_config shared this.is &&\n+\ttest_cmp_config -C wt1 shared this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'config is shared (set from another worktree)' '\n+\tgit -C wt1 config that.is also-shared &&\n+\ttest_cmp_config also-shared that.is &&\n+\ttest_cmp_config -C wt1 also-shared that.is &&\n+\ttest_cmp_config -C wt2 also-shared that.is\n+'\n+\n+test_expect_success 'config private to main worktree' '\n+\tgit config --worktree this.is for-main &&\n+\ttest_cmp_config for-main this.is &&\n+\ttest_cmp_config -C wt1 shared this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'config private to linked worktree' '\n+\tgit -C wt1 config --worktree this.is for-wt1 &&\n+\ttest_cmp_config for-main this.is &&\n+\ttest_cmp_config -C wt1 for-wt1 this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_expect_success 'core.bare no longer for main only' '\n+\ttest_config core.bare true &&\n+\ttest \"$(git rev-parse --is-bare-repository)\" = true &&\n+\ttest \"$(git -C wt1 rev-parse --is-bare-repository)\" = true &&\n+\ttest \"$(git -C wt2 rev-parse --is-bare-repository)\" = true\n+'\n+\n+test_expect_success 'per-worktree core.bare is picked up' '\n+\tgit -C wt1 config --worktree core.bare true &&\n+\ttest \"$(git rev-parse --is-bare-repository)\" = false &&\n+\ttest \"$(git -C wt1 rev-parse --is-bare-repository)\" = true &&\n+\ttest \"$(git -C wt2 rev-parse --is-bare-repository)\" = false\n+'\n+\n+test_expect_success 'config.worktree no longer read without extension' '\n+\tgit config --unset extensions.worktreeConfig &&\n+\ttest_cmp_config shared this.is &&\n+\ttest_cmp_config -C wt1 shared this.is &&\n+\ttest_cmp_config -C wt2 shared this.is\n+'\n+\n+test_done\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361132","messageId":"xmqqefciwphv.fsf@gitster-ct.c.googlers.com","threadId":"49407","inReplyTo":"20181021140228.24941-3-pclouds@gmail.com","subject":"Re: [PATCH v4 2/2] worktree: add per-worktree config files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-10-22T04:54:04Z","receivedAt":"2018-10-22T04:54:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 552827935a..244560a35e 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -2,8 +2,9 @@ CONFIGURATION FILE\n>  ------------------\n>  \n>  The Git configuration file contains a number of variables that affect\n> -the Git commands' behavior. The `.git/config` file in each repository\n> -is used to store the configuration for that repository, and\n> +the Git commands' behavior. The files `.git/config` and optionally\n> +`config.worktree` (see `extensions.worktreeConfig` below) in each\n> +repository are used to store the configuration for that repository, and\n>  `$HOME/.gitconfig` is used to store a per-user configuration as\n>  fallback values for the `.git/config` file. The file `/etc/gitconfig`\n>  can be used to store a system-wide default configuration.\n> @@ -371,6 +372,13 @@ advice.*::\n>  \t\teditor input from the user.\n>  --\n>  \n> +extensions.worktreeConfig::\n> +\tIf set, by default \"git config\" reads from both \"config\" and\n> +\t\"config.worktree\" file from GIT_DIR in that order. In\n> +\tmultiple working directory mode, \"config\" file is shared while\n> +\t\"config.worktree\" is per-working directory (i.e., it's in\n> +\tGIT_COMMON_DIR/worktrees/<id>/config.worktree)\n> +\n\nThis obviously conflicts with your 59-patch series, but more\nimportantly\n\n - I notice that this is the only description of extensions.* key in\n   the configuration files.  Don't we have any other extension\n   defined, and if so shouldn't we be describing them already (not\n   as a part of this series, obviously)?\n\n - If we are going to describe other extensions.* keys, do we want\n   extensions-config.txt file to split this (and others) out and\n   later rename it to config/extensions.txt?  Or do we want to\n   collect related things together by logically not by name and have\n   this extension described in config/worktree.txt instead, perhaps\n   separate from other extensions.* keys?\n\n"},{"id":"361161","messageId":"CACsJy8BYJKKdnbNYBR-XNeSE9jpWqby+epMKWo9-wGRjVE8Lfg@mail.gmail.com","threadId":"49407","inReplyTo":"xmqqefciwphv.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v4 2/2] worktree: add per-worktree config files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-22T14:32:37Z","receivedAt":"2018-10-22T14:33:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Oct 22, 2018 at 6:54 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n> > diff --git a/Documentation/config.txt b/Documentation/config.txt\n> > index 552827935a..244560a35e 100644\n> > --- a/Documentation/config.txt\n> > +++ b/Documentation/config.txt\n> > @@ -2,8 +2,9 @@ CONFIGURATION FILE\n> >  ------------------\n> >\n> >  The Git configuration file contains a number of variables that affect\n> > -the Git commands' behavior. The `.git/config` file in each repository\n> > -is used to store the configuration for that repository, and\n> > +the Git commands' behavior. The files `.git/config` and optionally\n> > +`config.worktree` (see `extensions.worktreeConfig` below) in each\n> > +repository are used to store the configuration for that repository, and\n> >  `$HOME/.gitconfig` is used to store a per-user configuration as\n> >  fallback values for the `.git/config` file. The file `/etc/gitconfig`\n> >  can be used to store a system-wide default configuration.\n> > @@ -371,6 +372,13 @@ advice.*::\n> >               editor input from the user.\n> >  --\n> >\n> > +extensions.worktreeConfig::\n> > +     If set, by default \"git config\" reads from both \"config\" and\n> > +     \"config.worktree\" file from GIT_DIR in that order. In\n> > +     multiple working directory mode, \"config\" file is shared while\n> > +     \"config.worktree\" is per-working directory (i.e., it's in\n> > +     GIT_COMMON_DIR/worktrees/<id>/config.worktree)\n> > +\n>\n> This obviously conflicts with your 59-patch series, but more\n> importantly\n>\n>  - I notice that this is the only description of extensions.* key in\n>    the configuration files.  Don't we have any other extension\n>    defined, and if so shouldn't we be describing them already (not\n>    as a part of this series, obviously)?\n\nRight. We have two extensions already but it's described in\ntechnical/repository-format.txt. I'll move this extension there\nbecause it's written \"This document will serve as the master list for\nextensions.\" in that document.\n\n>  - If we are going to describe other extensions.* keys, do we want\n>    extensions-config.txt file to split this (and others) out and\n>    later rename it to config/extensions.txt?  Or do we want to\n>    collect related things together by logically not by name and have\n>    this extension described in config/worktree.txt instead, perhaps\n>    separate from other extensions.* keys?\n\nI think we would go with config/extensions.txt because if grouping\nlogically, I'm not sure where extensions.preciousObjects and\nextensions.partialClone would go.\n-- \nDuy\n"},{"id":"361496","messageId":"xmqqftwugzdi.fsf@gitster-ct.c.googlers.com","threadId":"49407","inReplyTo":"CACsJy8BYJKKdnbNYBR-XNeSE9jpWqby+epMKWo9-wGRjVE8Lfg@mail.gmail.com","subject":"Re: [PATCH v4 2/2] worktree: add per-worktree config files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-10-25T09:16:25Z","receivedAt":"2018-10-25T09:16:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> > +extensions.worktreeConfig::\n>> > +     If set, by default \"git config\" reads from both \"config\" and\n>> > +     \"config.worktree\" file from GIT_DIR in that order. In\n>> > +     multiple working directory mode, \"config\" file is shared while\n>> > +     \"config.worktree\" is per-working directory (i.e., it's in\n>> > +     GIT_COMMON_DIR/worktrees/<id>/config.worktree)\n>> > +\n>>\n>> This obviously conflicts with your 59-patch series, but more\n>> importantly\n>>\n>>  - I notice that this is the only description of extensions.* key in\n>>    the configuration files.  Don't we have any other extension\n>>    defined, and if so shouldn't we be describing them already (not\n>>    as a part of this series, obviously)?\n>\n> Right. We have two extensions already but it's described in\n> technical/repository-format.txt. I'll move this extension there\n> because it's written \"This document will serve as the master list for\n> extensions.\" in that document.\n>\n>>  - If we are going to describe other extensions.* keys, do we want\n>>    extensions-config.txt file to split this (and others) out and\n>>    later rename it to config/extensions.txt?  Or do we want to\n>>    collect related things together by logically not by name and have\n>>    this extension described in config/worktree.txt instead, perhaps\n>>    separate from other extensions.* keys?\n>\n> I think we would go with config/extensions.txt because if grouping\n> logically, I'm not sure where extensions.preciousObjects and\n> extensions.partialClone would go.\n\nOK, that sounds sensible.\n\nOther than that, I am getting the feeling that everybody agrees that\nthe problem this topic addresses is worth addressing, and the design\nand the implementation of the solution presented here is sensible.\n\nIf so, let's move it forward to 'next' and plan to merge it down to\n'master'.  The \"extensions.*\" description can happen incrementally,.\nI'd think.\n\n"}]}