{"thread":{"id":"41571","subject":"[PATCHv20 01/12] submodule-config: keep update strategy around","startedAt":"2016-02-29T19:13:58Z","lastAt":"2016-02-29T20:50:07Z","messageCount":16,"participants":["Stefan Beller","Jonathan Nieder","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"279815","messageId":"1456773250-5510-1-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":null,"subject":"[PATCHv20 00/12] Expose submodule parallelism to the user","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:13:58Z","receivedAt":"2016-02-29T19:13:58Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Thanks Duy for reviewing!\nI added your suggestions as amending and as a new patch.\n\nThanks,\nStefan\n\nInterdiff to v19 (current origin/sb/submodule-parallel-update):\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 0272c98..1b0b13a 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -299,10 +299,10 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,\n \n \tif (ce_stage(ce)) {\n \t\tif (suc->recursive_prefix) {\n-\t\t\tstrbuf_addf(out, \"Skipping unmerged submodule %s/%s\\n\",\n+\t\t\tstrbuf_addf(out,_(\"Skipping unmerged submodule %s/%s\\n\"),\n \t\t\t\t    suc->recursive_prefix, ce->name);\n \t\t} else {\n-\t\t\tstrbuf_addf(out, \"Skipping unmerged submodule %s\\n\",\n+\t\t\tstrbuf_addf(out, _(\"Skipping unmerged submodule %s\\n\"),\n \t\t\t\t    ce->name);\n \t\t}\n \t\tgoto cleanup;\n@@ -319,7 +319,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,\n \tif (suc->update.type == SM_UPDATE_NONE\n \t    || (suc->update.type == SM_UPDATE_UNSPECIFIED\n \t\t&& sub->update_strategy.type == SM_UPDATE_NONE)) {\n-\t\tstrbuf_addf(out, \"Skipping submodule '%s'\\n\",\n+\t\tstrbuf_addf(out, _(\"Skipping submodule '%s'\\n\"),\n \t\t\t    displaypath);\n \t\tgoto cleanup;\n \t}\n@@ -525,13 +525,13 @@ int cmd_submodule__helper(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n \tif (argc < 2)\n-\t\tdie(_(\"fatal: submodule--helper subcommand must be \"\n+\t\tdie(_(\"submodule--helper subcommand must be \"\n \t\t      \"called with a subcommand\"));\n \n \tfor (i = 0; i < ARRAY_SIZE(commands); i++)\n \t\tif (!strcmp(argv[1], commands[i].cmd))\n \t\t\treturn commands[i].fn(argc - 1, argv + 1, prefix);\n \n-\tdie(_(\"fatal: '%s' is not a valid submodule--helper \"\n+\tdie(_(\"'%s' is not a valid submodule--helper \"\n \t      \"subcommand\"), argv[1]);\n }\n\n\nStefan Beller (12):\n  submodule-config: keep update strategy around\n  submodule-config: drop check against NULL\n  fetching submodules: respect `submodule.fetchJobs` config option\n  submodule update: direct error message to stderr\n  run_processes_parallel: treat output of children as byte array\n  run-command: expose default_{start_failure, task_finished}\n  run_processes_parallel: rename parameters for the callbacks\n  run_processes_parallel: correctly terminate callbacks with an LF\n  git submodule update: have a dedicated helper for cloning\n  submodule helper: remove double 'fatal: ' prefix\n  submodule update: expose parallelism to the user\n  clone: allow an explicit argument for parallel submodule clones\n\n Documentation/config.txt        |   6 +\n Documentation/git-clone.txt     |   6 +-\n Documentation/git-submodule.txt |   7 +-\n builtin/clone.c                 |  19 ++-\n builtin/fetch.c                 |   2 +-\n builtin/submodule--helper.c     | 259 +++++++++++++++++++++++++++++++++++++++-\n git-submodule.sh                |  56 ++++-----\n run-command.c                   |  36 +++---\n run-command.h                   |  29 ++++-\n strbuf.c                        |   6 +\n strbuf.h                        |   6 +\n submodule-config.c              |  19 ++-\n submodule-config.h              |   2 +\n submodule.c                     |  37 +++++-\n submodule.h                     |  18 +++\n t/t5526-fetch-submodules.sh     |  14 +++\n t/t7400-submodule-basic.sh      |   4 +-\n t/t7406-submodule-update.sh     |  27 +++++\n 18 files changed, 480 insertions(+), 73 deletions(-)\n\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279814","messageId":"1456773250-5510-2-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 01/12] submodule-config: keep update strategy around","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:13:59Z","receivedAt":"2016-02-29T19:13:59Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Currently submodule.<name>.update is only handled by git-submodule.sh.\nC code will start to need to make use of that value as more of the\nfunctionality of git-submodule.sh moves into library code in C.\n\nAdd the update field to 'struct submodule' and populate it so it can\nbe read as sm->update or from sm->update_command.\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n submodule-config.c | 13 +++++++++++++\n submodule-config.h |  2 ++\n submodule.c        | 21 +++++++++++++++++++++\n submodule.h        | 16 ++++++++++++++++\n 4 files changed, 52 insertions(+)\n\ndiff --git a/submodule-config.c b/submodule-config.c\nindex afe0ea8..a5cd2ee 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -59,6 +59,7 @@ static void free_one_config(struct submodule_entry *entry)\n {\n \tfree((void *) entry->config->path);\n \tfree((void *) entry->config->name);\n+\tfree((void *) entry->config->update_strategy.command);\n \tfree(entry->config);\n }\n \n@@ -194,6 +195,8 @@ static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache,\n \n \tsubmodule->path = NULL;\n \tsubmodule->url = NULL;\n+\tsubmodule->update_strategy.type = SM_UPDATE_UNSPECIFIED;\n+\tsubmodule->update_strategy.command = NULL;\n \tsubmodule->fetch_recurse = RECURSE_SUBMODULES_NONE;\n \tsubmodule->ignore = NULL;\n \n@@ -311,6 +314,16 @@ static int parse_config(const char *var, const char *value, void *data)\n \t\t\tfree((void *) submodule->url);\n \t\t\tsubmodule->url = xstrdup(value);\n \t\t}\n+\t} else if (!strcmp(item.buf, \"update\")) {\n+\t\tif (!value)\n+\t\t\tret = config_error_nonbool(var);\n+\t\telse if (!me->overwrite &&\n+\t\t\t submodule->update_strategy.type != SM_UPDATE_UNSPECIFIED)\n+\t\t\twarn_multiple_config(me->commit_sha1, submodule->name,\n+\t\t\t\t\t     \"update\");\n+\t\telse if (parse_submodule_update_strategy(value,\n+\t\t\t &submodule->update_strategy) < 0)\n+\t\t\t\tdie(_(\"invalid value for %s\"), var);\n \t}\n \n \tstrbuf_release(&name);\ndiff --git a/submodule-config.h b/submodule-config.h\nindex 9061e4e..092ebfc 100644\n--- a/submodule-config.h\n+++ b/submodule-config.h\n@@ -2,6 +2,7 @@\n #define SUBMODULE_CONFIG_CACHE_H\n \n #include \"hashmap.h\"\n+#include \"submodule.h\"\n #include \"strbuf.h\"\n \n /*\n@@ -14,6 +15,7 @@ struct submodule {\n \tconst char *url;\n \tint fetch_recurse;\n \tconst char *ignore;\n+\tstruct submodule_update_strategy update_strategy;\n \t/* the sha1 blob id of the responsible .gitmodules file */\n \tunsigned char gitmodules_sha1[20];\n };\ndiff --git a/submodule.c b/submodule.c\nindex b83939c..b38dd51 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -210,6 +210,27 @@ void gitmodules_config(void)\n \t}\n }\n \n+int parse_submodule_update_strategy(const char *value,\n+\t\tstruct submodule_update_strategy *dst)\n+{\n+\tfree((void*)dst->command);\n+\tdst->command = NULL;\n+\tif (!strcmp(value, \"none\"))\n+\t\tdst->type = SM_UPDATE_NONE;\n+\telse if (!strcmp(value, \"checkout\"))\n+\t\tdst->type = SM_UPDATE_CHECKOUT;\n+\telse if (!strcmp(value, \"rebase\"))\n+\t\tdst->type = SM_UPDATE_REBASE;\n+\telse if (!strcmp(value, \"merge\"))\n+\t\tdst->type = SM_UPDATE_MERGE;\n+\telse if (skip_prefix(value, \"!\", &value)) {\n+\t\tdst->type = SM_UPDATE_COMMAND;\n+\t\tdst->command = xstrdup(value);\n+\t} else\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n void handle_ignore_submodules_arg(struct diff_options *diffopt,\n \t\t\t\t  const char *arg)\n {\ndiff --git a/submodule.h b/submodule.h\nindex cbc0003..3464500 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -13,6 +13,20 @@ enum {\n \tRECURSE_SUBMODULES_ON = 2\n };\n \n+enum submodule_update_type {\n+\tSM_UPDATE_UNSPECIFIED = 0,\n+\tSM_UPDATE_CHECKOUT,\n+\tSM_UPDATE_REBASE,\n+\tSM_UPDATE_MERGE,\n+\tSM_UPDATE_NONE,\n+\tSM_UPDATE_COMMAND\n+};\n+\n+struct submodule_update_strategy {\n+\tenum submodule_update_type type;\n+\tconst char *command;\n+};\n+\n int is_staging_gitmodules_ok(void);\n int update_path_in_gitmodules(const char *oldpath, const char *newpath);\n int remove_path_from_gitmodules(const char *path);\n@@ -21,6 +35,8 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,\n \t\tconst char *path);\n int submodule_config(const char *var, const char *value, void *cb);\n void gitmodules_config(void);\n+int parse_submodule_update_strategy(const char *value,\n+\t\tstruct submodule_update_strategy *dst);\n void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);\n void show_submodule_summary(FILE *f, const char *path,\n \t\tconst char *line_prefix,\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279816","messageId":"1456773250-5510-3-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 02/12] submodule-config: drop check against NULL","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:14:00Z","receivedAt":"2016-02-29T19:14:00Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Adhere to the common coding style of Git and not check explicitly\nfor NULL throughout the file. There are still other occurrences in the\ncode base but that is usually inside of conditions with side effects.\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n submodule-config.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/submodule-config.c b/submodule-config.c\nindex a5cd2ee..9fa2269 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -267,7 +267,7 @@ static int parse_config(const char *var, const char *value, void *data)\n \tif (!strcmp(item.buf, \"path\")) {\n \t\tif (!value)\n \t\t\tret = config_error_nonbool(var);\n-\t\telse if (!me->overwrite && submodule->path != NULL)\n+\t\telse if (!me->overwrite && submodule->path)\n \t\t\twarn_multiple_config(me->commit_sha1, submodule->name,\n \t\t\t\t\t\"path\");\n \t\telse {\n@@ -291,7 +291,7 @@ static int parse_config(const char *var, const char *value, void *data)\n \t} else if (!strcmp(item.buf, \"ignore\")) {\n \t\tif (!value)\n \t\t\tret = config_error_nonbool(var);\n-\t\telse if (!me->overwrite && submodule->ignore != NULL)\n+\t\telse if (!me->overwrite && submodule->ignore)\n \t\t\twarn_multiple_config(me->commit_sha1, submodule->name,\n \t\t\t\t\t\"ignore\");\n \t\telse if (strcmp(value, \"untracked\") &&\n@@ -307,7 +307,7 @@ static int parse_config(const char *var, const char *value, void *data)\n \t} else if (!strcmp(item.buf, \"url\")) {\n \t\tif (!value) {\n \t\t\tret = config_error_nonbool(var);\n-\t\t} else if (!me->overwrite && submodule->url != NULL) {\n+\t\t} else if (!me->overwrite && submodule->url) {\n \t\t\twarn_multiple_config(me->commit_sha1, submodule->name,\n \t\t\t\t\t\"url\");\n \t\t} else {\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279819","messageId":"1456773250-5510-4-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 03/12] fetching submodules: respect `submodule.fetchJobs` config option","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:14:01Z","receivedAt":"2016-02-29T19:14:01Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This allows to configure fetching and updating in parallel\nwithout having the command line option.\n\nThis moved the responsibility to determine how many parallel processes\nto start from builtin/fetch to submodule.c as we need a way to communicate\n\"The user did not specify the number of parallel processes in the command\nline options\" in the builtin fetch. The submodule code takes care of\nthe precedence (CLI > config > default).\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt    |  6 ++++++\n builtin/fetch.c             |  2 +-\n submodule.c                 | 16 +++++++++++++++-\n submodule.h                 |  2 ++\n t/t5526-fetch-submodules.sh | 14 ++++++++++++++\n 5 files changed, 38 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2d06b11..3b02732 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2646,6 +2646,12 @@ submodule.<name>.ignore::\n \t\"--ignore-submodules\" option. The 'git submodule' commands are not\n \taffected by this setting.\n \n+submodule.fetchJobs::\n+\tSpecifies how many submodules are fetched/cloned at the same time.\n+\tA positive integer allows up to that number of submodules fetched\n+\tin parallel. A value of 0 will give some reasonable default.\n+\tIf unset, it defaults to 1.\n+\n tag.sort::\n \tThis variable controls the sort ordering of tags when displayed by\n \tlinkgit:git-tag[1]. Without the \"--sort=<value>\" option provided, the\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 586840d..5aa1c2d 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -37,7 +37,7 @@ static int prune = -1; /* unspecified */\n static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;\n static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;\n static int tags = TAGS_DEFAULT, unshallow, update_shallow;\n-static int max_children = 1;\n+static int max_children = -1;\n static const char *depth;\n static const char *upload_pack;\n static struct strbuf default_rla = STRBUF_INIT;\ndiff --git a/submodule.c b/submodule.c\nindex b38dd51..051f722 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -15,6 +15,7 @@\n #include \"thread-utils.h\"\n \n static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;\n+static int parallel_jobs = 1;\n static struct string_list changed_submodule_paths;\n static int initialized_fetch_ref_tips;\n static struct sha1_array ref_tips_before_fetch;\n@@ -169,7 +170,12 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,\n \n int submodule_config(const char *var, const char *value, void *cb)\n {\n-\tif (starts_with(var, \"submodule.\"))\n+\tif (!strcmp(var, \"submodule.fetchjobs\")) {\n+\t\tparallel_jobs = git_config_int(var, value);\n+\t\tif (parallel_jobs < 0)\n+\t\t\tdie(_(\"negative values not allowed for submodule.fetchJobs\"));\n+\t\treturn 0;\n+\t} else if (starts_with(var, \"submodule.\"))\n \t\treturn parse_submodule_config_option(var, value);\n \telse if (!strcmp(var, \"fetch.recursesubmodules\")) {\n \t\tconfig_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);\n@@ -772,6 +778,9 @@ int fetch_populated_submodules(const struct argv_array *options,\n \targv_array_push(&spf.args, \"--recurse-submodules-default\");\n \t/* default value, \"--submodule-prefix\" and its value are added later */\n \n+\tif (max_parallel_jobs < 0)\n+\t\tmax_parallel_jobs = parallel_jobs;\n+\n \tcalculate_changed_submodule_paths();\n \trun_processes_parallel(max_parallel_jobs,\n \t\t\t       get_next_submodule,\n@@ -1118,3 +1127,8 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)\n \tstrbuf_release(&rel_path);\n \tfree((void *)real_work_tree);\n }\n+\n+int parallel_submodules(void)\n+{\n+\treturn parallel_jobs;\n+}\ndiff --git a/submodule.h b/submodule.h\nindex 3464500..3166608 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -26,6 +26,7 @@ struct submodule_update_strategy {\n \tenum submodule_update_type type;\n \tconst char *command;\n };\n+#define SUBMODULE_UPDATE_STRATEGY_INIT {SM_UPDATE_UNSPECIFIED, NULL}\n \n int is_staging_gitmodules_ok(void);\n int update_path_in_gitmodules(const char *oldpath, const char *newpath);\n@@ -57,5 +58,6 @@ int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_nam\n \t\tstruct string_list *needs_pushing);\n int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);\n void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);\n+int parallel_submodules(void);\n \n #endif\ndiff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh\nindex 1241146..954d0e4 100755\n--- a/t/t5526-fetch-submodules.sh\n+++ b/t/t5526-fetch-submodules.sh\n@@ -471,4 +471,18 @@ test_expect_success \"don't fetch submodule when newly recorded commits are alrea\n \ttest_i18ncmp expect.err actual.err\n '\n \n+test_expect_success 'fetching submodules respects parallel settings' '\n+\tgit config fetch.recurseSubmodules true &&\n+\t(\n+\t\tcd downstream &&\n+\t\tGIT_TRACE=$(pwd)/trace.out git fetch --jobs 7 &&\n+\t\tgrep \"7 tasks\" trace.out &&\n+\t\tgit config submodule.fetchJobs 8 &&\n+\t\tGIT_TRACE=$(pwd)/trace.out git fetch &&\n+\t\tgrep \"8 tasks\" trace.out &&\n+\t\tGIT_TRACE=$(pwd)/trace.out git fetch --jobs 9 &&\n+\t\tgrep \"9 tasks\" trace.out\n+\t)\n+'\n+\n test_done\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279820","messageId":"1456773250-5510-5-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 04/12] submodule update: direct error message to stderr","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:14:02Z","receivedAt":"2016-02-29T19:14:02Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Reroute the error message for specified but initialized submodules\nto stderr instead of stdout.\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n git-submodule.sh           | 4 ++--\n t/t7400-submodule-basic.sh | 4 ++--\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 9bc5c5f..9ee86d4 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -693,7 +693,7 @@ cmd_update()\n \n \t\tif test \"$update_module\" = \"none\"\n \t\tthen\n-\t\t\techo \"Skipping submodule '$displaypath'\"\n+\t\t\techo >&2 \"Skipping submodule '$displaypath'\"\n \t\t\tcontinue\n \t\tfi\n \n@@ -702,7 +702,7 @@ cmd_update()\n \t\t\t# Only mention uninitialized submodules when its\n \t\t\t# path have been specified\n \t\t\ttest \"$#\" != \"0\" &&\n-\t\t\tsay \"$(eval_gettext \"Submodule path '\\$displaypath' not initialized\n+\t\t\tsay >&2 \"$(eval_gettext \"Submodule path '\\$displaypath' not initialized\n Maybe you want to use 'update --init'?\")\"\n \t\t\tcontinue\n \t\tfi\ndiff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh\nindex 540771c..5991e3c 100755\n--- a/t/t7400-submodule-basic.sh\n+++ b/t/t7400-submodule-basic.sh\n@@ -462,7 +462,7 @@ test_expect_success 'update --init' '\n \tgit config --remove-section submodule.example &&\n \ttest_must_fail git config submodule.example.url &&\n \n-\tgit submodule update init > update.out &&\n+\tgit submodule update init 2> update.out &&\n \tcat update.out &&\n \ttest_i18ngrep \"not initialized\" update.out &&\n \ttest_must_fail git rev-parse --resolve-git-dir init/.git &&\n@@ -480,7 +480,7 @@ test_expect_success 'update --init from subdirectory' '\n \tmkdir -p sub &&\n \t(\n \t\tcd sub &&\n-\t\tgit submodule update ../init >update.out &&\n+\t\tgit submodule update ../init 2>update.out &&\n \t\tcat update.out &&\n \t\ttest_i18ngrep \"not initialized\" update.out &&\n \t\ttest_must_fail git rev-parse --resolve-git-dir ../init/.git &&\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279818","messageId":"1456773250-5510-6-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 05/12] run_processes_parallel: treat output of children as byte array","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:14:03Z","receivedAt":"2016-02-29T19:14:03Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"We do not want the output to be interrupted by a NUL byte, so we\ncannot use raw fputs. Introduce strbuf_write to avoid having long\narguments in run-command.c.\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n run-command.c | 8 ++++----\n strbuf.c      | 6 ++++++\n strbuf.h      | 6 ++++++\n 3 files changed, 16 insertions(+), 4 deletions(-)\n\ndiff --git a/run-command.c b/run-command.c\nindex 51fd72c..2f8f222 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -1011,7 +1011,7 @@ static void pp_cleanup(struct parallel_processes *pp)\n \t * When get_next_task added messages to the buffer in its last\n \t * iteration, the buffered output is non empty.\n \t */\n-\tfputs(pp->buffered_output.buf, stderr);\n+\tstrbuf_write(&pp->buffered_output, stderr);\n \tstrbuf_release(&pp->buffered_output);\n \n \tsigchain_pop_common();\n@@ -1097,7 +1097,7 @@ static void pp_output(struct parallel_processes *pp)\n \tint i = pp->output_owner;\n \tif (pp->children[i].state == GIT_CP_WORKING &&\n \t    pp->children[i].err.len) {\n-\t\tfputs(pp->children[i].err.buf, stderr);\n+\t\tstrbuf_write(&pp->children[i].err, stderr);\n \t\tstrbuf_reset(&pp->children[i].err);\n \t}\n }\n@@ -1135,11 +1135,11 @@ static int pp_collect_finished(struct parallel_processes *pp)\n \t\t\tstrbuf_addbuf(&pp->buffered_output, &pp->children[i].err);\n \t\t\tstrbuf_reset(&pp->children[i].err);\n \t\t} else {\n-\t\t\tfputs(pp->children[i].err.buf, stderr);\n+\t\t\tstrbuf_write(&pp->children[i].err, stderr);\n \t\t\tstrbuf_reset(&pp->children[i].err);\n \n \t\t\t/* Output all other finished child processes */\n-\t\t\tfputs(pp->buffered_output.buf, stderr);\n+\t\t\tstrbuf_write(&pp->buffered_output, stderr);\n \t\t\tstrbuf_reset(&pp->buffered_output);\n \n \t\t\t/*\ndiff --git a/strbuf.c b/strbuf.c\nindex 38686ff..5f6da82 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -395,6 +395,12 @@ ssize_t strbuf_read_once(struct strbuf *sb, int fd, size_t hint)\n \treturn cnt;\n }\n \n+ssize_t strbuf_write(struct strbuf *sb, FILE *f)\n+{\n+\treturn sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;\n+}\n+\n+\n #define STRBUF_MAXLINK (2*PATH_MAX)\n \n int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\ndiff --git a/strbuf.h b/strbuf.h\nindex 2bf90e7..d4f2aa1 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -387,6 +387,12 @@ extern ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint\n extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);\n \n /**\n+ * Write the whole content of the strbuf to the stream not stopping at\n+ * NUL bytes.\n+ */\n+extern ssize_t strbuf_write(struct strbuf *sb, FILE *stream);\n+\n+/**\n  * Read a line from a FILE *, overwriting the existing contents\n  * of the strbuf. The second argument specifies the line\n  * terminator character, typically `'\\n'`.\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279822","messageId":"1456773250-5510-7-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 06/12] run-command: expose default_{start_failure, task_finished}","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:14:04Z","receivedAt":"2016-02-29T19:14:04Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"We want to reuse the error reporting facilities in a later patch.\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n run-command.c | 18 +++++++++---------\n run-command.h | 19 +++++++++++++++++++\n 2 files changed, 28 insertions(+), 9 deletions(-)\n\ndiff --git a/run-command.c b/run-command.c\nindex 2f8f222..c9b13cf 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -902,10 +902,10 @@ struct parallel_processes {\n \tstruct strbuf buffered_output; /* of finished children */\n };\n \n-static int default_start_failure(struct child_process *cp,\n-\t\t\t\t struct strbuf *err,\n-\t\t\t\t void *pp_cb,\n-\t\t\t\t void *pp_task_cb)\n+int default_start_failure(struct child_process *cp,\n+\t\t\t  struct strbuf *err,\n+\t\t\t  void *pp_cb,\n+\t\t\t  void *pp_task_cb)\n {\n \tint i;\n \n@@ -916,11 +916,11 @@ static int default_start_failure(struct child_process *cp,\n \treturn 0;\n }\n \n-static int default_task_finished(int result,\n-\t\t\t\t struct child_process *cp,\n-\t\t\t\t struct strbuf *err,\n-\t\t\t\t void *pp_cb,\n-\t\t\t\t void *pp_task_cb)\n+int default_task_finished(int result,\n+\t\t\t  struct child_process *cp,\n+\t\t\t  struct strbuf *err,\n+\t\t\t  void *pp_cb,\n+\t\t\t  void *pp_task_cb)\n {\n \tint i;\n \ndiff --git a/run-command.h b/run-command.h\nindex d5a57f9..a054fa6 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -164,6 +164,15 @@ typedef int (*start_failure_fn)(struct child_process *cp,\n \t\t\t\tvoid *pp_task_cb);\n \n /**\n+ * If a command fails to start, then print an error message stating the\n+ * exact command which failed.\n+ */\n+int default_start_failure(struct child_process *cp,\n+\t\t\t  struct strbuf *err,\n+\t\t\t  void *pp_cb,\n+\t\t\t  void *pp_task_cb);\n+\n+/**\n  * This callback is called on every child process that finished processing.\n  *\n  * You must not write to stdout or stderr in this function. Add your\n@@ -184,6 +193,16 @@ typedef int (*task_finished_fn)(int result,\n \t\t\t\tvoid *pp_task_cb);\n \n /**\n+ * If the child process returns with a non zero error code, print\n+ * an error message of the exact command which failed.\n+ */\n+int default_task_finished(int result,\n+\t\t\t  struct child_process *cp,\n+\t\t\t  struct strbuf *err,\n+\t\t\t  void *pp_cb,\n+\t\t\t  void *pp_task_cb);\n+\n+/**\n  * Runs up to n processes at the same time. Whenever a process can be\n  * started, the callback get_next_task_fn is called to obtain the data\n  * required to start another child process.\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279821","messageId":"1456773250-5510-8-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 07/12] run_processes_parallel: rename parameters for the callbacks","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:14:05Z","receivedAt":"2016-02-29T19:14:05Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"The refs code has a similar pattern of passing around 'struct strbuf *err',\nwhich is strictly used for error reporting. This is not the case here,\nas the strbuf is used to accumulate all the output (whether it is error\nor not) for the user. Rename it to 'out'.\n\nSuggested-by: Jonathan Nieder <jrnieder@gmail.com>\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n run-command.c | 12 ++++++------\n run-command.h | 14 +++++++-------\n 2 files changed, 13 insertions(+), 13 deletions(-)\n\ndiff --git a/run-command.c b/run-command.c\nindex c9b13cf..d2964e1 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -903,22 +903,22 @@ struct parallel_processes {\n };\n \n int default_start_failure(struct child_process *cp,\n-\t\t\t  struct strbuf *err,\n+\t\t\t  struct strbuf *out,\n \t\t\t  void *pp_cb,\n \t\t\t  void *pp_task_cb)\n {\n \tint i;\n \n-\tstrbuf_addstr(err, \"Starting a child failed:\");\n+\tstrbuf_addstr(out, \"Starting a child failed:\");\n \tfor (i = 0; cp->argv[i]; i++)\n-\t\tstrbuf_addf(err, \" %s\", cp->argv[i]);\n+\t\tstrbuf_addf(out, \" %s\", cp->argv[i]);\n \n \treturn 0;\n }\n \n int default_task_finished(int result,\n \t\t\t  struct child_process *cp,\n-\t\t\t  struct strbuf *err,\n+\t\t\t  struct strbuf *out,\n \t\t\t  void *pp_cb,\n \t\t\t  void *pp_task_cb)\n {\n@@ -927,9 +927,9 @@ int default_task_finished(int result,\n \tif (!result)\n \t\treturn 0;\n \n-\tstrbuf_addf(err, \"A child failed with return code %d:\", result);\n+\tstrbuf_addf(out, \"A child failed with return code %d:\", result);\n \tfor (i = 0; cp->argv[i]; i++)\n-\t\tstrbuf_addf(err, \" %s\", cp->argv[i]);\n+\t\tstrbuf_addf(out, \" %s\", cp->argv[i]);\n \n \treturn 0;\n }\ndiff --git a/run-command.h b/run-command.h\nindex a054fa6..2bd8fee 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -139,7 +139,7 @@ int in_async(void);\n  * return the negative signal number.\n  */\n typedef int (*get_next_task_fn)(struct child_process *cp,\n-\t\t\t\tstruct strbuf *err,\n+\t\t\t\tstruct strbuf *out,\n \t\t\t\tvoid *pp_cb,\n \t\t\t\tvoid **pp_task_cb);\n \n@@ -148,7 +148,7 @@ typedef int (*get_next_task_fn)(struct child_process *cp,\n  * a new process.\n  *\n  * You must not write to stdout or stderr in this function. Add your\n- * message to the strbuf err instead, which will be printed without\n+ * message to the strbuf out instead, which will be printed without\n  * messing up the output of the other parallel processes.\n  *\n  * pp_cb is the callback cookie as passed into run_processes_parallel,\n@@ -159,7 +159,7 @@ typedef int (*get_next_task_fn)(struct child_process *cp,\n  * the negative signal number.\n  */\n typedef int (*start_failure_fn)(struct child_process *cp,\n-\t\t\t\tstruct strbuf *err,\n+\t\t\t\tstruct strbuf *out,\n \t\t\t\tvoid *pp_cb,\n \t\t\t\tvoid *pp_task_cb);\n \n@@ -168,7 +168,7 @@ typedef int (*start_failure_fn)(struct child_process *cp,\n  * exact command which failed.\n  */\n int default_start_failure(struct child_process *cp,\n-\t\t\t  struct strbuf *err,\n+\t\t\t  struct strbuf *out,\n \t\t\t  void *pp_cb,\n \t\t\t  void *pp_task_cb);\n \n@@ -176,7 +176,7 @@ int default_start_failure(struct child_process *cp,\n  * This callback is called on every child process that finished processing.\n  *\n  * You must not write to stdout or stderr in this function. Add your\n- * message to the strbuf err instead, which will be printed without\n+ * message to the strbuf out instead, which will be printed without\n  * messing up the output of the other parallel processes.\n  *\n  * pp_cb is the callback cookie as passed into run_processes_parallel,\n@@ -188,7 +188,7 @@ int default_start_failure(struct child_process *cp,\n  */\n typedef int (*task_finished_fn)(int result,\n \t\t\t\tstruct child_process *cp,\n-\t\t\t\tstruct strbuf *err,\n+\t\t\t\tstruct strbuf *out,\n \t\t\t\tvoid *pp_cb,\n \t\t\t\tvoid *pp_task_cb);\n \n@@ -198,7 +198,7 @@ typedef int (*task_finished_fn)(int result,\n  */\n int default_task_finished(int result,\n \t\t\t  struct child_process *cp,\n-\t\t\t  struct strbuf *err,\n+\t\t\t  struct strbuf *out,\n \t\t\t  void *pp_cb,\n \t\t\t  void *pp_task_cb);\n \n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279817","messageId":"1456773250-5510-9-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 08/12] run_processes_parallel: correctly terminate callbacks with an LF","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:14:06Z","receivedAt":"2016-02-29T19:14:06Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"As the strbufs passed around collect all output to the user, and there\nis no post processing involved we need to care about the line ending\nourselves.\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n run-command.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/run-command.c b/run-command.c\nindex d2964e1..6fad42f 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -912,6 +912,7 @@ int default_start_failure(struct child_process *cp,\n \tstrbuf_addstr(out, \"Starting a child failed:\");\n \tfor (i = 0; cp->argv[i]; i++)\n \t\tstrbuf_addf(out, \" %s\", cp->argv[i]);\n+\tstrbuf_addch(out, '\\n');\n \n \treturn 0;\n }\n@@ -930,6 +931,7 @@ int default_task_finished(int result,\n \tstrbuf_addf(out, \"A child failed with return code %d:\", result);\n \tfor (i = 0; cp->argv[i]; i++)\n \t\tstrbuf_addf(out, \" %s\", cp->argv[i]);\n+\tstrbuf_addch(out, '\\n');\n \n \treturn 0;\n }\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279826","messageId":"1456773250-5510-10-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 09/12] git submodule update: have a dedicated helper for cloning","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:14:07Z","receivedAt":"2016-02-29T19:14:07Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This introduces a new helper function in git submodule--helper\nwhich takes care of cloning all submodules, which we want to\nparallelize eventually.\n\nSome tests (such as empty URL, update_mode=none) are required in the\nhelper to make the decision for cloning. These checks have been\nmoved into the C function as well (no need to repeat them in the\nshell script).\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/submodule--helper.c | 249 ++++++++++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  47 +++------\n 2 files changed, 262 insertions(+), 34 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex f4c3eff..e1b60ff 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -255,6 +255,254 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n+struct submodule_update_clone {\n+\t/* index into 'list', the list of submodules to look into for cloning */\n+\tint current;\n+\tstruct module_list list;\n+\tunsigned warn_if_uninitialized : 1;\n+\n+\t/* update parameter passed via commandline */\n+\tstruct submodule_update_strategy update;\n+\n+\t/* configuration parameters which are passed on to the children */\n+\tint quiet;\n+\tconst char *reference;\n+\tconst char *depth;\n+\tconst char *recursive_prefix;\n+\tconst char *prefix;\n+\n+\t/* Machine-readable status lines to be consumed by git-submodule.sh */\n+\tstruct string_list projectlines;\n+\n+\t/* If we want to stop as fast as possible and return an error */\n+\tunsigned quickstop : 1;\n+};\n+#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \\\n+\tSUBMODULE_UPDATE_STRATEGY_INIT, 0, NULL, NULL, NULL, NULL, \\\n+\tSTRING_LIST_INIT_DUP, 0}\n+\n+/**\n+ * Determine whether 'ce' needs to be cloned. If so, prepare the 'child' to\n+ * run the clone. Returns 1 if 'ce' needs to be cloned, 0 otherwise.\n+ */\n+static int prepare_to_clone_next_submodule(const struct cache_entry *ce,\n+\t\t\t\t\t   struct child_process *child,\n+\t\t\t\t\t   struct submodule_update_clone *suc,\n+\t\t\t\t\t   struct strbuf *out)\n+{\n+\tconst struct submodule *sub = NULL;\n+\tstruct strbuf displaypath_sb = STRBUF_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *displaypath = NULL;\n+\tchar *url = NULL;\n+\tint needs_cloning = 0;\n+\n+\tif (ce_stage(ce)) {\n+\t\tif (suc->recursive_prefix) {\n+\t\t\tstrbuf_addf(out,_(\"Skipping unmerged submodule %s/%s\\n\"),\n+\t\t\t\t    suc->recursive_prefix, ce->name);\n+\t\t} else {\n+\t\t\tstrbuf_addf(out, _(\"Skipping unmerged submodule %s\\n\"),\n+\t\t\t\t    ce->name);\n+\t\t}\n+\t\tgoto cleanup;\n+\t}\n+\n+\tsub = submodule_from_path(null_sha1, ce->name);\n+\n+\tif (suc->recursive_prefix)\n+\t\tdisplaypath = relative_path(suc->recursive_prefix,\n+\t\t\t\t\t    ce->name, &displaypath_sb);\n+\telse\n+\t\tdisplaypath = ce->name;\n+\n+\tif (suc->update.type == SM_UPDATE_NONE\n+\t    || (suc->update.type == SM_UPDATE_UNSPECIFIED\n+\t\t&& sub->update_strategy.type == SM_UPDATE_NONE)) {\n+\t\tstrbuf_addf(out, _(\"Skipping submodule '%s'\\n\"),\n+\t\t\t    displaypath);\n+\t\tgoto cleanup;\n+\t}\n+\n+\t/*\n+\t * Looking up the url in .git/config.\n+\t * We must not fall back to .gitmodules as we only want\n+\t * to process configured submodules.\n+\t */\n+\tstrbuf_reset(&sb);\n+\tstrbuf_addf(&sb, \"submodule.%s.url\", sub->name);\n+\tgit_config_get_string(sb.buf, &url);\n+\tif (!url) {\n+\t\t/*\n+\t\t * Only mention uninitialized submodules when their\n+\t\t * path have been specified\n+\t\t */\n+\t\tif (suc->warn_if_uninitialized)\n+\t\t\tstrbuf_addf(out, _(\"Submodule path '%s' not initialized\\n\"\n+\t\t\t\t    \"Maybe you want to use 'update --init'?\\n\"),\n+\t\t\t\t    displaypath);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tstrbuf_reset(&sb);\n+\tstrbuf_addf(&sb, \"%s/.git\", ce->name);\n+\tneeds_cloning = !file_exists(sb.buf);\n+\n+\tstrbuf_reset(&sb);\n+\tstrbuf_addf(&sb, \"%06o %s %d %d\\t%s\\n\", ce->ce_mode,\n+\t\t\tsha1_to_hex(ce->sha1), ce_stage(ce),\n+\t\t\tneeds_cloning, ce->name);\n+\tstring_list_append(&suc->projectlines, sb.buf);\n+\n+\tif (!needs_cloning)\n+\t\tgoto cleanup;\n+\n+\tchild->git_cmd = 1;\n+\tchild->no_stdin = 1;\n+\tchild->stdout_to_stderr = 1;\n+\tchild->err = -1;\n+\targv_array_push(&child->args, \"submodule--helper\");\n+\targv_array_push(&child->args, \"clone\");\n+\tif (suc->quiet)\n+\t\targv_array_push(&child->args, \"--quiet\");\n+\tif (suc->prefix)\n+\t\targv_array_pushl(&child->args, \"--prefix\", suc->prefix, NULL);\n+\targv_array_pushl(&child->args, \"--path\", sub->path, NULL);\n+\targv_array_pushl(&child->args, \"--name\", sub->name, NULL);\n+\targv_array_pushl(&child->args, \"--url\", url, NULL);\n+\tif (suc->reference)\n+\t\targv_array_push(&child->args, suc->reference);\n+\tif (suc->depth)\n+\t\targv_array_push(&child->args, suc->depth);\n+\n+cleanup:\n+\tfree(url);\n+\tstrbuf_reset(&displaypath_sb);\n+\tstrbuf_reset(&sb);\n+\n+\treturn needs_cloning;\n+}\n+\n+static int update_clone_get_next_task(struct child_process *child,\n+\t\t\t\t      struct strbuf *err,\n+\t\t\t\t      void *suc_cb,\n+\t\t\t\t      void **void_task_cb)\n+{\n+\tstruct submodule_update_clone *suc = suc_cb;\n+\n+\tfor (; suc->current < suc->list.nr; suc->current++) {\n+\t\tconst struct cache_entry *ce = suc->list.entries[suc->current];\n+\t\tif (prepare_to_clone_next_submodule(ce, child, suc, err)) {\n+\t\t\tsuc->current++;\n+\t\t\treturn 1;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static int update_clone_start_failure(struct child_process *child,\n+\t\t\t\t      struct strbuf *err,\n+\t\t\t\t      void *suc_cb,\n+\t\t\t\t      void *void_task_cb)\n+{\n+\tstruct submodule_update_clone *suc = suc_cb;\n+\n+\tdefault_start_failure(child, err, suc_cb, void_task_cb);\n+\tsuc->quickstop = 1;\n+\n+\treturn 1;\n+}\n+\n+static int update_clone_task_finished(int result,\n+\t\t\t\t      struct child_process *child,\n+\t\t\t\t      struct strbuf *err,\n+\t\t\t\t      void *suc_cb,\n+\t\t\t\t      void *void_task_cb)\n+{\n+\tstruct submodule_update_clone *suc = suc_cb;\n+\n+\tif (!result)\n+\t\treturn 0;\n+\n+\tdefault_task_finished(result, child, err, suc_cb, void_task_cb);\n+\tsuc->quickstop = 1;\n+\n+\treturn 1;\n+}\n+\n+static int update_clone(int argc, const char **argv, const char *prefix)\n+{\n+\tconst char *update = NULL;\n+\tstruct string_list_item *item;\n+\tstruct pathspec pathspec;\n+\tstruct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;\n+\n+\tstruct option module_update_clone_options[] = {\n+\t\tOPT_STRING(0, \"prefix\", &prefix,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"path into the working tree\")),\n+\t\tOPT_STRING(0, \"recursive-prefix\", &suc.recursive_prefix,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"path into the working tree, across nested \"\n+\t\t\t      \"submodule boundaries\")),\n+\t\tOPT_STRING(0, \"update\", &update,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"rebase, merge, checkout or none\")),\n+\t\tOPT_STRING(0, \"reference\", &suc.reference, N_(\"repo\"),\n+\t\t\t   N_(\"reference repository\")),\n+\t\tOPT_STRING(0, \"depth\", &suc.depth, \"<depth>\",\n+\t\t\t   N_(\"Create a shallow clone truncated to the \"\n+\t\t\t      \"specified number of revisions\")),\n+\t\tOPT__QUIET(&suc.quiet, N_(\"don't print cloning progress\")),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const git_submodule_helper_usage[] = {\n+\t\tN_(\"git submodule--helper update_clone [--prefix=<path>] [<path>...]\"),\n+\t\tNULL\n+\t};\n+\tsuc.prefix = prefix;\n+\n+\targc = parse_options(argc, argv, prefix, module_update_clone_options,\n+\t\t\t     git_submodule_helper_usage, 0);\n+\n+\tif (update)\n+\t\tif (parse_submodule_update_strategy(update, &suc.update) < 0)\n+\t\t\tdie(_(\"bad value for update parameter\"));\n+\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &suc.list) < 0)\n+\t\treturn 1;\n+\n+\tif (pathspec.nr)\n+\t\tsuc.warn_if_uninitialized = 1;\n+\n+\t/* Overlay the parsed .gitmodules file with .git/config */\n+\tgitmodules_config();\n+\tgit_config(submodule_config, NULL);\n+\n+\trun_processes_parallel(1,\n+\t\t\t       update_clone_get_next_task,\n+\t\t\t       update_clone_start_failure,\n+\t\t\t       update_clone_task_finished,\n+\t\t\t       &suc);\n+\n+\t/*\n+\t * We saved the output and put it out all at once now.\n+\t * That means:\n+\t * - the listener does not have to interleave their (checkout)\n+\t *   work with our fetching.  The writes involved in a\n+\t *   checkout involve more straightforward sequential I/O.\n+\t * - the listener can avoid doing any work if fetching failed.\n+\t */\n+\tif (suc.quickstop)\n+\t\treturn 1;\n+\n+\tfor_each_string_list_item(item, &suc.projectlines)\n+\t\tutf8_fprintf(stdout, \"%s\", item->string);\n+\n+\treturn 0;\n+}\n+\n struct cmd_struct {\n \tconst char *cmd;\n \tint (*fn)(int, const char **, const char *);\n@@ -264,6 +512,7 @@ static struct cmd_struct commands[] = {\n \t{\"list\", module_list},\n \t{\"name\", module_name},\n \t{\"clone\", module_clone},\n+\t{\"update-clone\", update_clone}\n };\n \n int cmd_submodule__helper(int argc, const char **argv, const char *prefix)\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 9ee86d4..a6a82d2 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -664,17 +664,20 @@ cmd_update()\n \t\tcmd_init \"--\" \"$@\" || return\n \tfi\n \n-\tcloned_modules=\n-\tgit submodule--helper list --prefix \"$wt_prefix\" \"$@\" | {\n+\t{\n+\tgit submodule--helper update-clone ${GIT_QUIET:+--quiet} \\\n+\t\t${wt_prefix:+--prefix \"$wt_prefix\"} \\\n+\t\t${prefix:+--recursive-prefix \"$prefix\"} \\\n+\t\t${update:+--update \"$update\"} \\\n+\t\t${reference:+--reference \"$reference\"} \\\n+\t\t${depth:+--depth \"$depth\"} \\\n+\t\t\"$@\" || echo \"#unmatched\"\n+\t} | {\n \terr=\n-\twhile read mode sha1 stage sm_path\n+\twhile read mode sha1 stage just_cloned sm_path\n \tdo\n \t\tdie_if_unmatched \"$mode\"\n-\t\tif test \"$stage\" = U\n-\t\tthen\n-\t\t\techo >&2 \"Skipping unmerged submodule $prefix$sm_path\"\n-\t\t\tcontinue\n-\t\tfi\n+\n \t\tname=$(git submodule--helper name \"$sm_path\") || exit\n \t\turl=$(git config submodule.\"$name\".url)\n \t\tbranch=$(get_submodule_config \"$name\" branch master)\n@@ -691,27 +694,10 @@ cmd_update()\n \n \t\tdisplaypath=$(relative_path \"$prefix$sm_path\")\n \n-\t\tif test \"$update_module\" = \"none\"\n-\t\tthen\n-\t\t\techo >&2 \"Skipping submodule '$displaypath'\"\n-\t\t\tcontinue\n-\t\tfi\n-\n-\t\tif test -z \"$url\"\n-\t\tthen\n-\t\t\t# Only mention uninitialized submodules when its\n-\t\t\t# path have been specified\n-\t\t\ttest \"$#\" != \"0\" &&\n-\t\t\tsay >&2 \"$(eval_gettext \"Submodule path '\\$displaypath' not initialized\n-Maybe you want to use 'update --init'?\")\"\n-\t\t\tcontinue\n-\t\tfi\n-\n-\t\tif ! test -d \"$sm_path\"/.git && ! test -f \"$sm_path\"/.git\n+\t\tif test $just_cloned -eq 1\n \t\tthen\n-\t\t\tgit submodule--helper clone ${GIT_QUIET:+--quiet} --prefix \"$prefix\" --path \"$sm_path\" --name \"$name\" --url \"$url\" \"$reference\" \"$depth\" || exit\n-\t\t\tcloned_modules=\"$cloned_modules;$name\"\n \t\t\tsubsha1=\n+\t\t\tupdate_module=checkout\n \t\telse\n \t\t\tsubsha1=$(clear_local_git_env; cd \"$sm_path\" &&\n \t\t\t\tgit rev-parse --verify HEAD) ||\n@@ -751,13 +737,6 @@ Maybe you want to use 'update --init'?\")\"\n \t\t\t\tdie \"$(eval_gettext \"Unable to fetch in submodule path '\\$displaypath'\")\"\n \t\t\tfi\n \n-\t\t\t# Is this something we just cloned?\n-\t\t\tcase \";$cloned_modules;\" in\n-\t\t\t*\";$name;\"*)\n-\t\t\t\t# then there is no local change to integrate\n-\t\t\t\tupdate_module=checkout ;;\n-\t\t\tesac\n-\n \t\t\tmust_die_on_failure=\n \t\t\tcase \"$update_module\" in\n \t\t\tcheckout)\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279823","messageId":"1456773250-5510-11-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 10/12] submodule helper: remove double 'fatal: ' prefix","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:14:08Z","receivedAt":"2016-02-29T19:14:08Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"The prefix is added by die(...), so we don't have to do it.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n builtin/submodule--helper.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex e1b60ff..a111fd2 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -519,13 +519,13 @@ int cmd_submodule__helper(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n \tif (argc < 2)\n-\t\tdie(_(\"fatal: submodule--helper subcommand must be \"\n+\t\tdie(_(\"submodule--helper subcommand must be \"\n \t\t      \"called with a subcommand\"));\n \n \tfor (i = 0; i < ARRAY_SIZE(commands); i++)\n \t\tif (!strcmp(argv[1], commands[i].cmd))\n \t\t\treturn commands[i].fn(argc - 1, argv + 1, prefix);\n \n-\tdie(_(\"fatal: '%s' is not a valid submodule--helper \"\n+\tdie(_(\"'%s' is not a valid submodule--helper \"\n \t      \"subcommand\"), argv[1]);\n }\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279824","messageId":"1456773250-5510-12-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 11/12] submodule update: expose parallelism to the user","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:14:09Z","receivedAt":"2016-02-29T19:14:09Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Expose possible parallelism either via the \"--jobs\" CLI parameter or\nthe \"submodule.fetchJobs\" setting.\n\nBy having the variable initialized to -1, we make sure 0 can be passed\ninto the parallel processing machine, which will then pick as many parallel\nworkers as there are CPUs.\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git-submodule.txt |  7 ++++++-\n builtin/submodule--helper.c     |  8 +++++++-\n git-submodule.sh                |  9 +++++++++\n t/t7406-submodule-update.sh     | 12 ++++++++++++\n 4 files changed, 34 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt\nindex 1572f05..13adebf 100644\n--- a/Documentation/git-submodule.txt\n+++ b/Documentation/git-submodule.txt\n@@ -16,7 +16,7 @@ SYNOPSIS\n 'git submodule' [--quiet] deinit [-f|--force] [--] <path>...\n 'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]\n \t      [-f|--force] [--rebase|--merge] [--reference <repository>]\n-\t      [--depth <depth>] [--recursive] [--] [<path>...]\n+\t      [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]\n 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]\n \t      [commit] [--] [<path>...]\n 'git submodule' [--quiet] foreach [--recursive] <command>\n@@ -377,6 +377,11 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.\n \tclone with a history truncated to the specified number of revisions.\n \tSee linkgit:git-clone[1]\n \n+-j <n>::\n+--jobs <n>::\n+\tThis option is only valid for the update command.\n+\tClone new submodules in parallel with as many jobs.\n+\tDefaults to the `submodule.fetchJobs` option.\n \n <path>...::\n \tPaths to submodule(s). When specified this will restrict the command\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a111fd2..1b0b13a 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -433,6 +433,7 @@ static int update_clone_task_finished(int result,\n static int update_clone(int argc, const char **argv, const char *prefix)\n {\n \tconst char *update = NULL;\n+\tint max_jobs = -1;\n \tstruct string_list_item *item;\n \tstruct pathspec pathspec;\n \tstruct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;\n@@ -453,6 +454,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"depth\", &suc.depth, \"<depth>\",\n \t\t\t   N_(\"Create a shallow clone truncated to the \"\n \t\t\t      \"specified number of revisions\")),\n+\t\tOPT_INTEGER('j', \"jobs\", &max_jobs,\n+\t\t\t    N_(\"parallel jobs\")),\n \t\tOPT__QUIET(&suc.quiet, N_(\"don't print cloning progress\")),\n \t\tOPT_END()\n \t};\n@@ -480,7 +483,10 @@ static int update_clone(int argc, const char **argv, const char *prefix)\n \tgitmodules_config();\n \tgit_config(submodule_config, NULL);\n \n-\trun_processes_parallel(1,\n+\tif (max_jobs < 0)\n+\t\tmax_jobs = parallel_submodules();\n+\n+\trun_processes_parallel(max_jobs,\n \t\t\t       update_clone_get_next_task,\n \t\t\t       update_clone_start_failure,\n \t\t\t       update_clone_task_finished,\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex a6a82d2..86018ee 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -645,6 +645,14 @@ cmd_update()\n \t\t--depth=*)\n \t\t\tdepth=$1\n \t\t\t;;\n+\t\t-j|--jobs)\n+\t\t\tcase \"$2\" in '') usage ;; esac\n+\t\t\tjobs=\"--jobs=$2\"\n+\t\t\tshift\n+\t\t\t;;\n+\t\t--jobs=*)\n+\t\t\tjobs=$1\n+\t\t\t;;\n \t\t--)\n \t\t\tshift\n \t\t\tbreak\n@@ -671,6 +679,7 @@ cmd_update()\n \t\t${update:+--update \"$update\"} \\\n \t\t${reference:+--reference \"$reference\"} \\\n \t\t${depth:+--depth \"$depth\"} \\\n+\t\t${jobs:+$jobs} \\\n \t\t\"$@\" || echo \"#unmatched\"\n \t} | {\n \terr=\ndiff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh\nindex dda3929..7fd5142 100755\n--- a/t/t7406-submodule-update.sh\n+++ b/t/t7406-submodule-update.sh\n@@ -774,4 +774,16 @@ test_expect_success 'submodule update --recursive drops module name before recur\n \t test_i18ngrep \"Submodule path .deeper/submodule/subsubmodule.: checked out\" actual\n \t)\n '\n+\n+test_expect_success 'submodule update can be run in parallel' '\n+\t(cd super2 &&\n+\t GIT_TRACE=$(pwd)/trace.out git submodule update --jobs 7 &&\n+\t grep \"7 tasks\" trace.out &&\n+\t git config submodule.fetchJobs 8 &&\n+\t GIT_TRACE=$(pwd)/trace.out git submodule update &&\n+\t grep \"8 tasks\" trace.out &&\n+\t GIT_TRACE=$(pwd)/trace.out git submodule update --jobs 9 &&\n+\t grep \"9 tasks\" trace.out\n+\t)\n+'\n test_done\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279825","messageId":"1456773250-5510-13-git-send-email-sbeller@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"[PATCHv20 12/12] clone: allow an explicit argument for parallel submodule clones","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T19:14:10Z","receivedAt":"2016-02-29T19:14:10Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Just pass it along to \"git submodule update\", which may pick reasonable\ndefaults if you don't specify an explicit number.\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git-clone.txt |  6 +++++-\n builtin/clone.c             | 19 +++++++++++++------\n t/t7406-submodule-update.sh | 15 +++++++++++++++\n 3 files changed, 33 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 6bf000d..6db7b6d 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -14,7 +14,7 @@ SYNOPSIS\n \t  [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch]\n-\t  [--recursive | --recurse-submodules] [--] <repository>\n+\t  [--recursive | --recurse-submodules] [--jobs <n>] [--] <repository>\n \t  [<directory>]\n \n DESCRIPTION\n@@ -221,6 +221,10 @@ objects from the source repository into a pack in the cloned repository.\n \tThe result is Git repository can be separated from working\n \ttree.\n \n+-j <n>::\n+--jobs <n>::\n+\tThe number of submodules fetched at the same time.\n+\tDefaults to the `submodule.fetchJobs` option.\n \n <repository>::\n \tThe (possibly remote) repository to clone from.  See the\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex a0b3cd9..b004fb4 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -50,6 +50,7 @@ static int option_progress = -1;\n static struct string_list option_config;\n static struct string_list option_reference;\n static int option_dissociate;\n+static int max_jobs = -1;\n \n static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n@@ -72,6 +73,8 @@ static struct option builtin_clone_options[] = {\n \t\t    N_(\"initialize submodules in the clone\")),\n \tOPT_BOOL(0, \"recurse-submodules\", &option_recursive,\n \t\t    N_(\"initialize submodules in the clone\")),\n+\tOPT_INTEGER('j', \"jobs\", &max_jobs,\n+\t\t    N_(\"number of submodules cloned in parallel\")),\n \tOPT_STRING(0, \"template\", &option_template, N_(\"template-directory\"),\n \t\t   N_(\"directory from which templates will be used\")),\n \tOPT_STRING_LIST(0, \"reference\", &option_reference, N_(\"repo\"),\n@@ -95,10 +98,6 @@ static struct option builtin_clone_options[] = {\n \tOPT_END()\n };\n \n-static const char *argv_submodule[] = {\n-\t\"submodule\", \"update\", \"--init\", \"--recursive\", NULL\n-};\n-\n static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)\n {\n \tstatic char *suffix[] = { \"/.git\", \"\", \".git/.git\", \".git\" };\n@@ -724,8 +723,16 @@ static int checkout(void)\n \terr |= run_hook_le(NULL, \"post-checkout\", sha1_to_hex(null_sha1),\n \t\t\t   sha1_to_hex(sha1), \"1\", NULL);\n \n-\tif (!err && option_recursive)\n-\t\terr = run_command_v_opt(argv_submodule, RUN_GIT_CMD);\n+\tif (!err && option_recursive) {\n+\t\tstruct argv_array args = ARGV_ARRAY_INIT;\n+\t\targv_array_pushl(&args, \"submodule\", \"update\", \"--init\", \"--recursive\", NULL);\n+\n+\t\tif (max_jobs != -1)\n+\t\t\targv_array_pushf(&args, \"--jobs=%d\", max_jobs);\n+\n+\t\terr = run_command_v_opt(args.argv, RUN_GIT_CMD);\n+\t\targv_array_clear(&args);\n+\t}\n \n \treturn err;\n }\ndiff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh\nindex 7fd5142..090891e 100755\n--- a/t/t7406-submodule-update.sh\n+++ b/t/t7406-submodule-update.sh\n@@ -786,4 +786,19 @@ test_expect_success 'submodule update can be run in parallel' '\n \t grep \"9 tasks\" trace.out\n \t)\n '\n+\n+test_expect_success 'git clone passes the parallel jobs config on to submodules' '\n+\ttest_when_finished \"rm -rf super4\" &&\n+\tGIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules --jobs 7 . super4 &&\n+\tgrep \"7 tasks\" trace.out &&\n+\trm -rf super4 &&\n+\tgit config --global submodule.fetchJobs 8 &&\n+\tGIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules . super4 &&\n+\tgrep \"8 tasks\" trace.out &&\n+\trm -rf super4 &&\n+\tGIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules --jobs 9 . super4 &&\n+\tgrep \"9 tasks\" trace.out &&\n+\trm -rf super4\n+'\n+\n test_done\n-- \n2.7.0.rc0.37.gb7b9e8e\n"},{"id":"279830","messageId":"20160229193222.GX28749@google.com","threadId":"41571","inReplyTo":"1456773250-5510-1-git-send-email-sbeller@google.com","subject":"Re: [PATCHv20 00/12] Expose submodule parallelism to the user","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2016-02-29T19:32:22Z","receivedAt":"2016-02-29T19:32:22Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Stefan Beller wrote:\n\n> I added your suggestions as amending and as a new patch.\n\nI think we're at the point where patches on top would work better.  I\nadmit I was a little scared to see another reroll.\n\n[...]\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -299,10 +299,10 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,\n>  \n>  \tif (ce_stage(ce)) {\n>  \t\tif (suc->recursive_prefix) {\n> -\t\t\tstrbuf_addf(out, \"Skipping unmerged submodule %s/%s\\n\",\n> +\t\t\tstrbuf_addf(out,_(\"Skipping unmerged submodule %s/%s\\n\"),\n\nMissing space after comma.\n\nUsual practice for i18n would be something like\n\n\tstruct strbuf path = STRBUF_INIT;\n\tif (suc->recursive_prefix)\n\t\tstrbuf_addf(&path, \"%s/%s\", suc->recursive_prefix, ce->name);\n\telse\n\t\tstrbuf_addstr(&path, ce->name);\n\n\tstrbuf_addf(out, _(\"Skipping unmerged submodule %s\"), path.buf);\n\tstrbuf_addch(out, '\\n');\n\tstrbuf_release(&path);\n\nReasons:\n - translators shouldn't have to worry about the trailing newline\n - minimizing number of strings to translate\n - minimizing the chance that a translator typo produces an invalid path\n\n[...]\n> @@ -319,7 +319,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,\n>  \tif (suc->update.type == SM_UPDATE_NONE\n>  \t    || (suc->update.type == SM_UPDATE_UNSPECIFIED\n>  \t\t&& sub->update_strategy.type == SM_UPDATE_NONE)) {\n> -\t\tstrbuf_addf(out, \"Skipping submodule '%s'\\n\",\n> +\t\tstrbuf_addf(out, _(\"Skipping submodule '%s'\\n\"),\n>  \t\t\t    displaypath);\n\nSame issue here with the trailing \\n.\n\nIf the strbuf_addf + strbuf_addch('\\n') pattern is common enough, we\ncould introduce a helper (e.g. strbuf_addf_nl) to save typing.\n\nThanks and hope that helps,\nJonathan\n"},{"id":"279836","messageId":"CAGZ79ka7Wrc4kzXWwi8c+6JC0FVC45g=fu-F6QxXFe37sm5GMQ@mail.gmail.com","threadId":"41571","inReplyTo":"20160229193222.GX28749@google.com","subject":"Re: [PATCHv20 00/12] Expose submodule parallelism to the user","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-29T20:40:08Z","receivedAt":"2016-02-29T20:40:08Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Feb 29, 2016 at 11:32 AM, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> Stefan Beller wrote:\n>\n>> I added your suggestions as amending and as a new patch.\n>\n> I think we're at the point where patches on top would work better.  I\n> admit I was a little scared to see another reroll.\n\nYeah I am a bit scared too, so I'll do patches on top for further fixes\nafter one last reroll, fixing the issues below.\n\n>\n> [...]\n>> --- a/builtin/submodule--helper.c\n>> +++ b/builtin/submodule--helper.c\n>> @@ -299,10 +299,10 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,\n>>\n>>       if (ce_stage(ce)) {\n>>               if (suc->recursive_prefix) {\n>> -                     strbuf_addf(out, \"Skipping unmerged submodule %s/%s\\n\",\n>> +                     strbuf_addf(out,_(\"Skipping unmerged submodule %s/%s\\n\"),\n>\n> Missing space after comma.\n>\n> Usual practice for i18n would be something like\n>\n>         struct strbuf path = STRBUF_INIT;\n>         if (suc->recursive_prefix)\n>                 strbuf_addf(&path, \"%s/%s\", suc->recursive_prefix, ce->name);\n>         else\n>                 strbuf_addstr(&path, ce->name);\n>\n>         strbuf_addf(out, _(\"Skipping unmerged submodule %s\"), path.buf);\n>         strbuf_addch(out, '\\n');\n>         strbuf_release(&path);\n>\n> Reasons:\n>  - translators shouldn't have to worry about the trailing newline\n>  - minimizing number of strings to translate\n>  - minimizing the chance that a translator typo produces an invalid path\n\nThanks for reminding me of that practice!\n\n>\n> [...]\n>> @@ -319,7 +319,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,\n>>       if (suc->update.type == SM_UPDATE_NONE\n>>           || (suc->update.type == SM_UPDATE_UNSPECIFIED\n>>               && sub->update_strategy.type == SM_UPDATE_NONE)) {\n>> -             strbuf_addf(out, \"Skipping submodule '%s'\\n\",\n>> +             strbuf_addf(out, _(\"Skipping submodule '%s'\\n\"),\n>>                           displaypath);\n>\n> Same issue here with the trailing \\n.\n>\n> If the strbuf_addf + strbuf_addch('\\n') pattern is common enough, we\n> could introduce a helper (e.g. strbuf_addf_nl) to save typing.\n\nI don't think it is common enough yet.\n\nThanks,\nStefan\n\n>\n> Thanks and hope that helps,\n> Jonathan\n"},{"id":"279839","messageId":"xmqq60x79r7k.fsf@gitster.mtv.corp.google.com","threadId":"41571","inReplyTo":"20160229193222.GX28749@google.com","subject":"Re: [PATCHv20 00/12] Expose submodule parallelism to the user","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-29T20:50:07Z","receivedAt":"2016-02-29T20:50:07Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> Stefan Beller wrote:\n>\n>> I added your suggestions as amending and as a new patch.\n>\n> I think we're at the point where patches on top would work better.  I\n> admit I was a little scared to see another reroll.\n\nYup, let's stop rerolling this during pre-release period, and\ninstead concentrate on fixing what's already in 'master' ;-)\n"}]}