{"thread":{"id":"42406","subject":"[PATCH 0/2] Persistent submodule pathspec specification","startedAt":"2016-05-21T00:28:09Z","lastAt":"2016-05-21T00:28:11Z","messageCount":3,"participants":["Stefan Beller"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"287142","messageId":"20160521002811.24656-1-sbeller@google.com","threadId":"42406","inReplyTo":null,"subject":"[PATCH 0/2] Persistent submodule pathspec specification","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-05-21T00:28:09Z","receivedAt":"2016-05-21T00:28:09Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This was part of the former series 'submodule groups'.\nHowever the labeling was ripped out and goes in its own series\nsb/pathspec-label.\n\nFirst we introduce a switch `--init-default-path` for `git submodule update`\nwhich will read the pathspec to initialize the submodules not from the command\nline but from `submodule.defaultUpdatePath`, which can be configured permanently.\n\nThe second patch utilizes this by having `clone` set that config option\nand using that new option when calling to update the submodules.\n\nThanks,\nStefan\n\nStefan Beller (2):\n  submodule update: add `--init-default-path` switch\n  clone: add --init-submodule=<pathspec> switch\n\n 6 files changed, 216 insertions(+), 14 deletions(-)\n Documentation/config.txt        |   5 ++\n Documentation/git-clone.txt     |  25 +++++---\n Documentation/git-submodule.txt |  11 +++-\n builtin/clone.c                 |  34 +++++++++-\n git-submodule.sh                |  21 ++++++-\n t/t7400-submodule-basic.sh      | 134 ++++++++++++++++++++++++++++++++++++++++\n\n-- \n2.8.3.396.g0eed146\n"},{"id":"287144","messageId":"20160521002811.24656-2-sbeller@google.com","threadId":"42406","inReplyTo":"20160521002811.24656-1-sbeller@google.com","subject":"[PATCH 1/2] submodule update: add `--init-default-path` switch","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-05-21T00:28:10Z","receivedAt":"2016-05-21T00:28:10Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"The new switch `--init-default-path` initializes the submodules which are\nconfigured in `submodule.defaultUpdatePath` instead of those given as\ncommand line arguments before updating. In the first implementation this\nis made incompatible with further command line arguments as it is\nunclear what the user means by\n\n    git submodule update --init --init-default-path <paths>\n\nThis new switch allows to record more complex patterns as it saves\nretyping them whenever you invoke update.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n Documentation/config.txt        |  5 ++++\n Documentation/git-submodule.txt | 11 ++++++++-\n git-submodule.sh                | 21 +++++++++++++---\n t/t7400-submodule-basic.sh      | 53 +++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 86 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex e4cd291..121b236 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2786,6 +2786,11 @@ submodule.fetchJobs::\n \tin parallel. A value of 0 will give some reasonable default.\n \tIf unset, it defaults to 1.\n \n+submodule.defaultUpdatePath::\n+\tSpecifies a set of submodules to initialize when calling\n+\t`git submodule --init-default-group` by using the pathspec\n+\tsyntax.\n+\n tag.forceSignAnnotated::\n \tA boolean to specify whether annotated tags created should be GPG signed.\n \tIf `--annotate` is specified on the command line, it takes\ndiff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt\nindex 9226c43..000231e 100644\n--- a/Documentation/git-submodule.txt\n+++ b/Documentation/git-submodule.txt\n@@ -14,7 +14,7 @@ SYNOPSIS\n 'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]\n 'git submodule' [--quiet] init [--] [<path>...]\n 'git submodule' [--quiet] deinit [-f|--force] (--all|[--] <path>...)\n-'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]\n+'git submodule' [--quiet] update [--init[-default-path]] [--remote] [-N|--no-fetch]\n \t      [-f|--force] [--rebase|--merge] [--reference <repository>]\n \t      [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]\n 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]\n@@ -193,6 +193,10 @@ If the submodule is not yet initialized, and you just want to use the\n setting as stored in .gitmodules, you can automatically initialize the\n submodule with the `--init` option.\n \n+You can configure a set of submodules using pathspec syntax in\n+submodule.defaultUpdatePath you can use `--init-default-path` to initialize\n+those before updating.\n+\n If `--recursive` is specified, this command will recurse into the\n registered submodules, and update any nested submodules within.\n --\n@@ -360,6 +364,11 @@ the submodule itself.\n \tInitialize all submodules for which \"git submodule init\" has not been\n \tcalled so far before updating.\n \n+--init-default-path::\n+\tThis option is only valid for the update command.\n+\tInitialize all submodules configured in \"`submodule.defaultUpdatePath`\"\n+\tthat have not been updated before.\n+\n --name::\n \tThis option is only valid for the add command. It sets the submodule's\n \tname to the given string instead of defaulting to its path. The name\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 5a4dec0..3d12145 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -9,7 +9,7 @@ USAGE=\"[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <re\n    or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]\n    or: $dashless [--quiet] init [--] [<path>...]\n    or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...)\n-   or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--reference <repository>] [--recursive] [--] [<path>...]\n+   or: $dashless [--quiet] update [--init[-default-path]] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--reference <repository>] [--recursive] [--] [<path>...]\n    or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]\n    or: $dashless [--quiet] foreach [--recursive] <command>\n    or: $dashless [--quiet] sync [--recursive] [--] [<path>...]\"\n@@ -528,7 +528,12 @@ cmd_update()\n \t\t\tGIT_QUIET=1\n \t\t\t;;\n \t\t-i|--init)\n-\t\t\tinit=1\n+\t\t\ttest -z $init || test $init = by_args || die \"$(gettext \"Only one of --init or --init-default-path may be used.\")\"\n+\t\t\tinit=by_args\n+\t\t\t;;\n+\t\t--init-default-path)\n+\t\t\ttest -z $init || test $init = by_config || die \"$(gettext \"Only one of --init or --init-default-path may be used.\")\"\n+\t\t\tinit=by_config\n \t\t\t;;\n \t\t--remote)\n \t\t\tremote=1\n@@ -591,7 +596,17 @@ cmd_update()\n \n \tif test -n \"$init\"\n \tthen\n-\t\tcmd_init \"--\" \"$@\" || return\n+\t\tif test \"$init\" = \"by_config\"\n+\t\tthen\n+\t\t\tif test $# -gt 0\n+\t\t\tthen\n+\t\t\t\tdie \"$(gettext \"path arguments are incompatible with --init-default-path\")\"\n+\t\t\tfi\n+\t\t\tcmd_init \"--\" $(git config --get-all submodule.defaultUpdatePath) || return\n+\t\telse\n+\t\t\tcmd_init \"--\" \"$@\" || return\n+\t\tfi\n+\n \tfi\n \n \t{\ndiff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh\nindex 3570f7b..4173c72 100755\n--- a/t/t7400-submodule-basic.sh\n+++ b/t/t7400-submodule-basic.sh\n@@ -1116,5 +1116,58 @@ test_expect_success 'submodule helper list is not confused by common prefixes' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'setup superproject with submodules' '\n+\tmkdir sub1 &&\n+\t(\n+\t\tcd sub1 &&\n+\t\tgit init &&\n+\t\ttest_commit test\n+\t\ttest_commit test2\n+\t) &&\n+\tmkdir multisuper &&\n+\t(\n+\t\tcd multisuper &&\n+\t\tgit init &&\n+\t\tgit submodule add ../sub1 sub0 &&\n+\t\tgit submodule add ../sub1 sub1 &&\n+\t\tgit submodule add ../sub1 sub2 &&\n+\t\tgit submodule add ../sub1 sub3 &&\n+\t\tgit commit -m \"add some submodules\"\n+\t)\n+'\n+\n+cat >expect <<-EOF\n+-sub0\n+ sub1 (test2)\n+ sub2 (test2)\n+ sub3 (test2)\n+EOF\n+\n+test_expect_success 'submodule update --init with a specification' '\n+\ttest_when_finished \"rm -rf multisuper_clone\" &&\n+\tpwd=$(pwd) &&\n+\tgit clone file://\"$pwd\"/multisuper multisuper_clone &&\n+\t(\n+\t\tcd multisuper_clone &&\n+\t\tgit submodule update --init . \":(exclude)sub0\" &&\n+\t\tgit submodule status |cut -c 1,43- >../actual\n+\t) &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'submodule update --init-default-path' '\n+\ttest_when_finished \"rm -rf multisuper_clone\" &&\n+\tpwd=$(pwd) &&\n+\tgit clone file://\"$pwd\"/multisuper multisuper_clone &&\n+\t(\n+\t\tcd multisuper_clone &&\n+\t\tgit config submodule.defaultUpdatePath \".\" &&\n+\t\tgit config --add submodule.defaultUpdatePath \":(exclude)sub0\" &&\n+\t\tgit submodule update --init-default-path &&\n+\t\tgit submodule status |cut -c 1,43- >../actual &&\n+\t\ttest_must_fail git submodule update --init-default-path sub0\n+\t) &&\n+\ttest_cmp expect actual\n+'\n \n test_done\n-- \n2.8.3.395.ga2acc22.dirty\n"},{"id":"287143","messageId":"20160521002811.24656-3-sbeller@google.com","threadId":"42406","inReplyTo":"20160521002811.24656-1-sbeller@google.com","subject":"[PATCH 2/2] clone: add --init-submodule=<pathspec> switch","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-05-21T00:28:11Z","receivedAt":"2016-05-21T00:28:11Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"The new switch passes the pathspec to `git submodule update --init`\nwhich is called after the actual clone is done.\n\nAdditionally this configures the submodule.defaultUpdatePath to\nbe the given pathspec, such that any future invocation of\n`git submodule update --init-default-paths` will keep up\nwith the pathspec.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n Documentation/git-clone.txt | 25 +++++++++-----\n builtin/clone.c             | 34 +++++++++++++++++--\n t/t7400-submodule-basic.sh  | 81 +++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 130 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 1b15cd7..33e5894 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -14,8 +14,9 @@ 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] [--[no-]shallow-submodules]\n-\t  [--jobs <n>] [--] <repository> [<directory>]\n+\t  [--recursive | --recurse-submodules] [--jobs <n>]\n+\t  [--init-submodule <submodulespec>] [--] <repository>\n+\t  [<directory>]\n \n DESCRIPTION\n -----------\n@@ -207,12 +208,20 @@ objects from the source repository into a pack in the cloned repository.\n \n --recursive::\n --recurse-submodules::\n-\tAfter the clone is created, initialize all submodules within,\n-\tusing their default settings. This is equivalent to running\n-\t`git submodule update --init --recursive` immediately after\n-\tthe clone is finished. This option is ignored if the cloned\n-\trepository does not have a worktree/checkout (i.e. if any of\n-\t`--no-checkout`/`-n`, `--bare`, or `--mirror` is given)\n+\tAfter the clone is created, initialize and clone all submodules\n+\twithin, using their default settings. This is equivalent to\n+\trunning `git submodule update --recursive --init `\n+\timmediately after the clone is finished. This option is ignored\n+\tif the cloned repository does not have a worktree/checkout (i.e.\n+\tif any of `--no-checkout`/`-n`, `--bare`, or `--mirror` is given)\n+\n+--init-submodule::\n+\tAfter the clone is created, initialize and clone specified\n+\tsubmodules within, using their default settings. It is possible\n+\tto give multiple specifications by giving this argument multiple\n+\ttimes. This is equivalent to configure `submodule.defaultUpdateGroup`\n+\tand then running `git submodule update --init-default-path`\n+\timmediately after the clone is finished.\n \n --[no-]shallow-submodules::\n \tAll submodules which are cloned will be shallow with a depth of 1.\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 5f867e6..56967f9 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -53,6 +53,16 @@ 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+static struct string_list init_submodules;\n+\n+static int init_submodules_cb(const struct option *opt, const char *arg, int unset)\n+{\n+\tif (unset)\n+\t\treturn -1;\n+\n+\tstring_list_append((struct string_list *)opt->value, arg);\n+\treturn 0;\n+}\n \n static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n@@ -103,6 +113,9 @@ static struct option builtin_clone_options[] = {\n \t\t\tTRANSPORT_FAMILY_IPV4),\n \tOPT_SET_INT('6', \"ipv6\", &family, N_(\"use IPv6 addresses only\"),\n \t\t\tTRANSPORT_FAMILY_IPV6),\n+\tOPT_CALLBACK(0, \"init-submodule\", &init_submodules, N_(\"<pathspec>\"),\n+\t\t\tN_(\"clone specific submodules. Pass ultiple times for complex pathspecs\"),\n+\t\t\tinit_submodules_cb),\n \tOPT_END()\n };\n \n@@ -734,14 +747,22 @@ 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+\tif (!err && (option_recursive || init_submodules.nr > 0)) {\n \t\tstruct argv_array args = ARGV_ARRAY_INIT;\n-\t\targv_array_pushl(&args, \"submodule\", \"update\", \"--init\", \"--recursive\", NULL);\n+\t\targv_array_pushl(&args, \"submodule\", \"update\", NULL);\n+\n+\t\tif (init_submodules.nr > 0)\n+\t\t\targv_array_pushf(&args, \"--init-default-path\");\n+\t\telse\n+\t\t\targv_array_pushf(&args, \"--init\");\n \n \t\tif (option_shallow_submodules == 1\n \t\t    || (option_shallow_submodules == -1 && option_depth))\n \t\t\targv_array_push(&args, \"--depth=1\");\n \n+\t\tif (option_recursive)\n+\t\t\targv_array_pushf(&args, \"--recursive\");\n+\n \t\tif (max_jobs != -1)\n \t\t\targv_array_pushf(&args, \"--jobs=%d\", max_jobs);\n \n@@ -883,6 +904,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\toption_no_checkout = 1;\n \t}\n \n+\tif (init_submodules.nr > 0) {\n+\t\tstruct string_list_item *item;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tfor_each_string_list_item(item, &init_submodules) {\n+\t\t\tstrbuf_addf(&sb, \"submodule.defaultUpdatePath=%s\", item->string);\n+\t\t\tstring_list_append(&option_config, strbuf_detach(&sb, 0));\n+\t\t}\n+\t}\n+\n \tif (!option_origin)\n \t\toption_origin = \"origin\";\n \ndiff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh\nindex 4173c72..af5d0ce 100755\n--- a/t/t7400-submodule-basic.sh\n+++ b/t/t7400-submodule-basic.sh\n@@ -1170,4 +1170,85 @@ test_expect_success 'submodule update --init-default-path' '\n \ttest_cmp expect actual\n '\n \n+cat <<EOF > expected\n+ sub0 (test2)\n+-sub1\n+-sub2\n+-sub3\n+EOF\n+\n+test_expect_success 'clone --init-submodule works' '\n+\ttest_when_finished \"rm -rf multisuper_clone\" &&\n+\tgit clone --recurse-submodules --init-submodule=\"sub0\" multisuper multisuper_clone &&\n+\t(\n+\t\tcd multisuper_clone &&\n+\t\tgit submodule status |cut -c 1,43- >../actual\n+\t) &&\n+\ttest_cmp actual expected\n+'\n+\n+cat <<EOF > expect\n+-sub0\n+ sub1 (test2)\n+-sub2\n+ sub3 (test2)\n+EOF\n+test_expect_success 'clone with multiple --init-submodule options' '\n+\ttest_when_finished \"rm -rf multisuper_clone\" &&\n+\tgit clone --recurse-submodules \\\n+\t\t  --init-submodule=\".\" \\\n+\t\t  --init-submodule \":(exclude)sub0\" \\\n+\t\t  --init-submodule \":(exclude)sub2\" \\\n+\t\t\tmultisuper multisuper_clone &&\n+\t(\n+\t\tcd multisuper_clone &&\n+\t\tgit submodule status |cut -c1,43- >../actual\n+\t) &&\n+\ttest_cmp expect actual\n+'\n+\n+cat <<EOF > expect\n+-sub0\n+ sub1 (test2)\n+-sub2\n+ sub3 (test2)\n+EOF\n+\n+cat <<EOF > expect2\n+-sub0\n+ sub1 (test2)\n+-sub2\n+ sub3 (test2)\n+-sub4\n+ sub5 (test2)\n+EOF\n+\n+test_expect_success 'clone and subsequent updates correctly auto-initialize submodules' '\n+\ttest_when_finished \"rm -rf multisuper_clone\" &&\n+\tgit clone --recurse-submodules --init-submodule=\".\" \\\n+\t\t\t\t       --init-submodule \":(exclude)sub0\" \\\n+\t\t\t\t       --init-submodule \":(exclude)sub2\" \\\n+\t\t\t\t       --init-submodule \":(exclude)sub4\" \\\n+\t\t\t\t       multisuper multisuper_clone &&\n+\t(\n+\t\tcd multisuper_clone &&\n+\t\tgit submodule status |cut -c1,43- >../actual\n+\t) &&\n+\ttest_cmp expect actual &&\n+\t(\n+\t\tcd multisuper &&\n+\t\tgit submodule add ../sub1 sub4 &&\n+\t\tgit submodule add ../sub1 sub5 &&\n+\t\tgit commit -m \"add more submodules\"\n+\t) &&\n+\t(\n+\t\tcd multisuper_clone &&\n+\t\t# obtain the new superproject\n+\t\tgit pull &&\n+\t\tgit submodule update --init-default-path &&\n+\t\tgit submodule status |cut -c1,43- >../actual\n+\t) &&\n+\ttest_cmp expect2 actual\n+'\n+\n test_done\n-- \n2.8.3.395.ga2acc22.dirty\n"}]}