Re: [PATCH v4 1/4] worktree: add per-worktree config files

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Jul 20, 2016 at 10:24 AM, Nguyễn Thái Ngọc Duy
<pclouds@xxxxxxxxx> wrote:
> A new repo extension is added, worktreeConfig. When it is present:
>
>  - Repository config reading by default includes $GIT_DIR/config _and_
>    $GIT_DIR/config.worktree. "config" file remains shared in multiple
>    worktree setup.
>
>  - The special treatment for core.bare and core.worktree, to stay
>    effective only in main worktree, is gone. These config files are
>    supposed to be in config.worktree.
>
> This extension is most useful in multiple worktree setup because you
> now have an option to store per-worktree config (which is either
> .git/config.worktree for main worktree, or
> .git/worktrees/xx/config.worktree for linked ones).
>
> This extension can be used in single worktree mode, even though it's
> pretty much useless (but this can happen after you remove all linked
> worktrees and move back to single worktree).
>
> "git config" reads from both "config" and "config.worktree" by default
> (i.e. without either --user, --file...) when this extension is
> present. Default writes still go to "config", not "config.worktree". A
> new option --worktree is added for that (*).
>
> Since a new repo extension is introduced, existing git binaries should
> refuse to access to the repo (both from main and linked worktrees). So
> they will not misread the config file (i.e. skip the config.worktree
> part). They may still accidentally write to the config file anyway if
> they use with "git config --file <path>".
>
> This design places a bet on the assumption that the majority of config
> variables are shared so it is the default mode. A safer move would be
> default writes go to per-worktree file, so that accidental changes are
> isolated.
>
> (*) "git config --worktree" points back to "config" file when this
>     extension is not present so that it works in any setup.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx>

I like the user facing design, but how am I supposed to use it internally?

Say I want to read a value preferably from the worktree I'd do a
    /*
     * maybe I don't even have to set it to 1 as
     * the user is supposed to do that?
     */
    repository_format_worktree_config = 1;
    git_config_get_{string,bool,int} (... as usual ...)

and if I want to read the value globally I would set the variable to 0
and read? (I would need to restore it, so I'll have a temporary variable
to keep the original value of repository_format_worktree_config)

Thanks,
Stefan


> ---
>  Documentation/config.txt               | 11 ++++-
>  Documentation/git-config.txt           | 26 ++++++++----
>  Documentation/git-worktree.txt         | 31 ++++++++++++++
>  Documentation/gitrepository-layout.txt |  8 ++++
>  builtin/config.c                       | 18 +++++++-
>  cache.h                                |  2 +
>  config.c                               |  7 ++++
>  environment.c                          |  1 +
>  setup.c                                |  5 ++-
>  t/t2028-worktree-config.sh (new +x)    | 77 ++++++++++++++++++++++++++++++++++
>  10 files changed, 175 insertions(+), 11 deletions(-)
>  create mode 100755 t/t2028-worktree-config.sh
>
> diff --git a/Documentation/config.txt b/Documentation/config.txt
> index 16dc22d..7d64da0 100644
> --- a/Documentation/config.txt
> +++ b/Documentation/config.txt
> @@ -2,8 +2,9 @@ CONFIGURATION FILE
>  ------------------
>
>  The Git configuration file contains a number of variables that affect
> -the Git commands' behavior. The `.git/config` file in each repository
> -is used to store the configuration for that repository, and
> +the Git commands' behavior. The files `.git/config` and optionally
> +`config.worktree` (see `extensions.worktreeConfig` below) are each
> +repository is used to store the configuration for that repository, and
>  `$HOME/.gitconfig` is used to store a per-user configuration as
>  fallback values for the `.git/config` file. The file `/etc/gitconfig`
>  can be used to store a system-wide default configuration.
> @@ -264,6 +265,12 @@ advice.*::
>                 show directions on how to proceed from the current state.
>  --
>
> +extensions.worktreeConfig::
> +       If set, by default "git config" reads from both "config" and
> +       "config.worktree" file in that order. In multiple working
> +       directory mode, "config" file is shared while
> +       "config.worktree" is per-working directory.
> +
>  core.fileMode::
>         Tells Git if the executable bit of files in the working tree
>         is to be honored.
> diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
> index f163113..9dfdb6a 100644
> --- a/Documentation/git-config.txt
> +++ b/Documentation/git-config.txt
> @@ -47,13 +47,15 @@ checks or transformations are performed on the value.
>
>  When reading, the values are read from the system, global and
>  repository local configuration files by default, and options
> -`--system`, `--global`, `--local` and `--file <filename>` can be
> -used to tell the command to read from only that location (see <<FILES>>).
> +`--system`, `--global`, `--local`, `--worktree` and
> +`--file <filename>` can be used to tell the command to read from only
> +that location (see <<FILES>>).
>
>  When writing, the new value is written to the repository local
>  configuration file by default, and options `--system`, `--global`,
> -`--file <filename>` can be used to tell the command to write to
> -that location (you can say `--local` but that is the default).
> +`--worktree`, `--file <filename>` can be used to tell the command to
> +write to that location (you can say `--local` but that is the
> +default).
>
>  This command will fail with non-zero status upon error.  Some exit
>  codes are:
> @@ -133,6 +135,11 @@ from all available files.
>  +
>  See also <<FILES>>.
>
> +--worktree::
> +       Similar to `--local` except that `.git/config.worktree` is
> +       read from or written to if `extensions.worktreeConfig` is
> +       present. If not it's the same as `--local`.
> +
>  -f config-file::
>  --file config-file::
>         Use the given config file instead of the one specified by GIT_CONFIG.
> @@ -253,6 +260,10 @@ $XDG_CONFIG_HOME/git/config::
>  $GIT_DIR/config::
>         Repository specific configuration file.
>
> +$GIT_DIR/config.worktree::
> +       This is optional and is only searched when
> +       `extensions.worktreeConfig` is present in $GIT_DIR/config.
> +
>  If no further options are given, all reading options will read all of these
>  files that are available. If the global or the system-wide configuration
>  file are not available they will be ignored. If the repository configuration
> @@ -268,9 +279,10 @@ configuration file. Note that this also affects options like `--replace-all`
>  and `--unset`. *'git config' will only ever change one file at a time*.
>
>  You can override these rules either by command-line options or by environment
> -variables. The `--global` and the `--system` options will limit the file used
> -to the global or system-wide file respectively. The `GIT_CONFIG` environment
> -variable has a similar effect, but you can specify any filename you want.
> +variables. The `--global`, `--system` and `--worktree` options will limit
> +the file used to the global, system-wide or per-worktree file respectively.
> +The `GIT_CONFIG` environment variable has a similar effect, but you
> +can specify any filename you want.
>
>
>  ENVIRONMENT
> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
> index 7c4cfb0..41350db 100644
> --- a/Documentation/git-worktree.txt
> +++ b/Documentation/git-worktree.txt
> @@ -111,6 +111,37 @@ OPTIONS
>  --expire <time>::
>         With `prune`, only expire unused working trees older than <time>.
>
> +CONFIGURATION FILE
> +------------------
> +By default, the repository "config" file is shared across all working
> +directories. If the config variables `core.bare` or `core.worktree`
> +are already present in the config file, they will be applied to the
> +main working directory only.
> +
> +In order to have configuration specific to working directories, you
> +can turn on "worktreeConfig" extension, e.g.:
> +
> +------------
> +$ git config extensions.worktreeConfig true
> +------------
> +
> +In this mode, specific configuration stays in the path pointed by `git
> +rev-parse --git-path config.worktree`. You can add or update
> +configuration in this file with `git config --worktree`. Git before
> +version XXX will refuse to access repositories with this extension.
> +
> +Note that in this file, the exception for `core.bare` and
> +core.worktree` is gone. If you have them before, you need to move them
> +to the config.worktree of the main working directory. You may also
> +take this opportunity to move other configuration that you do not want
> +to share to all working directories:
> +
> + - `core.worktree` and `core.bare` should never be shared
> +
> + - `core.sparseCheckout` is recommended per working directory, unless
> +   you are sure you always use sparse checkout for all working
> +   directories.
> +
>  DETAILS
>  -------
>  Each linked working tree has a private sub-directory in the repository's
> diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
> index 577ee84..6cfdb4c 100644
> --- a/Documentation/gitrepository-layout.txt
> +++ b/Documentation/gitrepository-layout.txt
> @@ -143,6 +143,11 @@ config::
>         if $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/config" will be
>         used instead.
>
> +config.worktree::
> +       Working directory specific configuration file for the main
> +       working directory in multiple working directory setup (see
> +       linkgit:git-worktree[1]).
> +
>  branches::
>         A slightly deprecated way to store shorthands to be used
>         to specify a URL to 'git fetch', 'git pull' and 'git push'.
> @@ -276,6 +281,9 @@ worktrees/<id>/link::
>         file. It is used to detect if the linked repository is
>         manually removed.
>
> +worktrees/<id>/config.worktree::
> +       Working directory specific configuration file.
> +
>  SEE ALSO
>  --------
>  linkgit:git-init[1],
> diff --git a/builtin/config.c b/builtin/config.c
> index 1d7c6ef..535707c 100644
> --- a/builtin/config.c
> +++ b/builtin/config.c
> @@ -4,6 +4,7 @@
>  #include "parse-options.h"
>  #include "urlmatch.h"
>  #include "quote.h"
> +#include "worktree.h"
>
>  static const char *const builtin_config_usage[] = {
>         N_("git config [<options>]"),
> @@ -23,6 +24,7 @@ static char key_delim = ' ';
>  static char term = '\n';
>
>  static int use_global_config, use_system_config, use_local_config;
> +static int use_worktree_config;
>  static struct git_config_source given_config_source;
>  static int actions, types;
>  static const char *get_color_slot, *get_colorbool_slot;
> @@ -57,6 +59,7 @@ static struct option builtin_config_options[] = {
>         OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
>         OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
>         OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
> +       OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")),
>         OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
>         OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
>         OPT_GROUP(N_("Action")),
> @@ -491,6 +494,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
>                              PARSE_OPT_STOP_AT_NON_OPTION);
>
>         if (use_global_config + use_system_config + use_local_config +
> +           use_worktree_config +
>             !!given_config_source.file + !!given_config_source.blob > 1) {
>                 error("only one config file at a time.");
>                 usage_with_options(builtin_config_usage, builtin_config_options);
> @@ -525,7 +529,19 @@ int cmd_config(int argc, const char **argv, const char *prefix)
>                 given_config_source.file = git_etc_gitconfig();
>         else if (use_local_config)
>                 given_config_source.file = git_pathdup("config");
> -       else if (given_config_source.file) {
> +       else if (use_worktree_config) {
> +               if (repository_format_worktree_config)
> +                       given_config_source.file = git_pathdup("config.worktree");
> +               else {
> +                       struct worktree **worktrees = get_worktrees();
> +                       if (worktrees[0] && worktrees[1])
> +                               die(_("Per-worktree configuration requires extensions.worktreeConfig\n"
> +                                     "Please read section CONFIGURATION in `git help worktree` before\n"
> +                                     "enabling it."));
> +                       free_worktrees(worktrees);
> +                       given_config_source.file = git_pathdup("config");
> +               }
> +       } else if (given_config_source.file) {
>                 if (!is_absolute_path(given_config_source.file) && prefix)
>                         given_config_source.file =
>                                 xstrdup(prefix_filename(prefix,
> diff --git a/cache.h b/cache.h
> index f1dc289..606500e 100644
> --- a/cache.h
> +++ b/cache.h
> @@ -757,10 +757,12 @@ extern int grafts_replace_parents;
>  #define GIT_REPO_VERSION 0
>  #define GIT_REPO_VERSION_READ 1
>  extern int repository_format_precious_objects;
> +extern int repository_format_worktree_config;
>
>  struct repository_format {
>         int version;
>         int precious_objects;
> +       int worktree_config;
>         int is_bare;
>         char *work_tree;
>         struct string_list unknown_extensions;
> diff --git a/config.c b/config.c
> index bea937e..99ff6be 100644
> --- a/config.c
> +++ b/config.c
> @@ -1254,6 +1254,13 @@ static int do_git_config_sequence(config_fn_t fn, void *data)
>         if (repo_config && !access_or_die(repo_config, R_OK, 0))
>                 ret += git_config_from_file(fn, repo_config, data);
>
> +       if (repository_format_worktree_config) {
> +               char *path = git_pathdup("config.worktree");
> +               if (!access_or_die(path, R_OK, 0))
> +                       ret += git_config_from_file(fn, path, data);
> +               free(path);
> +       }
> +
>         current_parsing_scope = CONFIG_SCOPE_CMDLINE;
>         if (git_config_from_parameters(fn, data) < 0)
>                 die(_("unable to parse command-line config"));
> diff --git a/environment.c b/environment.c
> index ca72464..b4d56ef 100644
> --- a/environment.c
> +++ b/environment.c
> @@ -26,6 +26,7 @@ int warn_ambiguous_refs = 1;
>  int warn_on_object_refname_ambiguity = 1;
>  int ref_paranoia = -1;
>  int repository_format_precious_objects;
> +int repository_format_worktree_config;
>  const char *git_commit_encoding;
>  const char *git_log_output_encoding;
>  const char *apply_default_whitespace;
> diff --git a/setup.c b/setup.c
> index 6d0e0c9..75c784f 100644
> --- a/setup.c
> +++ b/setup.c
> @@ -389,6 +389,8 @@ static int check_repo_format(const char *var, const char *value, void *vdata)
>                         ;
>                 else if (!strcmp(ext, "preciousobjects"))
>                         data->precious_objects = git_config_bool(var, value);
> +               else if (!strcmp(ext, "worktreeconfig"))
> +                       data->worktree_config = git_config_bool(var, value);
>                 else
>                         string_list_append(&data->unknown_extensions, ext);
>         } else if (strcmp(var, "core.bare") == 0) {
> @@ -432,8 +434,9 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
>         }
>
>         repository_format_precious_objects = candidate.precious_objects;
> +       repository_format_worktree_config = candidate.worktree_config;
>         string_list_clear(&candidate.unknown_extensions, 0);
> -       if (!has_common) {
> +       if (!has_common || repository_format_worktree_config) {
>                 if (candidate.is_bare != -1) {
>                         is_bare_repository_cfg = candidate.is_bare;
>                         if (is_bare_repository_cfg == 1)
> diff --git a/t/t2028-worktree-config.sh b/t/t2028-worktree-config.sh
> new file mode 100755
> index 0000000..34067df
> --- /dev/null
> +++ b/t/t2028-worktree-config.sh
> @@ -0,0 +1,77 @@
> +#!/bin/sh
> +
> +test_description="config file in multi worktree"
> +
> +. ./test-lib.sh
> +
> +cmp_config() {
> +       if [ "$1" = "-C" ]; then
> +               shift &&
> +               GD="-C $1" &&
> +               shift
> +       else
> +               GD=
> +       fi &&
> +       echo "$1" >expected &&
> +       shift &&
> +       git $GD config "$@" >actual &&
> +       test_cmp expected actual
> +}
> +
> +test_expect_success 'setup' '
> +       test_commit start &&
> +       git config --worktree per.worktree is-ok &&
> +       git worktree add wt1 &&
> +       git worktree add wt2 &&
> +       test_must_fail git config --worktree per.worktree is-not-ok &&
> +       git config extensions.worktreeConfig true
> +'
> +
> +test_expect_success 'config is shared as before' '
> +       git config this.is shared &&
> +       cmp_config shared this.is &&
> +       cmp_config -C wt1 shared this.is &&
> +       cmp_config -C wt2 shared this.is
> +'
> +
> +test_expect_success 'config is shared (set from another worktree)' '
> +       git -C wt1 config that.is also-shared &&
> +       cmp_config also-shared that.is &&
> +       cmp_config -C wt1 also-shared that.is &&
> +       cmp_config -C wt2 also-shared that.is
> +'
> +
> +test_expect_success 'config private to main worktree' '
> +       git config --worktree this.is for-main &&
> +       cmp_config for-main this.is &&
> +       cmp_config -C wt1 shared this.is &&
> +       cmp_config -C wt2 shared this.is
> +'
> +
> +test_expect_success 'config private to linked worktree' '
> +       git -C wt1 config --worktree this.is for-wt1 &&
> +       cmp_config for-main this.is &&
> +       cmp_config -C wt1 for-wt1 this.is &&
> +       cmp_config -C wt2 shared this.is
> +'
> +
> +test_expect_success 'core.bare no longer for main only' '
> +       git config core.bare true &&
> +       cmp_config true core.bare &&
> +       cmp_config -C wt1 true core.bare &&
> +       cmp_config -C wt2 true core.bare &&
> +       git config --unset core.bare
> +'
> +
> +test_expect_success 'config.worktree no longer read without extension' '
> +       git config --unset extensions.worktreeConfig &&
> +       cmp_config shared this.is &&
> +       cmp_config -C wt1 shared this.is &&
> +       cmp_config -C wt2 shared this.is
> +'
> +
> +test_expect_success 'config --worktree fails in multi worktree without extension' '
> +       test_must_fail git config --worktree foo.bar true
> +'
> +
> +test_done
> --
> 2.9.1.566.gbd532d4
>
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]