{"thread":{"id":"42579","subject":"[PATCHv4 0/3] Persistent submodule pathspec specification","startedAt":"2016-06-16T02:19:52Z","lastAt":"2016-06-16T02:19:52Z","messageCount":4,"participants":["Stefan Beller"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"289002","messageId":"20160610230753.11641-1-sbeller@google.com","threadId":"42579","inReplyTo":null,"subject":"[PATCHv4 0/3] Persistent submodule pathspec specification","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-06-10T23:07:50Z","receivedAt":"2016-06-16T02:19:52Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This is a reroll of origin/sb/submodule-default-paths.\n\nIt is based on top of the current origin/sb/pathspec-label (d8e47e7d5c62e,\n2016-06-02 pathspec: disable preload-index when attribute pathspec magic is in use)\nwhich got merged with the current origin/sb/clone-shallow-passthru (d22eb0447,\nclone: add `--shallow-submodules` flag, 2016-04-25)\n\nAs the merge itself produced merge conflicts, you can find my version of\nresolving the merge at [1].\n\nAfter the resolved conflict, this reroll\n * fixes 2 typos:\n    s/Pass ultiple/Pass multiple/ in builtin/clone.c and\n    s/submodulespec/pathspec/ in Documentation/git-clone.txt\n * adds another patch on top for bash completion of clone to include\n    --recurse and --init-submodule\n\nThanks,\nStefan\n\n[1] https://github.com/stefanbeller/git/tree/submodule-default-paths\n\nStefan Beller (3):\n  submodule update: add `--init-default-path` switch\n  clone: add --init-submodule=<pathspec> switch\n  completion: clone can recurse into submodules\n\n Documentation/config.txt               |   5 ++\n Documentation/git-clone.txt            |  23 ++++--\n Documentation/git-submodule.txt        |  13 +++-\n builtin/clone.c                        |  36 ++++++++-\n contrib/completion/git-completion.bash |   2 +\n git-submodule.sh                       |  23 +++++-\n t/t7400-submodule-basic.sh             | 134 +++++++++++++++++++++++++++++++++\n 7 files changed, 221 insertions(+), 15 deletions(-)\n\n-- \n2.8.2.141.g4e46f88\n"},{"id":"289003","messageId":"20160610230753.11641-4-sbeller@google.com","threadId":"42579","inReplyTo":"20160610230753.11641-1-sbeller@google.com","subject":"[PATCH 3/3] completion: clone can recurse into submodules","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-06-10T23:07:53Z","receivedAt":"2016-06-16T02:19:52Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Signed-off-by: Stefan Beller <sbeller@google.com>\n---\n contrib/completion/git-completion.bash | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex e3918c8..3d4d24a 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1092,6 +1092,8 @@ _git_clone ()\n \t\t\t--depth\n \t\t\t--single-branch\n \t\t\t--branch\n+\t\t\t--recursive\n+\t\t\t--init-submodule\n \t\t\t\"\n \t\treturn\n \t\t;;\n-- \n2.8.2.141.g4e46f88\n"},{"id":"289004","messageId":"20160610230753.11641-2-sbeller@google.com","threadId":"42579","inReplyTo":"20160610230753.11641-1-sbeller@google.com","subject":"[PATCH 1/3] submodule update: add `--init-default-path` switch","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-06-10T23:07:51Z","receivedAt":"2016-06-16T02:19:52Z","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 | 13 ++++++++--\n git-submodule.sh                | 23 ++++++++++++++----\n t/t7400-submodule-basic.sh      | 53 +++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 88 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 59d7046..1bc50af 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2735,6 +2735,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.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/Documentation/git-submodule.txt b/Documentation/git-submodule.txt\nindex 13adebf..66f1de0 100644\n--- a/Documentation/git-submodule.txt\n+++ b/Documentation/git-submodule.txt\n@@ -13,8 +13,8 @@ SYNOPSIS\n \t      [--reference <repository>] [--depth <depth>] [--] <repository> [<path>]\n 'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]\n 'git submodule' [--quiet] init [--] [<path>...]\n-'git submodule' [--quiet] deinit [-f|--force] [--] <path>...\n-'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]\n+'git submodule' [--quiet] deinit [-f|--force] (--all|[--] <path>...)\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@@ -190,6 +190,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@@ -353,6 +357,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 07290d0..22984b5 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -8,8 +8,8 @@ dashless=$(basename \"$0\" | sed -e 's/-/ /')\n USAGE=\"[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]\n    or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]\n    or: $dashless [--quiet] init [--] [<path>...]\n-   or: $dashless [--quiet] deinit [-f|--force] [--] <path>...\n-   or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--reference <repository>] [--recursive] [--] [<path>...]\n+   or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <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@@ -624,7 +624,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@@ -687,7 +692,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 f99f674..f126fcc 100755\n--- a/t/t7400-submodule-basic.sh\n+++ b/t/t7400-submodule-basic.sh\n@@ -1078,5 +1078,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.2.141.g4e46f88\n"},{"id":"289005","messageId":"20160610230753.11641-3-sbeller@google.com","threadId":"42579","inReplyTo":"20160610230753.11641-1-sbeller@google.com","subject":"[PATCH 2/3] clone: add --init-submodule=<pathspec> switch","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-06-10T23:07:52Z","receivedAt":"2016-06-16T02:19:52Z","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 | 23 +++++++++----\n builtin/clone.c             | 36 ++++++++++++++++++--\n t/t7400-submodule-basic.sh  | 81 +++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 131 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 1b15cd7..be05a87 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,8 @@ SYNOPSIS\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  [--init-submodule <pathspec>] [--jobs <n>] [--]\n+\t  <repository> [<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..368014e 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 multiple 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,17 @@ 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\",\n+\t\t\t\t    item->string);\n+\t\t\tstring_list_append(&option_config,\n+\t\t\t\t\t   strbuf_detach(&sb, NULL));\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 f126fcc..011129e 100755\n--- a/t/t7400-submodule-basic.sh\n+++ b/t/t7400-submodule-basic.sh\n@@ -1132,4 +1132,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.2.141.g4e46f88\n"}]}