Skip to content

Commit

Permalink
Merge branch 'bw/submodule-is-active'
Browse files Browse the repository at this point in the history
"what URL do we want to update this submodule?" and "are we
interested in this submodule?" are split into two distinct
concepts, and then the way used to express the latter got extended,
paving a way to make it easier to manage a project with many
submodules and make it possible to later extend use of multiple
worktrees for a project with submodules.

* bw/submodule-is-active:
  submodule add: respect submodule.active and submodule.<name>.active
  submodule--helper init: set submodule.<name>.active
  clone: teach --recurse-submodules to optionally take a pathspec
  submodule init: initialize active submodules
  submodule: decouple url and submodule interest
  submodule--helper clone: check for configured submodules using helper
  submodule sync: use submodule--helper is-active
  submodule sync: skip work for inactive submodules
  submodule status: use submodule--helper is-active
  submodule--helper: add is-active subcommand
  • Loading branch information
gitster committed Mar 30, 2017
2 parents 7a09a61 + 1b614c0 commit a93dcb0
Show file tree
Hide file tree
Showing 9 changed files with 445 additions and 54 deletions.
15 changes: 13 additions & 2 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3013,8 +3013,9 @@ submodule.<name>.url::
The URL for a submodule. This variable is copied from the .gitmodules
file to the git config via 'git submodule init'. The user can change
the configured URL before obtaining the submodule via 'git submodule
update'. After obtaining the submodule, the presence of this variable
is used as a sign whether the submodule is of interest to git commands.
update'. If neither submodule.<name>.active or submodule.active are
set, the presence of this variable is used as a fallback to indicate
whether the submodule is of interest to git commands.
See linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.

submodule.<name>.update::
Expand Down Expand Up @@ -3052,6 +3053,16 @@ submodule.<name>.ignore::
"--ignore-submodules" option. The 'git submodule' commands are not
affected by this setting.

submodule.<name>.active::
Boolean value indicating if the submodule is of interest to git
commands. This config option takes precedence over the
submodule.active config option.

submodule.active::
A repeated field which contains a pathspec used to match against a
submodule's path to determine if the submodule is of interest to git
commands.

submodule.fetchJobs::
Specifies how many submodules are fetched/cloned at the same time.
A positive integer allows up to that number of submodules fetched
Expand Down
14 changes: 9 additions & 5 deletions Documentation/git-clone.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ SYNOPSIS
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
[--dissociate] [--separate-git-dir <git dir>]
[--depth <depth>] [--[no-]single-branch]
[--recursive | --recurse-submodules] [--[no-]shallow-submodules]
[--recurse-submodules] [--[no-]shallow-submodules]
[--jobs <n>] [--] <repository> [<directory>]

DESCRIPTION
Expand Down Expand Up @@ -215,10 +215,14 @@ objects from the source repository into a pack in the cloned repository.
branch when `--single-branch` clone was made, no remote-tracking
branch is created.

--recursive::
--recurse-submodules::
After the clone is created, initialize all submodules within,
using their default settings. This is equivalent to running
--recurse-submodules[=<pathspec]::
After the clone is created, initialize and clone submodules
within based on the provided pathspec. If no pathspec is
provided, all submodules are initialized and cloned.
Submodules are initialized and cloned using their default
settings. The resulting clone has `submodule.active` set to
the provided pathspec, or "." (meaning all submodules) if no
pathspec is provided. This is equivalent to running
`git submodule update --init --recursive` immediately after
the clone is finished. This option is ignored if the cloned
repository does not have a worktree/checkout (i.e. if any of
Expand Down
4 changes: 3 additions & 1 deletion Documentation/git-submodule.txt
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ init [--] [<path>...]::
repository will be assumed to be upstream.
+
Optional <path> arguments limit which submodules will be initialized.
If no path is specified, all submodules are initialized.
If no path is specified and submodule.active has been configured, submodules
configured to be active will be initialized, otherwise all submodules are
initialized.
+
When present, it will also copy the value of `submodule.$name.update`.
This command does not alter existing information in .git/config.
Expand Down
50 changes: 43 additions & 7 deletions builtin/clone.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ static const char * const builtin_clone_usage[] = {
};

static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;
static int option_local = -1, option_no_hardlinks, option_shared;
static int option_shallow_submodules;
static int deepen;
static char *option_template, *option_depth, *option_since;
Expand All @@ -56,6 +56,21 @@ static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
static int option_dissociate;
static int max_jobs = -1;
static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;

static int recurse_submodules_cb(const struct option *opt,
const char *arg, int unset)
{
if (unset)
string_list_clear((struct string_list *)opt->value, 0);
else if (arg)
string_list_append((struct string_list *)opt->value, arg);
else
string_list_append((struct string_list *)opt->value,
(const char *)opt->defval);

return 0;
}

static struct option builtin_clone_options[] = {
OPT__VERBOSITY(&option_verbosity),
Expand All @@ -74,10 +89,13 @@ static struct option builtin_clone_options[] = {
N_("don't use local hardlinks, always copy")),
OPT_BOOL('s', "shared", &option_shared,
N_("setup as shared repository")),
OPT_BOOL(0, "recursive", &option_recursive,
N_("initialize submodules in the clone")),
OPT_BOOL(0, "recurse-submodules", &option_recursive,
N_("initialize submodules in the clone")),
{ OPTION_CALLBACK, 0, "recursive", &option_recurse_submodules,
N_("pathspec"), N_("initialize submodules in the clone"),
PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, recurse_submodules_cb,
(intptr_t)"." },
{ OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules,
N_("pathspec"), N_("initialize submodules in the clone"),
PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." },
OPT_INTEGER('j', "jobs", &max_jobs,
N_("number of submodules cloned in parallel")),
OPT_STRING(0, "template", &option_template, N_("template-directory"),
Expand Down Expand Up @@ -733,7 +751,7 @@ static int checkout(int submodule_progress)
err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
oid_to_hex(&oid), "1", NULL);

if (!err && option_recursive) {
if (!err && (option_recurse_submodules.nr > 0)) {
struct argv_array args = ARGV_ARRAY_INIT;
argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);

Expand Down Expand Up @@ -957,7 +975,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
fprintf(stderr, _("Cloning into '%s'...\n"), dir);
}

if (option_recursive) {
if (option_recurse_submodules.nr > 0) {
struct string_list_item *item;
struct strbuf sb = STRBUF_INIT;

/* remove duplicates */
string_list_sort(&option_recurse_submodules);
string_list_remove_duplicates(&option_recurse_submodules, 0);

/*
* NEEDSWORK: In a multi-working-tree world, this needs to be
* set in the per-worktree config.
*/
for_each_string_list_item(item, &option_recurse_submodules) {
strbuf_addf(&sb, "submodule.active=%s",
item->string);
string_list_append(&option_config,
strbuf_detach(&sb, NULL));
}

if (option_required_reference.nr &&
option_optional_reference.nr)
die(_("clone --recursive is not compatible with "
Expand Down
68 changes: 56 additions & 12 deletions builtin/submodule--helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,29 @@ static int module_list_compute(int argc, const char **argv,
return result;
}

static void module_list_active(struct module_list *list)
{
int i;
struct module_list active_modules = MODULE_LIST_INIT;

gitmodules_config();

for (i = 0; i < list->nr; i++) {
const struct cache_entry *ce = list->entries[i];

if (!is_submodule_initialized(ce->name))
continue;

ALLOC_GROW(active_modules.entries,
active_modules.nr + 1,
active_modules.alloc);
active_modules.entries[active_modules.nr++] = ce;
}

free(list->entries);
*list = active_modules;
}

static int module_list(int argc, const char **argv, const char *prefix)
{
int i;
Expand Down Expand Up @@ -333,6 +356,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
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
*/
if (!is_submodule_initialized(path)) {
strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.active", sub->name);
git_config_set_gently(sb.buf, "true");
}

/*
* Copy url setting when it is not set yet.
* To look up the url in .git/config, we must not fall back to
Expand Down Expand Up @@ -420,6 +455,13 @@ static int module_init(int argc, const char **argv, const char *prefix)
if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
return 1;

/*
* If there are no path args and submodule.active is set then,
* by default, only initialize 'active' modules.
*/
if (!argc && git_config_get_value_multi("submodule.active"))
module_list_active(&list);

for (i = 0; i < list.nr; i++)
init_submodule(list.entries[i]->name, prefix, quiet);

Expand Down Expand Up @@ -741,7 +783,6 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
struct strbuf displaypath_sb = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
const char *displaypath = NULL;
char *url = NULL;
int needs_cloning = 0;

if (ce_stage(ce)) {
Expand Down Expand Up @@ -775,15 +816,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
goto cleanup;
}

/*
* Looking up the url in .git/config.
* We must not fall back to .gitmodules as we only want
* to process configured submodules.
*/
strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.url", sub->name);
git_config_get_string(sb.buf, &url);
if (!url) {
/* Check if the submodule has been initialized. */
if (!is_submodule_initialized(ce->name)) {
next_submodule_warn_missing(suc, out, displaypath);
goto cleanup;
}
Expand Down Expand Up @@ -817,7 +851,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
argv_array_push(&child->args, "--depth=1");
argv_array_pushl(&child->args, "--path", sub->path, NULL);
argv_array_pushl(&child->args, "--name", sub->name, NULL);
argv_array_pushl(&child->args, "--url", url, NULL);
argv_array_pushl(&child->args, "--url", sub->url, NULL);
if (suc->references.nr) {
struct string_list_item *item;
for_each_string_list_item(item, &suc->references)
Expand All @@ -827,7 +861,6 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
argv_array_push(&child->args, suc->depth);

cleanup:
free(url);
strbuf_reset(&displaypath_sb);
strbuf_reset(&sb);

Expand Down Expand Up @@ -1109,6 +1142,16 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
return 0;
}

static int is_active(int argc, const char **argv, const char *prefix)
{
if (argc != 2)
die("submodule--helper is-active takes exactly 1 arguments");

gitmodules_config();

return !is_submodule_initialized(argv[1]);
}

#define SUPPORT_SUPER_PREFIX (1<<0)

struct cmd_struct {
Expand All @@ -1128,6 +1171,7 @@ static struct cmd_struct commands[] = {
{"init", module_init, SUPPORT_SUPER_PREFIX},
{"remote-branch", resolve_remote_submodule_branch, 0},
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
{"is-active", is_active, 0},
};

int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
Expand Down
55 changes: 36 additions & 19 deletions git-submodule.sh
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,20 @@ or you are unsure what this means choose another name with the '--name' option."
fi &&
git add --force .gitmodules ||
die "$(eval_gettext "Failed to register submodule '\$sm_path'")"

# NEEDSWORK: In a multi-working-tree world, this needs to be
# set in the per-worktree config.
if git config --get submodule.active >/dev/null
then
# If the submodule being adding isn't already covered by the
# current configured pathspec, set the submodule's active flag
if ! git submodule--helper is-active "$sm_path"
then
git config submodule."$sm_name".active "true"
fi
else
git config submodule."$sm_name".active "true"
fi
}

#
Expand Down Expand Up @@ -1010,14 +1024,13 @@ cmd_status()
do
die_if_unmatched "$mode" "$sha1"
name=$(git submodule--helper name "$sm_path") || exit
url=$(git config submodule."$name".url)
displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
if test "$stage" = U
then
say "U$sha1 $displaypath"
continue
fi
if test -z "$url" ||
if ! git submodule--helper is-active "$sm_path" ||
{
! test -d "$sm_path"/.git &&
! test -f "$sm_path"/.git
Expand Down Expand Up @@ -1090,6 +1103,13 @@ cmd_sync()
while read mode sha1 stage sm_path
do
die_if_unmatched "$mode" "$sha1"

# skip inactive submodules
if ! git submodule--helper is-active "$sm_path"
then
continue
fi

name=$(git submodule--helper name "$sm_path")
url=$(git config -f .gitmodules --get submodule."$name".url)

Expand All @@ -1112,27 +1132,24 @@ cmd_sync()
;;
esac

if git config "submodule.$name.url" >/dev/null 2>/dev/null
displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
git config submodule."$name".url "$super_config_url"

if test -e "$sm_path"/.git
then
displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
git config submodule."$name".url "$super_config_url"
(
sanitize_submodule_env
cd "$sm_path"
remote=$(get_default_remote)
git config remote."$remote".url "$sub_origin_url"

if test -e "$sm_path"/.git
if test -n "$recursive"
then
(
sanitize_submodule_env
cd "$sm_path"
remote=$(get_default_remote)
git config remote."$remote".url "$sub_origin_url"

if test -n "$recursive"
then
prefix="$prefix$sm_path/"
eval cmd_sync
fi
)
prefix="$prefix$sm_path/"
eval cmd_sync
fi
)
fi
done
}
Expand Down
Loading

0 comments on commit a93dcb0

Please sign in to comment.