Commit d54ac2cb authored by Duy Nguyen's avatar Duy Nguyen

Merge remote-tracking branch 'pclouds/submodules-in-worktrees' into HEAD

parents de2b49a8 c0c25680
......@@ -20,6 +20,8 @@ SYNOPSIS
'git config' [<file-option>] --unset-all name [value_regex]
'git config' [<file-option>] --rename-section old_name new_name
'git config' [<file-option>] --remove-section name
'git config' [<file-option>] --move-to name
'git config' [<file-option>] --move-to-regexp name-regexp
'git config' [<file-option>] [--show-origin] [-z|--null] [--name-only] -l | --list
'git config' [<file-option>] --get-color name [default]
'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
......@@ -161,6 +163,13 @@ See also <<FILES>>.
--unset-all::
Remove all lines matching the key from config file.
--move-to::
--move-to-regexp::
Move a config variable (or multiple variables matching the
given regular expression) to a new file. Any option about the
config file location after `--move-to` or `--move-to-regexp`
specifies the move destination.
-l::
--list::
List all variables set in config file, along with their values.
......
......@@ -252,17 +252,29 @@ rev-parse --git-path config.worktree`. You can add or update
configuration in this file with `git config --worktree`. Older Git
versions 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 in $GIT_DIR/config before, you must move
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:
Note that in this file, the exception for `core.bare` and
`core.worktree` is gone. If you have them in $GIT_DIR/config before,
you must move them to the `config.worktree` of the main working tree.
- `core.worktree` and `core.bare` should never be shared
------------
$ git config --local --move-to --worktree core.bare
$ git config --local --move-to --worktree core.worktree
------------
You may also take this opportunity to review and move other
configuration that you do not want to share to all working trees:
- `core.sparseCheckout` is recommended per working tree, unless you
are sure you always use sparse checkout for all working trees.
- Most configuration variables under `submodule` group in superproject
should not be shared.
+
------------
$ git config --local --move-to --worktree submodule.active
$ git config --local --move-to-regexp --worktree 'submodule\..*\..*'
------------
DETAILS
-------
Each linked working tree has a private sub-directory in the repository's
......
......@@ -222,6 +222,23 @@ submodule active pathspec, which specifies that any submodule
starting with 'b' except 'baz' are also active, regardless of the
presence of the .url field.
MULTIPLE WORKING TREE SUPPORT
-----------------------------
When you have more than one working tree, created by
linkgit:git-worktree[1], submodules will not work on any working tree
until `extensions.worktreeConfig` is enabled. Since this config
affects more than just submodules, please see "CONFIGURATION FILE"
section for more information before turning it on.
Once on, submodules can be added in any working tree. The submodule
itself though cannot have more than one working tree.
When submodules are created in a working tree, their git directory is
also per-worktree, e.g. inside
'$GIT_COMMON_DIR/worktrees/<worktree>/modules' and not shared with
other working trees. This means if you have the same submodule on
different working trees, you need disk space for multiple clones.
Workflow for a third party library
----------------------------------
......
......@@ -313,14 +313,14 @@ void bkl_prune_all_or_die(struct repository *r, timestamp_t expire)
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
if (bkl_prune(r, worktree_git_path(wt, "index.bkl"), expire)) {
if (bkl_prune(r, worktree_git_path(r, wt, "index.bkl"), expire)) {
if (wt->id)
die(_("failed to prune %s on working tree '%s'"),
"index.bkl", wt->id);
else
die(_("failed to prune %s"), "index.bkl");
}
if (bkl_prune(r, worktree_git_path(wt, "worktree.bkl"), expire)) {
if (bkl_prune(r, worktree_git_path(r, wt, "worktree.bkl"), expire)) {
if (wt->id)
die(_("failed to prune %s on working tree '%s'"),
"worktree.bkl", wt->id);
......@@ -380,11 +380,11 @@ void add_backup_logs_to_pending(struct rev_info *revs, unsigned flags)
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
path = xstrdup(worktree_git_path(wt, "index.bkl"));
path = xstrdup(worktree_git_path(revs->repo, wt, "index.bkl"));
add_backup_log_to_pending(path, &cb);
free(path);
path = xstrdup(worktree_git_path(wt, "worktree.bkl"));
path = xstrdup(worktree_git_path(revs->repo, wt, "worktree.bkl"));
add_backup_log_to_pending(path, &cb);
free(path);
}
......
......@@ -380,7 +380,7 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref,
if (strcmp(oldref, worktrees[i]->head_ref))
continue;
refs = get_worktree_ref_store(worktrees[i]);
refs = get_worktree_ref_store(the_repository, worktrees[i]);
if (refs_create_symref(refs, "HEAD", newref, logmsg))
ret = error(_("HEAD of working tree %s is not updated"),
worktrees[i]->path);
......
......@@ -460,7 +460,8 @@ static void print_current_branch_name(void)
static void reject_rebase_or_bisect_branch(const char *target)
{
struct worktree **worktrees = get_worktrees(0);
struct repository *r = the_repository;
struct worktree **worktrees = repo_get_worktrees(r, 0);
int i;
for (i = 0; worktrees[i]; i++) {
......@@ -469,11 +470,12 @@ static void reject_rebase_or_bisect_branch(const char *target)
if (!wt->is_detached)
continue;
if (is_worktree_being_rebased(wt, target))
if (is_worktree_being_rebased(r, wt, target))
die(_("Branch %s is being rebased at %s"),
target, wt->path);
if (is_worktree_being_bisected(wt, target))
if (is_worktree_being_bisected(r, wt, target))
die(_("Branch %s is being bisected at %s"),
target, wt->path);
}
......
......@@ -27,6 +27,7 @@ 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 move_source;
static struct git_config_source given_config_source;
static int actions, type;
static char *default_value;
......@@ -51,6 +52,8 @@ static int show_origin;
#define ACTION_GET_COLOR (1<<13)
#define ACTION_GET_COLORBOOL (1<<14)
#define ACTION_GET_URLMATCH (1<<15)
#define ACTION_MOVE (1<<16)
#define ACTION_MOVE_REGEXP (1<<17)
/*
* The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
......@@ -72,6 +75,64 @@ static int show_origin;
static NORETURN void usage_builtin_config(void);
static void set_config_source_file(void)
{
int nongit = !startup_info->have_repository;
if (use_global_config + use_system_config + use_local_config +
use_worktree_config +
!!given_config_source.file + !!given_config_source.blob > 1)
die(_("only one config file at a time"));
if (use_local_config && nongit)
die(_("--local can only be used inside a git repository"));
if (given_config_source.blob && nongit)
die(_("--blob can only be used inside a git repository"));
if (given_config_source.file &&
!strcmp(given_config_source.file, "-")) {
given_config_source.file = NULL;
given_config_source.use_stdin = 1;
}
if (use_global_config) {
char *user_config = expand_user_path("~/.gitconfig", 0);
char *xdg_config = xdg_config_home("config");
if (!user_config)
/*
* It is unknown if HOME/.gitconfig exists, so
* we do not know if we should write to XDG
* location; error out even if XDG_CONFIG_HOME
* is set and points at a sane location.
*/
die(_("$HOME not set"));
if (access_or_warn(user_config, R_OK, 0) &&
xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
given_config_source.file = xdg_config;
free(user_config);
} else {
given_config_source.file = user_config;
free(xdg_config);
}
}
else if (use_system_config)
given_config_source.file = git_etc_gitconfig();
else if (use_local_config)
given_config_source.file = git_pathdup("config");
else if (use_worktree_config) {
given_config_source.file = get_worktree_config(the_repository);
if (!given_config_source.file)
die(_("--worktree cannot be used with multiple "
"working trees unless the config\n"
"extension worktreeConfig is enabled. "
"Please read \"CONFIGURATION FILE\"\n"
"section in \"git help worktree\" for details"));
}
}
static int option_parse_type(const struct option *opt, const char *arg,
int unset)
{
......@@ -121,13 +182,32 @@ static int option_parse_type(const struct option *opt, const char *arg,
return 0;
}
static int option_move_cb(const struct option *opt,
const char *arg, int unset)
{
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
set_config_source_file();
memcpy(&move_source, &given_config_source, sizeof(move_source));
memset(&given_config_source, 0, sizeof(given_config_source));
use_global_config = 0;
use_system_config = 0;
use_local_config = 0;
use_worktree_config = 0;
actions = opt->defval;
return 0;
}
static struct option builtin_config_options[] = {
OPT_GROUP(N_("Config file location")),
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_FILENAME('f', "file", &given_config_source.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")),
OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
......@@ -140,6 +220,14 @@ static struct option builtin_config_options[] = {
OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL),
OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
{ OPTION_CALLBACK, 0, "move-to", NULL, NULL,
N_("move a variable to a different config file"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG,
option_move_cb, ACTION_MOVE },
{ OPTION_CALLBACK, 0, "move-to-regexp", NULL, NULL,
N_("move matching variables to a different config file"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG,
option_move_cb, ACTION_MOVE_REGEXP },
OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
......@@ -370,6 +458,72 @@ static int get_value(const char *key_, const char *regex_)
return ret;
}
struct move_config_cb {
struct string_list keys;
int is_regexp;
const char *key;
regex_t key_re;
};
static int collect_move_config(const char *key, const char *value, void *cb)
{
struct move_config_cb *data = cb;
if (!data->is_regexp && strcasecmp(data->key, key))
return 0;
if (data->is_regexp && regexec(&data->key_re, key, 0, NULL, 0))
return 0;
string_list_append(&data->keys, key)->util = xstrdup(value);
return 0;
}
static int move_config(const char *key, int is_regexp)
{
struct move_config_cb cb;
int i, ret = 0;
config_options.respect_includes = 0;
if (!move_source.file && !move_source.use_stdin && !move_source.blob)
die(_("unknown config source"));
string_list_init(&cb.keys, 1);
cb.key = key;
cb.is_regexp = is_regexp;
if (is_regexp && regcomp(&cb.key_re, key, REG_EXTENDED | REG_ICASE))
die(_("invalid key pattern: %s"), key);
config_with_options(collect_move_config, &cb,
&move_source, &config_options);
for (i = 0; i < cb.keys.nr && !ret; i++) {
const char *key = cb.keys.items[i].string;
const char *value = cb.keys.items[i].util;
const char *dest = given_config_source.file;
ret = git_config_set_multivar_in_file_gently(
dest, key, value, CONFIG_REGEX_NONE, 0);
}
/*
* OK all keys have been copied successfully, time to delete
* old ones
*/
if (!ret && move_source.file) {
for (i = 0; i < cb.keys.nr; i++) {
const char *key = cb.keys.items[i].string;
const char *src = move_source.file;
git_config_set_in_file_gently(src, key, NULL);
}
}
string_list_clear(&cb.keys, 1);
if (is_regexp)
regfree(&cb.key_re);
return ret;
}
static char *normalize_value(const char *key, const char *value)
{
if (!value)
......@@ -615,69 +769,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
builtin_config_usage,
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_builtin_config();
}
if (use_local_config && nongit)
die(_("--local can only be used inside a git repository"));
if (given_config_source.blob && nongit)
die(_("--blob can only be used inside a git repository"));
if (given_config_source.file &&
!strcmp(given_config_source.file, "-")) {
given_config_source.file = NULL;
given_config_source.use_stdin = 1;
}
if (use_global_config) {
char *user_config = expand_user_path("~/.gitconfig", 0);
char *xdg_config = xdg_config_home("config");
if (!user_config)
/*
* It is unknown if HOME/.gitconfig exists, so
* we do not know if we should write to XDG
* location; error out even if XDG_CONFIG_HOME
* is set and points at a sane location.
*/
die(_("$HOME not set"));
if (access_or_warn(user_config, R_OK, 0) &&
xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
given_config_source.file = xdg_config;
free(user_config);
} else {
given_config_source.file = user_config;
free(xdg_config);
}
}
else if (use_system_config)
given_config_source.file = git_etc_gitconfig();
else if (use_local_config)
given_config_source.file = git_pathdup("config");
else if (use_worktree_config) {
struct worktree **worktrees = get_worktrees(0);
if (repository_format_worktree_config)
given_config_source.file = git_pathdup("config.worktree");
else if (worktrees[0] && worktrees[1])
die(_("--worktree cannot be used with multiple "
"working trees unless the config\n"
"extension worktreeConfig is enabled. "
"Please read \"CONFIGURATION FILE\"\n"
"section in \"git help worktree\" for details"));
else
given_config_source.file = git_pathdup("config");
free_worktrees(worktrees);
} else if (given_config_source.file) {
if (!is_absolute_path(given_config_source.file) && prefix)
given_config_source.file =
prefix_filename(prefix, given_config_source.file);
}
set_config_source_file();
if (respect_includes_opt == -1)
config_options.respect_includes = !given_config_source.file;
......@@ -892,6 +984,16 @@ int cmd_config(int argc, const char **argv, const char *prefix)
color_stdout_is_tty = git_config_bool("command line", argv[1]);
return get_colorbool(argv[0], argc == 2);
}
else if (actions == ACTION_MOVE) {
check_write();
check_argc(argc, 1, 1);
return move_config(argv[0], 0);
}
else if (actions == ACTION_MOVE_REGEXP) {
check_write();
check_argc(argc, 1, 1);
return move_config(argv[0], 1);
}
return 0;
}
......@@ -598,7 +598,7 @@ static void get_default_heads(void)
strbuf_release(&ref);
if (include_reflogs)
refs_for_each_reflog(get_worktree_ref_store(wt),
refs_for_each_reflog(get_worktree_ref_store(the_repository, wt),
fsck_handle_reflog, wt);
}
free_worktrees(worktrees);
......
......@@ -619,7 +619,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
if (!all_worktrees && !(*p)->is_current)
continue;
collected.wt = *p;
refs_for_each_reflog(get_worktree_ref_store(*p),
refs_for_each_reflog(get_worktree_ref_store(the_repository, *p),
collect_reflog, &collected);
}
free_worktrees(worktrees);
......
......@@ -19,6 +19,7 @@
#include "diffcore.h"
#include "diff.h"
#include "object-store.h"
#include "worktree.h"
#define OPT_QUIET (1 << 0)
#define OPT_CACHED (1 << 1)
......@@ -28,6 +29,18 @@
typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
void *cb_data);
static void check_multi_worktree_support(void)
{
char *worktree_config = get_worktree_config(the_repository);
if (!worktree_config)
die(_("submodules cannot be used with multiple "
"working trees unless the config\n"
"extension worktreeConfig is enabled. "
"Please read \"CONFIGURATION FILE\"\n"
"section in \"git help worktree\" for details"));
free(worktree_config);
}
static char *get_default_remote(void)
{
char *dest = NULL, *ret;
......@@ -627,15 +640,11 @@ static void init_submodule(const char *path, const char *prefix,
die(_("No url found for submodule path '%s' in .gitmodules"),
displaypath);
/*
* NEEDSWORK: In a multi-working-tree world, this needs to be
* set in the per-worktree config.
*
* Set active flag for the submodule being initialized
*/
check_multi_worktree_support();
if (!is_submodule_active(the_repository, path)) {
strbuf_addf(&sb, "submodule.%s.active", sub->name);
git_config_set_gently(sb.buf, "true");
repo_config_set_worktree_gently(the_repository, sb.buf, "true");
strbuf_reset(&sb);
}
......@@ -660,7 +669,7 @@ static void init_submodule(const char *path, const char *prefix,
free(oldurl);
}
if (git_config_set_gently(sb.buf, url))
if (repo_config_set_worktree_gently(the_repository, sb.buf, url))
die(_("Failed to register url for submodule path '%s'"),
displaypath);
if (!(flags & OPT_QUIET))
......@@ -681,7 +690,7 @@ static void init_submodule(const char *path, const char *prefix,
} else
upd = xstrdup(submodule_strategy_to_string(&sub->update_strategy));
if (git_config_set_gently(sb.buf, upd))
if (repo_config_set_worktree_gently(the_repository, sb.buf, upd))
die(_("Failed to register update mode for submodule path '%s'"), displaypath);
}
strbuf_release(&sb);
......@@ -931,6 +940,7 @@ static void sync_submodule(const char *path, const char *prefix,
struct strbuf sb = STRBUF_INIT;
struct child_process cp = CHILD_PROCESS_INIT;
char *sub_config_path = NULL;
struct repository subrepo;
if (!is_submodule_active(the_repository, path))
return;
......@@ -971,7 +981,7 @@ static void sync_submodule(const char *path, const char *prefix,
strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.url", sub->name);
if (git_config_set_gently(sb.buf, super_config_url))
if (repo_config_set_worktree_gently(the_repository, sb.buf, super_config_url))
die(_("failed to register url for submodule path '%s'"),
displaypath);
......@@ -992,14 +1002,15 @@ static void sync_submodule(const char *path, const char *prefix,
strbuf_strip_suffix(&sb, "\n");
remote_key = xstrfmt("remote.%s.url", sb.buf);
strbuf_reset(&sb);
submodule_to_gitdir(&sb, path);
strbuf_addstr(&sb, "/config");
if (repo_submodule_init(&subrepo, the_repository, sub))
die(_("could not get a repository handle for submodule '%s'"), path);
if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url))
if (repo_config_set_worktree_gently(&subrepo, remote_key, sub_origin_url))
die(_("failed to update remote for submodule '%s'"),
path);
repo_clear(&subrepo);
if (flags & OPT_RECURSIVE) {
struct child_process cpr = CHILD_PROCESS_INIT;
......@@ -1087,6 +1098,8 @@ static void deinit_submodule(const char *path, const char *prefix,
struct strbuf sb_config = STRBUF_INIT;
char *sub_git_dir = xstrfmt("%s/.git", path);
check_multi_worktree_support();
sub = submodule_from_path(the_repository, &null_oid, path);
if (!sub || !sub->name)
......@@ -1142,21 +1155,23 @@ static void deinit_submodule(const char *path, const char *prefix,
displaypath);
cp_config.git_cmd = 1;
argv_array_pushl(&cp_config.args, "config", "--get-regexp", NULL);
argv_array_pushl(&cp_config.args, "config", "--worktree", "--get-regexp", NULL);
argv_array_pushf(&cp_config.args, "submodule.%s\\.", sub->name);
/* remove the .git/config entries (unless the user already did it) */
if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) {
char *config_file = get_worktree_config(the_repository);
char *sub_key = xstrfmt("submodule.%s", sub->name);
/*
* remove the whole section so we have a clean state when
* the user later decides to init this submodule again
*/
git_config_rename_section_in_file(NULL, sub_key, NULL);
git_config_rename_section_in_file(config_file, sub_key, NULL);
if (!(flags & OPT_QUIET))
printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"),
sub->name, sub->url, displaypath);
free(sub_key);
free(config_file);
}
cleanup:
......@@ -1355,11 +1370,11 @@ static int module_clone(int argc, const char **argv, const char *prefix)
const char *name = NULL, *url = NULL, *depth = NULL;
int quiet = 0;
int progress = 0;
char *p, *path = NULL, *sm_gitdir;
char *path = NULL, *sm_gitdir;
struct strbuf sb = STRBUF_INIT;
struct string_list reference = STRING_LIST_INIT_NODUP;
int dissociate = 0;
char *sm_alternate = NULL, *error_strategy = NULL;
struct repository subrepo;
struct option module_clone_options[] = {
OPT_STRING(0, "prefix", &prefix,
......@@ -1432,27 +1447,18 @@ static int module_clone(int argc, const char **argv, const char *prefix)
connect_work_tree_and_git_dir(path, sm_gitdir, 0);
p = git_pathdup_submodule(path, "config");
if (!p)
die(_("could not get submodule directory for '%s'"), path);
if (repo_submodule_init_early(&subrepo, the_repository, path, name))
die(_("could not get a repository handle for submodule '%s'"), path);
/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
git_config_get_string("submodule.alternateLocation", &sm_alternate);
if (sm_alternate)
git_config_set_in_file(p, "submodule.alternateLocation",
sm_alternate);
git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
if (error_strategy)
git_config_set_in_file(p, "submodule.alternateErrorStrategy",
error_strategy);
free(sm_alternate);
free(error_strategy);
repo_config_copy(&subrepo, the_repository, "submodule.alternateLocation");
repo_config_copy(&subrepo, the_repository, "submodule.alternateErrorStrategy");
repo_config_copy(&subrepo, the_repository, "extensions.worktreeConfig");
repo_clear(&subrepo);
strbuf_release(&sb);
free(sm_gitdir);
free(path);
free(p);
return 0;
}
......@@ -2060,18 +2066,16 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
die(_("could not get a repository handle for submodule '%s'"), path);
if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) {
char *cfg_file, *abs_path;
char *abs_path;
const char *rel_path;
struct strbuf sb = STRBUF_INIT;
cfg_file = repo_git_path(&subrepo, "config");
abs_path = absolute_pathdup(path);
rel_path = relative_path(abs_path, subrepo.gitdir, &sb);
git_config_set_in_file(cfg_file, "core.worktree", rel_path);
if (repo_config_set_worktree_gently(&subrepo, "core.worktree", rel_path))
die(_("could not set '%s' to '%s'"), "core.worktree", rel_path);
free(cfg_file);
free(abs_path);
strbuf_release(&sb);
}
......@@ -2148,7 +2152,8 @@ static int module_config(int argc, const char **argv, const char *prefix)
{
enum {
CHECK_WRITEABLE = 1,
DO_UNSET = 2
CHECK_WORKTREE_SETUP,
DO_UNSET
} command = 0;
struct option module_config_options[] = {
......@@ -2158,6 +2163,9 @@ static int module_config(int argc, const char **argv, const char *prefix)
OPT_CMDMODE(0, "unset", &command,
N_("unset the config in the .gitmodules file"),
DO_UNSET),
OPT_CMDMODE(0, "check-worktree-setup", &command,
N_("check if the current working tree setup is supported"),
CHECK_WORKTREE_SETUP),
OPT_END()
};
const char *const git_submodule_helper_usage[] = {
......@@ -2170,8 +2178,14 @@ static int module_config(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, module_config_options,
git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0);
if (argc == 1 && command == CHECK_WRITEABLE)
if (argc == 1 && command == CHECK_WRITEABLE) {
return is_writing_gitmodules_ok() ? 0 : -1;
}
if (argc == 1 && command == CHECK_WORKTREE_SETUP) {
check_multi_worktree_support();
return 0;
}
/* Equivalent to ACTION_GET in builtin/config.c */
if (argc == 2 && command != DO_UNSET)
......
......@@ -730,16 +730,17 @@ static void validate_no_submodules(const struct worktree *wt)
struct index_state istate = { NULL };
struct strbuf path = STRBUF_INIT;
int i, found_submodules = 0;
struct repository *r = the_repository;
if (is_directory(worktree_git_path(wt, "modules"))) {
if (is_directory(worktree_git_path(r, wt, "modules"))) {
/*
* There could be false positives, e.g. the "modules"
* directory exists but is empty. But it's a rare case and
* this simpler check is probably good enough for now.
*/
found_submodules = 1;
} else if (read_index_from(&istate, worktree_git_path(wt, "index"),
get_worktree_git_dir(wt)) > 0) {
} else if (read_index_from(&istate, worktree_git_path(r, wt, "index"),
get_worktree_git_dir(r, wt)) > 0) {
for (i = 0; i < istate.cache_nr; i++) {
struct cache_entry *ce = istate.cache[i];
int err;
......
......@@ -19,6 +19,7 @@
#include "utf8.h"
#include "dir.h"
#include "color.h"
#include "worktree.h"
struct config_source {
struct config_source *prev;
......@@ -1670,6 +1671,7 @@ static int do_git_config_sequence(const struct config_options *opts,