Skip to content

Commit

Permalink
Merge branch 'ds/sparse-checkout-requires-per-worktree-config' into jch
Browse files Browse the repository at this point in the history
"git sparse-checkout" wants to work with per-worktree configration,
but did not work well in a worktree attached to a bare repository.

* ds/sparse-checkout-requires-per-worktree-config:
  config: make git_configset_get_string_tmp() private
  worktree: copy sparse-checkout patterns and config on add
  sparse-checkout: set worktree-config correctly
  config: add repo_config_set_worktree_gently()
  worktree: create init_worktree_config()
  Documentation: add extensions.worktreeConfig details
  • Loading branch information
gitster committed Feb 14, 2022
2 parents a09a62c + 3ce1138 commit 69e1a1e
Show file tree
Hide file tree
Showing 13 changed files with 353 additions and 49 deletions.
31 changes: 31 additions & 0 deletions Documentation/config/extensions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,34 @@ extensions.objectFormat::
Note that this setting should only be set by linkgit:git-init[1] or
linkgit:git-clone[1]. Trying to change it after initialization will not
work and will produce hard-to-diagnose issues.

extensions.worktreeConfig::
If enabled, then worktrees will load config settings from the
`$GIT_DIR/config.worktree` file in addition to the
`$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and
`$GIT_DIR` are the same for the main working tree, while other
working trees have `$GIT_DIR` equal to
`$GIT_COMMON_DIR/worktrees/<id>/`. The settings in the
`config.worktree` file will override settings from any other
config files.
+
When enabling `extensions.worktreeConfig`, you must be careful to move
certain values from the common config file to the main working tree's
`config.worktree` file, if present:
+
* `core.worktree` must be moved from `$GIT_COMMON_DIR/config` to
`$GIT_COMMON_DIR/config.worktree`.
* If `core.bare` is true, then it must be moved from `$GIT_COMMON_DIR/config`
to `$GIT_COMMON_DIR/config.worktree`.
+
It may also be beneficial to adjust the locations of `core.sparseCheckout`
and `core.sparseCheckoutCone` depending on your desire for customizable
sparse-checkout settings for each worktree. By default, the `git
sparse-checkout` builtin enables `extensions.worktreeConfig`, assigns
these config values on a per-worktree basis, and uses the
`$GIT_DIR/info/sparse-checkout` file to specify the sparsity for each
worktree independently. See linkgit:git-sparse-checkout[1] for more
details.
+
For historical reasons, `extensions.worktreeConfig` is respected
regardless of the `core.repositoryFormatVersion` setting.
8 changes: 6 additions & 2 deletions Documentation/git-config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,13 @@ from all available files.
See also <<FILES>>.

--worktree::
Similar to `--local` except that `.git/config.worktree` is
Similar to `--local` except that `$GIT_DIR/config.worktree` is
read from or written to if `extensions.worktreeConfig` is
present. If not it's the same as `--local`.
enabled. If not it's the same as `--local`. Note that `$GIT_DIR`
is equal to `$GIT_COMMON_DIR` for the main working tree, but is of
the form `$GIT_DIR/worktrees/<id>/` for other working trees. See
linkgit:git-worktree[1] to learn how to enable
`extensions.worktreeConfig`.

-f <config-file>::
--file <config-file>::
Expand Down
16 changes: 12 additions & 4 deletions Documentation/git-sparse-checkout.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,21 @@ COMMANDS
Describe the patterns in the sparse-checkout file.

'set'::
Enable the necessary config settings
(extensions.worktreeConfig, core.sparseCheckout,
core.sparseCheckoutCone) if they are not already enabled, and
write a set of patterns to the sparse-checkout file from the
Enable the necessary sparse-checkout config settings
(`core.sparseCheckout`, `core.sparseCheckoutCone`, and
`index.sparse`) if they are not already set to the desired values,
and write a set of patterns to the sparse-checkout file from the
list of arguments following the 'set' subcommand. Update the
working directory to match the new patterns.
+
To ensure that adjusting the sparse-checkout settings within a worktree
does not alter the sparse-checkout settings in other worktrees, the 'set'
subcommand will upgrade your repository config to use worktree-specific
config if not already present. The sparsity defined by the arguments to
the 'set' subcommand are stored in the worktree-specific sparse-checkout
file. See linkgit:git-worktree[1] and the documentation of
`extensions.worktreeConfig` in linkgit:git-config[1] for more details.
+
When the `--stdin` option is provided, the patterns are read from
standard in as a newline-delimited list instead of from the arguments.
+
Expand Down
11 changes: 8 additions & 3 deletions Documentation/git-worktree.txt
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,8 @@ CONFIGURATION FILE
------------------
By default, the repository `config` file is shared across all working
trees. If the config variables `core.bare` or `core.worktree` are
already present in the config file, they will be applied to the main
working trees only.
present in the common config file and `extensions.worktreeConfig` is
disabled, then they will be applied to the main working tree only.

In order to have configuration specific to working trees, you can turn
on the `worktreeConfig` extension, e.g.:
Expand All @@ -307,11 +307,16 @@ them to the `config.worktree` of the main working tree. You may also
take this opportunity to review and move other configuration that you
do not want to share to all working trees:

- `core.worktree` and `core.bare` should never be shared
- `core.worktree` should never be shared.

- `core.bare` should not be shared if the value is `core.bare=true`.

- `core.sparseCheckout` is recommended per working tree, unless you
are sure you always use sparse checkout for all working trees.

See the documentation of `extensions.worktreeConfig` in
linkgit:git-config[1] for more details.

DETAILS
-------
Each linked working tree has a private sub-directory in the repository's
Expand Down
28 changes: 13 additions & 15 deletions builtin/sparse-checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "wt-status.h"
#include "quote.h"
#include "sparse-index.h"
#include "worktree.h"

static const char *empty_base = "";

Expand Down Expand Up @@ -361,26 +362,23 @@ enum sparse_checkout_mode {

static int set_config(enum sparse_checkout_mode mode)
{
const char *config_path;

if (upgrade_repository_format(1) < 0)
die(_("unable to upgrade repository format to enable worktreeConfig"));
if (git_config_set_gently("extensions.worktreeConfig", "true")) {
error(_("failed to set extensions.worktreeConfig setting"));
/* Update to use worktree config, if not already. */
if (init_worktree_config(the_repository)) {
error(_("failed to initialize worktree config"));
return 1;
}

config_path = git_path("config.worktree");
git_config_set_in_file_gently(config_path,
"core.sparseCheckout",
mode ? "true" : NULL);

git_config_set_in_file_gently(config_path,
"core.sparseCheckoutCone",
mode == MODE_CONE_PATTERNS ? "true" : NULL);
if (repo_config_set_worktree_gently(the_repository,
"core.sparseCheckout",
mode ? "true" : "false") ||
repo_config_set_worktree_gently(the_repository,
"core.sparseCheckoutCone",
mode == MODE_CONE_PATTERNS ?
"true" : "false"))
return 1;

if (mode == MODE_NO_PATTERNS)
set_sparse_index_config(the_repository, 0);
return set_sparse_index_config(the_repository, 0);

return 0;
}
Expand Down
63 changes: 63 additions & 0 deletions builtin/worktree.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,69 @@ static int add_worktree(const char *path, const char *refname,
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");

/*
* If the current worktree has sparse-checkout enabled, then copy
* the sparse-checkout patterns from the current worktree.
*/
if (core_apply_sparse_checkout) {
char *from_file = git_pathdup("info/sparse-checkout");
char *to_file = xstrfmt("%s/info/sparse-checkout",
sb_repo.buf);

if (file_exists(from_file)) {
if (safe_create_leading_directories(to_file) ||
copy_file(to_file, from_file, 0666))
error(_("failed to copy '%s' to '%s'; sparse-checkout may not work correctly"),
from_file, to_file);
}

free(from_file);
free(to_file);
}

/*
* If we are using worktree config, then copy all current config
* values from the current worktree into the new one, that way the
* new worktree behaves the same as this one.
*/
if (repository_format_worktree_config) {
char *from_file = git_pathdup("config.worktree");
char *to_file = xstrfmt("%s/config.worktree",
sb_repo.buf);

if (file_exists(from_file)) {
struct config_set cs = { { 0 } };
const char *core_worktree;
int bare;

if (safe_create_leading_directories(to_file) ||
copy_file(to_file, from_file, 0666)) {
error(_("failed to copy worktree config from '%s' to '%s'"),
from_file, to_file);
goto worktree_copy_cleanup;
}

git_configset_init(&cs);
git_configset_add_file(&cs, from_file);

if (!git_configset_get_bool(&cs, "core.bare", &bare) &&
bare &&
git_config_set_multivar_in_file_gently(
to_file, "core.bare", NULL, "true", 0))
error(_("failed to unset 'core.bare' in '%s'"), to_file);
if (!git_configset_get_value(&cs, "core.worktree", &core_worktree) &&
git_config_set_in_file_gently(to_file,
"core.worktree", NULL))
error(_("failed to unset 'core.worktree' in '%s'"), to_file);

git_configset_clear(&cs);
}

worktree_copy_cleanup:
free(from_file);
free(to_file);
}

strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
cp.git_cmd = 1;
Expand Down
39 changes: 34 additions & 5 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "dir.h"
#include "color.h"
#include "refs.h"
#include "worktree.h"

struct config_source {
struct config_source *prev;
Expand Down Expand Up @@ -2294,8 +2295,8 @@ int git_configset_get_string(struct config_set *cs, const char *key, char **dest
return 1;
}

int git_configset_get_string_tmp(struct config_set *cs, const char *key,
const char **dest)
static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
const char **dest)
{
const char *value;
if (!git_configset_get_value(cs, key, &value)) {
Expand Down Expand Up @@ -3000,6 +3001,20 @@ int git_config_set_gently(const char *key, const char *value)
return git_config_set_multivar_gently(key, value, NULL, 0);
}

int repo_config_set_worktree_gently(struct repository *r,
const char *key, const char *value)
{
/* Only use worktree-specific config if it is is already enabled. */
if (repository_format_worktree_config) {
char *file = repo_git_path(r, "config.worktree");
int ret = git_config_set_multivar_in_file_gently(
file, key, value, NULL, 0);
free(file);
return ret;
}
return repo_config_set_multivar_gently(r, key, value, NULL, 0);
}

void git_config_set(const char *key, const char *value)
{
git_config_set_multivar(key, value, NULL, 0);
Expand Down Expand Up @@ -3297,14 +3312,28 @@ void git_config_set_multivar_in_file(const char *config_filename,
int git_config_set_multivar_gently(const char *key, const char *value,
const char *value_pattern, unsigned flags)
{
return git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,
flags);
return repo_config_set_multivar_gently(the_repository, key, value,
value_pattern, flags);
}

int repo_config_set_multivar_gently(struct repository *r, const char *key,
const char *value,
const char *value_pattern, unsigned flags)
{
char *file = repo_git_path(r, "config");
int res = git_config_set_multivar_in_file_gently(file,
key, value,
value_pattern,
flags);
free(file);
return res;
}

void git_config_set_multivar(const char *key, const char *value,
const char *value_pattern, unsigned flags)
{
git_config_set_multivar_in_file(NULL, key, value, value_pattern,
git_config_set_multivar_in_file(git_path("config"),
key, value, value_pattern,
flags);
}

Expand Down
9 changes: 8 additions & 1 deletion config.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,13 @@ void git_config_set_in_file(const char *, const char *, const char *);

int git_config_set_gently(const char *, const char *);

/**
* Write a config value that should apply to the current worktree. If
* extensions.worktreeConfig is enabled, then the write will happen in the
* current worktree's config. Otherwise, write to the common config file.
*/
int repo_config_set_worktree_gently(struct repository *, const char *, const char *);

/**
* write config values to `.git/config`, takes a key/value pair as parameter.
*/
Expand Down Expand Up @@ -294,6 +301,7 @@ int git_config_parse_key(const char *, char **, size_t *);

int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);
void git_config_set_multivar(const char *, const char *, const char *, unsigned);
int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);

/**
Expand Down Expand Up @@ -466,7 +474,6 @@ void git_configset_clear(struct config_set *cs);
int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);

int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
int git_configset_get_string_tmp(struct config_set *cs, const char *key, const char **dest);
int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
Expand Down
10 changes: 3 additions & 7 deletions sparse-index.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,9 @@ static int convert_to_sparse_rec(struct index_state *istate,

int set_sparse_index_config(struct repository *repo, int enable)
{
int res;
char *config_path = repo_git_path(repo, "config.worktree");
res = git_config_set_in_file_gently(config_path,
"index.sparse",
enable ? "true" : NULL);
free(config_path);

int res = repo_config_set_worktree_gently(repo,
"index.sparse",
enable ? "true" : "false");
prepare_repo_settings(repo);
repo->settings.sparse_index = enable;
return res;
Expand Down

0 comments on commit 69e1a1e

Please sign in to comment.