{"thread":{"id":"11615","subject":"[PATCH] - Introduced 'recurse' command for git-submodule","startedAt":"2008-01-14T03:23:00Z","lastAt":"2008-01-14T03:23:00Z","messageCount":1,"participants":["imyousuf@gmail.com"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"65294","messageId":"1200280980-20081-1-git-send-email-imyousuf@gmail.com","threadId":"11615","inReplyTo":null,"subject":"[PATCH] - Introduced 'recurse' command for git-submodule","fromName":"","fromEmail":"imyousuf@gmail.com","sentAt":"2008-01-14T03:23:00Z","receivedAt":"2008-01-14T03:23:00Z","isPatch":true,"sender":{"key":"imyousuf@gmail.com","avatar":"https://gravatar.com/avatar/fda3c870262849d03c7b9c4d288842e128d6d80769fa7bc2d22731b7597928be?d=mp&s=160"},"body":"From: Imran M Yousuf <imyousuf@smartitengineering.com>\n\n- The purpose of the recurse command in the git submodule is to recurse\na command in its submodule. For example if one wants to do a diff on its\nproject with submodules at once, one can simply do\n\tgit-submodule recurse diff HEAD\nand would see the diff for all the modules it contains.\n\n- The recurse commands behavior can be customized with several arguments\nthat it accepts. The synopsis for the recurse command is:\n\n\tgit-submodule [-q|--quiet] recurse [-i|--initialize]\n\t[-e|--exit-after-error] [-d|--depth <recursion depth>]\n\t[-df|--depth-first] [-ca|--customized-argument] [-p|--pre-command]\n\t<command> [<arguments> ...]\n\n- When traversing modules, a module could be uninitialized that is git\nsubmodule init and update has not been called for it; if [-i|--initialize]\noption is specified, it will initialize any module that is not initialized;\nelse if the module is not initialized it will simply skip it.\n\n- There are commands that can fail for a certain submodule but succeed for\nothers; if one wants to stop execution once the top level module's execution\nfails, one can specify [-e|--exit-after-error]. It will ensure that once\nexecution of git <command> fails in the top level module it will not recurse\ninto its submodules.\n\n- If the project has submodule hierarchy upto n depth and we want to restrict\nrecursion to (n-p) depth; we can use the [-d|--depth <recursion depth>] option.\nValue has to be greater than 0 and command will at least recurse into the first\ndepth. If depth is specified to p than all depths <= p will be recursed over.\n\n- While discussion on the recurse command one thing which was put forward\nin several occassions is that there might be scenario where a command should be\nexecuted over the child module before the parent module. For such scenario\n[-df|--depth-first] option can be used; one use case in particualar presented\nas an example is git commit; where almost everybody mentioned that they prefer\nto commit the child module before the parent and -df will enable just that.\nE.g. p -> a, b, c, e; a ->d is a module structure. If the following command is\nused,\n\tgit submodule recurse -df commit -a\nit will execute git commit -a in the following sequence - d, a, b, c, e, p.\n\n- There is also another scenario which has been put forward several times in\ndiscussion over the recurse command and it is that commands chould have\ndifferent arguments for different modules. For example for the same example\nmentioned above, one wants to check a_1 for submdoule a, while it wants to\ncheckout d_2 for d. It can be achieved by using [-ca|--customized-argument].\nThis results the script to prompt for user input, which will be passed as\nargument to the command for that module.\n\tgit submodule recurse -ca checkout\n\tWorking in mod a .......\n\tPlease provide arguments for this module: a_1\n\tWorking in mod d .......\n\tPlease provide arguments for this module: a_1\n\n- I usually found that when typing a command being able to see some options\ncome in handy. For example if I can see the available branches before checking\nout a branch that would be useful, IOW, if I could git branch before git\ncheckout; it is now possible using the [-p|--pre-command] option. Using this\ncommand you can actually execute other git commands before specifying the\narguments to the original command. E.g. if the above command is changed to,\n\tgit submodule recurse -ca -p checkout\nit will prompt the user for the pre command until one is satisfied and later\nthe user can actually use them in the argument.\n\nSigned-off-by: Imran M Yousuf <imyousuf@smartitengineering.com>\n---\n git-submodule.sh |  183 +++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 182 insertions(+), 1 deletions(-)\n\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 22e7e5f..ceca985 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -9,7 +9,8 @@\n # git-submodule [-q|--quiet] [status] [-c|--cached] [--] [<path>...]\n # git-submodule [-q|--quiet] init [--] [<path>...]\n # git-submodule [-q|--quiet] update [--] [<path>...]\n-USAGE='[-q|--quiet] [[[add [-b|--branch branch] <repo>]|[[[status [-c|--cached]]|init|update] [--]]]  [<path>...]]'\n+# git-submodule [-q|--quiet] recurse [-i|--initialize] [-e|--exit-after-error] [-d|--depth <recursion depth>] [-df|--depth-first] [-ca|--customized-argument] [-p|--pre-command] <command> [<arguments> ...]\n+USAGE='[-q|--quiet] [[[add [-b|--branch branch] <repo>]|[[[status [-c|--cached]]|init|update] [--]]]  [<path>...]]|[recurse [-i|--initialize] [-e|--exit-after-error] [-d|--depth <recursion depth>] [-df|--depth-first] [-ca|--customized-argument] [-p|--pre-command] <command> [<arguments> ...]]'\n OPTIONS_SPEC=\n . git-sh-setup\n require_work_tree\n@@ -21,6 +22,14 @@ branch=\n quiet=\n cached=\n command=\n+depth=0\n+current_depth=0\n+auto_initialize=\n+depth_first=\n+use_custom_args=\n+custom_args=\n+pre_cmd=\n+on_error=\n \n #\n # print stuff on stdout unless -q was specified\n@@ -359,7 +368,174 @@ check_for_terminator()\n \tfi\n }\n \n+# Initializes the submodule if already not initialized\n+# and auto initialize is enabled\n+initialize_sub_module()\n+{\n+\tif test ! -d \"$1\"/.git &&\n+\t   test -n \"$auto_initialize\"\n+\tthen\n+\t\tsay \"Initializing and updating $1\"\n+\t\tgit-submodule init \"$1\" &&\n+\t\tgit-submodule update \"$1\" &&\n+\t\treturn 0\n+\t# Returns true if module is already initialized\n+\telif test -d \"$1\"/.git\n+\tthen\n+\t\treturn 0\n+\tfi\n+\tsay \"Module $1 is not initialized and skipped\"\n+\treturn 1\n+}\n+\n+# Take command from user and execute it until user wants to discontinue\n+do_pre_command()\n+{\n+\tsay \"Starting pre-comamnd execution!\"\n+\twhile :\n+\tdo\n+\t\t(\n+\t\t\tread -p \"Please provide a git command: \" pre_command\n+\t\t\ttest -z \"$pre_command\" || git \"$pre_command\"\n+\t\t)\n+\t\tread -p \"Press y to continue with another git command... \" keypress\n+\t\tif test \"$keypress\" != \"y\" &&\n+\t\t\ttest \"$keypress\" != \"Y\"\n+\t\tthen\n+\t\t\tbreak\n+\t\tfi\n+\tdone\n+}\n \n+# Take arguments from user to pass as custom arguments\n+get_custom_args()\n+{\n+\tread -p \"Please provide arguments for this module: \" custom_args\n+}\n+\n+traverse_submodule()\n+{\n+\t# If current depth is the range specified than it will continue\n+\t# else return with success\n+\tif test \"$depth\" -gt 0 &&\n+\t\ttest \"$current_depth\" -ge \"$depth\"\n+\tthen\n+\t\treturn 0;\n+\tfi\n+\t# If submodules exists than it will traverse over them\n+\tif test -f .gitmodules\n+\tthen\n+\t\t# Incrementing the depth for the next level of submodules\n+\t\tcurrent_depth=$(($current_depth + 1))\n+                for mod_path in `sed -n -e 's/path = //p' .gitmodules`; do\n+                        traverse_module \"$mod_path\" \"$@\"\n+                done\n+\t\t# Decremented the depth to bring it back to the depth of\n+\t\t# the current module\n+\t\tcurrent_depth=$(($current_depth - 1))\n+\tfi\n+}\n+\n+# This actually traverses the module; checks\n+# whether the module is initialized or not.\n+# if not initialized, then done so and then the\n+# intended command is evaluated. Then it\n+# recursively goes into it modules.\n+traverse_module()\n+{\n+\t# Will work in the module if and only if the module is initialized\n+\tinitialize_sub_module \"$1\" &&\n+\t(\n+\t\tsubmod_path=\"$1\"\n+\t\tshift\n+\t\tcd \"$submod_path\"\n+\t\t# If depth-first is specified in that case submodules are\n+\t\t# are traversed before executing the command on this module\n+\t\ttest -n \"$depth_first\" && traverse_submodule \"$@\"\n+\t\t# pwd is mentioned in order to enable the ser to distinguish\n+\t\t# between same name modules, e.g. a/lib and b/lib.\n+\t\tsay \"Working in mod $submod_path\" @ `pwd` \"with $@ ($#)\"\n+\t\ttest -n \"$pre_cmd\" && do_pre_command\n+\t\ttest -n \"$use_custom_args\" && get_custom_args\n+\t\tcmd_status=\n+\t\tgit \"$@\" \"$custom_args\" || cmd_status=1\n+\t\t# if exit on error is specifed than script will exit if any\n+\t\t# command fails. As there is no transaction there will be\n+\t\t# no rollback either\n+\t\tif  test -n \"$cmd_status\" && test -n \"$on_error\"\n+\t\tthen\n+\t\t\tdie \"git $@ failed in module $submod_path @ $(pwd)\"\n+\t\tfi\n+\t\t# If depth-first is not specified in that case submodules are\n+\t\t# are traversed after executing the command on this module\n+\t\ttest -z \"$depth_first\" && traverse_submodule \"$@\"\n+\t)\n+}\n+\n+# Propagates or recurses over all the submodules at any\n+# depth with any git command, e.g. git-clone, git-status,\n+# git-commit etc., with the arguments supplied exactly as\n+# it would have been supplied to the command otherwise.\n+# This actually starts the recursive propagation\n+modules_recurse() {\n+\twhile :\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t\t-d|--depth)\n+\t\t\t\tshift\n+\t\t\t\tif test -z \"$1\"\n+\t\t\t\tthen\n+\t\t\t\t\techo \"No <recursion depth> specified\"\n+\t\t\t\t\tusage\n+\t\t\t\t# Arithmatic operation will give an error if depth is not number\n+\t\t\t\t# thus chose to check intergerness with regular expression\n+\t\t\t\telif test \"$(expr $1 : '[1-9][0-9]*')\" -eq \"$(expr $1 : '.*')\"\n+\t\t\t\tthen\n+\t\t\t\t\tdepth=\"$1\"\n+\t\t\t\telse\n+\t\t\t\t\techo \"<recursion depth> not an integer\"\n+\t\t\t\t\tusage\n+\t\t\t\tfi\n+\t\t\t\t;;\n+\t\t\t-df|--depth-first)\n+\t\t\t\tdepth_first=1\n+\t\t\t\t;;\n+\t\t\t-e|--exit-after-error)\n+\t\t\t\ton_error=1\n+\t\t\t\t;;\n+\t\t\t-i|--initialize)\n+\t\t\t\tauto_initialize=1\n+\t\t\t\t;;\n+\t\t\t-p|--pre-command)\n+\t\t\t\tpre_cmd=1\n+\t\t\t\t;;\n+\t\t\t-ca|--customized-argument)\n+\t\t\t\tuse_custom_args=1\n+\t\t\t\t;;\n+\t\t\t-*)\n+\t\t\t\tusage\n+\t\t\t\t;;\n+\t\t\t*)\n+\t\t\t\tbreak\n+\t\t\t\t;;\n+\t\tesac\n+\t\tshift\n+\tdone\n+\ttest \"$#\" -le 0 && die \"No git command specified\"\n+\tproject_home=\"$(pwd)\"\n+\tsay \"Project Home: $project_home\"\n+\tif test \"$depth\" -gt 0\n+\tthen\n+\t\tsay Command will recurse upto \"$depth\" depth\n+\tfi\n+\tif test -d \"$project_home\"/.git/\n+\tthen\n+\t\tsay \"Command to recurse: git $@\"\n+\t\ttraverse_module . \"$@\"\n+\telse\n+\t\tdie \"$project_home not a git repo thus exiting\"\n+\tfi\n+}\n \n # Command synopsis clearly shows that all arguments after\n # subcommand are arguments to the command itself. Thus\n@@ -394,6 +570,11 @@ do\n \t-c|--cached)\n \t\tcached=1\n \t\t;;\n+\trecurse)\n+\t\tcommand=\"modules_$1\"\n+\t\tshift\n+\t\tbreak\n+\t\t;;\n \t--)\n \t\t# It is shifted so that it is not passed\n \t\t# as an argument to the default subcommand\n-- \n1.5.3.7\n"}]}