Skip to content

Commit 6249ce2

Browse files
committed
Merge branch 'ds/sparse-checkout-requires-per-worktree-config'
"git sparse-checkout" wants to work with per-worktree configuration, 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
2 parents 268e6b8 + 3ce1138 commit 6249ce2

13 files changed

+353
-49
lines changed

Documentation/config/extensions.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,34 @@ extensions.objectFormat::
66
Note that this setting should only be set by linkgit:git-init[1] or
77
linkgit:git-clone[1]. Trying to change it after initialization will not
88
work and will produce hard-to-diagnose issues.
9+
10+
extensions.worktreeConfig::
11+
If enabled, then worktrees will load config settings from the
12+
`$GIT_DIR/config.worktree` file in addition to the
13+
`$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and
14+
`$GIT_DIR` are the same for the main working tree, while other
15+
working trees have `$GIT_DIR` equal to
16+
`$GIT_COMMON_DIR/worktrees/<id>/`. The settings in the
17+
`config.worktree` file will override settings from any other
18+
config files.
19+
+
20+
When enabling `extensions.worktreeConfig`, you must be careful to move
21+
certain values from the common config file to the main working tree's
22+
`config.worktree` file, if present:
23+
+
24+
* `core.worktree` must be moved from `$GIT_COMMON_DIR/config` to
25+
`$GIT_COMMON_DIR/config.worktree`.
26+
* If `core.bare` is true, then it must be moved from `$GIT_COMMON_DIR/config`
27+
to `$GIT_COMMON_DIR/config.worktree`.
28+
+
29+
It may also be beneficial to adjust the locations of `core.sparseCheckout`
30+
and `core.sparseCheckoutCone` depending on your desire for customizable
31+
sparse-checkout settings for each worktree. By default, the `git
32+
sparse-checkout` builtin enables `extensions.worktreeConfig`, assigns
33+
these config values on a per-worktree basis, and uses the
34+
`$GIT_DIR/info/sparse-checkout` file to specify the sparsity for each
35+
worktree independently. See linkgit:git-sparse-checkout[1] for more
36+
details.
37+
+
38+
For historical reasons, `extensions.worktreeConfig` is respected
39+
regardless of the `core.repositoryFormatVersion` setting.

Documentation/git-config.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,13 @@ from all available files.
141141
See also <<FILES>>.
142142

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

148152
-f <config-file>::
149153
--file <config-file>::

Documentation/git-sparse-checkout.txt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,21 @@ COMMANDS
3131
Describe the patterns in the sparse-checkout file.
3232

3333
'set'::
34-
Enable the necessary config settings
35-
(extensions.worktreeConfig, core.sparseCheckout,
36-
core.sparseCheckoutCone) if they are not already enabled, and
37-
write a set of patterns to the sparse-checkout file from the
34+
Enable the necessary sparse-checkout config settings
35+
(`core.sparseCheckout`, `core.sparseCheckoutCone`, and
36+
`index.sparse`) if they are not already set to the desired values,
37+
and write a set of patterns to the sparse-checkout file from the
3838
list of arguments following the 'set' subcommand. Update the
3939
working directory to match the new patterns.
4040
+
41+
To ensure that adjusting the sparse-checkout settings within a worktree
42+
does not alter the sparse-checkout settings in other worktrees, the 'set'
43+
subcommand will upgrade your repository config to use worktree-specific
44+
config if not already present. The sparsity defined by the arguments to
45+
the 'set' subcommand are stored in the worktree-specific sparse-checkout
46+
file. See linkgit:git-worktree[1] and the documentation of
47+
`extensions.worktreeConfig` in linkgit:git-config[1] for more details.
48+
+
4149
When the `--stdin` option is provided, the patterns are read from
4250
standard in as a newline-delimited list instead of from the arguments.
4351
+

Documentation/git-worktree.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,8 @@ CONFIGURATION FILE
286286
------------------
287287
By default, the repository `config` file is shared across all working
288288
trees. If the config variables `core.bare` or `core.worktree` are
289-
already present in the config file, they will be applied to the main
290-
working trees only.
289+
present in the common config file and `extensions.worktreeConfig` is
290+
disabled, then they will be applied to the main working tree only.
291291

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

310-
- `core.worktree` and `core.bare` should never be shared
310+
- `core.worktree` should never be shared.
311+
312+
- `core.bare` should not be shared if the value is `core.bare=true`.
311313

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

317+
See the documentation of `extensions.worktreeConfig` in
318+
linkgit:git-config[1] for more details.
319+
315320
DETAILS
316321
-------
317322
Each linked working tree has a private sub-directory in the repository's

builtin/sparse-checkout.c

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "wt-status.h"
1616
#include "quote.h"
1717
#include "sparse-index.h"
18+
#include "worktree.h"
1819

1920
static const char *empty_base = "";
2021

@@ -361,26 +362,23 @@ enum sparse_checkout_mode {
361362

362363
static int set_config(enum sparse_checkout_mode mode)
363364
{
364-
const char *config_path;
365-
366-
if (upgrade_repository_format(1) < 0)
367-
die(_("unable to upgrade repository format to enable worktreeConfig"));
368-
if (git_config_set_gently("extensions.worktreeConfig", "true")) {
369-
error(_("failed to set extensions.worktreeConfig setting"));
365+
/* Update to use worktree config, if not already. */
366+
if (init_worktree_config(the_repository)) {
367+
error(_("failed to initialize worktree config"));
370368
return 1;
371369
}
372370

373-
config_path = git_path("config.worktree");
374-
git_config_set_in_file_gently(config_path,
375-
"core.sparseCheckout",
376-
mode ? "true" : NULL);
377-
378-
git_config_set_in_file_gently(config_path,
379-
"core.sparseCheckoutCone",
380-
mode == MODE_CONE_PATTERNS ? "true" : NULL);
371+
if (repo_config_set_worktree_gently(the_repository,
372+
"core.sparseCheckout",
373+
mode ? "true" : "false") ||
374+
repo_config_set_worktree_gently(the_repository,
375+
"core.sparseCheckoutCone",
376+
mode == MODE_CONE_PATTERNS ?
377+
"true" : "false"))
378+
return 1;
381379

382380
if (mode == MODE_NO_PATTERNS)
383-
set_sparse_index_config(the_repository, 0);
381+
return set_sparse_index_config(the_repository, 0);
384382

385383
return 0;
386384
}

builtin/worktree.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,69 @@ static int add_worktree(const char *path, const char *refname,
335335
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
336336
write_file(sb.buf, "../..");
337337

338+
/*
339+
* If the current worktree has sparse-checkout enabled, then copy
340+
* the sparse-checkout patterns from the current worktree.
341+
*/
342+
if (core_apply_sparse_checkout) {
343+
char *from_file = git_pathdup("info/sparse-checkout");
344+
char *to_file = xstrfmt("%s/info/sparse-checkout",
345+
sb_repo.buf);
346+
347+
if (file_exists(from_file)) {
348+
if (safe_create_leading_directories(to_file) ||
349+
copy_file(to_file, from_file, 0666))
350+
error(_("failed to copy '%s' to '%s'; sparse-checkout may not work correctly"),
351+
from_file, to_file);
352+
}
353+
354+
free(from_file);
355+
free(to_file);
356+
}
357+
358+
/*
359+
* If we are using worktree config, then copy all current config
360+
* values from the current worktree into the new one, that way the
361+
* new worktree behaves the same as this one.
362+
*/
363+
if (repository_format_worktree_config) {
364+
char *from_file = git_pathdup("config.worktree");
365+
char *to_file = xstrfmt("%s/config.worktree",
366+
sb_repo.buf);
367+
368+
if (file_exists(from_file)) {
369+
struct config_set cs = { { 0 } };
370+
const char *core_worktree;
371+
int bare;
372+
373+
if (safe_create_leading_directories(to_file) ||
374+
copy_file(to_file, from_file, 0666)) {
375+
error(_("failed to copy worktree config from '%s' to '%s'"),
376+
from_file, to_file);
377+
goto worktree_copy_cleanup;
378+
}
379+
380+
git_configset_init(&cs);
381+
git_configset_add_file(&cs, from_file);
382+
383+
if (!git_configset_get_bool(&cs, "core.bare", &bare) &&
384+
bare &&
385+
git_config_set_multivar_in_file_gently(
386+
to_file, "core.bare", NULL, "true", 0))
387+
error(_("failed to unset 'core.bare' in '%s'"), to_file);
388+
if (!git_configset_get_value(&cs, "core.worktree", &core_worktree) &&
389+
git_config_set_in_file_gently(to_file,
390+
"core.worktree", NULL))
391+
error(_("failed to unset 'core.worktree' in '%s'"), to_file);
392+
393+
git_configset_clear(&cs);
394+
}
395+
396+
worktree_copy_cleanup:
397+
free(from_file);
398+
free(to_file);
399+
}
400+
338401
strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
339402
strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
340403
cp.git_cmd = 1;

config.c

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "dir.h"
2222
#include "color.h"
2323
#include "refs.h"
24+
#include "worktree.h"
2425

2526
struct config_source {
2627
struct config_source *prev;
@@ -2294,8 +2295,8 @@ int git_configset_get_string(struct config_set *cs, const char *key, char **dest
22942295
return 1;
22952296
}
22962297

2297-
int git_configset_get_string_tmp(struct config_set *cs, const char *key,
2298-
const char **dest)
2298+
static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
2299+
const char **dest)
22992300
{
23002301
const char *value;
23012302
if (!git_configset_get_value(cs, key, &value)) {
@@ -3000,6 +3001,20 @@ int git_config_set_gently(const char *key, const char *value)
30003001
return git_config_set_multivar_gently(key, value, NULL, 0);
30013002
}
30023003

3004+
int repo_config_set_worktree_gently(struct repository *r,
3005+
const char *key, const char *value)
3006+
{
3007+
/* Only use worktree-specific config if it is is already enabled. */
3008+
if (repository_format_worktree_config) {
3009+
char *file = repo_git_path(r, "config.worktree");
3010+
int ret = git_config_set_multivar_in_file_gently(
3011+
file, key, value, NULL, 0);
3012+
free(file);
3013+
return ret;
3014+
}
3015+
return repo_config_set_multivar_gently(r, key, value, NULL, 0);
3016+
}
3017+
30033018
void git_config_set(const char *key, const char *value)
30043019
{
30053020
git_config_set_multivar(key, value, NULL, 0);
@@ -3297,14 +3312,28 @@ void git_config_set_multivar_in_file(const char *config_filename,
32973312
int git_config_set_multivar_gently(const char *key, const char *value,
32983313
const char *value_pattern, unsigned flags)
32993314
{
3300-
return git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,
3301-
flags);
3315+
return repo_config_set_multivar_gently(the_repository, key, value,
3316+
value_pattern, flags);
3317+
}
3318+
3319+
int repo_config_set_multivar_gently(struct repository *r, const char *key,
3320+
const char *value,
3321+
const char *value_pattern, unsigned flags)
3322+
{
3323+
char *file = repo_git_path(r, "config");
3324+
int res = git_config_set_multivar_in_file_gently(file,
3325+
key, value,
3326+
value_pattern,
3327+
flags);
3328+
free(file);
3329+
return res;
33023330
}
33033331

33043332
void git_config_set_multivar(const char *key, const char *value,
33053333
const char *value_pattern, unsigned flags)
33063334
{
3307-
git_config_set_multivar_in_file(NULL, key, value, value_pattern,
3335+
git_config_set_multivar_in_file(git_path("config"),
3336+
key, value, value_pattern,
33083337
flags);
33093338
}
33103339

config.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,13 @@ void git_config_set_in_file(const char *, const char *, const char *);
266266

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

269+
/**
270+
* Write a config value that should apply to the current worktree. If
271+
* extensions.worktreeConfig is enabled, then the write will happen in the
272+
* current worktree's config. Otherwise, write to the common config file.
273+
*/
274+
int repo_config_set_worktree_gently(struct repository *, const char *, const char *);
275+
269276
/**
270277
* write config values to `.git/config`, takes a key/value pair as parameter.
271278
*/
@@ -294,6 +301,7 @@ int git_config_parse_key(const char *, char **, size_t *);
294301

295302
int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);
296303
void git_config_set_multivar(const char *, const char *, const char *, unsigned);
304+
int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
297305
int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);
298306

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

468476
int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
469-
int git_configset_get_string_tmp(struct config_set *cs, const char *key, const char **dest);
470477
int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
471478
int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
472479
int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);

sparse-index.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,9 @@ static int convert_to_sparse_rec(struct index_state *istate,
9999

100100
int set_sparse_index_config(struct repository *repo, int enable)
101101
{
102-
int res;
103-
char *config_path = repo_git_path(repo, "config.worktree");
104-
res = git_config_set_in_file_gently(config_path,
105-
"index.sparse",
106-
enable ? "true" : NULL);
107-
free(config_path);
108-
102+
int res = repo_config_set_worktree_gently(repo,
103+
"index.sparse",
104+
enable ? "true" : "false");
109105
prepare_repo_settings(repo);
110106
repo->settings.sparse_index = enable;
111107
return res;

0 commit comments

Comments
 (0)