From: Stefan Beller <sbeller@google.com>
To: gitster@pobox.com, jrnieder@gmail.com, git@vger.kernel.org,
Jens.Lehmann@web.de
Cc: Stefan Beller <sbeller@google.com>
Subject: [PATCHv9 4/6] git submodule update: have a dedicated helper for cloning
Date: Tue, 9 Feb 2016 12:54:32 -0800 [thread overview]
Message-ID: <1455051274-15256-5-git-send-email-sbeller@google.com> (raw)
In-Reply-To: <1455051274-15256-1-git-send-email-sbeller@google.com>
This introduces a new helper function in git submodule--helper
which takes care of cloning all submodules, which we want to
parallelize eventually.
Some tests (such as empty URL, update_mode=none) are required in the
helper to make the decision for cloning. These checks have been
moved into the C function as well (no need to repeat them in the
shell script).
As we can only access the stderr channel from within the parallel
processing engine, we need to reroute the error message for
specified but initialized submodules to stderr. As it is an error
message, this should have gone to stderr in the first place, so it
is a bug fix along the way.
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/submodule--helper.c | 230 ++++++++++++++++++++++++++++++++++++++++++++
git-submodule.sh | 45 +++------
t/t7400-submodule-basic.sh | 4 +-
3 files changed, 243 insertions(+), 36 deletions(-)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f4c3eff..53356dc 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -255,6 +255,235 @@ static int module_clone(int argc, const char **argv, const char *prefix)
return 0;
}
+static int git_submodule_config(const char *var, const char *value, void *cb)
+{
+ return submodule_config(var, value, cb);
+}
+
+struct submodule_update_clone {
+ /* states */
+ int count;
+ int print_unmatched;
+ /* configuration */
+ int quiet;
+ const char *reference;
+ const char *depth;
+ const char *update;
+ const char *recursive_prefix;
+ const char *prefix;
+ struct module_list list;
+ struct string_list projectlines;
+ struct pathspec pathspec;
+};
+#define SUBMODULE_UPDATE_CLONE_INIT {0, 0, 0, NULL, NULL, NULL, NULL, NULL, MODULE_LIST_INIT, STRING_LIST_INIT_DUP}
+
+static void fill_clone_command(struct child_process *cp, int quiet,
+ const char *prefix, const char *path,
+ const char *name, const char *url,
+ const char *reference, const char *depth)
+{
+ cp->git_cmd = 1;
+ cp->no_stdin = 1;
+ cp->stdout_to_stderr = 1;
+ cp->err = -1;
+ argv_array_push(&cp->args, "submodule--helper");
+ argv_array_push(&cp->args, "clone");
+ if (quiet)
+ argv_array_push(&cp->args, "--quiet");
+
+ if (prefix)
+ argv_array_pushl(&cp->args, "--prefix", prefix, NULL);
+
+ argv_array_pushl(&cp->args, "--path", path, NULL);
+ argv_array_pushl(&cp->args, "--name", name, NULL);
+ argv_array_pushl(&cp->args, "--url", url, NULL);
+ if (reference)
+ argv_array_push(&cp->args, reference);
+ if (depth)
+ argv_array_push(&cp->args, depth);
+}
+
+static int update_clone_get_next_task(struct child_process *cp,
+ struct strbuf *err,
+ void *pp_cb,
+ void **pp_task_cb)
+{
+ struct submodule_update_clone *pp = pp_cb;
+
+ for (; pp->count < pp->list.nr; pp->count++) {
+ const struct submodule *sub = NULL;
+ const struct cache_entry *ce = pp->list.entries[pp->count];
+ 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)) {
+ if (pp->recursive_prefix)
+ strbuf_addf(err,
+ "Skipping unmerged submodule %s/%s\n",
+ pp->recursive_prefix, ce->name);
+ else
+ strbuf_addf(err,
+ "Skipping unmerged submodule %s\n",
+ ce->name);
+ goto cleanup_and_continue;
+ }
+
+ sub = submodule_from_path(null_sha1, ce->name);
+
+ if (pp->recursive_prefix)
+ displaypath = relative_path(pp->recursive_prefix,
+ ce->name, &displaypath_sb);
+ else
+ displaypath = ce->name;
+
+ if ((pp->update && !strcmp(pp->update, "none")) ||
+ (!pp->update && sub->update == SM_UPDATE_NONE)) {
+ strbuf_addf(err, "Skipping submodule '%s'\n",
+ displaypath);
+ goto cleanup_and_continue;
+ }
+
+ /*
+ * 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) {
+ /*
+ * Only mention uninitialized submodules when its
+ * path have been specified
+ */
+ if (pp->pathspec.nr)
+ strbuf_addf(err,
+ _("Submodule path '%s' not initialized\n"
+ "Maybe you want to use 'update --init'?"),
+ displaypath);
+ goto cleanup_and_continue;
+ }
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/.git", ce->name);
+ needs_cloning = !file_exists(sb.buf);
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode,
+ sha1_to_hex(ce->sha1), ce_stage(ce),
+ needs_cloning, ce->name);
+ string_list_append(&pp->projectlines, sb.buf);
+
+ if (needs_cloning) {
+ fill_clone_command(cp, pp->quiet, pp->prefix,
+ ce->name, sub->name, strdup(url),
+ pp->reference, pp->depth);
+ pp->count++;
+ }
+
+cleanup_and_continue:
+ free(url);
+ strbuf_reset(&displaypath_sb);
+ strbuf_reset(&sb);
+
+ if (needs_cloning)
+ return 1;
+ }
+ return 0;
+}
+
+static int update_clone_start_failure(struct child_process *cp,
+ struct strbuf *err,
+ void *pp_cb,
+ void *pp_task_cb)
+{
+ struct submodule_update_clone *pp = pp_cb;
+
+ strbuf_addf(err, "error when starting a child process");
+ pp->print_unmatched = 1;
+
+ return 1;
+}
+
+static int update_clone_task_finished(int result,
+ struct child_process *cp,
+ struct strbuf *err,
+ void *pp_cb,
+ void *pp_task_cb)
+{
+ struct submodule_update_clone *pp = pp_cb;
+
+ if (!result) {
+ return 0;
+ } else {
+ strbuf_addf(err, "error in one child process");
+ pp->print_unmatched = 1;
+ return 1;
+ }
+}
+
+static int update_clone(int argc, const char **argv, const char *prefix)
+{
+ struct string_list_item *item;
+ struct submodule_update_clone pp = SUBMODULE_UPDATE_CLONE_INIT;
+
+ struct option module_list_options[] = {
+ OPT_STRING(0, "prefix", &prefix,
+ N_("path"),
+ N_("path into the working tree")),
+ OPT_STRING(0, "recursive_prefix", &pp.recursive_prefix,
+ N_("path"),
+ N_("path into the working tree, across nested "
+ "submodule boundaries")),
+ OPT_STRING(0, "update", &pp.update,
+ N_("string"),
+ N_("update command for submodules")),
+ OPT_STRING(0, "reference", &pp.reference, "<repository>",
+ N_("Use the local reference repository "
+ "instead of a full clone")),
+ OPT_STRING(0, "depth", &pp.depth, "<depth>",
+ N_("Create a shallow clone truncated to the "
+ "specified number of revisions")),
+ OPT__QUIET(&pp.quiet, N_("do't print cloning progress")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
+ NULL
+ };
+ pp.prefix = prefix;
+
+ argc = parse_options(argc, argv, prefix, module_list_options,
+ git_submodule_helper_usage, 0);
+
+ if (module_list_compute(argc, argv, prefix, &pp.pathspec, &pp.list) < 0) {
+ printf("#unmatched\n");
+ return 1;
+ }
+
+ gitmodules_config();
+ /* Overlay the parsed .gitmodules file with .git/config */
+ git_config(git_submodule_config, NULL);
+ run_processes_parallel(1, update_clone_get_next_task,
+ update_clone_start_failure,
+ update_clone_task_finished,
+ &pp);
+
+ if (pp.print_unmatched) {
+ printf("#unmatched\n");
+ return 1;
+ }
+
+ for_each_string_list_item(item, &pp.projectlines)
+ utf8_fprintf(stdout, "%s", item->string);
+
+ return 0;
+}
+
struct cmd_struct {
const char *cmd;
int (*fn)(int, const char **, const char *);
@@ -264,6 +493,7 @@ static struct cmd_struct commands[] = {
{"list", module_list},
{"name", module_name},
{"clone", module_clone},
+ {"update-clone", update_clone}
};
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --git a/git-submodule.sh b/git-submodule.sh
index 9bc5c5f..9f554fb 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -664,17 +664,18 @@ cmd_update()
cmd_init "--" "$@" || return
fi
- cloned_modules=
- git submodule--helper list --prefix "$wt_prefix" "$@" | {
+ git submodule--helper update-clone ${GIT_QUIET:+--quiet} \
+ ${wt_prefix:+--prefix "$wt_prefix"} \
+ ${prefix:+--recursive_prefix "$prefix"} \
+ ${update:+--update "$update"} \
+ ${reference:+--reference "$reference"} \
+ ${depth:+--depth "$depth"} \
+ "$@" | {
err=
- while read mode sha1 stage sm_path
+ while read mode sha1 stage just_cloned sm_path
do
die_if_unmatched "$mode"
- if test "$stage" = U
- then
- echo >&2 "Skipping unmerged submodule $prefix$sm_path"
- continue
- fi
+
name=$(git submodule--helper name "$sm_path") || exit
url=$(git config submodule."$name".url)
branch=$(get_submodule_config "$name" branch master)
@@ -691,27 +692,10 @@ cmd_update()
displaypath=$(relative_path "$prefix$sm_path")
- if test "$update_module" = "none"
- then
- echo "Skipping submodule '$displaypath'"
- continue
- fi
-
- if test -z "$url"
- then
- # Only mention uninitialized submodules when its
- # path have been specified
- test "$#" != "0" &&
- say "$(eval_gettext "Submodule path '\$displaypath' not initialized
-Maybe you want to use 'update --init'?")"
- continue
- fi
-
- if ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git
+ if test $just_cloned -eq 1
then
- git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$prefix" --path "$sm_path" --name "$name" --url "$url" "$reference" "$depth" || exit
- cloned_modules="$cloned_modules;$name"
subsha1=
+ update_module=checkout
else
subsha1=$(clear_local_git_env; cd "$sm_path" &&
git rev-parse --verify HEAD) ||
@@ -751,13 +735,6 @@ Maybe you want to use 'update --init'?")"
die "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
fi
- # Is this something we just cloned?
- case ";$cloned_modules;" in
- *";$name;"*)
- # then there is no local change to integrate
- update_module=checkout ;;
- esac
-
must_die_on_failure=
case "$update_module" in
checkout)
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 540771c..5991e3c 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -462,7 +462,7 @@ test_expect_success 'update --init' '
git config --remove-section submodule.example &&
test_must_fail git config submodule.example.url &&
- git submodule update init > update.out &&
+ git submodule update init 2> update.out &&
cat update.out &&
test_i18ngrep "not initialized" update.out &&
test_must_fail git rev-parse --resolve-git-dir init/.git &&
@@ -480,7 +480,7 @@ test_expect_success 'update --init from subdirectory' '
mkdir -p sub &&
(
cd sub &&
- git submodule update ../init >update.out &&
+ git submodule update ../init 2>update.out &&
cat update.out &&
test_i18ngrep "not initialized" update.out &&
test_must_fail git rev-parse --resolve-git-dir ../init/.git &&
--
2.7.0.rc0.35.gb0f7869.dirty
next prev parent reply other threads:[~2016-02-09 20:55 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-02-09 20:54 [PATCHv9 0/6] Expose submodule parallelism to the user Stefan Beller
2016-02-09 20:54 ` [PATCHv9 1/6] submodule-config: keep update strategy around Stefan Beller
2016-02-09 21:08 ` Junio C Hamano
2016-02-09 22:19 ` Stefan Beller
2016-02-09 22:29 ` Junio C Hamano
2016-02-09 21:49 ` Jonathan Nieder
2016-02-09 22:32 ` Junio C Hamano
2016-02-11 20:00 ` Junio C Hamano
2016-02-09 20:54 ` [PATCHv9 2/6] submodule-config: drop check against NULL Stefan Beller
2016-02-09 21:50 ` Jonathan Nieder
2016-02-09 20:54 ` [PATCHv9 3/6] fetching submodules: respect `submodule.fetchJobs` config option Stefan Beller
2016-02-09 21:12 ` Junio C Hamano
2016-02-09 22:34 ` Jonathan Nieder
2016-02-10 0:11 ` Stefan Beller
2016-02-10 1:12 ` Junio C Hamano
2016-02-10 2:04 ` Junio C Hamano
2016-02-09 20:54 ` Stefan Beller [this message]
2016-02-09 21:24 ` [PATCHv9 4/6] git submodule update: have a dedicated helper for cloning Junio C Hamano
2016-02-10 0:37 ` Jonathan Nieder
2016-02-10 2:26 ` Jacob Keller
2016-02-10 17:49 ` Stefan Beller
2016-02-11 7:46 ` Jacob Keller
2016-02-09 20:54 ` [PATCHv9 5/6] submodule update: expose parallelism to the user Stefan Beller
2016-02-09 20:54 ` [PATCHv9 6/6] clone: allow an explicit argument for parallel submodule clones Stefan Beller
2016-02-09 21:39 ` [PATCHv9 0/6] Expose submodule parallelism to the user Junio C Hamano
2016-02-09 21:46 ` Stefan Beller
2016-02-09 22:03 ` Junio C Hamano
2016-02-11 20:23 ` Junio C Hamano
2016-02-11 20:28 ` Junio C Hamano
2016-02-11 20:33 ` Stefan Beller
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1455051274-15256-5-git-send-email-sbeller@google.com \
--to=sbeller@google.com \
--cc=Jens.Lehmann@web.de \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=jrnieder@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).