Re: [PATCH v1 0/5] Incremental rewrite of git-submodules: git-foreach
Since due to some reason, the previous patch-series list was unavailable on the mailing list, I have re-posted the series. It is available at: https://public-inbox.org/git/20180202045745.5076-1-pc44...@gmail.com/ Thanks, Prathamesh Chavan
[PATCH v1 5/5] submodule: port submodule subcommand 'foreach' from shell to C
This aims to make git-submodule foreach a builtin. This is the very first step taken in this direction. Hence, 'foreach' is ported to submodule--helper, and submodule--helper is called from git-submodule.sh. The code is split up to have one function to obtain all the list of submodules. This function acts as the front-end of git-submodule foreach subcommand. It calls the function for_each_listed_submodule(), which basically loops through the list and calls function fn, which in this case is runcommand_in_submodule_cb(). This third function is a callback function that calls runcommand_in_submodule() with the appropriate parameters and then takes care of running the command in that submodule, and recursively performing the same when --recursive is flagged. The first function module_foreach first parses the options present in argv, and then with the help of module_list_compute(), generates the list of submodules present in the current working tree. The second function for_each_listed_submodule() traverses through the list, and calls function fn (which in case of submodule subcommand foreach is runcommand_in_submodule_cb()) is called for each entry. The third function runcommand_in_submodule_cb() calls the function runcommand_in_submodule() after passing appropraite parameters. The fourth function runcommand_in_submodule(), generates a submodule struct sub for $name, value and then later prepends name=sub->name; and other value assignment to the env argv_array structure of a child_process. Also the of submodule-foreach is push to args argv_array structure and finally, using run_command the commands are executed using a shell. The fourth function also takes care of the recursive flag, by creating a separate child_process structure and prepending "--super-prefix displaypath", to the args argv_array structure. Other required arguments and the input of submodule-foreach is also appended to this argv_array. Helped-by: Brandon Williams <bmw...@google.com> Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 151 git-submodule.sh| 39 +--- 2 files changed, 152 insertions(+), 38 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a5c4a8a69..46dee6bf5 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -718,6 +718,156 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct cb_foreach { + int argc; + const char **argv; + const char *prefix; + unsigned int flags; +}; +#define CB_FOREACH_INIT { 0, NULL, NULL, 0 } + +static void runcommand_in_submodule(const char *path, const struct object_id *ce_oid, + int argc, const char **argv, const char *prefix, + unsigned int flags) +{ + const struct submodule *sub; + struct child_process cp = CHILD_PROCESS_INIT; + char *displaypath; + + displaypath = get_submodule_displaypath(path, prefix); + + sub = submodule_from_path(_oid, path); + + if (!sub) + die(_("No url found for submodule path '%s' in .gitmodules"), + displaypath); + + if (!is_submodule_populated_gently(path, NULL)) + goto cleanup; + + prepare_submodule_repo_env(_array); + + /* +* For the purpose of executing in the submodule, +* separate shell is used for the purpose of running the +* child process. +*/ + cp.use_shell = 1; + cp.dir = path; + + /* +* NEEDSWORK: the command currently has access to the variables $name, +* $sm_path, $displaypath, $sha1 and $toplevel only when the command +* contains a single argument. This is done for maintianing a faithful +* translation from shell script. +*/ + if (argc == 1) { + char *toplevel = xgetcwd(); + + argv_array_pushf(_array, "name=%s", sub->name); + argv_array_pushf(_array, "sm_path=%s", path); + argv_array_pushf(_array, "displaypath=%s", displaypath); + argv_array_pushf(_array, "sha1=%s", +oid_to_hex(ce_oid)); + argv_array_pushf(_array, "toplevel=%s", toplevel); + + /* +* Since the path variable was accessible from the script +* before porting, it is also made available after porting. +* The environment variable "PATH" has a very special purpose +* on windows. And since environment variables are +* case-insensitive in windows, it interferes w
[PATCH v1 2/5] submodule foreach: document '$sm_path' instead of '$path'
As using a variable '$path' may be harmful to users due to capitalization issues, see 64394e3ae9 (git-submodule.sh: Don't use $path variable in eval_gettext string, 2012-04-17). Adjust the documentation to advocate for using $sm_path, which contains the same value. We still make the 'path' variable available and document it as a deprecated synonym of 'sm_path'. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- Documentation/git-submodule.txt | 10 ++ 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index ff612001d..a23baef62 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -183,12 +183,14 @@ information too. foreach [--recursive] :: Evaluates an arbitrary shell command in each checked out submodule. - The command has access to the variables $name, $path, $sha1 and + The command has access to the variables $name, $sm_path, $sha1 and $toplevel: $name is the name of the relevant submodule section in `.gitmodules`, - $path is the name of the submodule directory relative to the - superproject, $sha1 is the commit as recorded in the superproject, - and $toplevel is the absolute path to the top-level of the superproject. + $sm_path is the path of the submodule as recorded in the superproject, + $sha1 is the commit as recorded in the superproject, and + $toplevel is the absolute path to the top-level of the superproject. + Note that to avoid conflicts with '$PATH' on Windows, the '$path' + variable is now a deprecated synonym of '$sm_path' variable. Any submodules defined in the superproject but not checked out are ignored by this command. Unless given `--quiet`, foreach prints the name of each submodule before evaluating the command. -- 2.15.1
[PATCH v1 4/5] submodule foreach: document variable '$displaypath'
It was observed that the variable '$displaypath' was accessible but undocumented. Hence, document it. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- Documentation/git-submodule.txt | 6 -- t/t7407-submodule-foreach.sh| 22 +++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 8e7930ebc..0cca702cb 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -183,10 +183,12 @@ information too. foreach [--recursive] :: Evaluates an arbitrary shell command in each checked out submodule. - The command has access to the variables $name, $sm_path, $sha1 and - $toplevel: + The command has access to the variables $name, $sm_path, $displaypath, + $sha1 and $toplevel: $name is the name of the relevant submodule section in `.gitmodules`, $sm_path is the path of the submodule as recorded in the superproject, + $displaypath contains the relative path from the current working + directory to the submodules root directory, $sha1 is the commit as recorded in the superproject, and $toplevel is the absolute path to its superproject, such that $toplevel/$sm_path is the absolute path of the submodule. diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 0663622a4..6ad57e061 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -82,16 +82,16 @@ test_expect_success 'test basic "submodule foreach" usage' ' cat >expect <../../actual + git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual ) && test_i18ncmp expect actual ' @@ -206,25 +206,25 @@ submodulesha1=$(cd clone2/nested1/nested2/nested3/submodule && git rev-parse HEA cat >expect <../../actual + git submodule foreach --recursive "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual ) && test_i18ncmp expect actual ' -- 2.15.1
[PATCH v1 3/5] submodule foreach: clarify the '$toplevel' variable documentation
It does not contain the topmost superproject as the author assumed, but the direct superproject, such that $toplevel/$sm_path is the actual absolute path of the submodule. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- Documentation/git-submodule.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index a23baef62..8e7930ebc 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -188,7 +188,8 @@ foreach [--recursive] :: $name is the name of the relevant submodule section in `.gitmodules`, $sm_path is the path of the submodule as recorded in the superproject, $sha1 is the commit as recorded in the superproject, and - $toplevel is the absolute path to the top-level of the superproject. + $toplevel is the absolute path to its superproject, such that + $toplevel/$sm_path is the absolute path of the submodule. Note that to avoid conflicts with '$PATH' on Windows, the '$path' variable is now a deprecated synonym of '$sm_path' variable. Any submodules defined in the superproject but not checked out are -- 2.15.1
[PATCH v1 0/5] Incremental rewrite of git-submodules
Following series of patches focuses on porting submodule subcommand git-foreach from shell to C. An initial attempt for porting was introduced about 9 months back, and since then then patches have undergone many changes. Some of the notable discussion thread which I would like to point out is: [1] The previous version of this patch series which was floated is available at: [2]. The following changes were made to that: * As it was observed in other submodule subcommand's ported function that the number of params increased a lot, the variables quiet and recursive, were replaced in the cb_foreach struct with a single unsigned integer variable called flags. * To accomodate the possiblity of a direct call to the functions runcommand_in_submodule(), callback function runcommand_in_submodule_cb() was introduced. [1]: https://public-inbox.org/git/20170419170513.16475-1-pc44...@gmail.com/T/#u [2]: https://public-inbox.org/git/20170807211900.15001-14-pc44...@gmail.com/ As before you can find this series at: https://github.com/pratham-pc/git/commits/patch-series-3 And its build report is available at: https://travis-ci.org/pratham-pc/git/builds/ Branch: patch-series-3 Build #202 Prathamesh Chavan (5): submodule foreach: correct '$path' in nested submodules from a subdirectory submodule foreach: document '$sm_path' instead of '$path' submodule foreach: clarify the '$toplevel' variable documentation submodule foreach: document variable '$displaypath' submodule: port submodule subcommand 'foreach' from shell to C Documentation/git-submodule.txt | 15 ++-- builtin/submodule--helper.c | 151 git-submodule.sh| 40 +-- t/t7407-submodule-foreach.sh| 38 +- 4 files changed, 197 insertions(+), 47 deletions(-) -- 2.15.1
[PATCH v1 1/5] submodule foreach: correct '$path' in nested submodules from a subdirectory
When running 'git submodule foreach' from a subdirectory of your repository, nested submodules get a bogus value for $sm_path: For a submodule 'sub' that contains a nested submodule 'nested', running 'git -C dir submodule foreach echo $path' would report path='../nested' for the nested submodule. The first part '../' is derived from the logic computing the relative path from $pwd to the root of the superproject. The second part is the submodule path inside the submodule. This value is of little use and is hard to document. There are two different possible solutions that have more value: (a) The path value is documented as the path from the toplevel of the superproject to the mount point of the submodule. In this case we would want to have path='sub/nested'. (b) As Ramsay noticed the documented value is wrong. For the non-nested case the path is equal to the relative path from $pwd to the submodules working directory. When following this model, the expected value would be path='../sub/nested'. The behavior for (b) was introduced in 091a6eb0fe (submodule: drop the top-level requirement, 2013-06-16) the intent for $path seemed to be relative to $cwd to the submodule worktree, but that did not work for nested submodules, as the intermittent submodules were not included in the path. If we were to fix the meaning of the $path using (a) such that "path" is "the path from the toplevel of the superproject to the mount point of the submodule", we would break any existing submodule user that runs foreach from non-root of the superproject as the non-nested submodule '../sub' would change its path to 'sub'. If we would fix the meaning of the $path using (b), such that "path" is "the relative path from $pwd to the submodule", then we would break any user that uses nested submodules (even from the root directory) as the 'nested' would become 'sub/nested'. Both groups can be found in the wild. The author has no data if one group outweighs the other by large margin, and offending each one seems equally bad at first. However in the authors imagination it is better to go with (a) as running from a sub directory sounds like it is carried out by a human rather than by some automation task. With a human on the keyboard the feedback loop is short and the changed behavior can be adapted to quickly unlike some automation that can break silently. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> Signed-off-by: Stefan Beller <sbel...@google.com> --- git-submodule.sh | 1 - t/t7407-submodule-foreach.sh | 36 ++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/git-submodule.sh b/git-submodule.sh index 156255a9e..7305ee25f 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -345,7 +345,6 @@ cmd_foreach() prefix="$prefix$sm_path/" sanitize_submodule_env cd "$sm_path" && - sm_path=$(git submodule--helper relative-path "$sm_path" "$wt_prefix") && # we make $path available to scripts ... path=$sm_path && if test $# -eq 1 diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 6ba5daf42..0663622a4 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -82,9 +82,9 @@ test_expect_success 'test basic "submodule foreach" usage' ' cat >expect <expect <../../actual + ) && + test_i18ncmp expect actual +' cat > expect <
[PATCH v1 0/5] Incremental rewrite of git-submodules: git-foreach
Following series of patches focuses on porting submodule subcommand git-foreach from shell to C. An initial attempt for porting was introduced about 9 months back, and since then then patches have undergone many changes. Some of the notable discussion thread which I would like to point out is: [1] The previous version of this patch series which was floated is available at: [2]. The following changes were made to that: * As it was observed in other submodule subcommand's ported function that the number of params increased a lot, the variables quiet and recursive, were replaced in the cb_foreach struct with a single unsigned integer variable called flags. * To accomodate the possiblity of a direct call to the functions runcommand_in_submodule(), callback function runcommand_in_submodule_cb() was introduced. [1]: https://public-inbox.org/git/20170419170513.16475-1-pc44...@gmail.com/T/#u [2]: https://public-inbox.org/git/20170807211900.15001-14-pc44...@gmail.com/ As before you can find this series at: https://github.com/pratham-pc/git/commits/patch-series-3 And its build report is available at: https://travis-ci.org/pratham-pc/git/builds/ Branch: patch-series-3 Build #202 Prathamesh Chavan (5): submodule foreach: correct '$path' in nested submodules from a subdirectory submodule foreach: document '$sm_path' instead of '$path' submodule foreach: clarify the '$toplevel' variable documentation submodule foreach: document variable '$displaypath' submodule: port submodule subcommand 'foreach' from shell to C Documentation/git-submodule.txt | 15 ++-- builtin/submodule--helper.c | 151 git-submodule.sh| 40 +-- t/t7407-submodule-foreach.sh| 38 +- 4 files changed, 197 insertions(+), 47 deletions(-) -- 2.15.1
[PATCH v1 4/5] submodule foreach: document variable '$displaypath'
It was observed that the variable '$displaypath' was accessible but undocumented. Hence, document it. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- Documentation/git-submodule.txt | 6 -- t/t7407-submodule-foreach.sh| 22 +++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 8e7930ebc..0cca702cb 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -183,10 +183,12 @@ information too. foreach [--recursive] :: Evaluates an arbitrary shell command in each checked out submodule. - The command has access to the variables $name, $sm_path, $sha1 and - $toplevel: + The command has access to the variables $name, $sm_path, $displaypath, + $sha1 and $toplevel: $name is the name of the relevant submodule section in `.gitmodules`, $sm_path is the path of the submodule as recorded in the superproject, + $displaypath contains the relative path from the current working + directory to the submodules root directory, $sha1 is the commit as recorded in the superproject, and $toplevel is the absolute path to its superproject, such that $toplevel/$sm_path is the absolute path of the submodule. diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 0663622a4..6ad57e061 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -82,16 +82,16 @@ test_expect_success 'test basic "submodule foreach" usage' ' cat >expect <../../actual + git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual ) && test_i18ncmp expect actual ' @@ -206,25 +206,25 @@ submodulesha1=$(cd clone2/nested1/nested2/nested3/submodule && git rev-parse HEA cat >expect <../../actual + git submodule foreach --recursive "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual ) && test_i18ncmp expect actual ' -- 2.15.1
[PATCH v1 5/5] submodule: port submodule subcommand 'foreach' from shell to C
This aims to make git-submodule foreach a builtin. This is the very first step taken in this direction. Hence, 'foreach' is ported to submodule--helper, and submodule--helper is called from git-submodule.sh. The code is split up to have one function to obtain all the list of submodules. This function acts as the front-end of git-submodule foreach subcommand. It calls the function for_each_listed_submodule(), which basically loops through the list and calls function fn, which in this case is runcommand_in_submodule_cb(). This third function is a callback function that calls runcommand_in_submodule() with the appropriate parameters and then takes care of running the command in that submodule, and recursively performing the same when --recursive is flagged. The first function module_foreach first parses the options present in argv, and then with the help of module_list_compute(), generates the list of submodules present in the current working tree. The second function for_each_listed_submodule() traverses through the list, and calls function fn (which in case of submodule subcommand foreach is runcommand_in_submodule_cb()) is called for each entry. The third function runcommand_in_submodule_cb() calls the function runcommand_in_submodule() after passing appropraite parameters. The fourth function runcommand_in_submodule(), generates a submodule struct sub for $name, value and then later prepends name=sub->name; and other value assignment to the env argv_array structure of a child_process. Also the of submodule-foreach is push to args argv_array structure and finally, using run_command the commands are executed using a shell. The fourth function also takes care of the recursive flag, by creating a separate child_process structure and prepending "--super-prefix displaypath", to the args argv_array structure. Other required arguments and the input of submodule-foreach is also appended to this argv_array. Helped-by: Brandon Williams <bmw...@google.com> Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 151 git-submodule.sh| 39 +--- 2 files changed, 152 insertions(+), 38 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a5c4a8a69..46dee6bf5 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -718,6 +718,156 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct cb_foreach { + int argc; + const char **argv; + const char *prefix; + unsigned int flags; +}; +#define CB_FOREACH_INIT { 0, NULL, NULL, 0 } + +static void runcommand_in_submodule(const char *path, const struct object_id *ce_oid, + int argc, const char **argv, const char *prefix, + unsigned int flags) +{ + const struct submodule *sub; + struct child_process cp = CHILD_PROCESS_INIT; + char *displaypath; + + displaypath = get_submodule_displaypath(path, prefix); + + sub = submodule_from_path(_oid, path); + + if (!sub) + die(_("No url found for submodule path '%s' in .gitmodules"), + displaypath); + + if (!is_submodule_populated_gently(path, NULL)) + goto cleanup; + + prepare_submodule_repo_env(_array); + + /* +* For the purpose of executing in the submodule, +* separate shell is used for the purpose of running the +* child process. +*/ + cp.use_shell = 1; + cp.dir = path; + + /* +* NEEDSWORK: the command currently has access to the variables $name, +* $sm_path, $displaypath, $sha1 and $toplevel only when the command +* contains a single argument. This is done for maintianing a faithful +* translation from shell script. +*/ + if (argc == 1) { + char *toplevel = xgetcwd(); + + argv_array_pushf(_array, "name=%s", sub->name); + argv_array_pushf(_array, "sm_path=%s", path); + argv_array_pushf(_array, "displaypath=%s", displaypath); + argv_array_pushf(_array, "sha1=%s", +oid_to_hex(ce_oid)); + argv_array_pushf(_array, "toplevel=%s", toplevel); + + /* +* Since the path variable was accessible from the script +* before porting, it is also made available after porting. +* The environment variable "PATH" has a very special purpose +* on windows. And since environment variables are +* case-insensitive in windows, it interferes w
[PATCH v1 3/5] submodule foreach: clarify the '$toplevel' variable documentation
It does not contain the topmost superproject as the author assumed, but the direct superproject, such that $toplevel/$sm_path is the actual absolute path of the submodule. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- Documentation/git-submodule.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index a23baef62..8e7930ebc 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -188,7 +188,8 @@ foreach [--recursive] :: $name is the name of the relevant submodule section in `.gitmodules`, $sm_path is the path of the submodule as recorded in the superproject, $sha1 is the commit as recorded in the superproject, and - $toplevel is the absolute path to the top-level of the superproject. + $toplevel is the absolute path to its superproject, such that + $toplevel/$sm_path is the absolute path of the submodule. Note that to avoid conflicts with '$PATH' on Windows, the '$path' variable is now a deprecated synonym of '$sm_path' variable. Any submodules defined in the superproject but not checked out are -- 2.15.1
[PATCH v1 1/5] submodule foreach: correct '$path' in nested submodules from a subdirectory
When running 'git submodule foreach' from a subdirectory of your repository, nested submodules get a bogus value for $sm_path: For a submodule 'sub' that contains a nested submodule 'nested', running 'git -C dir submodule foreach echo $path' would report path='../nested' for the nested submodule. The first part '../' is derived from the logic computing the relative path from $pwd to the root of the superproject. The second part is the submodule path inside the submodule. This value is of little use and is hard to document. There are two different possible solutions that have more value: (a) The path value is documented as the path from the toplevel of the superproject to the mount point of the submodule. In this case we would want to have path='sub/nested'. (b) As Ramsay noticed the documented value is wrong. For the non-nested case the path is equal to the relative path from $pwd to the submodules working directory. When following this model, the expected value would be path='../sub/nested'. The behavior for (b) was introduced in 091a6eb0fe (submodule: drop the top-level requirement, 2013-06-16) the intent for $path seemed to be relative to $cwd to the submodule worktree, but that did not work for nested submodules, as the intermittent submodules were not included in the path. If we were to fix the meaning of the $path using (a) such that "path" is "the path from the toplevel of the superproject to the mount point of the submodule", we would break any existing submodule user that runs foreach from non-root of the superproject as the non-nested submodule '../sub' would change its path to 'sub'. If we would fix the meaning of the $path using (b), such that "path" is "the relative path from $pwd to the submodule", then we would break any user that uses nested submodules (even from the root directory) as the 'nested' would become 'sub/nested'. Both groups can be found in the wild. The author has no data if one group outweighs the other by large margin, and offending each one seems equally bad at first. However in the authors imagination it is better to go with (a) as running from a sub directory sounds like it is carried out by a human rather than by some automation task. With a human on the keyboard the feedback loop is short and the changed behavior can be adapted to quickly unlike some automation that can break silently. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> Signed-off-by: Stefan Beller <sbel...@google.com> --- git-submodule.sh | 1 - t/t7407-submodule-foreach.sh | 36 ++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/git-submodule.sh b/git-submodule.sh index 156255a9e..7305ee25f 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -345,7 +345,6 @@ cmd_foreach() prefix="$prefix$sm_path/" sanitize_submodule_env cd "$sm_path" && - sm_path=$(git submodule--helper relative-path "$sm_path" "$wt_prefix") && # we make $path available to scripts ... path=$sm_path && if test $# -eq 1 diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 6ba5daf42..0663622a4 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -82,9 +82,9 @@ test_expect_success 'test basic "submodule foreach" usage' ' cat >expect <expect <../../actual + ) && + test_i18ncmp expect actual +' cat > expect <
[PATCH v1 2/5] submodule foreach: document '$sm_path' instead of '$path'
As using a variable '$path' may be harmful to users due to capitalization issues, see 64394e3ae9 (git-submodule.sh: Don't use $path variable in eval_gettext string, 2012-04-17). Adjust the documentation to advocate for using $sm_path, which contains the same value. We still make the 'path' variable available and document it as a deprecated synonym of 'sm_path'. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- Documentation/git-submodule.txt | 10 ++ 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index ff612001d..a23baef62 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -183,12 +183,14 @@ information too. foreach [--recursive] :: Evaluates an arbitrary shell command in each checked out submodule. - The command has access to the variables $name, $path, $sha1 and + The command has access to the variables $name, $sm_path, $sha1 and $toplevel: $name is the name of the relevant submodule section in `.gitmodules`, - $path is the name of the submodule directory relative to the - superproject, $sha1 is the commit as recorded in the superproject, - and $toplevel is the absolute path to the top-level of the superproject. + $sm_path is the path of the submodule as recorded in the superproject, + $sha1 is the commit as recorded in the superproject, and + $toplevel is the absolute path to the top-level of the superproject. + Note that to avoid conflicts with '$PATH' on Windows, the '$path' + variable is now a deprecated synonym of '$sm_path' variable. Any submodules defined in the superproject but not checked out are ignored by this command. Unless given `--quiet`, foreach prints the name of each submodule before evaluating the command. -- 2.15.1
[PATCH v3 1/2] submodule: port submodule subcommand 'sync' from shell to C
Port the submodule subcommand 'sync' from shell to C using the same mechanism as that used for porting submodule subcommand 'status'. Hence, here the function cmd_sync() is ported from shell to C. This is done by introducing four functions: module_sync(), sync_submodule(), sync_submodule_cb() and print_default_remote(). The function print_default_remote() is introduced for getting the default remote as stdout. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 193 git-submodule.sh| 57 + 2 files changed, 194 insertions(+), 56 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a5c4a8a69..745d070ea 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -50,6 +50,20 @@ static char *get_default_remote(void) return ret; } +static int print_default_remote(int argc, const char **argv, const char *prefix) +{ + const char *remote; + + if (argc != 1) + die(_("submodule--helper print-default-remote takes no arguments")); + + remote = get_default_remote(); + if (remote) + printf("%s\n", remote); + + return 0; +} + static int starts_with_dot_slash(const char *str) { return str[0] == '.' && is_dir_sep(str[1]); @@ -358,6 +372,25 @@ static void module_list_active(struct module_list *list) *list = active_modules; } +static char *get_up_path(const char *path) +{ + int i; + struct strbuf sb = STRBUF_INIT; + + for (i = count_slashes(path); i; i--) + strbuf_addstr(, "../"); + + /* +* Check if 'path' ends with slash or not +* for having the same output for dir/sub_dir +* and dir/sub_dir/ +*/ + if (!is_dir_sep(path[strlen(path) - 1])) + strbuf_addstr(, "../"); + + return strbuf_detach(, NULL); +} + static int module_list(int argc, const char **argv, const char *prefix) { int i; @@ -718,6 +751,164 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct sync_cb { + const char *prefix; + unsigned int flags; +}; + +#define SYNC_CB_INIT { NULL, 0 } + +static void sync_submodule(const char *path, const char *prefix, + unsigned int flags) +{ + const struct submodule *sub; + char *remote_key = NULL; + char *sub_origin_url, *super_config_url, *displaypath; + struct strbuf sb = STRBUF_INIT; + struct child_process cp = CHILD_PROCESS_INIT; + char *sub_config_path = NULL; + + if (!is_submodule_active(the_repository, path)) + return; + + sub = submodule_from_path(_oid, path); + + if (sub && sub->url) { + if (starts_with_dot_dot_slash(sub->url) || + starts_with_dot_slash(sub->url)) { + char *remote_url, *up_path; + char *remote = get_default_remote(); + strbuf_addf(, "remote.%s.url", remote); + + if (git_config_get_string(sb.buf, _url)) + remote_url = xgetcwd(); + + up_path = get_up_path(path); + sub_origin_url = relative_url(remote_url, sub->url, up_path); + super_config_url = relative_url(remote_url, sub->url, NULL); + + free(remote); + free(up_path); + free(remote_url); + } else { + sub_origin_url = xstrdup(sub->url); + super_config_url = xstrdup(sub->url); + } + } else { + sub_origin_url = xstrdup(""); + super_config_url = xstrdup(""); + } + + displaypath = get_submodule_displaypath(path, prefix); + + if (!(flags & OPT_QUIET)) + printf(_("Synchronizing submodule url for '%s'\n"), +displaypath); + + strbuf_reset(); + strbuf_addf(, "submodule.%s.url", sub->name); + if (git_config_set_gently(sb.buf, super_config_url)) + die(_("failed to register url for submodule path '%s'"), + displaypath); + + if (!is_submodule_populated_gently(path, NULL)) + goto cleanup; + + prepare_submodule_repo_env(_array); + cp.git_cmd = 1; + cp.dir = path; + argv_array_pushl(, "submodule--helper", +"print-default-remote", NULL); + + strbuf_reset(); + if (capture_command(, , 0)) +
[PATCH v3 2/2] submodule: port submodule subcommand 'deinit' from shell to C
The same mechanism is used even for porting this submodule subcommand, as used in the ported subcommands till now. The function cmd_deinit in split up after porting into four functions: module_deinit(), for_each_listed_submodule(), deinit_submodule() and deinit_submodule_cb(). Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 147 git-submodule.sh| 55 + 2 files changed, 148 insertions(+), 54 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 745d070ea..b1daca995 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -20,6 +20,7 @@ #define OPT_QUIET (1 << 0) #define OPT_CACHED (1 << 1) #define OPT_RECURSIVE (1 << 2) +#define OPT_FORCE (1 << 3) typedef void (*each_submodule_fn)(const struct cache_entry *list_item, void *cb_data); @@ -909,6 +910,151 @@ static int module_sync(int argc, const char **argv, const char *prefix) return 0; } +struct deinit_cb { + const char *prefix; + unsigned int flags; +}; +#define DEINIT_CB_INIT { NULL, 0 } + +static void deinit_submodule(const char *path, const char *prefix, +unsigned int flags) +{ + const struct submodule *sub; + char *displaypath = NULL; + struct child_process cp_config = CHILD_PROCESS_INIT; + struct strbuf sb_config = STRBUF_INIT; + char *sub_git_dir = xstrfmt("%s/.git", path); + + sub = submodule_from_path(_oid, path); + + if (!sub || !sub->name) + goto cleanup; + + displaypath = get_submodule_displaypath(path, prefix); + + /* remove the submodule work tree (unless the user already did it) */ + if (is_directory(path)) { + struct strbuf sb_rm = STRBUF_INIT; + const char *format; + + /* +* protect submodules containing a .git directory +* NEEDSWORK: instead of dying, automatically call +* absorbgitdirs and (possibly) warn. +*/ + if (is_directory(sub_git_dir)) + die(_("Submodule work tree '%s' contains a .git " + "directory (use 'rm -rf' if you really want " + "to remove it including all of its history)"), + displaypath); + + if (!(flags & OPT_FORCE)) { + struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; + argv_array_pushl(_rm.args, "rm", "-qn", +path, NULL); + + if (run_command(_rm)) + die(_("Submodule work tree '%s' contains local " + "modifications; use '-f' to discard them"), + displaypath); + } + + strbuf_addstr(_rm, path); + + if (!remove_dir_recursively(_rm, 0)) + format = _("Cleared directory '%s'\n"); + else + format = _("Could not remove submodule work tree '%s'\n"); + + if (!(flags & OPT_QUIET)) + printf(format, displaypath); + + strbuf_release(_rm); + } + + if (mkdir(path, 0777)) + printf(_("could not create empty submodule directory %s"), + displaypath); + + cp_config.git_cmd = 1; + argv_array_pushl(_config.args, "config", "--get-regexp", NULL); + argv_array_pushf(_config.args, "submodule.%s\\.", sub->name); + + /* remove the .git/config entries (unless the user already did it) */ + if (!capture_command(_config, _config, 0) && sb_config.len) { + char *sub_key = xstrfmt("submodule.%s", sub->name); + /* +* remove the whole section so we have a clean state when +* the user later decides to init this submodule again +*/ + git_config_rename_section_in_file(NULL, sub_key, NULL); + if (!(flags & OPT_QUIET)) + printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"), +sub->name, sub->url, displaypath); + free(sub_key); + } + +cleanup: + free(displaypath); + free(sub_git_dir); + strbuf_release(_config); +} + +static void deinit_submodule_cb(const struct cache_entry *list_item, +
[PATCH v3 0/2] Incremental rewrite of git-submodules
Changes in v3: * For the variables: super_config_url and sub_origin_url, xstrdup() was used while assigning "" to them, before freeing. * In case of the function deinit_submodule, since the orignal code doesn't die upon failure of the function mkdir(), printf was used instead of die_errno. As before you can find this series at: https://github.com/pratham-pc/git/commits/patch-series-2 And its build report is available at: https://travis-ci.org/pratham-pc/git/builds/ Branch: patch-series-2 Build #197 Prathamesh Chavan (2): submodule: port submodule subcommand 'sync' from shell to C submodule: port submodule subcommand 'deinit' from shell to C builtin/submodule--helper.c | 340 git-submodule.sh| 112 +-- 2 files changed, 342 insertions(+), 110 deletions(-) -- 2.15.1
[PATCH v2 2/2] submodule: port submodule subcommand 'deinit' from shell to C
The same mechanism is used even for porting this submodule subcommand, as used in the ported subcommands till now. The function cmd_deinit in split up after porting into four functions: module_deinit(), for_each_listed_submodule(), deinit_submodule() and deinit_submodule_cb(). Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 147 git-submodule.sh| 55 + 2 files changed, 148 insertions(+), 54 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index eb6f96981..b93e1d50b 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -20,6 +20,7 @@ #define OPT_QUIET (1 << 0) #define OPT_CACHED (1 << 1) #define OPT_RECURSIVE (1 << 2) +#define OPT_FORCE (1 << 3) typedef void (*each_submodule_fn)(const struct cache_entry *list_item, void *cb_data); @@ -911,6 +912,151 @@ static int module_sync(int argc, const char **argv, const char *prefix) return 0; } +struct deinit_cb { + const char *prefix; + unsigned int flags; +}; +#define DEINIT_CB_INIT { NULL, 0 } + +static void deinit_submodule(const char *path, const char *prefix, +unsigned int flags) +{ + const struct submodule *sub; + char *displaypath = NULL; + struct child_process cp_config = CHILD_PROCESS_INIT; + struct strbuf sb_config = STRBUF_INIT; + char *sub_git_dir = xstrfmt("%s/.git", path); + + sub = submodule_from_path(_oid, path); + + if (!sub || !sub->name) + goto cleanup; + + displaypath = get_submodule_displaypath(path, prefix); + + /* remove the submodule work tree (unless the user already did it) */ + if (is_directory(path)) { + struct strbuf sb_rm = STRBUF_INIT; + const char *format; + + /* +* protect submodules containing a .git directory +* NEEDSWORK: instead of dying, automatically call +* absorbgitdirs and (possibly) warn. +*/ + if (is_directory(sub_git_dir)) + die(_("Submodule work tree '%s' contains a .git " + "directory (use 'rm -rf' if you really want " + "to remove it including all of its history)"), + displaypath); + + if (!(flags & OPT_FORCE)) { + struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; + argv_array_pushl(_rm.args, "rm", "-qn", +path, NULL); + + if (run_command(_rm)) + die(_("Submodule work tree '%s' contains local " + "modifications; use '-f' to discard them"), + displaypath); + } + + strbuf_addstr(_rm, path); + + if (!remove_dir_recursively(_rm, 0)) + format = _("Cleared directory '%s'\n"); + else + format = _("Could not remove submodule work tree '%s'\n"); + + if (!(flags & OPT_QUIET)) + printf(format, displaypath); + + strbuf_release(_rm); + } + + if (mkdir(path, 0777)) + die_errno(_("could not create empty submodule directory %s"), + displaypath); + + cp_config.git_cmd = 1; + argv_array_pushl(_config.args, "config", "--get-regexp", NULL); + argv_array_pushf(_config.args, "submodule.%s\\.", sub->name); + + /* remove the .git/config entries (unless the user already did it) */ + if (!capture_command(_config, _config, 0) && sb_config.len) { + char *sub_key = xstrfmt("submodule.%s", sub->name); + /* +* remove the whole section so we have a clean state when +* the user later decides to init this submodule again +*/ + git_config_rename_section_in_file(NULL, sub_key, NULL); + if (!(flags & OPT_QUIET)) + printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"), +sub->name, sub->url, displaypath); + free(sub_key); + } + +cleanup: + free(displaypath); + free(sub_git_dir); + strbuf_release(_config); +} + +static void deinit_submodule_cb(const struct cache_entry *list_item, +
[PATCH v2 1/2] submodule: port submodule subcommand 'sync' from shell to C
Port the submodule subcommand 'sync' from shell to C using the same mechanism as that used for porting submodule subcommand 'status'. Hence, here the function cmd_sync() is ported from shell to C. This is done by introducing four functions: module_sync(), sync_submodule(), sync_submodule_cb() and print_default_remote(). The function print_default_remote() is introduced for getting the default remote as stdout. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 195 git-submodule.sh| 57 + 2 files changed, 196 insertions(+), 56 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a5c4a8a69..eb6f96981 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -50,6 +50,20 @@ static char *get_default_remote(void) return ret; } +static int print_default_remote(int argc, const char **argv, const char *prefix) +{ + const char *remote; + + if (argc != 1) + die(_("submodule--helper print-default-remote takes no arguments")); + + remote = get_default_remote(); + if (remote) + printf("%s\n", remote); + + return 0; +} + static int starts_with_dot_slash(const char *str) { return str[0] == '.' && is_dir_sep(str[1]); @@ -358,6 +372,25 @@ static void module_list_active(struct module_list *list) *list = active_modules; } +static char *get_up_path(const char *path) +{ + int i; + struct strbuf sb = STRBUF_INIT; + + for (i = count_slashes(path); i; i--) + strbuf_addstr(, "../"); + + /* +* Check if 'path' ends with slash or not +* for having the same output for dir/sub_dir +* and dir/sub_dir/ +*/ + if (!is_dir_sep(path[strlen(path) - 1])) + strbuf_addstr(, "../"); + + return strbuf_detach(, NULL); +} + static int module_list(int argc, const char **argv, const char *prefix) { int i; @@ -718,6 +751,166 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct sync_cb { + const char *prefix; + unsigned int flags; +}; + +#define SYNC_CB_INIT { NULL, 0 } + +static void sync_submodule(const char *path, const char *prefix, + unsigned int flags) +{ + const struct submodule *sub; + char *remote_key = NULL; + char *sub_origin_url, *super_config_url, *displaypath; + struct strbuf sb = STRBUF_INIT; + struct child_process cp = CHILD_PROCESS_INIT; + char *sub_config_path = NULL; + + if (!is_submodule_active(the_repository, path)) + return; + + sub = submodule_from_path(_oid, path); + + if (sub && sub->url) { + if (starts_with_dot_dot_slash(sub->url) || + starts_with_dot_slash(sub->url)) { + char *remote_url, *up_path; + char *remote = get_default_remote(); + strbuf_addf(, "remote.%s.url", remote); + + if (git_config_get_string(sb.buf, _url)) + remote_url = xgetcwd(); + + up_path = get_up_path(path); + sub_origin_url = relative_url(remote_url, sub->url, up_path); + super_config_url = relative_url(remote_url, sub->url, NULL); + + free(remote); + free(up_path); + free(remote_url); + } else { + sub_origin_url = xstrdup(sub->url); + super_config_url = xstrdup(sub->url); + } + } else { + sub_origin_url = ""; + super_config_url = ""; + } + + displaypath = get_submodule_displaypath(path, prefix); + + if (!(flags & OPT_QUIET)) + printf(_("Synchronizing submodule url for '%s'\n"), +displaypath); + + strbuf_reset(); + strbuf_addf(, "submodule.%s.url", sub->name); + if (git_config_set_gently(sb.buf, super_config_url)) + die(_("failed to register url for submodule path '%s'"), + displaypath); + + if (!is_submodule_populated_gently(path, NULL)) + goto cleanup; + + prepare_submodule_repo_env(_array); + cp.git_cmd = 1; + cp.dir = path; + argv_array_pushl(, "submodule--helper", +"print-default-remote", NULL); + + strbuf_reset(); + if (capture_command(, , 0)) +
[PATCH v2 0/2] Incremental rewrite of git-submodules
Changes made to the previous version of the patch series[1]: * Since later on with certain patches, the number of bit-parameters to be passed to a few functions depend on many parameters, I prefered using a single flag bit. * Memory-leak of the variable 'remote' in the function: print_default_remote() was avoided. * Additional condition were introduced while freeing the variables: sub_origin_url and super_config_url. * print messages and comments in the deinit_submodule function were corrected as suggested in previous review of this patch[2]. * Call to the function lstat() for identifying the directory mode was avoided and instead 0777 was used. An additional improvement is to be made over this patch, but since the improvement can not directly be part of the "rewirte in C", the patch would be floated saperately on the mailing list. As before you can find this series at: https://github.com/pratham-pc/git/commits/patch-series-2 And its build report is available at: https://travis-ci.org/pratham-pc/git/builds/ Branch: patch-series-2 Build #196 [1]: https://public-inbox.org/git/20180109175703.4793-1-pc44...@gmail.com/ [2]: https://public-inbox.org/git/xmqq7esq4tf6@gitster.mtv.corp.google.com/ Prathamesh Chavan (2): submodule: port submodule subcommand 'sync' from shell to C submodule: port submodule subcommand 'deinit' from shell to C builtin/submodule--helper.c | 342 git-submodule.sh| 112 +-- 2 files changed, 344 insertions(+), 110 deletions(-) -- 2.15.1
Re: [PATCH v1 2/2] submodule: port submodule subcommand 'deinit' from shell to C
On Wed, Jan 10, 2018 at 2:54 AM, Junio C Hamano <gits...@pobox.com> wrote: > Prathamesh Chavan <pc44...@gmail.com> writes: > >> The same mechanism is used even for porting this submodule >> subcommand, as used in the ported subcommands till now. >> The function cmd_deinit in split up after porting into four >> functions: module_deinit(), for_each_listed_submodule(), >> deinit_submodule() and deinit_submodule_cb(). >> >> Mentored-by: Christian Couder <christian.cou...@gmail.com> >> Mentored-by: Stefan Beller <sbel...@google.com> >> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> >> --- >> builtin/submodule--helper.c | 153 >> >> git-submodule.sh| 55 +--- >> 2 files changed, 154 insertions(+), 54 deletions(-) >> >> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c >> index dd7737acd..54b0e46fc 100644 >> --- a/builtin/submodule--helper.c >> +++ b/builtin/submodule--helper.c >> @@ -20,6 +20,7 @@ >> #define OPT_QUIET (1 << 0) >> #define OPT_CACHED (1 << 1) >> #define OPT_RECURSIVE (1 << 2) >> +#define OPT_FORCE (1 << 3) >> >> typedef void (*each_submodule_fn)(const struct cache_entry *list_item, >> void *cb_data); >> @@ -908,6 +909,157 @@ static int module_sync(int argc, const char **argv, >> const char *prefix) >> return 0; >> } >> >> +struct deinit_cb { >> + const char *prefix; >> + unsigned int flags; >> +}; >> +#define DEINIT_CB_INIT { NULL, 0 } >> + >> +static void deinit_submodule(const char *path, const char *prefix, >> + unsigned int flags) >> +{ >> + const struct submodule *sub; >> + char *displaypath = NULL; >> + struct child_process cp_config = CHILD_PROCESS_INIT; >> + struct strbuf sb_config = STRBUF_INIT; >> + char *sub_git_dir = xstrfmt("%s/.git", path); >> + mode_t mode = 0777; >> + >> + sub = submodule_from_path(_oid, path); >> + >> + if (!sub || !sub->name) >> + goto cleanup; >> + >> + displaypath = get_submodule_displaypath(path, prefix); >> + >> + /* remove the submodule work tree (unless the user already did it) */ >> + if (is_directory(path)) { >> + struct stat st; >> + /* >> + * protect submodules containing a .git directory >> + * NEEDSWORK: automatically call absorbgitdirs before >> + * warning/die. >> + */ > > I guess that you mean "instead of dying, automatically call absorb > and (possibly) warn"? That sounds like a sensible improvement. > >> + if (is_directory(sub_git_dir)) >> + die(_("Submodule work tree '%s' contains a .git " >> + "directory use 'rm -rf' if you really want " >> + "to remove it including all of its history"), > > This changes the message text by removing () around "use ... history", > which I do not think you intended to do. > >> + displaypath); >> + >> + if (!(flags & OPT_FORCE)) { >> + struct child_process cp_rm = CHILD_PROCESS_INIT; >> + cp_rm.git_cmd = 1; >> + argv_array_pushl(_rm.args, "rm", "-qn", >> + path, NULL); >> + >> + if (run_command(_rm)) >> + die(_("Submodule work tree '%s' contains local >> " >> + "modifications; use '-f' to discard >> them"), >> + displaypath); >> + } >> + >> + if (!lstat(path, )) { > > What is this if statement doing here? It does not make sense, > especially without an 'else' clause on the other side, at least to > me. > > At this point in the flow, the code has already determined that path > is a directory above before starting to check if it has ".git/" > immediately below it, or trying to run "git rm" in the dry run mode > to see if it yields an error, so at this point lstat() should > succeed (and would say it is a directory). I would sort-of > understand it if this "if()" has an "else" clause to act on
[PATCH v1 2/2] submodule: port submodule subcommand 'deinit' from shell to C
The same mechanism is used even for porting this submodule subcommand, as used in the ported subcommands till now. The function cmd_deinit in split up after porting into four functions: module_deinit(), for_each_listed_submodule(), deinit_submodule() and deinit_submodule_cb(). Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 153 git-submodule.sh| 55 +--- 2 files changed, 154 insertions(+), 54 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index dd7737acd..54b0e46fc 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -20,6 +20,7 @@ #define OPT_QUIET (1 << 0) #define OPT_CACHED (1 << 1) #define OPT_RECURSIVE (1 << 2) +#define OPT_FORCE (1 << 3) typedef void (*each_submodule_fn)(const struct cache_entry *list_item, void *cb_data); @@ -908,6 +909,157 @@ static int module_sync(int argc, const char **argv, const char *prefix) return 0; } +struct deinit_cb { + const char *prefix; + unsigned int flags; +}; +#define DEINIT_CB_INIT { NULL, 0 } + +static void deinit_submodule(const char *path, const char *prefix, +unsigned int flags) +{ + const struct submodule *sub; + char *displaypath = NULL; + struct child_process cp_config = CHILD_PROCESS_INIT; + struct strbuf sb_config = STRBUF_INIT; + char *sub_git_dir = xstrfmt("%s/.git", path); + mode_t mode = 0777; + + sub = submodule_from_path(_oid, path); + + if (!sub || !sub->name) + goto cleanup; + + displaypath = get_submodule_displaypath(path, prefix); + + /* remove the submodule work tree (unless the user already did it) */ + if (is_directory(path)) { + struct stat st; + /* +* protect submodules containing a .git directory +* NEEDSWORK: automatically call absorbgitdirs before +* warning/die. +*/ + if (is_directory(sub_git_dir)) + die(_("Submodule work tree '%s' contains a .git " + "directory use 'rm -rf' if you really want " + "to remove it including all of its history"), + displaypath); + + if (!(flags & OPT_FORCE)) { + struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; + argv_array_pushl(_rm.args, "rm", "-qn", +path, NULL); + + if (run_command(_rm)) + die(_("Submodule work tree '%s' contains local " + "modifications; use '-f' to discard them"), + displaypath); + } + + if (!lstat(path, )) { + struct strbuf sb_rm = STRBUF_INIT; + const char *format; + + strbuf_addstr(_rm, path); + + if (!remove_dir_recursively(_rm, 0)) + format = _("Cleared directory '%s'\n"); + else + format = _("Could not remove submodule work tree '%s'\n"); + + if (!(flags & OPT_QUIET)) + printf(format, displaypath); + + mode = st.st_mode; + + strbuf_release(_rm); + } + } + + if (mkdir(path, mode)) + die_errno(_("could not create empty submodule directory %s"), + displaypath); + + cp_config.git_cmd = 1; + argv_array_pushl(_config.args, "config", "--get-regexp", NULL); + argv_array_pushf(_config.args, "submodule.%s\\.", sub->name); + + /* remove the .git/config entries (unless the user already did it) */ + if (!capture_command(_config, _config, 0) && sb_config.len) { + char *sub_key = xstrfmt("submodule.%s", sub->name); + /* +* remove the whole section so we have a clean state when +* the user later decides to init this submodule again +*/ + git_config_rename_section_in_file(NULL, sub_key, NULL); + if (!(flags & OPT_QUIET)) + printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"), +sub->name, sub
[PATCH v1 1/2] submodule: port submodule subcommand 'sync' from shell to C
Port the submodule subcommand 'sync' from shell to C using the same mechanism as that used for porting submodule subcommand 'status'. Hence, here the function cmd_sync() is ported from shell to C. This is done by introducing four functions: module_sync(), sync_submodule(), sync_submodule_cb() and print_default_remote(). The function print_default_remote() is introduced for getting the default remote as stdout. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 192 git-submodule.sh| 57 + 2 files changed, 193 insertions(+), 56 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a5c4a8a69..dd7737acd 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -50,6 +50,20 @@ static char *get_default_remote(void) return ret; } +static int print_default_remote(int argc, const char **argv, const char *prefix) +{ + const char *remote; + + if (argc != 1) + die(_("submodule--helper print-default-remote takes no arguments")); + + remote = get_default_remote(); + if (remote) + printf("%s\n", remote); + + return 0; +} + static int starts_with_dot_slash(const char *str) { return str[0] == '.' && is_dir_sep(str[1]); @@ -358,6 +372,25 @@ static void module_list_active(struct module_list *list) *list = active_modules; } +static char *get_up_path(const char *path) +{ + int i; + struct strbuf sb = STRBUF_INIT; + + for (i = count_slashes(path); i; i--) + strbuf_addstr(, "../"); + + /* +* Check if 'path' ends with slash or not +* for having the same output for dir/sub_dir +* and dir/sub_dir/ +*/ + if (!is_dir_sep(path[strlen(path) - 1])) + strbuf_addstr(, "../"); + + return strbuf_detach(, NULL); +} + static int module_list(int argc, const char **argv, const char *prefix) { int i; @@ -718,6 +751,163 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct sync_cb { + const char *prefix; + unsigned int flags; +}; + +#define SYNC_CB_INIT { NULL, 0 } + +static void sync_submodule(const char *path, const char *prefix, + unsigned int flags) +{ + const struct submodule *sub; + char *remote_key = NULL; + char *sub_origin_url, *super_config_url, *displaypath; + struct strbuf sb = STRBUF_INIT; + struct child_process cp = CHILD_PROCESS_INIT; + char *sub_config_path = NULL; + + if (!is_submodule_active(the_repository, path)) + return; + + sub = submodule_from_path(_oid, path); + + if (sub && sub->url) { + if (starts_with_dot_dot_slash(sub->url) || starts_with_dot_slash(sub->url)) { + char *remote_url, *up_path; + char *remote = get_default_remote(); + strbuf_addf(, "remote.%s.url", remote); + + if (git_config_get_string(sb.buf, _url)) + remote_url = xgetcwd(); + + up_path = get_up_path(path); + sub_origin_url = relative_url(remote_url, sub->url, up_path); + super_config_url = relative_url(remote_url, sub->url, NULL); + + free(remote); + free(up_path); + free(remote_url); + } else { + sub_origin_url = xstrdup(sub->url); + super_config_url = xstrdup(sub->url); + } + } else { + sub_origin_url = ""; + super_config_url = ""; + } + + displaypath = get_submodule_displaypath(path, prefix); + + if (!(flags & OPT_QUIET)) + printf(_("Synchronizing submodule url for '%s'\n"), +displaypath); + + strbuf_reset(); + strbuf_addf(, "submodule.%s.url", sub->name); + if (git_config_set_gently(sb.buf, super_config_url)) + die(_("failed to register url for submodule path '%s'"), + displaypath); + + if (!is_submodule_populated_gently(path, NULL)) + goto cleanup; + + prepare_submodule_repo_env(_array); + cp.git_cmd = 1; + cp.dir = path; + argv_array_pushl(, "submodule--helper", +"print-default-remote", NULL); + + strbuf_reset(); + if (capture_command(, , 0)) + die(_("failed to get
[PATCH v1 0/2] Incremental rewrite of git-submodules
The patches [1] and [2] concerning the porting of submodule subcommands: sync and deinit were updated in accoudance with the changes made in one of such similar portings made earlier for submodule subcommand status[3]. Following are the changes made: * It was observed that the number of params increased a lot due to flags like quiet, recursive, cached, etc, and keeping in mind the future subcommand's ported functions as well, a single unsigned int called flags was introduced to store all of these flags, instead of having parameter for each one. * To accomodate the possiblity of a direct call to the functions deinit_submodule() and sync_submodule(), callback functions were introduced. As before you can find this series at: https://github.com/pratham-pc/git/commits/patch-series-2 And its build report is available at: https://travis-ci.org/pratham-pc/git/builds/ Branch: patch-series-2 Build #195 [1]: https://public-inbox.org/git/20170807211900.15001-6-pc44...@gmail.com/ [2]: https://public-inbox.org/git/20170807211900.15001-7-pc44...@gmail.com/ [3]: https://public-inbox.org/git/20171006132415.2876-4-pc44...@gmail.com/ Prathamesh Chavan (2): submodule: port submodule subcommand 'sync' from shell to C submodule: port submodule subcommand 'deinit' from shell to C builtin/submodule--helper.c | 345 git-submodule.sh| 112 +- 2 files changed, 347 insertions(+), 110 deletions(-) -- 2.14.2
[PATCH v7 1/3] submodule--helper: introduce get_submodule_displaypath()
Introduce function get_submodule_displaypath() to replace the code occurring in submodule_init() for generating displaypath of the submodule with a call to it. This new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 35 +++ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 06ed02f99..56c1c52e2 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -219,6 +219,26 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } +/* the result should be freed by the caller. */ +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, )); + strbuf_release(); + return displaypath; + } else if (super_prefix) { + return xstrfmt("%s%s", super_prefix, path); + } else { + return xstrdup(path); + } +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -334,15 +354,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, )); - else if (get_super_prefix()) { - strbuf_addf(, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(_oid, path); @@ -357,9 +369,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * Set active flag for the submodule being initialized */ if (!is_submodule_active(the_repository, path)) { - strbuf_reset(); strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); + strbuf_reset(); } /* @@ -367,7 +379,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * To look up the url in .git/config, we must not fall back to * .gitmodules, so look it up directly. */ - strbuf_reset(); strbuf_addf(, "submodule.%s.url", sub->name); if (git_config_get_string(sb.buf, )) { if (!sub->url) @@ -404,9 +415,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); } + strbuf_reset(); /* Copy "update" setting when it is not set yet */ - strbuf_reset(); strbuf_addf(, "submodule.%s.update", sub->name); if (git_config_get_string(sb.buf, ) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { -- 2.14.2
[PATCH v7 0/3] Incremental rewrite of git-submodules
Changes in v7: * Instead of using cb_flags in the callback data's struct, 'flags' is used. * Similar changes were applied to the CB_OPT_QUIET and other bits. * The function compute_rev_name() was formatted in accordance with the "make style", into a compact version. * Call to precompose_argv() in the function status_submodule() was dropped as the call was unnecessary. As before you can find this series at: https://github.com/pratham-pc/git/commits/patch-series-1 And its build report is available at: https://travis-ci.org/pratham-pc/git/builds/ Branch: patch-series-1 Build #190 The above changes were based on master branch. Another branch, similar to the above, was created, but was based on the 'next' branch. Complete build report of that is also available at: https://travis-ci.org/pratham-pc/git/builds Branch: patch-series-1-next Build #189 The above changes are also push on github and are available at: https://github.com/pratham-pc/git/commits/patch-series-1-next Prathamesh Chavan (3): submodule--helper: introduce get_submodule_displaypath() submodule--helper: introduce for_each_listed_submodule() submodule: port submodule subcommand 'status' from shell to C builtin/submodule--helper.c | 273 +--- git-submodule.sh| 61 +- 2 files changed, 257 insertions(+), 77 deletions(-) -- 2.14.2
[PATCH v7 2/3] submodule--helper: introduce for_each_listed_submodule()
Introduce function for_each_listed_submodule() and replace a loop in module_init() with a call to it. The new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 40 +++- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 56c1c52e2..29e3fde16 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -14,6 +14,11 @@ #include "refs.h" #include "connect.h" +#define OPT_QUIET (1 << 0) + +typedef void (*each_submodule_fn)(const struct cache_entry *list_item, + void *cb_data); + static char *get_default_remote(void) { char *dest = NULL, *ret; @@ -348,7 +353,23 @@ static int module_list(int argc, const char **argv, const char *prefix) return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_listed_submodule(const struct module_list *list, + each_submodule_fn fn, void *cb_data) +{ + int i; + for (i = 0; i < list->nr; i++) + fn(list->entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int flags; +}; + +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const char *path, const char *prefix, + unsigned int flags) { const struct submodule *sub; struct strbuf sb = STRBUF_INIT; @@ -410,7 +431,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!(flags & OPT_QUIET)) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); @@ -437,12 +458,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet) free(upd); } +static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data) +{ + struct init_cb *info = cb_data; + init_submodule(list_item->name, info->prefix, info->flags); +} + static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(, N_("Suppress output for initializing a submodule")), @@ -467,8 +494,11 @@ static int module_init(int argc, const char **argv, const char *prefix) if (!argc && git_config_get_value_multi("submodule.active")) module_list_active(); - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + info.prefix = prefix; + if (quiet) + info.flags |= OPT_QUIET; + + for_each_listed_submodule(, init_submodule_cb, ); return 0; } -- 2.14.2
[PATCH v7 3/3] submodule: port submodule subcommand 'status' from shell to C
This aims to make git-submodule 'status' a built-in. Hence, the function cmd_status() is ported from shell to C. This is done by introducing four functions: module_status(), submodule_status_cb(), submodule_status() and print_status(). The function module_status() acts as the front-end of the subcommand. It parses subcommand's options and then calls the function module_list_compute() for computing the list of submodules. Then this functions calls for_each_listed_submodule() looping through the list obtained. Then for_each_listed_submodule() calls submodule_status_cb() for each of the submodule in its list. The function submodule_status_cb() calls submodule_status() after passing appropriate arguments to the funciton. Function submodule_status() is responsible for generating the status each submodule it is called for, and then calls print_status(). Finally, the function print_status() handles the printing of submodule's status. Function set_name_rev() is also ported from git-submodule to the submodule--helper builtin function compute_rev_name(), which now generates the value of the revision name as required. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 198 git-submodule.sh| 61 +- 2 files changed, 199 insertions(+), 60 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 29e3fde16..d366e8e7b 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -13,8 +13,13 @@ #include "remote.h" #include "refs.h" #include "connect.h" +#include "revision.h" +#include "diffcore.h" +#include "diff.h" #define OPT_QUIET (1 << 0) +#define OPT_CACHED (1 << 1) +#define OPT_RECURSIVE (1 << 2) typedef void (*each_submodule_fn)(const struct cache_entry *list_item, void *cb_data); @@ -244,6 +249,44 @@ static char *get_submodule_displaypath(const char *path, const char *prefix) } } +static char *compute_rev_name(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { NULL }; + + static const char *describe_tags[] = { "--tags", NULL }; + + static const char *describe_contains[] = { "--contains", NULL }; + + static const char *describe_all_always[] = { "--all", "--always", NULL }; + + static const char **describe_argv[] = { describe_bare, describe_tags, + describe_contains, + describe_all_always, NULL }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(, "describe"); + argv_array_pushv(, *d); + argv_array_push(, object_id); + + if (!capture_command(, , 0)) { + strbuf_strip_suffix(, "\n"); + return strbuf_detach(, NULL); + } + } + + strbuf_release(); + return NULL; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -503,6 +546,160 @@ static int module_init(int argc, const char **argv, const char *prefix) return 0; } +struct status_cb { + const char *prefix; + unsigned int flags; +}; + +#define STATUS_CB_INIT { NULL, 0 } + +static void print_status(unsigned int flags, char state, const char *path, +const struct object_id *oid, const char *displaypath) +{ + if (flags & OPT_QUIET) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') + printf(" (%s)", compute_rev_name(path, oid_to_hex(oid))); + + printf("\n"); +} + +static int handle_submodule_head_ref(const char *refname, +const struct object_id *oid, int flags, +void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const char *path, const struct object_id *ce_oid, +unsigned int ce_flags, const char *prefix, +unsigned int flags) +{ + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + struct rev_info rev
[PATCH v6 3/3] submodule: port submodule subcommand 'status' from shell to C
This aims to make git-submodule 'status' a built-in. Hence, the function cmd_status() is ported from shell to C. This is done by introducing four functions: module_status(), submodule_status_cb(), submodule_status() and print_status(). The function module_status() acts as the front-end of the subcommand. It parses subcommand's options and then calls the function module_list_compute() for computing the list of submodules. Then this functions calls for_each_listed_submodule() looping through the list obtained. Then for_each_listed_submodule() calls submodule_status_cb() for each of the submodule in its list. The function submodule_status_cb() calls submodule_status() after passing appropriate arguments to the funciton. Function submodule_status() is responsible for generating the status each submodule it is called for, and then calls print_status(). Finally, the function print_status() handles the printing of submodule's status. Function set_name_rev() is also ported from git-submodule to the submodule--helper builtin function compute_rev_name(), which now generates the value of the revision name as required. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 207 git-submodule.sh| 61 + 2 files changed, 208 insertions(+), 60 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 20a1ef868..50d38fc20 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -13,8 +13,13 @@ #include "remote.h" #include "refs.h" #include "connect.h" +#include "revision.h" +#include "diffcore.h" +#include "diff.h" #define CB_OPT_QUIET (1<<0) +#define CB_OPT_CACHED (1<<1) +#define CB_OPT_RECURSIVE (1<<2) typedef void (*each_submodule_fn)(const struct cache_entry *list_item, void *cb_data); @@ -245,6 +250,53 @@ static char *get_submodule_displaypath(const char *path, const char *prefix) } } +static char *compute_rev_name(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { + NULL + }; + + static const char *describe_tags[] = { + "--tags", NULL + }; + + static const char *describe_contains[] = { + "--contains", NULL + }; + + static const char *describe_all_always[] = { + "--all", "--always", NULL + }; + + static const char **describe_argv[] = { + describe_bare, describe_tags, describe_contains, + describe_all_always, NULL + }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(, "describe"); + argv_array_pushv(, *d); + argv_array_push(, object_id); + + if (!capture_command(, , 0)) { + strbuf_strip_suffix(, "\n"); + return strbuf_detach(, NULL); + } + } + + strbuf_release(); + return NULL; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -503,6 +555,160 @@ static int module_init(int argc, const char **argv, const char *prefix) return 0; } +struct status_cb { + const char *prefix; + unsigned int cb_flags; +}; +#define STATUS_CB_INIT { NULL, 0 } + +static void print_status(unsigned int flags, char state, const char *path, +const struct object_id *oid, const char *displaypath) +{ + if (flags & CB_OPT_QUIET) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') + printf(" (%s)", compute_rev_name(path, oid_to_hex(oid))); + + printf("\n"); +} + +static int handle_submodule_head_ref(const char *refname, +const struct object_id *oid, int flags, +void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const char *path, const struct object_id *ce_oid, +unsigned int ce_flags, const char *prefix, +unsigned int cb_flags) +{ + char *displaypath; + struct argv_ar
[PATCH v6 2/3] submodule--helper: introduce for_each_listed_submodule()
Introduce function for_each_listed_submodule() and replace a loop in module_init() with a call to it. The new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 39 ++- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index cdae54426..20a1ef868 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -14,6 +14,11 @@ #include "refs.h" #include "connect.h" +#define CB_OPT_QUIET (1<<0) + +typedef void (*each_submodule_fn)(const struct cache_entry *list_item, + void *cb_data); + static char *get_default_remote(void) { char *dest = NULL, *ret; @@ -349,7 +354,22 @@ static int module_list(int argc, const char **argv, const char *prefix) return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_listed_submodule(const struct module_list *list, + each_submodule_fn fn, void *cb_data) +{ + int i; + for (i = 0; i < list->nr; i++) + fn(list->entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int cb_flags; +}; +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const char *path, const char *prefix, + unsigned int cb_flags) { const struct submodule *sub; struct strbuf sb = STRBUF_INIT; @@ -411,7 +431,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!(cb_flags & CB_OPT_QUIET)) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); @@ -438,12 +458,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet) free(upd); } +static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data) +{ + struct init_cb *info = cb_data; + init_submodule(list_item->name, info->prefix, info->cb_flags); +} + static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(, N_("Suppress output for initializing a submodule")), @@ -468,8 +494,11 @@ static int module_init(int argc, const char **argv, const char *prefix) if (!argc && git_config_get_value_multi("submodule.active")) module_list_active(); - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + info.prefix = prefix; + if (quiet) + info.cb_flags |= CB_OPT_QUIET; + + for_each_listed_submodule(, init_submodule_cb, ); return 0; } -- 2.13.0
[PATCH v6 1/3] submodule--helper: introduce get_submodule_displaypath()
Introduce function get_submodule_displaypath() to replace the code occurring in submodule_init() for generating displaypath of the submodule with a call to it. This new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 35 +++ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 818fe74f0..cdae54426 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -220,6 +220,26 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } +/* the result should be freed by the caller. */ +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, )); + strbuf_release(); + return displaypath; + } else if (super_prefix) { + return xstrfmt("%s%s", super_prefix, path); + } else { + return xstrdup(path); + } +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -335,15 +355,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, )); - else if (get_super_prefix()) { - strbuf_addf(, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(_oid, path); @@ -358,9 +370,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * Set active flag for the submodule being initialized */ if (!is_submodule_active(the_repository, path)) { - strbuf_reset(); strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); + strbuf_reset(); } /* @@ -368,7 +380,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * To look up the url in .git/config, we must not fall back to * .gitmodules, so look it up directly. */ - strbuf_reset(); strbuf_addf(, "submodule.%s.url", sub->name); if (git_config_get_string(sb.buf, )) { if (!sub->url) @@ -405,9 +416,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); } + strbuf_reset(); /* Copy "update" setting when it is not set yet */ - strbuf_reset(); strbuf_addf(, "submodule.%s.update", sub->name); if (git_config_get_string(sb.buf, ) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { -- 2.13.0
[PATCH v6 0/3] Incremental rewrite of git-submodules
changes in v6: * The function get_submodule_displaypath() was modified for the case when get_super_prefix() returns a non-null value. The condition to check if the super-prefix ends with a '/' is removed. To accomodate this change appropriate value of super_prefix is passed instead in the recursive calls of init_submodule() and status_submodule(). * To accomodate the possiblity of a direct call to the function init_submodule(), a callback function init_submodule_cb() is introduced which takes cache_entry and init_cb structures as input params, and calls init_submodule() with parameters which are more appropriate for a direct call of this function. * Similar changes were even done for status_submodule(). But as it was observed that the number of params increased a lot due to flags like quiet, recursive, cached, etc, and keeping in mind the future subcommand's ported functions as well, a single unsigned int called cb_flags was introduced to store all of these flags, instead of having parameter for each one. * Patches [3/4] and [4/4] from the previous series were merged as a single step. * Call to function cmd_diff_files was avoided in the function status_submodule() and instead used the function run_diff_files() for the same purpose. Since there were many changes the patches required, I took more time on making these changes. Thank you, Junio for the last reviews. They helped a lot for improving the patch series. As before you can find this series at: https://github.com/pratham-pc/git/commits/patch-series-1 And its build report is available at: https://travis-ci.org/pratham-pc/git/builds/ Branch: patch-series-1 Build #184 Prathamesh Chavan (3): submodule--helper: introduce get_submodule_displaypath() submodule--helper: introduce for_each_listed_submodule() submodule: port submodule subcommand 'status' from shell to C builtin/submodule--helper.c | 281 +--- git-submodule.sh| 61 +- 2 files changed, 265 insertions(+), 77 deletions(-) -- 2.13.0
[PATCH v5 2/4] submodule--helper: introduce for_each_listed_submodule()
Introduce function for_each_listed_submodule() and replace a loop in module_init() with a call to it. The new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 36 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index d24ac9028..d12790b5c 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -14,6 +14,9 @@ #include "refs.h" #include "connect.h" +typedef void (*each_submodule_fn)(const struct cache_entry *list_item, + void *cb_data); + static char *get_default_remote(void) { char *dest = NULL, *ret; @@ -352,15 +355,30 @@ static int module_list(int argc, const char **argv, const char *prefix) return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_listed_submodule(const struct module_list *list, + each_submodule_fn fn, void *cb_data) +{ + int i; + for (i = 0; i < list->nr; i++) + fn(list->entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int quiet: 1; +}; +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const struct cache_entry *list_item, void *cb_data) { + struct init_cb *info = cb_data; const struct submodule *sub; struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - displaypath = get_submodule_displaypath(path, prefix); + displaypath = get_submodule_displaypath(list_item->name, info->prefix); - sub = submodule_from_path(_oid, path); + sub = submodule_from_path(_oid, list_item->name); if (!sub) die(_("No url found for submodule path '%s' in .gitmodules"), @@ -372,7 +390,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * * Set active flag for the submodule being initialized */ - if (!is_submodule_active(the_repository, path)) { + if (!is_submodule_active(the_repository, list_item->name)) { strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); strbuf_reset(); @@ -414,7 +432,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!info->quiet) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); @@ -443,10 +461,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet) static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(, N_("Suppress output for initializing a submodule")), @@ -471,8 +489,10 @@ static int module_init(int argc, const char **argv, const char *prefix) if (!argc && git_config_get_value_multi("submodule.active")) module_list_active(); - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + info.prefix = prefix; + info.quiet = !!quiet; + + for_each_listed_submodule(, init_submodule, ); return 0; } -- 2.13.0
[PATCH v5 1/4] submodule--helper: introduce get_submodule_displaypath()
Introduce function get_submodule_displaypath() to replace the code occurring in submodule_init() for generating displaypath of the submodule with a call to it. This new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 38 ++ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 818fe74f0..d24ac9028 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -220,6 +220,29 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } +/* the result should be freed by the caller. */ +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, )); + strbuf_release(); + return displaypath; + } else if (super_prefix) { + int len = strlen(super_prefix); + const char *format = (len > 0 && is_dir_sep(super_prefix[len - 1])) ? "%s%s" : "%s/%s"; + + return xstrfmt(format, super_prefix, path); + } else { + return xstrdup(path); + } +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -335,15 +358,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, )); - else if (get_super_prefix()) { - strbuf_addf(, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(_oid, path); @@ -358,9 +373,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * Set active flag for the submodule being initialized */ if (!is_submodule_active(the_repository, path)) { - strbuf_reset(); strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); + strbuf_reset(); } /* @@ -368,7 +383,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * To look up the url in .git/config, we must not fall back to * .gitmodules, so look it up directly. */ - strbuf_reset(); strbuf_addf(, "submodule.%s.url", sub->name); if (git_config_get_string(sb.buf, )) { if (!sub->url) @@ -405,9 +419,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); } + strbuf_reset(); /* Copy "update" setting when it is not set yet */ - strbuf_reset(); strbuf_addf(, "submodule.%s.update", sub->name); if (git_config_get_string(sb.buf, ) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { -- 2.13.0
[PATCH v5 3/4] submodule: port set_name_rev() from shell to C
Function set_name_rev() is ported from git-submodule to the submodule--helper builtin. The function compute_rev_name() generates the value of the revision name as required. The function get_rev_name() calls compute_rev_name() and receives the revision name, and later handles its formatting and printing. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 63 + git-submodule.sh| 16 ++-- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index d12790b5c..7ca8e8153 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -246,6 +246,68 @@ static char *get_submodule_displaypath(const char *path, const char *prefix) } } +static char *compute_rev_name(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { + NULL + }; + + static const char *describe_tags[] = { + "--tags", NULL + }; + + static const char *describe_contains[] = { + "--contains", NULL + }; + + static const char *describe_all_always[] = { + "--all", "--always", NULL + }; + + static const char **describe_argv[] = { + describe_bare, describe_tags, describe_contains, + describe_all_always, NULL + }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(, "describe"); + argv_array_pushv(, *d); + argv_array_push(, object_id); + + if (!capture_command(, , 0) && sb.len) { + strbuf_strip_suffix(, "\n"); + return strbuf_detach(, NULL); + } + } + + strbuf_release(); + return NULL; +} + +static int get_rev_name(int argc, const char **argv, const char *prefix) +{ + char *revname; + if (argc != 3) + die("get-rev-name only accepts two arguments: "); + + revname = compute_rev_name(argv[1], argv[2]); + if (revname && revname[0]) + printf(" (%s)", revname); + printf("\n"); + + free(revname); + return 0; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -1293,6 +1355,7 @@ static struct cmd_struct commands[] = { {"relative-path", resolve_relative_path, 0}, {"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, + {"get-rev-name", get_rev_name, 0}, {"init", module_init, SUPPORT_SUPER_PREFIX}, {"remote-branch", resolve_remote_submodule_branch, 0}, {"push-check", push_check, 0}, diff --git a/git-submodule.sh b/git-submodule.sh index 66d1ae8ef..5211361c5 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -758,18 +758,6 @@ cmd_update() } } -set_name_rev () { - revname=$( ( - sanitize_submodule_env - cd "$1" && { - git describe "$2" 2>/dev/null || - git describe --tags "$2" 2>/dev/null || - git describe --contains "$2" 2>/dev/null || - git describe --all --always "$2" - } - ) ) - test -z "$revname" || revname=" ($revname)" -} # # Show commit summary for submodules in index or working tree # @@ -1041,14 +1029,14 @@ cmd_status() fi if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path" then - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper get-rev-name "$sm_path" "$sha1") say " $sha1 $displaypath$revname" else if test -z "$cached" then sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD) fi - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper get-rev-name "$sm_path" "$sha1") say "+$sha1 $displaypath$revname" fi -- 2.13.0
[PATCH v5 0/4] Incremental rewrite of git-submodules
Changes in v5: * in print_status() function, we now call compute_rev_name() directly instead of doing the argv setup and calling get_rev_name() function. * Since we no longer use the helper function get_rev_name(), we have removed it from the code base after porting submodule subcommand 'status'. * function get_submodule_displaypath() has been modified, and now handles the case of super_prefix being non-null and of length zero. * I have still kept the name of the function get_rev_name() unchanged, since in the function cmd_status(), it is used to get the value of the variable revname. And in the next commit since we do not require the function anymore, we reomve it from the code base. As before you can find this series at: https://github.com/pratham-pc/git/commits/patch-series-1 And its build report is available at: https://travis-ci.org/pratham-pc/git/builds/ Branch: patch-series-1 Build #179 Thanks, Han-Wen Nienhuys for reviewing the previous patch series. Prathamesh Chavan (4): submodule--helper: introduce get_submodule_displaypath() submodule--helper: introduce for_each_listed_submodule() submodule: port set_name_rev() from shell to C submodule: port submodule subcommand 'status' from shell to C builtin/submodule--helper.c | 265 git-submodule.sh| 61 +- 2 files changed, 247 insertions(+), 79 deletions(-) -- 2.13.0
[PATCH v5 4/4] submodule: port submodule subcommand 'status' from shell to C
This aims to make git-submodule 'status' a built-in. Hence, the function cmd_status() is ported from shell to C. This is done by introducing three functions: module_status(), submodule_status() and print_status(). The function module_status() acts as the front-end of the subcommand. It parses subcommand's options and then calls the function module_list_compute() for computing the list of submodules. Then this functions calls for_each_listed_submodule() looping through the list obtained. Then for_each_listed_submodule() calls submodule_status() for each of the submodule in its list. The function submodule_status() is responsible for generating the status each submodule it is called for, and then calls print_status(). Finally, the function print_status() handles the printing of submodule's status. Helper function get_rev_name() removed after porting the above subcommand as it is no longer used. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 162 +++- git-submodule.sh| 49 +- 2 files changed, 147 insertions(+), 64 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 7ca8e8153..8876a4a08 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -293,21 +293,6 @@ static char *compute_rev_name(const char *sub_path, const char* object_id) return NULL; } -static int get_rev_name(int argc, const char **argv, const char *prefix) -{ - char *revname; - if (argc != 3) - die("get-rev-name only accepts two arguments: "); - - revname = compute_rev_name(argv[1], argv[2]); - if (revname && revname[0]) - printf(" (%s)", revname); - printf("\n"); - - free(revname); - return 0; -} - struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -559,6 +544,151 @@ static int module_init(int argc, const char **argv, const char *prefix) return 0; } +struct status_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; + unsigned int cached: 1; +}; +#define STATUS_CB_INIT { NULL, 0, 0, 0 } + +static void print_status(struct status_cb *info, char state, const char *path, +const struct object_id *oid, const char *displaypath) +{ + if (info->quiet) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') + printf(" (%s)", compute_rev_name(path, oid_to_hex(oid))); + + printf("\n"); +} + +static int handle_submodule_head_ref(const char *refname, +const struct object_id *oid, int flags, +void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct status_cb *info = cb_data; + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + + if (!submodule_from_path(_oid, list_item->name)) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + list_item->name); + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + if (ce_stage(list_item)) { + print_status(info, 'U', list_item->name, +_oid, displaypath); + goto cleanup; + } + + if (!is_submodule_active(the_repository, list_item->name)) { + print_status(info, '-', list_item->name, _item->oid, +displaypath); + goto cleanup; + } + + argv_array_pushl(_files_args, "diff-files", +"--ignore-submodules=dirty", "--quiet", "--", +list_item->name, NULL); + + if (!cmd_diff_files(diff_files_args.argc, diff_files_args.argv, + info->prefix)) { + print_status(info, ' ', list_item->name, _item->oid, +displaypath); + } else { + if (!info->cached) { + struct object_id oid; + + if (refs_head_ref(get_submodule_ref_store(list_item->name), handle_submodule_head_ref, )) + die(_("could not resolve HEAD ref inside the" + "submodule '%s'"), list_item->name); + +
[GSoC][PATCH v4 4/4] submodule: port submodule subcommand 'status' from shell to C
This aims to make git-submodule 'status' a built-in. Hence, the function cmd_status() is ported from shell to C. This is done by introducing three functions: module_status(), submodule_status() and print_status(). The function module_status() acts as the front-end of the subcommand. It parses subcommand's options and then calls the function module_list_compute() for computing the list of submodules. Then this functions calls for_each_listed_submodule() looping through the list obtained. Then for_each_listed_submodule() calls submodule_status() for each of the submodule in its list. The function submodule_status() is responsible for generating the status each submodule it is called for, and then calls print_status(). Finally, the function print_status() handles the printing of submodule's status. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 155 git-submodule.sh| 49 +- 2 files changed, 156 insertions(+), 48 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 85df11129..abf5c126a 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -558,6 +558,160 @@ static int module_init(int argc, const char **argv, const char *prefix) return 0; } +struct status_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; + unsigned int cached: 1; +}; +#define STATUS_CB_INIT { NULL, 0, 0, 0 } + +static void print_status(struct status_cb *info, char state, const char *path, +const struct object_id *oid, const char *displaypath) +{ + if (info->quiet) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') { + struct argv_array get_rev_args = ARGV_ARRAY_INIT; + + argv_array_pushl(_rev_args, "get-rev-name", +path, oid_to_hex(oid), NULL); + get_rev_name(get_rev_args.argc, get_rev_args.argv, +info->prefix); + + argv_array_clear(_rev_args); + } else { + printf("\n"); + } +} + +static int handle_submodule_head_ref(const char *refname, +const struct object_id *oid, int flags, +void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct status_cb *info = cb_data; + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + + if (!submodule_from_path(_oid, list_item->name)) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + list_item->name); + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + if (ce_stage(list_item)) { + print_status(info, 'U', list_item->name, +_oid, displaypath); + goto cleanup; + } + + if (!is_submodule_active(the_repository, list_item->name)) { + print_status(info, '-', list_item->name, _item->oid, +displaypath); + goto cleanup; + } + + argv_array_pushl(_files_args, "diff-files", +"--ignore-submodules=dirty", "--quiet", "--", +list_item->name, NULL); + + if (!cmd_diff_files(diff_files_args.argc, diff_files_args.argv, + info->prefix)) { + print_status(info, ' ', list_item->name, _item->oid, +displaypath); + } else { + if (!info->cached) { + struct object_id oid; + + if (head_ref_submodule(list_item->name, + handle_submodule_head_ref, )) + die(_("could not resolve HEAD ref inside the" + "submodule '%s'"), list_item->name); + + print_status(info, '+', list_item->name, , +displaypath); + } else { + print_status(info, '+', list_item->name, +_item->oid, displaypath); + } + } + + if (info->recursive) { + struct child_process cpr = CHILD_PROCESS_INIT; + +
[GSoC][PATCH v4 2/4] submodule--helper: introduce for_each_listed_submodule()
Introduce function for_each_listed_submodule() and replace a loop in module_init() with a call to it. The new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 36 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index e25854371..ea99d8e39 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -14,6 +14,9 @@ #include "refs.h" #include "connect.h" +typedef void (*each_submodule_fn)(const struct cache_entry *list_item, + void *cb_data); + static char *get_default_remote(void) { char *dest = NULL, *ret; @@ -351,15 +354,30 @@ static int module_list(int argc, const char **argv, const char *prefix) return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_listed_submodule(const struct module_list *list, + each_submodule_fn fn, void *cb_data) +{ + int i; + for (i = 0; i < list->nr; i++) + fn(list->entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int quiet: 1; +}; +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const struct cache_entry *list_item, void *cb_data) { + struct init_cb *info = cb_data; const struct submodule *sub; struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - displaypath = get_submodule_displaypath(path, prefix); + displaypath = get_submodule_displaypath(list_item->name, info->prefix); - sub = submodule_from_path(_oid, path); + sub = submodule_from_path(_oid, list_item->name); if (!sub) die(_("No url found for submodule path '%s' in .gitmodules"), @@ -371,7 +389,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * * Set active flag for the submodule being initialized */ - if (!is_submodule_active(the_repository, path)) { + if (!is_submodule_active(the_repository, list_item->name)) { strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); strbuf_reset(); @@ -413,7 +431,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!info->quiet) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); @@ -442,10 +460,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet) static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(, N_("Suppress output for initializing a submodule")), @@ -470,8 +488,10 @@ static int module_init(int argc, const char **argv, const char *prefix) if (!argc && git_config_get_value_multi("submodule.active")) module_list_active(); - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + info.prefix = prefix; + info.quiet = !!quiet; + + for_each_listed_submodule(, init_submodule, ); return 0; } -- 2.13.0
[GSoC][PATCH v4 1/4] submodule--helper: introduce get_submodule_displaypath()
Introduce function get_submodule_displaypath() to replace the code occurring in submodule_init() for generating displaypath of the submodule with a call to it. This new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 37 + 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 818fe74f0..e25854371 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -220,6 +220,28 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, )); + strbuf_release(); + return displaypath; + } else if (super_prefix) { + int len = strlen(super_prefix); + const char *format = is_dir_sep(super_prefix[len - 1]) ? "%s%s" : "%s/%s"; + + return xstrfmt(format, super_prefix, path); + } else { + return xstrdup(path); + } +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -335,15 +357,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, )); - else if (get_super_prefix()) { - strbuf_addf(, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(_oid, path); @@ -358,9 +372,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * Set active flag for the submodule being initialized */ if (!is_submodule_active(the_repository, path)) { - strbuf_reset(); strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); + strbuf_reset(); } /* @@ -368,7 +382,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * To look up the url in .git/config, we must not fall back to * .gitmodules, so look it up directly. */ - strbuf_reset(); strbuf_addf(, "submodule.%s.url", sub->name); if (git_config_get_string(sb.buf, )) { if (!sub->url) @@ -405,9 +418,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); } + strbuf_reset(); /* Copy "update" setting when it is not set yet */ - strbuf_reset(); strbuf_addf(, "submodule.%s.update", sub->name); if (git_config_get_string(sb.buf, ) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { -- 2.13.0
[GSoC][PATCH v4 3/4] submodule: port set_name_rev() from shell to C
Function set_name_rev() is ported from git-submodule to the submodule--helper builtin. The function compute_rev_name() generates the value of the revision name as required. The function get_rev_name() calls compute_rev_name() and receives the revision name, and later handles its formating and printing. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 63 + git-submodule.sh| 16 ++-- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index ea99d8e39..85df11129 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -245,6 +245,68 @@ static char *get_submodule_displaypath(const char *path, const char *prefix) } } +static char *compute_rev_name(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { + NULL + }; + + static const char *describe_tags[] = { + "--tags", NULL + }; + + static const char *describe_contains[] = { + "--contains", NULL + }; + + static const char *describe_all_always[] = { + "--all", "--always", NULL + }; + + static const char **describe_argv[] = { + describe_bare, describe_tags, describe_contains, + describe_all_always, NULL + }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(, "describe"); + argv_array_pushv(, *d); + argv_array_push(, object_id); + + if (!capture_command(, , 0) && sb.len) { + strbuf_strip_suffix(, "\n"); + return strbuf_detach(, NULL); + } + } + + strbuf_release(); + return NULL; +} + +static int get_rev_name(int argc, const char **argv, const char *prefix) +{ + char *revname; + if (argc != 3) + die("get-rev-name only accepts two arguments: "); + + revname = compute_rev_name(argv[1], argv[2]); + if (revname && revname[0]) + printf(" (%s)", revname); + printf("\n"); + + free(revname); + return 0; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -1292,6 +1354,7 @@ static struct cmd_struct commands[] = { {"relative-path", resolve_relative_path, 0}, {"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, + {"get-rev-name", get_rev_name, 0}, {"init", module_init, SUPPORT_SUPER_PREFIX}, {"remote-branch", resolve_remote_submodule_branch, 0}, {"push-check", push_check, 0}, diff --git a/git-submodule.sh b/git-submodule.sh index 66d1ae8ef..5211361c5 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -758,18 +758,6 @@ cmd_update() } } -set_name_rev () { - revname=$( ( - sanitize_submodule_env - cd "$1" && { - git describe "$2" 2>/dev/null || - git describe --tags "$2" 2>/dev/null || - git describe --contains "$2" 2>/dev/null || - git describe --all --always "$2" - } - ) ) - test -z "$revname" || revname=" ($revname)" -} # # Show commit summary for submodules in index or working tree # @@ -1041,14 +1029,14 @@ cmd_status() fi if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path" then - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper get-rev-name "$sm_path" "$sha1") say " $sha1 $displaypath$revname" else if test -z "$cached" then sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD) fi - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper get-rev-name "$sm_path" "$sha1") say "+$sha1 $displaypath$revname" fi -- 2.13.0
[GSoC][PATCH v4 0/4] Incremental rewrite of git-submodules
Changes in v4: * The patches were adjusted to recent codebase. Also, the gitmodules_config() was call was removed from the functions module_init() and module_status() which was essential after the merge of the branch bw/submodule-config-cleanup. Since it was mentioned earlier, I even tried basing the patch series on the branch nd/prune-in-worktree. Conflicts occured while doing so, since the later branch is based on the master branch with the HEAD pointing to the commit: The fourth batch post 2.14 I think there won't be any conflicts, if that is changed. As before you can find this series at: https://github.com/pratham-pc/git/commits/patch-series-1 And its build report is available at: https://travis-ci.org/pratham-pc/git/builds/ Branch: patch-series-1 Build #167 The above changes were based on master branch. Another branch, similar to the above, was created, but was based on the 'next' branch. Complete build report of that is also available at: https://travis-ci.org/pratham-pc/git/builds Branch: patch-series-1-next Build #168 The above changes are also push on github and are available at: https://github.com/pratham-pc/git/commits/patch-series-1-next Prathamesh Chavan (4): submodule--helper: introduce get_submodule_displaypath() submodule--helper: introduce for_each_listed_submodule() submodule: port set_name_rev() from shell to C submodule: port submodule subcommand 'status' from shell to C builtin/submodule--helper.c | 289 +--- git-submodule.sh| 61 +- 2 files changed, 271 insertions(+), 79 deletions(-) -- 2.13.0
Re: [GSoC][PATCH v3 0/4] Incremental rewrite of git-submodules
On Sat, Aug 26, 2017 at 12:21 AM, Junio C Hamano <gits...@pobox.com> wrote: > Thanks. I'll try to queue these before I'll go offline. > > Mentors may want to help the student further in adjusting the patch > series to the more recent codebase; unfortunately the area the GSoC > project touches is a bit fluid these days. I resolved the conflicts > with nd/pune-in-worktree and bw/submodule-config-cleanup topics so > that the result would compile, but I am not sure if the resolution > is correct (e.g. there may be a new leak I introduced while doing > so). > Thanks. I'll see the dirty merges and will resend the whole series after reviewing the dirty merge and sending a new one with/without changes as required. Thanks, Prathamesh Chavan
[GSoC][PATCH v3 4/4] submodule: port submodule subcommand 'status' from shell to C
This aims to make git-submodule 'status' a built-in. Hence, the function cmd_status() is ported from shell to C. This is done by introducing three functions: module_status(), submodule_status() and print_status(). The function module_status() acts as the front-end of the subcommand. It parses subcommand's options and then calls the function module_list_compute() for computing the list of submodules. Then this functions calls for_each_listed_submodule() looping through the list obtained. Then for_each_listed_submodule() calls submodule_status() for each of the submodule in its list. The function submodule_status() is responsible for generating the status each submodule it is called for, and then calls print_status(). Finally, the function print_status() handles the printing of submodule's status. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 156 git-submodule.sh| 49 +- 2 files changed, 157 insertions(+), 48 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 6ea6408c2..577494e31 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -561,6 +561,161 @@ static int module_init(int argc, const char **argv, const char *prefix) return 0; } +struct status_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; + unsigned int cached: 1; +}; +#define STATUS_CB_INIT { NULL, 0, 0, 0 } + +static void print_status(struct status_cb *info, char state, const char *path, +const struct object_id *oid, const char *displaypath) +{ + if (info->quiet) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') { + struct argv_array get_rev_args = ARGV_ARRAY_INIT; + + argv_array_pushl(_rev_args, "get-rev-name", +path, oid_to_hex(oid), NULL); + get_rev_name(get_rev_args.argc, get_rev_args.argv, +info->prefix); + + argv_array_clear(_rev_args); + } else { + printf("\n"); + } +} + +static int handle_submodule_head_ref(const char *refname, +const struct object_id *oid, int flags, +void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct status_cb *info = cb_data; + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + + if (!submodule_from_path(_oid, list_item->name)) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + list_item->name); + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + if (ce_stage(list_item)) { + print_status(info, 'U', list_item->name, +_oid, displaypath); + goto cleanup; + } + + if (!is_submodule_active(the_repository, list_item->name)) { + print_status(info, '-', list_item->name, _item->oid, +displaypath); + goto cleanup; + } + + argv_array_pushl(_files_args, "diff-files", +"--ignore-submodules=dirty", "--quiet", "--", +list_item->name, NULL); + + if (!cmd_diff_files(diff_files_args.argc, diff_files_args.argv, + info->prefix)) { + print_status(info, ' ', list_item->name, _item->oid, +displaypath); + } else { + if (!info->cached) { + struct object_id oid; + + if (head_ref_submodule(list_item->name, + handle_submodule_head_ref, )) + die(_("could not resolve HEAD ref inside the" + "submodule '%s'"), list_item->name); + + print_status(info, '+', list_item->name, , +displaypath); + } else { + print_status(info, '+', list_item->name, +_item->oid, displaypath); + } + } + + if (info->recursive) { + struct child_process cpr = CHILD_PROCESS_INIT; + +
[GSoC][PATCH v3 2/4] submodule--helper: introduce for_each_listed_submodule()
Introduce function for_each_listed_submodule() and replace a loop in module_init() with a call to it. The new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 39 +-- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index e666f84ba..8cd81b144 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -14,6 +14,9 @@ #include "refs.h" #include "connect.h" +typedef void (*each_submodule_fn)(const struct cache_entry *list_item, + void *cb_data); + static char *get_default_remote(void) { char *dest = NULL, *ret; @@ -353,17 +356,30 @@ static int module_list(int argc, const char **argv, const char *prefix) return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_listed_submodule(const struct module_list *list, + each_submodule_fn fn, void *cb_data) +{ + int i; + for (i = 0; i < list->nr; i++) + fn(list->entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int quiet: 1; +}; +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const struct cache_entry *list_item, void *cb_data) { + struct init_cb *info = cb_data; const struct submodule *sub; struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - /* Only loads from .gitmodules, no overlay with .git/config */ - gitmodules_config(); - displaypath = get_submodule_displaypath(path, prefix); + displaypath = get_submodule_displaypath(list_item->name, info->prefix); - sub = submodule_from_path(_oid, path); + sub = submodule_from_path(_oid, list_item->name); if (!sub) die(_("No url found for submodule path '%s' in .gitmodules"), @@ -375,7 +391,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * * Set active flag for the submodule being initialized */ - if (!is_submodule_active(the_repository, path)) { + if (!is_submodule_active(the_repository, list_item->name)) { strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); strbuf_reset(); @@ -417,7 +433,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!info->quiet) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); @@ -446,10 +462,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet) static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(, N_("Suppress output for initializing a submodule")), @@ -474,8 +490,11 @@ static int module_init(int argc, const char **argv, const char *prefix) if (!argc && git_config_get_value_multi("submodule.active")) module_list_active(); - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + info.prefix = prefix; + info.quiet = !!quiet; + + gitmodules_config(); + for_each_listed_submodule(, init_submodule, ); return 0; } -- 2.13.0
[GSoC][PATCH v3 3/4] submodule: port set_name_rev() from shell to C
Function set_name_rev() is ported from git-submodule to the submodule--helper builtin. The function compute_rev_name() generates the value of the revision name as required. The function get_rev_name() calls compute_rev_name() and receives the revision name, and later handles its formating and printing. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 63 + git-submodule.sh| 16 ++-- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 8cd81b144..6ea6408c2 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -245,6 +245,68 @@ static char *get_submodule_displaypath(const char *path, const char *prefix) } } +static char *compute_rev_name(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { + NULL + }; + + static const char *describe_tags[] = { + "--tags", NULL + }; + + static const char *describe_contains[] = { + "--contains", NULL + }; + + static const char *describe_all_always[] = { + "--all", "--always", NULL + }; + + static const char **describe_argv[] = { + describe_bare, describe_tags, describe_contains, + describe_all_always, NULL + }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(, "describe"); + argv_array_pushv(, *d); + argv_array_push(, object_id); + + if (!capture_command(, , 0) && sb.len) { + strbuf_strip_suffix(, "\n"); + return strbuf_detach(, NULL); + } + } + + strbuf_release(); + return NULL; +} + +static int get_rev_name(int argc, const char **argv, const char *prefix) +{ + char *revname; + if (argc != 3) + die("get-rev-name only accepts two arguments: "); + + revname = compute_rev_name(argv[1], argv[2]); + if (revname && revname[0]) + printf(" (%s)", revname); + printf("\n"); + + free(revname); + return 0; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -1243,6 +1305,7 @@ static struct cmd_struct commands[] = { {"relative-path", resolve_relative_path, 0}, {"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, + {"get-rev-name", get_rev_name, 0}, {"init", module_init, SUPPORT_SUPER_PREFIX}, {"remote-branch", resolve_remote_submodule_branch, 0}, {"push-check", push_check, 0}, diff --git a/git-submodule.sh b/git-submodule.sh index e131760ee..91f043ec6 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -759,18 +759,6 @@ cmd_update() } } -set_name_rev () { - revname=$( ( - sanitize_submodule_env - cd "$1" && { - git describe "$2" 2>/dev/null || - git describe --tags "$2" 2>/dev/null || - git describe --contains "$2" 2>/dev/null || - git describe --all --always "$2" - } - ) ) - test -z "$revname" || revname=" ($revname)" -} # # Show commit summary for submodules in index or working tree # @@ -1042,14 +1030,14 @@ cmd_status() fi if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path" then - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper get-rev-name "$sm_path" "$sha1") say " $sha1 $displaypath$revname" else if test -z "$cached" then sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD) fi - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper get-rev-name "$sm_path" "$sha1") say "+$sha1 $displaypath$revname" fi -- 2.13.0
[GSoC][PATCH v3 1/4] submodule--helper: introduce get_submodule_displaypath()
Introduce function get_submodule_displaypath() to replace the code occurring in submodule_init() for generating displaypath of the submodule with a call to it. This new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 38 +- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 84562ec83..e666f84ba 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -220,6 +220,28 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, )); + strbuf_release(); + return displaypath; + } else if (super_prefix) { + int len = strlen(super_prefix); + const char *format = is_dir_sep(super_prefix[len - 1]) ? "%s%s" : "%s/%s"; + + return xstrfmt(format, super_prefix, path); + } else { + return xstrdup(path); + } +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -339,16 +361,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) /* Only loads from .gitmodules, no overlay with .git/config */ gitmodules_config(); - - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, )); - else if (get_super_prefix()) { - strbuf_addf(, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(_oid, path); @@ -363,9 +376,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * Set active flag for the submodule being initialized */ if (!is_submodule_active(the_repository, path)) { - strbuf_reset(); strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); + strbuf_reset(); } /* @@ -373,7 +386,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * To look up the url in .git/config, we must not fall back to * .gitmodules, so look it up directly. */ - strbuf_reset(); strbuf_addf(, "submodule.%s.url", sub->name); if (git_config_get_string(sb.buf, )) { if (!sub->url) @@ -410,9 +422,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); } + strbuf_reset(); /* Copy "update" setting when it is not set yet */ - strbuf_reset(); strbuf_addf(, "submodule.%s.update", sub->name); if (git_config_get_string(sb.buf, ) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { -- 2.13.0
[GSoC][PATCH v3 0/4] Incremental rewrite of git-submodules
Changes in v3: * The name of the iterator function for_each_submodule() was changed to the for_each_listed_submodule(), as the function fits the naming pattern for_each_Y_X(), as here we iterate over group of listed submodules (X) which are listed (Y) by the function module_list_compute() * The name of the call back function type for the above pattern for_each_Y_X() is each_X_fn. Hence, this pattern was followed and the name of the call back function type for for_each_listed_submodule() was changed from submodule_list_func_t to each_submodule_fn. As before you can find this series at: https://github.com/pratham-pc/git/commits/week-14-1 And its build report is available at: https://travis-ci.org/pratham-pc/git/builds/ Branch: week-14-1 Build #164 Prathamesh Chavan (4): submodule--helper: introduce get_submodule_displaypath() submodule--helper: introduce for_each_listed_submodule() submodule: port set_name_rev() from shell to C submodule: port submodule subcommand 'status' from shell to C builtin/submodule--helper.c | 294 git-submodule.sh| 61 + 2 files changed, 273 insertions(+), 82 deletions(-) -- 2.13.0
[GSoC][PATCH v2 4/4] submodule: port submodule subcommand 'status' from shell to C
This aims to make git-submodule 'status' a built-in. Hence, the function cmd_status() is ported from shell to C. This is done by introducing three functions: module_status(), submodule_status() and print_status(). The function module_status() acts as the front-end of the subcommand. It parses subcommand's options and then calls the function module_list_compute() for computing the list of submodules. Then this functions calls for_each_submodule() looping through the list obtained. Then for_each_submodule() calls submodule_status() for each of the submodule in its list. The function submodule_status() is responsible for generating the status each submodule it is called for, and then calls print_status(). Finally, the function print_status() handles the printing of submodule's status. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 156 git-submodule.sh| 49 +- 2 files changed, 157 insertions(+), 48 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 6ae93ce38..933073251 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -561,6 +561,161 @@ static int module_init(int argc, const char **argv, const char *prefix) return 0; } +struct status_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; + unsigned int cached: 1; +}; +#define STATUS_CB_INIT { NULL, 0, 0, 0 } + +static void print_status(struct status_cb *info, char state, const char *path, +const struct object_id *oid, const char *displaypath) +{ + if (info->quiet) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') { + struct argv_array get_rev_args = ARGV_ARRAY_INIT; + + argv_array_pushl(_rev_args, "get-rev-name", +path, oid_to_hex(oid), NULL); + get_rev_name(get_rev_args.argc, get_rev_args.argv, +info->prefix); + + argv_array_clear(_rev_args); + } else { + printf("\n"); + } +} + +static int handle_submodule_head_ref(const char *refname, +const struct object_id *oid, int flags, +void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct status_cb *info = cb_data; + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + + if (!submodule_from_path(_oid, list_item->name)) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + list_item->name); + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + if (ce_stage(list_item)) { + print_status(info, 'U', list_item->name, +_oid, displaypath); + goto cleanup; + } + + if (!is_submodule_active(the_repository, list_item->name)) { + print_status(info, '-', list_item->name, _item->oid, +displaypath); + goto cleanup; + } + + argv_array_pushl(_files_args, "diff-files", +"--ignore-submodules=dirty", "--quiet", "--", +list_item->name, NULL); + + if (!cmd_diff_files(diff_files_args.argc, diff_files_args.argv, + info->prefix)) { + print_status(info, ' ', list_item->name, _item->oid, +displaypath); + } else { + if (!info->cached) { + struct object_id oid; + + if (head_ref_submodule(list_item->name, + handle_submodule_head_ref, )) + die(_("could not resolve HEAD ref inside the" + "submodule '%s'"), list_item->name); + + print_status(info, '+', list_item->name, , +displaypath); + } else { + print_status(info, '+', list_item->name, +_item->oid, displaypath); + } + } + + if (info->recursive) { + struct child_process cpr = CHILD_PROCESS_INIT; + +
[GSoC][PATCH v2 3/4] submodule: port set_name_rev() from shell to C
Function set_name_rev() is ported from git-submodule to the submodule--helper builtin. The function compute_rev_name() generates the value of the revision name as required. The function get_rev_name() calls compute_rev_name() and receives the revision name, and later handles its formating and printing. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 63 + git-submodule.sh| 16 ++-- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 847fba854..6ae93ce38 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -245,6 +245,68 @@ static char *get_submodule_displaypath(const char *path, const char *prefix) } } +static char *compute_rev_name(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { + NULL + }; + + static const char *describe_tags[] = { + "--tags", NULL + }; + + static const char *describe_contains[] = { + "--contains", NULL + }; + + static const char *describe_all_always[] = { + "--all", "--always", NULL + }; + + static const char **describe_argv[] = { + describe_bare, describe_tags, describe_contains, + describe_all_always, NULL + }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(, "describe"); + argv_array_pushv(, *d); + argv_array_push(, object_id); + + if (!capture_command(, , 0) && sb.len) { + strbuf_strip_suffix(, "\n"); + return strbuf_detach(, NULL); + } + } + + strbuf_release(); + return NULL; +} + +static int get_rev_name(int argc, const char **argv, const char *prefix) +{ + char *revname; + if (argc != 3) + die("get-rev-name only accepts two arguments: "); + + revname = compute_rev_name(argv[1], argv[2]); + if (revname && revname[0]) + printf(" (%s)", revname); + printf("\n"); + + free(revname); + return 0; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -1243,6 +1305,7 @@ static struct cmd_struct commands[] = { {"relative-path", resolve_relative_path, 0}, {"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, + {"get-rev-name", get_rev_name, 0}, {"init", module_init, SUPPORT_SUPER_PREFIX}, {"remote-branch", resolve_remote_submodule_branch, 0}, {"push-check", push_check, 0}, diff --git a/git-submodule.sh b/git-submodule.sh index e131760ee..91f043ec6 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -759,18 +759,6 @@ cmd_update() } } -set_name_rev () { - revname=$( ( - sanitize_submodule_env - cd "$1" && { - git describe "$2" 2>/dev/null || - git describe --tags "$2" 2>/dev/null || - git describe --contains "$2" 2>/dev/null || - git describe --all --always "$2" - } - ) ) - test -z "$revname" || revname=" ($revname)" -} # # Show commit summary for submodules in index or working tree # @@ -1042,14 +1030,14 @@ cmd_status() fi if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path" then - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper get-rev-name "$sm_path" "$sha1") say " $sha1 $displaypath$revname" else if test -z "$cached" then sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD) fi - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper get-rev-name "$sm_path" "$sha1") say "+$sha1 $displaypath$revname" fi -- 2.13.0
[GSoC][PATCH v2 2/4] submodule--helper: introduce for_each_submodule()
Introduce function for_each_submodule() and replace a loop in module_init() with a call to it. The new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 39 +-- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index e666f84ba..847fba854 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -14,6 +14,9 @@ #include "refs.h" #include "connect.h" +typedef void (*submodule_list_func_t)(const struct cache_entry *list_item, + void *cb_data); + static char *get_default_remote(void) { char *dest = NULL, *ret; @@ -353,17 +356,30 @@ static int module_list(int argc, const char **argv, const char *prefix) return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_submodule(const struct module_list *list, + submodule_list_func_t fn, void *cb_data) +{ + int i; + for (i = 0; i < list->nr; i++) + fn(list->entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int quiet: 1; +}; +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const struct cache_entry *list_item, void *cb_data) { + struct init_cb *info = cb_data; const struct submodule *sub; struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - /* Only loads from .gitmodules, no overlay with .git/config */ - gitmodules_config(); - displaypath = get_submodule_displaypath(path, prefix); + displaypath = get_submodule_displaypath(list_item->name, info->prefix); - sub = submodule_from_path(_oid, path); + sub = submodule_from_path(_oid, list_item->name); if (!sub) die(_("No url found for submodule path '%s' in .gitmodules"), @@ -375,7 +391,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * * Set active flag for the submodule being initialized */ - if (!is_submodule_active(the_repository, path)) { + if (!is_submodule_active(the_repository, list_item->name)) { strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); strbuf_reset(); @@ -417,7 +433,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!info->quiet) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); @@ -446,10 +462,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet) static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(, N_("Suppress output for initializing a submodule")), @@ -474,8 +490,11 @@ static int module_init(int argc, const char **argv, const char *prefix) if (!argc && git_config_get_value_multi("submodule.active")) module_list_active(); - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + info.prefix = prefix; + info.quiet = !!quiet; + + gitmodules_config(); + for_each_submodule(, init_submodule, ); return 0; } -- 2.13.0
[GSoC][PATCH v2 1/4] submodule--helper: introduce get_submodule_displaypath()
Introduce function get_submodule_displaypath() to replace the code occurring in submodule_init() for generating displaypath of the submodule with a call to it. This new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 38 +- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 84562ec83..e666f84ba 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -220,6 +220,28 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, )); + strbuf_release(); + return displaypath; + } else if (super_prefix) { + int len = strlen(super_prefix); + const char *format = is_dir_sep(super_prefix[len - 1]) ? "%s%s" : "%s/%s"; + + return xstrfmt(format, super_prefix, path); + } else { + return xstrdup(path); + } +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -339,16 +361,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) /* Only loads from .gitmodules, no overlay with .git/config */ gitmodules_config(); - - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, )); - else if (get_super_prefix()) { - strbuf_addf(, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(_oid, path); @@ -363,9 +376,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * Set active flag for the submodule being initialized */ if (!is_submodule_active(the_repository, path)) { - strbuf_reset(); strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); + strbuf_reset(); } /* @@ -373,7 +386,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * To look up the url in .git/config, we must not fall back to * .gitmodules, so look it up directly. */ - strbuf_reset(); strbuf_addf(, "submodule.%s.url", sub->name); if (git_config_get_string(sb.buf, )) { if (!sub->url) @@ -410,9 +422,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); } + strbuf_reset(); /* Copy "update" setting when it is not set yet */ - strbuf_reset(); strbuf_addf(, "submodule.%s.update", sub->name); if (git_config_get_string(sb.buf, ) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { -- 2.13.0
[GSoC][PATCH v2 0/4] submodule: Incremental rewrite of git-submodules
Changes in v2: * In the function get_submodule_displaypath(), I tried avoiding the extra memory allocation, but it rather invoked test 5 from t7406-submodule-update. The details of the test failiure can be seen in the build report as well. (Build #162) This failure occured as even though the function relative_path(), returned the value correctly, NULL was stored in sb.buf Hence currently the function is still using the old way of allocating extra memory and releasing the strbuf first, and return the extra allocated memory. * It was observed that strbuf_reset was being done just before the strbuf was being reused. It made more sense to reset them just after their use instead. Hence, these function calls of strbuf_reset() were moved accordingly. (The above change was limited to the function init_submodule() only, since already there was a deletion of one such funciton call, and IMO, it won't be suitable to carryout the operation for the complete file in the same commit.) * The function name for_each_submodule_list() was changed to for_each_submodule(). * Instead of passing the complete list to the function for_each_submodule(), only its pointer is passed to avoid the compiler from making copy of the list, and to make the code more efficient. * The function names of print_name_rev() and get_name_rev() were changed to get_rev_name() and compute_rev_name(). Now the function get_rev_name() acts as the front end of the subcommand and calls the function compute_rev_name() for generating and receiving the value of revision name. * The above change was also accompanied by the change in names of some variables used internally in the functions. As before you can find this series at: https://github.com/pratham-pc/git/commits/week-14-1 And the build report is available at: https://travis-ci.org/pratham-pc/git/builds/ Branch: week-14-1 Build #163 Prathamesh Chavan (4): submodule--helper: introduce get_submodule_displaypath() submodule--helper: introduce for_each_submodule() submodule: port set_name_rev() from shell to C submodule: port submodule subcommand 'status' from shell to C builtin/submodule--helper.c | 294 git-submodule.sh| 61 + 2 files changed, 273 insertions(+), 82 deletions(-) -- 2.13.0
[GSoC] Update: Week 14
SUMMARY OF MY PROJECT: Git submodule subcommands are currently implemented by using shell script 'git-submodule.sh'. There are several reasons why we'll prefer not to use the shell script. My project intends to convert the subcommands into C code, thus making them builtins. This will increase Git's portability and hence the efficiency of working with the git-submodule commands. Link to the complete proposal: [1] Mentors: Stefan BellerChristian Couder UPDATES: Following are the updates about my ongoing project: * add: Although much time was spent by me on trying to debug the patch, the attempts made were unsuccessful or were rather partially successful (since while fixing one issue, they invoked a different one). After getting suggestions and help from mentors for the remaining tests, now there are only two tests which the patch is currently failing at. The current status of the patch can be viewed at [2]. * update: porting this subcommand is already underway, and porting of the functions is_tip_reachable() and fetch_in_submodule() as completed. * patches: a smaller patch series is floated on the mailing list for the maintainer's review. The small patch series which recently was floated is pushed on Github and is available at [3]. PLAN FOR WEEK-15 (22 August 2017 to 28 August 2017): * add: One of the main aims of the next week is to resolve the the two tests which the current patch fails at. * patches: will be working on getting the smaller series merged, and once its done, will prepare and float the next patch for the same purpose. Till then, I aim to stay more active on the mailing list as well for responding to the reviews. Since I am a bit busier the following week (due to college academics), I will resume porting submodule subcommand 'update' in the following week. [1]: https://docs.google.com/document/d/1krxVLooWl--75Pot3dazhfygR3wCUUWZWzTXtK1L-xU/ [2]: https://github.com/pratham-pc/git/commits/sub-add [3]: https://github.com/pratham-pc/git/commits/week-14-1
Re: [GSoC][PATCH 3/4] submodule: port set_name_rev() from shell to C
On Mon, Aug 21, 2017 at 10:17 PM, Heiko Voigt <hvo...@hvoigt.net> wrote: > On Mon, Aug 21, 2017 at 09:45:14PM +0530, Prathamesh Chavan wrote: >> Function set_name_rev() is ported from git-submodule to the >> submodule--helper builtin. The function get_name_rev() generates the >> value of the revision name as required, and the function >> print_name_rev() handles the formating and printing of the obtained >> revision name. >> >> Mentored-by: Christian Couder <christian.cou...@gmail.com> >> Mentored-by: Stefan Beller <sbel...@google.com> >> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> >> --- >> builtin/submodule--helper.c | 63 >> + >> git-submodule.sh| 16 ++-- >> 2 files changed, 65 insertions(+), 14 deletions(-) >> >> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c >> index 7803457ba..a4bff3f38 100644 >> --- a/builtin/submodule--helper.c >> +++ b/builtin/submodule--helper.c > > [...] > >> @@ -1242,6 +1304,7 @@ static struct cmd_struct commands[] = { >> {"relative-path", resolve_relative_path, 0}, >> {"resolve-relative-url", resolve_relative_url, 0}, >> {"resolve-relative-url-test", resolve_relative_url_test, 0}, >> + {"print-name-rev", print_name_rev, 0}, > > I see the function in git-submodule.sh was named kind of reverse. How > about we do it more naturally here and call this 'rev-name' instead? > That makes is more clear to me and is also similar to the used variable > name 'revname'. The functions 'print_name_rev()' and 'get_name_rev()' are the ported C functions of the function 'set_name_rev()' Their names were assigned so due to the existing function's name, and hence to faithfully port the functions. But, thanks for the above suggestion, and also for reviewing the patch. I will update the names as print_rev_name() and get_rev_name() respectively. > > I would also prefix it differently like 'get' or 'calculate' instead of > 'print' since it tries to find a name and is not just a simple lookup. The former function from the shell script, 'set_name_rev()' is split into two functions, namely: 'print_name_rev()' and 'get_name_rev()' The function print_name_rev() is just the front_end of the function, and exists to printf the return value of the get_name_rev() function is the required format. Calculation of the value is actually done by the function get_name_rev(). Hence, I named the functions the way they are. Thanks, Prathamesh Chavan
[GSoC][PATCH 4/4] submodule: port submodule subcommand 'status' from shell to C
This aims to make git-submodule 'status' a built-in. Hence, the function cmd_status() is ported from shell to C. This is done by introducing three functions: module_status(), submodule_status() and print_status(). The function module_status() acts as the front-end of the subcommand. It parses subcommand's options and then calls the function module_list_compute() for computing the list of submodules. Then this functions calls for_each_submodule_list() looping through the list obtained. Then for_each_submodule_list() calls submodule_status() for each of the submodule in its list. The function submodule_status() is responsible for generating the status each submodule it is called for, and then calls print_status(). Finally, the function print_status() handles the printing of submodule's status. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- The patch below underwent some changes after its previous version. In the shell script, the submodules, which had merge-conflicts are identified by checking if the $stage variable has the value 'U' Till the last patch series, this was done by checking if ce_flags have a non-zero value. In this new patch series, this was changed and instead ce_stage() was called on the list_item(cache_entry), and then we check whether it has a non-zero value. builtin/submodule--helper.c | 156 git-submodule.sh| 49 +- 2 files changed, 157 insertions(+), 48 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a4bff3f38..831370d6e 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -560,6 +560,161 @@ static int module_init(int argc, const char **argv, const char *prefix) return 0; } +struct status_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; + unsigned int cached: 1; +}; +#define STATUS_CB_INIT { NULL, 0, 0, 0 } + +static void print_status(struct status_cb *info, char state, const char *path, +const struct object_id *oid, const char *displaypath) +{ + if (info->quiet) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') { + struct argv_array name_rev_args = ARGV_ARRAY_INIT; + + argv_array_pushl(_rev_args, "print-name-rev", +path, oid_to_hex(oid), NULL); + print_name_rev(name_rev_args.argc, name_rev_args.argv, + info->prefix); + + argv_array_clear(_rev_args); + } else { + printf("\n"); + } +} + +static int handle_submodule_head_ref(const char *refname, +const struct object_id *oid, int flags, +void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct status_cb *info = cb_data; + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + + if (!submodule_from_path(_oid, list_item->name)) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + list_item->name); + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + if (ce_stage(list_item)) { + print_status(info, 'U', list_item->name, +_oid, displaypath); + goto cleanup; + } + + if (!is_submodule_active(the_repository, list_item->name)) { + print_status(info, '-', list_item->name, _item->oid, +displaypath); + goto cleanup; + } + + argv_array_pushl(_files_args, "diff-files", +"--ignore-submodules=dirty", "--quiet", "--", +list_item->name, NULL); + + if (!cmd_diff_files(diff_files_args.argc, diff_files_args.argv, + info->prefix)) { + print_status(info, ' ', list_item->name, _item->oid, +displaypath); + } else { + if (!info->cached) { + struct object_id oid; + + if (head_ref_submodule(list_item->name, + handle_submodule_head_ref, )) + die(_("could not resolve HEAD ref inside the" + "submodule '%s'&qu
[GSoC][PATCH 1/4] submodule--helper: introduce get_submodule_displaypath()
Introduce function get_submodule_displaypath() to replace the code occurring in submodule_init() for generating displaypath of the submodule with a call to it. This new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- As said in the previous update, a short patch series is floated for the maintainer's review, and is consisting of the following changes: * introduce function get_submodule_displaypath() * introduce function for_each_submodule_list() * port function set_name_rev() from shell to C * port submodule subcommand 'status' from shell to C The complete build report for the above series of patches is available at: https://travis-ci.org/pratham-pc/git/builds Branch: week-14-1 Build #158 The above changes are also push on github and are available at: https://github.com/pratham-pc/git/commits/week-14-1 The above changes were based on master branch. But along with the above changes, the same series was also applied to a separate branch based on the 'next' branch. The changes were applicable without any additional changes to the patches. Complete build report of that is also available at: https://travis-ci.org/pratham-pc/git/builds Branch: week-14-1-next Build #159 The above changes are also push on github and are available at: https://github.com/pratham-pc/git/commits/week-14-1-next builtin/submodule--helper.c | 33 ++--- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 84562ec83..dcdbde963 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -220,6 +220,27 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, )); + strbuf_release(); + return displaypath; + } else if (super_prefix) { + int len = strlen(super_prefix); + const char *format = is_dir_sep(super_prefix[len - 1]) ? "%s%s" : "%s/%s"; + return xstrfmt(format, super_prefix, path); + } else { + return xstrdup(path); + } +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -339,16 +360,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) /* Only loads from .gitmodules, no overlay with .git/config */ gitmodules_config(); - - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, )); - else if (get_super_prefix()) { - strbuf_addf(, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(_oid, path); @@ -363,7 +375,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * Set active flag for the submodule being initialized */ if (!is_submodule_active(the_repository, path)) { - strbuf_reset(); strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); } -- 2.13.0
[GSoC][PATCH 3/4] submodule: port set_name_rev() from shell to C
Function set_name_rev() is ported from git-submodule to the submodule--helper builtin. The function get_name_rev() generates the value of the revision name as required, and the function print_name_rev() handles the formating and printing of the obtained revision name. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 63 + git-submodule.sh| 16 ++-- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 7803457ba..a4bff3f38 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -244,6 +244,68 @@ static char *get_submodule_displaypath(const char *path, const char *prefix) } } +static char *get_name_rev(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { + NULL + }; + + static const char *describe_tags[] = { + "--tags", NULL + }; + + static const char *describe_contains[] = { + "--contains", NULL + }; + + static const char *describe_all_always[] = { + "--all", "--always", NULL + }; + + static const char **describe_argv[] = { + describe_bare, describe_tags, describe_contains, + describe_all_always, NULL + }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(, "describe"); + argv_array_pushv(, *d); + argv_array_push(, object_id); + + if (!capture_command(, , 0) && sb.len) { + strbuf_strip_suffix(, "\n"); + return strbuf_detach(, NULL); + } + } + + strbuf_release(); + return NULL; +} + +static int print_name_rev(int argc, const char **argv, const char *prefix) +{ + char *namerev; + if (argc != 3) + die("print-name-rev only accepts two arguments: "); + + namerev = get_name_rev(argv[1], argv[2]); + if (namerev && namerev[0]) + printf(" (%s)", namerev); + printf("\n"); + + free(namerev); + return 0; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -1242,6 +1304,7 @@ static struct cmd_struct commands[] = { {"relative-path", resolve_relative_path, 0}, {"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, + {"print-name-rev", print_name_rev, 0}, {"init", module_init, SUPPORT_SUPER_PREFIX}, {"remote-branch", resolve_remote_submodule_branch, 0}, {"push-check", push_check, 0}, diff --git a/git-submodule.sh b/git-submodule.sh index e131760ee..e988167e0 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -759,18 +759,6 @@ cmd_update() } } -set_name_rev () { - revname=$( ( - sanitize_submodule_env - cd "$1" && { - git describe "$2" 2>/dev/null || - git describe --tags "$2" 2>/dev/null || - git describe --contains "$2" 2>/dev/null || - git describe --all --always "$2" - } - ) ) - test -z "$revname" || revname=" ($revname)" -} # # Show commit summary for submodules in index or working tree # @@ -1042,14 +1030,14 @@ cmd_status() fi if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path" then - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper print-name-rev "$sm_path" "$sha1") say " $sha1 $displaypath$revname" else if test -z "$cached" then sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD) fi - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper print-name-rev "$sm_path" "$sha1") say "+$sha1 $displaypath$revname" fi -- 2.13.0
[GSoC][PATCH 2/4] submodule--helper: introduce for_each_submodule_list()
Introduce function for_each_submodule_list() and replace a loop in module_init() with a call to it. The new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 39 +-- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index dcdbde963..7803457ba 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -14,6 +14,9 @@ #include "refs.h" #include "connect.h" +typedef void (*submodule_list_func_t)(const struct cache_entry *list_item, + void *cb_data); + static char *get_default_remote(void) { char *dest = NULL, *ret; @@ -352,17 +355,30 @@ static int module_list(int argc, const char **argv, const char *prefix) return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_submodule_list(const struct module_list list, + submodule_list_func_t fn, void *cb_data) { + int i; + for (i = 0; i < list.nr; i++) + fn(list.entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int quiet: 1; +}; +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct init_cb *info = cb_data; const struct submodule *sub; struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - /* Only loads from .gitmodules, no overlay with .git/config */ - gitmodules_config(); - displaypath = get_submodule_displaypath(path, prefix); + displaypath = get_submodule_displaypath(list_item->name, info->prefix); - sub = submodule_from_path(_oid, path); + sub = submodule_from_path(_oid, list_item->name); if (!sub) die(_("No url found for submodule path '%s' in .gitmodules"), @@ -374,7 +390,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * * Set active flag for the submodule being initialized */ - if (!is_submodule_active(the_repository, path)) { + if (!is_submodule_active(the_repository, list_item->name)) { strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); } @@ -416,7 +432,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!info->quiet) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); @@ -445,10 +461,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet) static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(, N_("Suppress output for initializing a submodule")), @@ -473,8 +489,11 @@ static int module_init(int argc, const char **argv, const char *prefix) if (!argc && git_config_get_value_multi("submodule.active")) module_list_active(); - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + info.prefix = prefix; + info.quiet = !!quiet; + + gitmodules_config(); + for_each_submodule_list(list, init_submodule, ); return 0; } -- 2.13.0
[GSoC] Update: Week-13
SUMMARY OF MY PROJECT: Git submodule subcommands are currently implemented by using shell script 'git-submodule.sh'. There are several reasons why we'll prefer not to use the shell script. My project intends to convert the subcommands into C code, thus making them builtins. This will increase Git's portability and hence the efficiency of working with the git-submodule commands. Link to the complete proposal: [1] Mentors: Stefan BellerChristian Couder UPDATES: Following are the updates about my ongoing project: * add: most of the time of the week was spent in debugging the ported submodule subcommand functions. But, even after so, currently the ported functions fail in total 15 tests from the test suit. The WIP patches have been updated regularly, and currently the patch is under discussion with the mentors as well. The current status of the patch is pushed on github as well, and can be viewed at:[2] Since the rest of the patches were almost the same as that in the previous update(except for the 'summary' patch, which was last updated after Christian's review), the haven't been uploaded again to avoid unnecessary floating patches. The previous updated series is available at: [3] But given that, next week I plan to float a separate patch series, containing the initial patches from the subcommand porting series (till deinit), which IMO, are ready for maintainer's review. Also, this week's update was one-day late, since I was traveling on the previous day. But a prior idea about this was given to the mentors. PLAN FOR WEEK-14 (15 August 2017 to 21 August 2017): * patches: Float a separate series, till deinit, and ask the maintainer for its review. * add: The main aim of the next week is to resolve the issues with the current patch, and get all the tests pass. * update: it is the last remaining subcommand to be ported. I aim to atleast start with this in the following week. The work till week-13 is pushed on Github and is available at: [4]. [1]: https://docs.google.com/document/d/1krxVLooWl--75Pot3dazhfygR3wCUUWZWzTXtK1L-xU/ [2]: https://github.com/pratham-pc/git/commits/sub-add [3]: https://public-inbox.org/git/20170807211900.15001-1-pc44...@gmail.com/ [4]: https://github.com/pratham-pc/git/commits/week-13
[GSoC][PATCH 13/13] submodule: port submodule subcommand 'foreach' from shell to C
This aims to make git-submodule foreach a builtin. This is the very first step taken in this direction. Hence, 'foreach' is ported to submodule--helper, and submodule--helper is called from git-submodule.sh. The code is split up to have one function to obtain all the list of submodules. This function acts as the front-end of git-submodule foreach subcommand. It calls the function for_each_submodule_list, which basically loops through the list and calls function fn, which in this case is runcommand_in_submodule. This third function is a calling function that takes care of running the command in that submodule, and recursively perform the same when --recursive is flagged. The first function module_foreach first parses the options present in argv, and then with the help of module_list_compute, generates the list of submodules present in the current working tree. The second function for_each_submodule_list traverses through the list, and calls function fn (which in case of submodule subcommand foreach is runcommand_in_submodule) is called for each entry. The third function runcommand_in_submodule, generates a submodule struct sub for $name, value and then later prepends name=sub->name; and other value assignment to the env argv_array structure of a child_process. Also the of submodule-foreach is push to args argv_array structure and finally, using run_command the commands are executed using a shell. The third function also takes care of the recursive flag, by creating a separate child_process structure and prepending "--super-prefix displaypath", to the args argv_array structure. Other required arguments and the input of submodule-foreach is also appended to this argv_array. Helped-by: Brandon Williams <bmw...@google.com> Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * A comment was added to clarify why the env variables were made available only for the case of argc == 1. builtin/submodule--helper.c | 142 git-submodule.sh| 39 +--- 2 files changed, 143 insertions(+), 38 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 2080f4fb9..0717ecf80 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -770,6 +770,147 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct cb_foreach { + int argc; + const char **argv; + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; +}; +#define CB_FOREACH_INIT { 0, NULL, NULL, 0, 0 } + +static void runcommand_in_submodule(const struct cache_entry *list_item, + void *cb_data) +{ + struct cb_foreach *info = cb_data; + const struct submodule *sub; + struct child_process cp = CHILD_PROCESS_INIT; + char *displaypath; + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + sub = submodule_from_path(null_sha1, list_item->name); + + if (!sub) + die(_("No url found for submodule path '%s' in .gitmodules"), + displaypath); + + if (!is_submodule_populated_gently(list_item->name, NULL)) + goto cleanup; + + prepare_submodule_repo_env(_array); + + /* +* For the purpose of executing in the submodule, +* separate shell is used for the purpose of running the +* child process. +*/ + cp.use_shell = 1; + cp.dir = list_item->name; + + /* +* NEEDSWORK: the command currently has access to the variables $name, +* $sm_path, $displaypath, $sha1 and $toplevel only when the command +* contains a single argument. This is done for maintianing a faithful +* translation from shell script. +*/ + if (info->argc == 1) { + char *toplevel = xgetcwd(); + + argv_array_pushf(_array, "name=%s", sub->name); + argv_array_pushf(_array, "sm_path=%s", list_item->name); + argv_array_pushf(_array, "displaypath=%s", displaypath); + argv_array_pushf(_array, "sha1=%s", +oid_to_hex(_item->oid)); + argv_array_pushf(_array, "toplevel=%s", toplevel); + + /* +* Since the path variable was accessible from the script +* before porting, it is also made available after porting. +* The environment variable "PATH" has a very special purpose +* on windows. And since environment variables are +* case-insensitive in windows,
[GSoC][PATCH 12/13] submodule foreach: document variable '$displaypath'
It was observed that the variable '$displaypath' was accessible but undocumented. Hence, document it. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- Documentation/git-submodule.txt | 6 -- t/t7407-submodule-foreach.sh| 22 +++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 8e7930ebc..0cca702cb 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -183,10 +183,12 @@ information too. foreach [--recursive] :: Evaluates an arbitrary shell command in each checked out submodule. - The command has access to the variables $name, $sm_path, $sha1 and - $toplevel: + The command has access to the variables $name, $sm_path, $displaypath, + $sha1 and $toplevel: $name is the name of the relevant submodule section in `.gitmodules`, $sm_path is the path of the submodule as recorded in the superproject, + $displaypath contains the relative path from the current working + directory to the submodules root directory, $sha1 is the commit as recorded in the superproject, and $toplevel is the absolute path to its superproject, such that $toplevel/$sm_path is the absolute path of the submodule. diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 0663622a4..6ad57e061 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -82,16 +82,16 @@ test_expect_success 'test basic "submodule foreach" usage' ' cat >expect <../../actual + git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual ) && test_i18ncmp expect actual ' @@ -206,25 +206,25 @@ submodulesha1=$(cd clone2/nested1/nested2/nested3/submodule && git rev-parse HEA cat >expect <../../actual + git submodule foreach --recursive "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual ) && test_i18ncmp expect actual ' -- 2.13.0
[GSoC][PATCH 08/13] submodule: port submodule subcommand 'summary' from shell to C
The submodule subcommand 'summary' is ported in the process of making git-submodule a builtin. The function cmd_summary() from git-submodule.sh is ported to functions module_summary(), compute_summary_module_list(), prepare_submodule_summary() and generate_submodule_summary(), print_summary(). The first function module_summary() parses the options of submodule subcommand and also acts as the front-end of this subcommand. After parsing them, it calls the compute_summary_module_list() The functions compute_summary_module_list() runs the diff_cmd, and generates the modules list, as required by the subcommand. The generation of this module list is done by the using the callback function submodule_summary_callback(), and stored in the structure module_cb. Once the module list is generated, prepare_submodule_summary() further goes through the list and filters the list, for eventually calling the generate_submodule_summary() function. The function generate_submodule_summary() takes care of generating the summary for each submodule and then calls the function print_summary() for printing it. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- The major changes the patch underwent through is the splitting of the function print_submodule_summary() into generate_submodule_summary() and print_summary() Apart from this, there are also minor changes which include removal of variables sha1_abbr_dst and sha1_abbr_src, the variable remote_key wasn't freed earlier, but now it is. builtin/submodule--helper.c | 428 git-submodule.sh| 183 +-- 2 files changed, 429 insertions(+), 182 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 02787e4a4..2080f4fb9 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -13,6 +13,9 @@ #include "remote.h" #include "refs.h" #include "connect.h" +#include "revision.h" +#include "diffcore.h" +#include "diff.h" typedef void (*submodule_list_func_t)(const struct cache_entry *list_item, void *cb_data); @@ -767,6 +770,430 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct module_cb { + unsigned int mod_src; + unsigned int mod_dst; + struct object_id oid_src; + struct object_id oid_dst; + char status; + const char *sm_path; +}; +#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL } + +struct module_cb_list { + struct module_cb **entries; + int alloc, nr; +}; +#define MODULE_CB_LIST_INIT { NULL, 0, 0 } + +struct summary_cb { + int argc; + const char **argv; + const char *prefix; + unsigned int cached: 1; + unsigned int for_status: 1; + unsigned int quiet: 1; + unsigned int files: 1; + int summary_limits; +}; +#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0, 0 } + +static enum { + DIFF_INDEX, + DIFF_FILES +} diff_cmd = DIFF_INDEX; + +static int verify_submodule_object_name(const char *sm_path, const char *sha1) +{ + struct child_process cp_rev_parse = CHILD_PROCESS_INIT; + + cp_rev_parse.git_cmd = 1; + cp_rev_parse.no_stdout = 1; + cp_rev_parse.dir = sm_path; + prepare_submodule_repo_env(_rev_parse.env_array); + argv_array_pushl(_rev_parse.args, "rev-parse", "-q", +"--verify", NULL); + argv_array_pushf(_rev_parse.args, "%s^0", sha1); + + if (run_command(_rev_parse)) + return 1; + + return 0; +} + +static void print_summary(struct summary_cb *info, int errmsg, int total_commits, + int missing_src, int missing_dst, const char *displaypath, + int is_sm_git_dir, struct module_cb *p) +{ + if (p->status == 'T') { + if (S_ISGITLINK(p->mod_dst)) + printf(_("* %s %s(blob)->%s(submodule)"), +displaypath, find_unique_abbrev(p->oid_src.hash, 7), +find_unique_abbrev(p->oid_dst.hash, 7)); + else + printf(_("* %s %s(submodule)->%s(blob)"), +displaypath, find_unique_abbrev(p->oid_src.hash, 7), +find_unique_abbrev(p->oid_dst.hash, 7)); + } else { + printf("* %s %s...%s", displaypath, find_unique_abbrev(p->oid_src.hash, 7), +find_unique_abbrev(p->oid_dst.hash, 7)); + } + + if (total_commits < 0) + printf(":\n"); + else +
[GSoC][PATCH 10/13] submodule foreach: document '$sm_path' instead of '$path'
As using a variable '$path' may be harmful to users due to capitalization issues, see 64394e3ae9 (git-submodule.sh: Don't use $path variable in eval_gettext string, 2012-04-17). Adjust the documentation to advocate for using $sm_path, which contains the same value. We still make the 'path' variable available and document it as a deprecated synonym of 'sm_path'. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- Documentation/git-submodule.txt | 10 ++ 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index ff612001d..a23baef62 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -183,12 +183,14 @@ information too. foreach [--recursive] :: Evaluates an arbitrary shell command in each checked out submodule. - The command has access to the variables $name, $path, $sha1 and + The command has access to the variables $name, $sm_path, $sha1 and $toplevel: $name is the name of the relevant submodule section in `.gitmodules`, - $path is the name of the submodule directory relative to the - superproject, $sha1 is the commit as recorded in the superproject, - and $toplevel is the absolute path to the top-level of the superproject. + $sm_path is the path of the submodule as recorded in the superproject, + $sha1 is the commit as recorded in the superproject, and + $toplevel is the absolute path to the top-level of the superproject. + Note that to avoid conflicts with '$PATH' on Windows, the '$path' + variable is now a deprecated synonym of '$sm_path' variable. Any submodules defined in the superproject but not checked out are ignored by this command. Unless given `--quiet`, foreach prints the name of each submodule before evaluating the command. -- 2.13.0
[GSoC][PATCH 11/13] submodule foreach: clarify the '$toplevel' variable documentation
It does not contain the topmost superproject as the author assumed, but the direct superproject, such that $toplevel/$sm_path is the actual absolute path of the submodule. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- Documentation/git-submodule.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index a23baef62..8e7930ebc 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -188,7 +188,8 @@ foreach [--recursive] :: $name is the name of the relevant submodule section in `.gitmodules`, $sm_path is the path of the submodule as recorded in the superproject, $sha1 is the commit as recorded in the superproject, and - $toplevel is the absolute path to the top-level of the superproject. + $toplevel is the absolute path to its superproject, such that + $toplevel/$sm_path is the absolute path of the submodule. Note that to avoid conflicts with '$PATH' on Windows, the '$path' variable is now a deprecated synonym of '$sm_path' variable. Any submodules defined in the superproject but not checked out are -- 2.13.0
[GSoC][PATCH 09/13] submodule foreach: correct '$path' in nested submodules from a subdirectory
When running 'git submodule foreach' from a subdirectory of your repository, nested submodules get a bogus value for $sm_path: For a submodule 'sub' that contains a nested submodule 'nested', running 'git -C dir submodule foreach echo $path' would report path='../nested' for the nested submodule. The first part '../' is derived from the logic computing the relative path from $pwd to the root of the superproject. The second part is the submodule path inside the submodule. This value is of little use and is hard to document. There are two different possible solutions that have more value: (a) The path value is documented as the path from the toplevel of the superproject to the mount point of the submodule. In this case we would want to have path='sub/nested'. (b) As Ramsay noticed the documented value is wrong. For the non-nested case the path is equal to the relative path from $pwd to the submodules working directory. When following this model, the expected value would be path='../sub/nested'. The behavior for (b) was introduced in 091a6eb0fe (submodule: drop the top-level requirement, 2013-06-16) the intent for $path seemed to be relative to $cwd to the submodule worktree, but that did not work for nested submodules, as the intermittent submodules were not included in the path. If we were to fix the meaning of the $path using (a) such that "path" is "the path from the toplevel of the superproject to the mount point of the submodule", we would break any existing submodule user that runs foreach from non-root of the superproject as the non-nested submodule '../sub' would change its path to 'sub'. If we would fix the meaning of the $path using (b), such that "path" is "the relative path from $pwd to the submodule", then we would break any user that uses nested submodules (even from the root directory) as the 'nested' would become 'sub/nested'. Both groups can be found in the wild. The author has no data if one group outweighs the other by large margin, and offending each one seems equally bad at first. However in the authors imagination it is better to go with (a) as running from a sub directory sounds like it is carried out by a human rather than by some automation task. With a human on the keyboard the feedback loop is short and the changed behavior can be adapted to quickly unlike some automation that can break silently. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> Signed-off-by: Stefan Beller <sbel...@google.com> --- git-submodule.sh | 1 - t/t7407-submodule-foreach.sh | 36 ++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/git-submodule.sh b/git-submodule.sh index ec57d6528..4b7da2fc1 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -320,7 +320,6 @@ cmd_foreach() prefix="$prefix$sm_path/" sanitize_submodule_env cd "$sm_path" && - sm_path=$(git submodule--helper relative-path "$sm_path" "$wt_prefix") && # we make $path available to scripts ... path=$sm_path && if test $# -eq 1 diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 6ba5daf42..0663622a4 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -82,9 +82,9 @@ test_expect_success 'test basic "submodule foreach" usage' ' cat >expect <expect <../../actual + ) && + test_i18ncmp expect actual +' cat > expect <
[GSoC][PATCH 07/13] diff: change scope of the function count_lines()
Change the scope of function count_lines for allowing the function to be reused in other parts of the code as well. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- diff.c | 2 +- diff.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/diff.c b/diff.c index 85e714f6c..03ed64f93 100644 --- a/diff.c +++ b/diff.c @@ -425,7 +425,7 @@ struct emit_callback { struct strbuf *header; }; -static int count_lines(const char *data, int size) +int count_lines(const char *data, int size) { int count, ch, completely_empty = 1, nl_just_seen = 0; count = 0; diff --git a/diff.h b/diff.h index 2d442e296..8522514e9 100644 --- a/diff.h +++ b/diff.h @@ -273,6 +273,7 @@ extern struct diff_filepair *diff_unmerge(struct diff_options *, const char *pat extern int parse_long_opt(const char *opt, const char **argv, const char **optarg); +extern int count_lines(const char *data, int size); extern int git_diff_basic_config(const char *var, const char *value, void *cb); extern int git_diff_heuristic_config(const char *var, const char *value, void *cb); extern void init_diff_ui_defaults(void); -- 2.13.0
[GSoC][PATCH 06/13] submodule: port submodule subcommand 'deinit' from shell to C
The same mechanism is used even for porting this submodule subcommand, as used in the ported subcommands till now. The function cmd_deinit in split up after porting into three functions: module_deinit(), for_each_submodule_list() and deinit_submodule(). Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 148 git-submodule.sh| 55 +--- 2 files changed, 149 insertions(+), 54 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 82f1aed87..02787e4a4 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -915,6 +915,153 @@ static int module_sync(int argc, const char **argv, const char *prefix) return 0; } +struct deinit_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int force: 1; + unsigned int all: 1; +}; +#define DEINIT_CB_INIT { NULL, 0, 0, 0 } + +static void deinit_submodule(const struct cache_entry *list_item, +void *cb_data) +{ + struct deinit_cb *info = cb_data; + const struct submodule *sub; + char *displaypath = NULL; + struct child_process cp_config = CHILD_PROCESS_INIT; + struct strbuf sb_config = STRBUF_INIT; + char *sub_git_dir = xstrfmt("%s/.git", list_item->name); + mode_t mode = 0777; + + sub = submodule_from_path(null_sha1, list_item->name); + + if (!sub || !sub->name) + goto cleanup; + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + /* remove the submodule work tree (unless the user already did it) */ + if (is_directory(list_item->name)) { + struct stat st; + /* +* protect submodules containing a .git directory +* NEEDSWORK: automatically call absorbgitdirs before +* warning/die. +*/ + if (is_directory(sub_git_dir)) + die(_("Submodule work tree '%s' contains a .git " + "directory use 'rm -rf' if you really want " + "to remove it including all of its history"), + displaypath); + + if (!info->force) { + struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; + argv_array_pushl(_rm.args, "rm", "-qn", +list_item->name, NULL); + + if (run_command(_rm)) + die(_("Submodule work tree '%s' contains local " + "modifications; use '-f' to discard them"), + displaypath); + } + + if (!lstat(list_item->name, )) { + struct strbuf sb_rm = STRBUF_INIT; + const char *format; + + strbuf_addstr(_rm, list_item->name); + + if (!remove_dir_recursively(_rm, 0)) + format = _("Cleared directory '%s'\n"); + else + format = _("Could not remove submodule work tree '%s'\n"); + + if (!info->quiet) + printf(format, displaypath); + + mode = st.st_mode; + + strbuf_release(_rm); + } + } + + if (mkdir(list_item->name, mode)) + die_errno(_("could not create empty submodule directory %s"), + displaypath); + + cp_config.git_cmd = 1; + argv_array_pushl(_config.args, "config", "--get-regexp", NULL); + argv_array_pushf(_config.args, "submodule.%s\\.", sub->name); + + /* remove the .git/config entries (unless the user already did it) */ + if (!capture_command(_config, _config, 0) && sb_config.len) { + char *sub_key = xstrfmt("submodule.%s", sub->name); + /* +* remove the whole section so we have a clean state when +* the user later decides to init this submodule again +*/ + git_config_rename_section_in_file(NULL, sub_key, NULL); + if (!info->quiet) + printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"), +sub->name, sub->url, displaypath); + free(sub_key); + } + +cleanup: + free(displaypath); + free(sub_gi
[GSoC][PATCH 04/13] submodule: port submodule subcommand 'status' from shell to C
This aims to make git-submodule 'status' a built-in. Hence, the function cmd_status() is ported from shell to C. This is done by introducing three functions: module_status(), submodule_status() and print_status(). The function module_status() acts as the front-end of the subcommand. It parses subcommand's options and then calls the function module_list_compute() for computing the list of submodules. Then this functions calls for_each_submodule_list() looping through the list obtained. Then for_each_submodule_list() calls submodule_status() for each of the submodule in its list. The function submodule_status() is responsible for generating the status each submodule it is called for, and then calls print_status(). Finally, the function print_status() handles the printing of submodule's status. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 156 git-submodule.sh| 49 +- 2 files changed, 157 insertions(+), 48 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 421eee1e2..1bf7bb2a2 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -560,6 +560,161 @@ static int module_init(int argc, const char **argv, const char *prefix) return 0; } +struct status_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; + unsigned int cached: 1; +}; +#define STATUS_CB_INIT { NULL, 0, 0, 0 } + +static void print_status(struct status_cb *info, char state, const char *path, +const struct object_id *oid, const char *displaypath) +{ + if (info->quiet) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') { + struct argv_array name_rev_args = ARGV_ARRAY_INIT; + + argv_array_pushl(_rev_args, "print-name-rev", +path, oid_to_hex(oid), NULL); + print_name_rev(name_rev_args.argc, name_rev_args.argv, + info->prefix); + + argv_array_clear(_rev_args); + } else { + printf("\n"); + } +} + +static int handle_submodule_head_ref(const char *refname, +const struct object_id *oid, int flags, +void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct status_cb *info = cb_data; + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + + if (!submodule_from_path(null_sha1, list_item->name)) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + list_item->name); + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + if (list_item->ce_flags) { + print_status(info, 'U', list_item->name, +_oid, displaypath); + goto cleanup; + } + + if (!is_submodule_active(the_repository, list_item->name)) { + print_status(info, '-', list_item->name, _item->oid, +displaypath); + goto cleanup; + } + + argv_array_pushl(_files_args, "diff-files", +"--ignore-submodules=dirty", "--quiet", "--", +list_item->name, NULL); + + if (!cmd_diff_files(diff_files_args.argc, diff_files_args.argv, + info->prefix)) { + print_status(info, ' ', list_item->name, _item->oid, +displaypath); + } else { + if (!info->cached) { + struct object_id oid; + + if (head_ref_submodule(list_item->name, + handle_submodule_head_ref, )) + die(_("could not resolve HEAD ref inside the" + "submodule '%s'"), list_item->name); + + print_status(info, '+', list_item->name, , +displaypath); + } else { + print_status(info, '+', list_item->name, +_item->oid, displaypath); + } + } + + if (info->recursive) { + struct child_process cpr = CHILD_PROCESS_INIT; + +
[GSoC][PATCH 05/13] submodule: port submodule subcommand 'sync' from shell to C
Port the submodule subcommand 'sync' from shell to C using the same mechanism as that used for porting submodule subcommand 'status'. Hence, here the function cmd_sync() is ported from shell to C. This is done by introducing three functions: module_sync(), sync_submodule() and print_default_remote(). The function print_default_remote() is introduced for getting the default remote as stdout. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 183 git-submodule.sh| 57 +- 2 files changed, 184 insertions(+), 56 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 1bf7bb2a2..82f1aed87 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -44,6 +44,20 @@ static char *get_default_remote(void) return ret; } +static int print_default_remote(int argc, const char **argv, const char *prefix) +{ + const char *remote; + + if (argc != 1) + die(_("submodule--helper print-default-remote takes no arguments")); + + remote = get_default_remote(); + if (remote) + printf("%s\n", remote); + + return 0; +} + static int starts_with_dot_slash(const char *str) { return str[0] == '.' && is_dir_sep(str[1]); @@ -379,6 +393,25 @@ static void module_list_active(struct module_list *list) *list = active_modules; } +static char *get_up_path(const char *path) +{ + int i; + struct strbuf sb = STRBUF_INIT; + + for (i = count_slashes(path); i; i--) + strbuf_addstr(, "../"); + + /* +* Check if 'path' ends with slash or not +* for having the same output for dir/sub_dir +* and dir/sub_dir/ +*/ + if (!is_dir_sep(path[strlen(path) - 1])) + strbuf_addstr(, "../"); + + return strbuf_detach(, NULL); +} + static int module_list(int argc, const char **argv, const char *prefix) { int i; @@ -734,6 +767,154 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct sync_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; +}; +#define SYNC_CB_INIT { NULL, 0, 0 } + +static void sync_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct sync_cb *info = cb_data; + const struct submodule *sub; + char *remote_key = NULL; + char *sub_origin_url, *super_config_url, *displaypath; + struct strbuf sb = STRBUF_INIT; + struct child_process cp = CHILD_PROCESS_INIT; + char *sub_config_path = NULL; + + if (!is_submodule_active(the_repository, list_item->name)) + return; + + sub = submodule_from_path(null_sha1, list_item->name); + + if (sub && sub->url) { + if (starts_with_dot_dot_slash(sub->url) || starts_with_dot_slash(sub->url)) { + char *remote_url, *up_path; + char *remote = get_default_remote(); + strbuf_addf(, "remote.%s.url", remote); + + if (git_config_get_string(sb.buf, _url)) + remote_url = xgetcwd(); + + up_path = get_up_path(list_item->name); + sub_origin_url = relative_url(remote_url, sub->url, up_path); + super_config_url = relative_url(remote_url, sub->url, NULL); + + free(remote); + free(up_path); + free(remote_url); + } else { + sub_origin_url = xstrdup(sub->url); + super_config_url = xstrdup(sub->url); + } + } else { + sub_origin_url = ""; + super_config_url = ""; + } + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + if (!info->quiet) + printf(_("Synchronizing submodule url for '%s'\n"), +displaypath); + + strbuf_reset(); + strbuf_addf(, "submodule.%s.url", sub->name); + if (git_config_set_gently(sb.buf, super_config_url)) + die(_("failed to register url for submodule path '%s'"), + displaypath); + + if (!is_submodule_populated_gently(list_item->name, NULL)) + goto cleanup; + + prepare_submodule_repo_env(_array); + cp.git_cmd = 1; + cp.dir = list_item->name; + argv_array_pushl(, "submodule--helper", +"print-defa
[GSoC][PATCH 01/13] submodule--helper: introduce get_submodule_displaypath()
Introduce function get_submodule_displaypath() to replace the code occurring in submodule_init() for generating displaypath of the submodule with a call to it. This new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 33 ++--- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 6abdad329..7af4de09b 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -220,6 +220,27 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, )); + strbuf_release(); + return displaypath; + } else if (super_prefix) { + int len = strlen(super_prefix); + const char *format = is_dir_sep(super_prefix[len - 1]) ? "%s%s" : "%s/%s"; + return xstrfmt(format, super_prefix, path); + } else { + return xstrdup(path); + } +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -339,16 +360,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) /* Only loads from .gitmodules, no overlay with .git/config */ gitmodules_config(); - - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, )); - else if (get_super_prefix()) { - strbuf_addf(, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(null_sha1, path); @@ -363,7 +375,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * Set active flag for the submodule being initialized */ if (!is_submodule_active(the_repository, path)) { - strbuf_reset(); strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); } -- 2.13.0
[GSoC][PATCH 00/13] Update: Week-12
SUMMARY OF MY PROJECT: Git submodule subcommands are currently implemented by using shell script 'git-submodule.sh'. There are several reasons why we'll prefer not to use the shell script. My project intends to convert the subcommands into C code, thus making them builtins. This will increase Git's portability and hence the efficiency of working with the git-submodule commands. Link to the complete proposal: [1] Mentors: Stefan Beller <sbel...@google.com> Christian Couder <christian.cou...@gmail.com> UPDATES: Following are the updates about my ongoing project: * Following patches were updated after the previous reviews: submodule subcommands: - deinit - summary - foreach * summary: the function print_submodule_summary() is split-up into two separate functions: generate_submodule_summary() and print_summary(). * porting of submodule subcommand 'add' is completed and I have started with debugging ported function. Currently, the entire function cmd_add() is ported to the function module_add() in C. Soon, its first patch will be floated here as well once debugging is completed. Its progress can be viewed at [2]. * displaypath: Last week, there was some confusion produced with the way, the value of displaypath is being generated, which led to some discussion, which is available at: [3]. PLAN FOR WEEK-13 (8 August 2017 to 14 August 2017): * patches: IMO, the patches till deinit are reviewed many times, and hence will try to get at least these patches merged. * add: As this subcommand is widely used in the test suite, there are many tests this ported function is failing at. Hence, debugging the subcommand would be another task for the next week. * deinit: A bug was identified by Stefan in the last patch-series. its details are available at: [4] Currenlty, the bug was handled by adding a NEEDSWORK tagged comment as suggest. If possible, I will also start working on debugging the issue asap. A complete build report of these series of patches is available at: [5]. Build #151 Branch: week-12 The work is pushed on Github and is available at: [6]. [1]: https://docs.google.com/document/d/1krxVLooWl--75Pot3dazhfygR3wCUUWZWzTXtK1L-xU/ [2]: https://github.com/pratham-pc/git/commits/sub-add [3]: https://public-inbox.org/git/CAME+mvXsh53kLJ4se4uKY=SJcvSbHtEZQ6K2CgAPs=1wxux...@mail.gmail.com/ [4]: https://public-inbox.org/git/CAGZ79kbyyR54me_+wQDZRrikqKTp_a98yozVfr8P85QHfyyy=q...@mail.gmail.com/ [5]: https://travis-ci.org/pratham-pc/git/builds/ [6]: https://github.com/pratham-pc/git/commits/week-12 Prathamesh Chavan (13): submodule--helper: introduce get_submodule_displaypath() submodule--helper: introduce for_each_submodule_list() submodule: port set_name_rev() from shell to C submodule: port submodule subcommand 'status' from shell to C submodule: port submodule subcommand 'sync' from shell to C submodule: port submodule subcommand 'deinit' from shell to C diff: change scope of the function count_lines() submodule: port submodule subcommand 'summary' from shell to C submodule foreach: correct '$path' in nested submodules from a subdirectory submodule foreach: document '$sm_path' instead of '$path' submodule foreach: clarify the '$toplevel' variable documentation submodule foreach: document variable '$displaypath' submodule: port submodule subcommand 'foreach' from shell to C Documentation/git-submodule.txt | 15 +- builtin/submodule--helper.c | 1190 ++- diff.c |2 +- diff.h |1 + git-submodule.sh| 396 + t/t7407-submodule-foreach.sh| 38 +- 6 files changed, 1222 insertions(+), 420 deletions(-) -- 2.13.0
[GSoC][PATCH 02/13] submodule--helper: introduce for_each_submodule_list()
Introduce function for_each_submodule_list() and replace a loop in module_init() with a call to it. The new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 39 +-- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 7af4de09b..e41572f7a 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -14,6 +14,9 @@ #include "refs.h" #include "connect.h" +typedef void (*submodule_list_func_t)(const struct cache_entry *list_item, + void *cb_data); + static char *get_default_remote(void) { char *dest = NULL, *ret; @@ -352,17 +355,30 @@ static int module_list(int argc, const char **argv, const char *prefix) return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_submodule_list(const struct module_list list, + submodule_list_func_t fn, void *cb_data) { + int i; + for (i = 0; i < list.nr; i++) + fn(list.entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int quiet: 1; +}; +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct init_cb *info = cb_data; const struct submodule *sub; struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - /* Only loads from .gitmodules, no overlay with .git/config */ - gitmodules_config(); - displaypath = get_submodule_displaypath(path, prefix); + displaypath = get_submodule_displaypath(list_item->name, info->prefix); - sub = submodule_from_path(null_sha1, path); + sub = submodule_from_path(null_sha1, list_item->name); if (!sub) die(_("No url found for submodule path '%s' in .gitmodules"), @@ -374,7 +390,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * * Set active flag for the submodule being initialized */ - if (!is_submodule_active(the_repository, path)) { + if (!is_submodule_active(the_repository, list_item->name)) { strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); } @@ -416,7 +432,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!info->quiet) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); @@ -445,10 +461,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet) static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(, N_("Suppress output for initializing a submodule")), @@ -473,8 +489,11 @@ static int module_init(int argc, const char **argv, const char *prefix) if (!argc && git_config_get_value_multi("submodule.active")) module_list_active(); - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + info.prefix = prefix; + info.quiet = !!quiet; + + gitmodules_config(); + for_each_submodule_list(list, init_submodule, ); return 0; } -- 2.13.0
[GSoC][PATCH 03/13] submodule: port set_name_rev() from shell to C
Function set_name_rev() is ported from git-submodule to the submodule--helper builtin. The function get_name_rev() generates the value of the revision name as required, and the function print_name_rev() handles the formating and printing of the obtained revision name. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 63 + git-submodule.sh| 16 ++-- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index e41572f7a..421eee1e2 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -244,6 +244,68 @@ static char *get_submodule_displaypath(const char *path, const char *prefix) } } +static char *get_name_rev(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { + NULL + }; + + static const char *describe_tags[] = { + "--tags", NULL + }; + + static const char *describe_contains[] = { + "--contains", NULL + }; + + static const char *describe_all_always[] = { + "--all", "--always", NULL + }; + + static const char **describe_argv[] = { + describe_bare, describe_tags, describe_contains, + describe_all_always, NULL + }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(, "describe"); + argv_array_pushv(, *d); + argv_array_push(, object_id); + + if (!capture_command(, , 0) && sb.len) { + strbuf_strip_suffix(, "\n"); + return strbuf_detach(, NULL); + } + } + + strbuf_release(); + return NULL; +} + +static int print_name_rev(int argc, const char **argv, const char *prefix) +{ + char *namerev; + if (argc != 3) + die("print-name-rev only accepts two arguments: "); + + namerev = get_name_rev(argv[1], argv[2]); + if (namerev && namerev[0]) + printf(" (%s)", namerev); + printf("\n"); + + free(namerev); + return 0; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -1242,6 +1304,7 @@ static struct cmd_struct commands[] = { {"relative-path", resolve_relative_path, 0}, {"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, + {"print-name-rev", print_name_rev, 0}, {"init", module_init, SUPPORT_SUPER_PREFIX}, {"remote-branch", resolve_remote_submodule_branch, 0}, {"push-check", push_check, 0}, diff --git a/git-submodule.sh b/git-submodule.sh index e131760ee..e988167e0 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -759,18 +759,6 @@ cmd_update() } } -set_name_rev () { - revname=$( ( - sanitize_submodule_env - cd "$1" && { - git describe "$2" 2>/dev/null || - git describe --tags "$2" 2>/dev/null || - git describe --contains "$2" 2>/dev/null || - git describe --all --always "$2" - } - ) ) - test -z "$revname" || revname=" ($revname)" -} # # Show commit summary for submodules in index or working tree # @@ -1042,14 +1030,14 @@ cmd_status() fi if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path" then - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper print-name-rev "$sm_path" "$sha1") say " $sha1 $displaypath$revname" else if test -z "$cached" then sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD) fi - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper print-name-rev "$sm_path" "$sha1") say "+$sha1 $displaypath$revname" fi -- 2.13.0
Re: [GSoC][PATCH 08/13] submodule: port submodule subcommand 'summary' from shell to C
On Sat, Aug 5, 2017 at 10:25 PM, Christian Couder <christian.cou...@gmail.com> wrote: > On Sat, Aug 5, 2017 at 12:28 PM, Prathamesh Chavan <pc44...@gmail.com> wrote: >> On Tue, Aug 1, 2017 at 4:57 AM, Christian Couder >> <christian.cou...@gmail.com> wrote: >>> On Mon, Jul 31, 2017 at 10:56 PM, Prathamesh Chavan <pc44...@gmail.com> >>> wrote: >>> >> We can avoid it to behave same for "" and NULL, by checking if diff_cmd >> is "cmd_diff_files", since its value is set NULL by this case. >> >> ret = compute_summary_module_list(strcmp(diff_cmd, "diff-files") ? >> NULL: sb.buf, ); >> strbuf_release(); > > It looks error prone, more fagile and less efficient to me. I think by using enum { DIFF_INDEX, DIFF_FILES }, we can avoid using strcmp() here, and make it more efficient not only here but also in other parts of the code, since all such steps involving strcmp could be removed. Thanks, Prathamesh Chavan
Re: [GSoC][PATCH 08/13] submodule: port submodule subcommand 'summary' from shell to C
On Tue, Aug 1, 2017 at 4:57 AM, Christian Couder <christian.cou...@gmail.com> wrote: > On Mon, Jul 31, 2017 at 10:56 PM, Prathamesh Chavan <pc44...@gmail.com> wrote: > >> * variable head was no longer used in module_summary() and instead the strbuf >> was utilized. > > Good but there might be a few problems in the way it is used. See below. > >> +static int compute_summary_module_list(char *head, struct summary_cb *info) >> +{ >> + struct argv_array diff_args = ARGV_ARRAY_INIT; >> + struct rev_info rev; >> + struct module_cb_list list = MODULE_CB_LIST_INIT; >> + >> + argv_array_push(_args, info->diff_cmd); >> + if (info->cached) >> + argv_array_push(_args, "--cached"); >> + argv_array_pushl(_args, "--ignore-submodules=dirty", "--raw", >> +NULL); >> + if (head) >> + argv_array_push(_args, head); >> + argv_array_push(_args, "--"); >> + if (info->argc) >> + argv_array_pushv(_args, info->argv); >> + >> + git_config(git_diff_basic_config, NULL); >> + init_revisions(, info->prefix); >> + gitmodules_config(); >> + rev.abbrev = 0; >> + precompose_argv(diff_args.argc, diff_args.argv); >> + >> + diff_args.argc = setup_revisions(diff_args.argc, diff_args.argv, >> +, NULL); >> + rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | >> DIFF_FORMAT_CALLBACK; >> + rev.diffopt.format_callback = submodule_summary_callback; >> + rev.diffopt.format_callback_data = >> + >> + if (!info->cached) { >> + if (!strcmp(info->diff_cmd, "diff-index")) >> + setup_work_tree(); >> + if (read_cache_preload() < 0) { >> + perror("read_cache_preload"); >> + return -1; >> + } >> + } else if (read_cache() < 0) { >> + perror("read_cache"); >> + return -1; >> + } >> + >> + if (!strcmp(info->diff_cmd, "diff-index")) >> + run_diff_index(, info->cached); >> + else >> + run_diff_files(, 0); >> + prepare_submodule_summary(info, ); >> + >> + free(head); > > It is a bit strange to have this function free() an argument it is passed. > >> + return 0; >> + > > Spurious new line. > >> +} >> + >> +static int module_summary(int argc, const char **argv, const char *prefix) >> +{ >> + struct summary_cb info = SUMMARY_CB_INIT; >> + int cached = 0; >> + char *diff_cmd = "diff-index"; >> + int for_status = 0; >> + int quiet = 0; >> + int files = 0; >> + int summary_limits = -1; >> + struct child_process cp_rev = CHILD_PROCESS_INIT; >> + struct strbuf sb = STRBUF_INIT; > > [...] > >> + if (!capture_command(_rev, , 0)) { >> + strbuf_strip_suffix(, "\n"); >> + if (argc) { >> + argv++; >> + argc--; >> + } >> + } else if (!argc || !strcmp(argv[0], "HEAD")) { >> + /* before the first commit: compare with an empty tree */ >> + struct stat st; >> + struct object_id oid; >> + if (fstat(0, ) < 0 || index_fd(oid.hash, 0, , 2, >> prefix, 3)) >> + die("Unable to add %s to database", oid.hash); >> + strbuf_addstr(, oid_to_hex()); >> + if (argc) { >> + argv++; >> + argc--; >> + } >> + } else { >> + strbuf_addstr(, "HEAD"); >> + } >> + >> + if (files) { >> + if (cached) >> + die(_("The --cached option cannot be used with the >> --files option")); >> + diff_cmd = "diff-files"; >> + >> + strbuf_reset(); > > strbuf_reset() does not free the memory allocated to sb.buf... > >> + sb.buf = NULL; > > ...then this makes sure that the memory previously allocated to sb.buf > will not be free()d. > >
Re: [GSoC][PATCH 06/13] submodule: port submodule subcommand 'deinit' from shell to C
On Tue, Aug 1, 2017 at 3:12 AM, Stefan Beller <sbel...@google.com> wrote: > On Mon, Jul 31, 2017 at 1:56 PM, Prathamesh Chavan <pc44...@gmail.com> wrote: >> The same mechanism is used even for porting this submodule >> subcommand, as used in the ported subcommands till now. >> The function cmd_deinit in split up after porting into three >> functions: module_deinit(), for_each_submodule_list() and >> deinit_submodule(). >> >> Mentored-by: Christian Couder <christian.cou...@gmail.com> >> Mentored-by: Stefan Beller <sbel...@google.com> >> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> >> --- >> In this new version, the following changes have been made: >> * In the function deinit_submodule, since the test is_git_directory() >> adds an additional condition, instead is_directory() is used to check >> if "sm_path/.git" is a directory. > > Thanks for writing these patches. > I wonder if (some of) these notes are best put into the code > as a comment such as > > /* NEEDSWORK: convert to is_submodule_active */ > > such that people reading this code later realize that checking > for a directory may not be the "correct" thing, but a thing which > was easy to express using shell. > >> +struct deinit_cb { >> + const char *prefix; >> + unsigned int quiet: 1; >> + unsigned int force: 1; >> + unsigned int all: 1; > > The value 'all' seems to be unused, i.e. we assign it but never read it? > >> +}; >> +#define DEINIT_CB_INIT { NULL, 0, 0, 0 } >> + >> +static void deinit_submodule(const struct cache_entry *list_item, >> +void *cb_data) >> +{ >> + struct deinit_cb *info = cb_data; >> + const struct submodule *sub; >> + char *displaypath = NULL; >> + struct child_process cp_config = CHILD_PROCESS_INIT; >> + struct strbuf sb_config = STRBUF_INIT; >> + char *sub_git_dir = xstrfmt("%s/.git", list_item->name); >> + mode_t mode = 0777; >> + >> + sub = submodule_from_path(null_sha1, list_item->name); >> + >> + if (!sub || !sub->name) >> + goto cleanup; >> + >> + displaypath = get_submodule_displaypath(list_item->name, >> info->prefix); >> + >> + /* remove the submodule work tree (unless the user already did it) */ >> + if (is_directory(list_item->name)) { >> + struct stat st; >> + /* protect submodules containing a .git directory */ > > Here may a good place to put: > /* NEEDSWORK: automatically call absorbgitdirs before warning/die. */ > (It was not in the shell version, so feel free to ignore) > >> + if (!info->force) { >> + struct child_process cp_rm = CHILD_PROCESS_INIT; >> + cp_rm.git_cmd = 1; >> + argv_array_pushl(_rm.args, "rm", "-qn", >> +list_item->name, NULL); > > A bug that exists in the shell version as well as here: > What if the submodule has the name '--cached', which happens > to be a valid argument for git-rm? > > The call to git-rm would die claiming that the is missing, > as the file name was miss-interpreted as another flag. > > To solve this problem we would insert a '--' after the options, > before the file name to state that the last argument is a . > > Not sure if we want to fix the bug while we're here or if we rather > want to add > > /* NEEDSWORK: add '--' to confirm argument */ > IMO, I would first port the subcommand, and add an additional comment for pointing the bug out. And then later, we may have a bug-fix patch in this series of patch itself for tackling this bug out. Thanks, Prathamesh Chavan
Re: [GSoC][PATCH 04/13] submodule: port submodule subcommand 'status' from shell to C
On Tue, Aug 1, 2017 at 2:42 AM, Stefan Beller <sbel...@google.com> wrote: > On Mon, Jul 31, 2017 at 1:56 PM, Prathamesh Chavan <pc44...@gmail.com> wrote: >> This aims to make git-submodule 'status' a built-in. Hence, the function >> cmd_status() is ported from shell to C. This is done by introducing >> three functions: module_status(), submodule_status() and print_status(). >> >> The function module_status() acts as the front-end of the subcommand. >> It parses subcommand's options and then calls the function >> module_list_compute() for computing the list of submodules. Then >> this functions calls for_each_submodule_list() looping through the >> list obtained. >> >> Then for_each_submodule_list() calls submodule_status() for each of the >> submodule in its list. The function submodule_status() is responsible >> for generating the status each submodule it is called for, and >> then calls print_status(). >> >> Finally, the function print_status() handles the printing of submodule's >> status. >> >> Mentored-by: Christian Couder <christian.cou...@gmail.com> >> Mentored-by: Stefan Beller <sbel...@google.com> >> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> >> --- >> In this new version, the following changes have been made: >> * parameters passed to the function print_status() have been changed. >> Instead of passing char *sub_sha1, instead the object_id is being passed. >> >> * Also, since the passed parameter displaypath's value isn't changed >> by the function, it is passed to the funcition as const char *displaypath >> instead of char *displaypath. >> >> * the output type of the function handle_submodule_head_ref() is changed >> from strbuf to object_id, as we will use the object_id instead of the >> hex of sha1 being stored in a struct strbuf. >> >> * diff_files_args is cleared after using it by passing it as args in the >> function cmd_diff_files. >> >> * In the function status_submodule(), for checking if a submodule has merge >> conflicts, the patch currently checks if the value of any of the ce_flags >> is non-zero. Currently, I think the we aren't interested in a partiular >> flag, >> but I'm not sure on this. >> >> * Debugging leftovers and suprious new-lines are removed. >> >> * The confusion with displaypath being passed as te super-prefix in many >> of the ported subcommands may be a result of the fact that the >> function generating the displaypath: get_submodule_displaypath() >> uses the super-prefix as simply a path concatenated with the current >> submodule name to denote our current location. >> The function get_super_prefix() is declared in cache.h and defined in >> environment.c, but is majorly used in the builtin/submodule--helper.c >> and also in unpack-trees.c >> Also, for generating any submodule's displaypath, it would be important to >> have ".." passed to the submodule, and currently it is possible only via >> the >> super-prefix. >> This is also other instaces where the super-prefix contained ".." as well. >> One of such instance is Test 4 from t7406-submodule-update.sh >> Hence, maybe documenting the value of displaypath might a solution >> for the above problem. >> I'm just stating my views and would like to recieve your opinion on this >> matter. > > Yes, I agree that the display path is not quite easily understood as it can be > ambiguous. I am confused by this paragraph: > * does test 4 from 7406 fail here, or was it just the starting point > of the discussion and it all works fine? Sorry for misleading there. In the discussion earlier, Brandon mentioned that displaypath's value may contain ".." as well. To this, I replied pointing out one of the test which checks for the value of displaypath. In this test, ( Test 4 from t7406-submodule-update.sh), the value of displaypath is being calculated via the submodule_init() function present in submodule--helper.c Here, I wanted to point out that, even in the case of this function, the variable displaypath is evaluated after receiving the value of "../super/" as the value returned by the function get_super_prefix(). Hence, to correct this confusion about the usage of super-prefix, (which as pointed out by Brandon has been traditionally defined as the path from the root of the superproject down to the root of the submodule which further means that there should not ever be any relative '..'s.), we may either have to get the value of super-prefix's Documentation to accomodate this change, and to accept the way it is used in submodule--helper and the current patch series, or else, change the way it is being used in the various function, and used another method for evaluation of displaypath. Thanks, Prathamesh Chavan
[GSoC][PATCH 08/13] submodule: port submodule subcommand 'summary' from shell to C
The submodule subcommand 'summary' is ported in the process of making git-submodule a builtin. The function cmd_summary() from git-submodule.sh is ported to functions module_summary(), compute_summary_module_list(), prepare_submodule_summary() and print_submodule_summary(). The first function module_summary() parses the options of submodule subcommand and also acts as the front-end of this subcommand. After parsing them, it calls the compute_summary_module_list() The functions compute_summary_module_list() runs the diff_cmd, and generates the modules list, as required by the subcommand. The generation of this module list is done by the using the callback function submodule_summary_callback(), and stored in the structure module_cb. Once the module list is generated, prepare_submodule_summary() further goes through the list and filters the list, for eventually calling the print_submodule_summary() function. Finally, the print_submodule_summary() takes care of generating and printing the summary for each submodule. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * Firstly, about the function compute_summary_module_list(). This function is created to generate the list of modules, for which we will generate the summary further. Since the list is actually generated using the git-diff-files or git-diff-index command, but for porting this, we required to create a function similar to the builtin functions of the above commands. But we can't directly call cmd_diff_files() and cmd_diff_index() since we don't have to display the output and instead need to store it. Hence, this function is introduced. * Also, the module_cb_list *list is not freed since it is a non-heap object. Hence, free() can't be using on the non-heap objects. * In the function prepare_submodule_summary(), as suggested 'git_config_get_string_const' was used instead of instead of '_value' * Some variables which weren't modified throughout the function-call were passed as const. * The '!!' trick, which wasn't used in the last patch, is now used in this new version . * the variables sha1_dst and sha1_src are removed from the function print_submodule_summary(), and instead the p->oid_src and p->oid_dst are used. * The variable sm_git_dir is freed at the end of the function. * variable head was no longer used in module_summary() and instead the strbuf was utilized. builtin/submodule--helper.c | 425 git-submodule.sh| 182 +-- 2 files changed, 426 insertions(+), 181 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index f642f9889..94438d6ce 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -13,6 +13,9 @@ #include "remote.h" #include "refs.h" #include "connect.h" +#include "revision.h" +#include "diffcore.h" +#include "diff.h" typedef void (*submodule_list_func_t)(const struct cache_entry *list_item, void *cb_data); @@ -766,6 +769,427 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct module_cb { + unsigned int mod_src; + unsigned int mod_dst; + struct object_id oid_src; + struct object_id oid_dst; + char status; + const char *sm_path; +}; +#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL } + +struct module_cb_list { + struct module_cb **entries; + int alloc, nr; +}; +#define MODULE_CB_LIST_INIT { NULL, 0, 0 } + +struct summary_cb { + int argc; + const char **argv; + const char *prefix; + char *diff_cmd; + unsigned int cached: 1; + unsigned int for_status: 1; + unsigned int quiet: 1; + unsigned int files: 1; + int summary_limits; +}; +#define SUMMARY_CB_INIT { 0, NULL, NULL, NULL, 0, 0, 0, 0, 0 } + +static int verify_submodule_object_name(const char *sm_path, const char *sha1) +{ + struct child_process cp_rev_parse = CHILD_PROCESS_INIT; + + cp_rev_parse.git_cmd = 1; + cp_rev_parse.no_stdout = 1; + cp_rev_parse.dir = sm_path; + prepare_submodule_repo_env(_rev_parse.env_array); + + argv_array_pushl(_rev_parse.args, "rev-parse", "-q", +"--verify", NULL); + argv_array_pushf(_rev_parse.args, "%s^0", sha1); + + if (run_command(_rev_parse)) + return 1; + + return 0; +} + +static void print_submodule_summary(struct summary_cb *info, + struct module_cb *p) +{ + int missing_src = 0; + int missing_dst = 0; + char *displaypath; + const
[GSoC][PATCH 11/13] submodule foreach: clarify the '$toplevel' variable documentation
It does not contain the topmost superproject as the author assumed, but the direct superproject, such that $toplevel/$sm_path is the actual absolute path of the submodule. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- Documentation/git-submodule.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index a23baef62..8e7930ebc 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -188,7 +188,8 @@ foreach [--recursive] :: $name is the name of the relevant submodule section in `.gitmodules`, $sm_path is the path of the submodule as recorded in the superproject, $sha1 is the commit as recorded in the superproject, and - $toplevel is the absolute path to the top-level of the superproject. + $toplevel is the absolute path to its superproject, such that + $toplevel/$sm_path is the absolute path of the submodule. Note that to avoid conflicts with '$PATH' on Windows, the '$path' variable is now a deprecated synonym of '$sm_path' variable. Any submodules defined in the superproject but not checked out are -- 2.13.0
[GSoC][PATCH 05/13] submodule: port submodule subcommand 'sync' from shell to C
Port the submodule subcommand 'sync' from shell to C using the same mechanism as that used for porting submodule subcommand 'status'. Hence, here the function cmd_sync() is ported from shell to C. This is done by introducing three functions: module_sync(), sync_submodule() and print_default_remote(). The function print_default_remote() is introduced for getting the default remote as stdout. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * There was no good reason for using puts in the function print_default_remote() Hence, in this patch, we instead use printf to do the same, as it is what is generally used throughout the codebase. * As suggested, this patch ensures a more efficient use of variables, and removes most of the variables by reusing 'strbuf sb' at places required. builtin/submodule--helper.c | 182 git-submodule.sh| 56 +- 2 files changed, 183 insertions(+), 55 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a6e6a48cc..91945337f 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -44,6 +44,20 @@ static char *get_default_remote(void) return ret; } +static int print_default_remote(int argc, const char **argv, const char *prefix) +{ + const char *remote; + + if (argc != 1) + die(_("submodule--helper print-default-remote takes no arguments")); + + remote = get_default_remote(); + if (remote) + printf("%s\n", remote); + + return 0; +} + static int starts_with_dot_slash(const char *str) { return str[0] == '.' && is_dir_sep(str[1]); @@ -380,6 +394,25 @@ static void module_list_active(struct module_list *list) *list = active_modules; } +static char *get_up_path(const char *path) +{ + int i; + struct strbuf sb = STRBUF_INIT; + + for (i = count_slashes(path); i; i--) + strbuf_addstr(, "../"); + + /* +* Check if 'path' ends with slash or not +* for having the same output for dir/sub_dir +* and dir/sub_dir/ +*/ + if (!is_dir_sep(path[strlen(path) - 1])) + strbuf_addstr(, "../"); + + return strbuf_detach(, NULL); +} + static int module_list(int argc, const char **argv, const char *prefix) { int i; @@ -733,6 +766,153 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct sync_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; +}; +#define SYNC_CB_INIT { NULL, 0, 0 } + +static void sync_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct sync_cb *info = cb_data; + const struct submodule *sub; + char *remote_key; + char *sub_origin_url, *super_config_url, *displaypath; + struct strbuf sb = STRBUF_INIT; + struct child_process cp = CHILD_PROCESS_INIT; + char *sub_config_path = NULL; + + if (!is_submodule_active(the_repository, list_item->name)) + return; + + sub = submodule_from_path(null_sha1, list_item->name); + + if (sub && sub->url) { + if (starts_with_dot_dot_slash(sub->url) || starts_with_dot_slash(sub->url)) { + char *remote_url, *up_path; + char *remote = get_default_remote(); + strbuf_addf(, "remote.%s.url", remote); + + if (git_config_get_string(sb.buf, _url)) + remote_url = xgetcwd(); + + up_path = get_up_path(list_item->name); + sub_origin_url = relative_url(remote_url, sub->url, up_path); + super_config_url = relative_url(remote_url, sub->url, NULL); + + free(remote); + free(up_path); + free(remote_url); + } else { + sub_origin_url = xstrdup(sub->url); + super_config_url = xstrdup(sub->url); + } + } else { + sub_origin_url = ""; + super_config_url = ""; + } + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + if (!info->quiet) + printf(_("Synchronizing submodule url for '%s'\n"), +displaypath); + + strbuf_reset(); + strbuf_addf(, "submodule.%s.url", sub->name); + if (git_config_set_gently(sb.buf, super_config_url)) + die(_("failed to re
[GSoC][PATCH 07/13] diff: change scope of the function count_lines()
Change the scope of function count_lines for allowing the function to be reused in other parts of the code as well. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- diff.c | 2 +- diff.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/diff.c b/diff.c index 85e714f6c..03ed64f93 100644 --- a/diff.c +++ b/diff.c @@ -425,7 +425,7 @@ struct emit_callback { struct strbuf *header; }; -static int count_lines(const char *data, int size) +int count_lines(const char *data, int size) { int count, ch, completely_empty = 1, nl_just_seen = 0; count = 0; diff --git a/diff.h b/diff.h index 2d442e296..8522514e9 100644 --- a/diff.h +++ b/diff.h @@ -273,6 +273,7 @@ extern struct diff_filepair *diff_unmerge(struct diff_options *, const char *pat extern int parse_long_opt(const char *opt, const char **argv, const char **optarg); +extern int count_lines(const char *data, int size); extern int git_diff_basic_config(const char *var, const char *value, void *cb); extern int git_diff_heuristic_config(const char *var, const char *value, void *cb); extern void init_diff_ui_defaults(void); -- 2.13.0
[GSoC][PATCH 06/13] submodule: port submodule subcommand 'deinit' from shell to C
The same mechanism is used even for porting this submodule subcommand, as used in the ported subcommands till now. The function cmd_deinit in split up after porting into three functions: module_deinit(), for_each_submodule_list() and deinit_submodule(). Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * In the function deinit_submodule, since the test is_git_directory() adds an additional condition, instead is_directory() is used to check if "sm_path/.git" is a directory. * since it was possible in the previous path that the value st.st_mode passed to the function mkdir contained a garbage value, instead we intrduce a mode_t variable mode, initially containing a default mode value '0777'. This is what the default of mode is set in case, that the value is not set after the lstat call. builtin/submodule--helper.c | 144 git-submodule.sh| 55 + 2 files changed, 145 insertions(+), 54 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 91945337f..f642f9889 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -913,6 +913,149 @@ static int module_sync(int argc, const char **argv, const char *prefix) return 0; } +struct deinit_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int force: 1; + unsigned int all: 1; +}; +#define DEINIT_CB_INIT { NULL, 0, 0, 0 } + +static void deinit_submodule(const struct cache_entry *list_item, +void *cb_data) +{ + struct deinit_cb *info = cb_data; + const struct submodule *sub; + char *displaypath = NULL; + struct child_process cp_config = CHILD_PROCESS_INIT; + struct strbuf sb_config = STRBUF_INIT; + char *sub_git_dir = xstrfmt("%s/.git", list_item->name); + mode_t mode = 0777; + + sub = submodule_from_path(null_sha1, list_item->name); + + if (!sub || !sub->name) + goto cleanup; + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + /* remove the submodule work tree (unless the user already did it) */ + if (is_directory(list_item->name)) { + struct stat st; + /* protect submodules containing a .git directory */ + if (is_directory(sub_git_dir)) + die(_("Submodule work tree '%s' contains a .git " + "directory use 'rm -rf' if you really want " + "to remove it including all of its history"), + displaypath); + + if (!info->force) { + struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; + argv_array_pushl(_rm.args, "rm", "-qn", +list_item->name, NULL); + + if (run_command(_rm)) + die(_("Submodule work tree '%s' contains local " + "modifications; use '-f' to discard them"), + displaypath); + } + + if (!lstat(list_item->name, )) { + struct strbuf sb_rm = STRBUF_INIT; + const char *format; + + strbuf_addstr(_rm, list_item->name); + + if (!remove_dir_recursively(_rm, 0)) + format = _("Cleared directory '%s'\n"); + else + format = _("Could not remove submodule work tree '%s'\n"); + + if (!info->quiet) + printf(format, displaypath); + + mode = st.st_mode; + + strbuf_release(_rm); + } + } + + if (mkdir(list_item->name, mode)) + die(_("could not create empty submodule directory %s"), + displaypath); + + cp_config.git_cmd = 1; + argv_array_pushl(_config.args, "config", "--get-regexp", NULL); + argv_array_pushf(_config.args, "submodule.%s\\.", sub->name); + + /* remove the .git/config entries (unless the user already did it) */ + if (!capture_command(_config, _config, 0) && sb_config.len) { + char *sub_key = xstrfmt("submodule.%s", sub->name); + /* +* remove the whole section so we have a clean state when +* the user later d
[GSoC][PATCH 10/13] submodule foreach: document '$sm_path' instead of '$path'
As using a variable '$path' may be harmful to users due to capitalization issues, see 64394e3ae9 (git-submodule.sh: Don't use $path variable in eval_gettext string, 2012-04-17). Adjust the documentation to advocate for using $sm_path, which contains the same value. We still make the 'path' variable available and document it as a deprecated synonym of 'sm_path'. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- This patch is same as its previous version. Although here I'll like to add a point that we aim to slowly drop the support of the variable 'path'. Documentation/git-submodule.txt | 10 ++ 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index ff612001d..a23baef62 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -183,12 +183,14 @@ information too. foreach [--recursive] :: Evaluates an arbitrary shell command in each checked out submodule. - The command has access to the variables $name, $path, $sha1 and + The command has access to the variables $name, $sm_path, $sha1 and $toplevel: $name is the name of the relevant submodule section in `.gitmodules`, - $path is the name of the submodule directory relative to the - superproject, $sha1 is the commit as recorded in the superproject, - and $toplevel is the absolute path to the top-level of the superproject. + $sm_path is the path of the submodule as recorded in the superproject, + $sha1 is the commit as recorded in the superproject, and + $toplevel is the absolute path to the top-level of the superproject. + Note that to avoid conflicts with '$PATH' on Windows, the '$path' + variable is now a deprecated synonym of '$sm_path' variable. Any submodules defined in the superproject but not checked out are ignored by this command. Unless given `--quiet`, foreach prints the name of each submodule before evaluating the command. -- 2.13.0
[GSoC][PATCH 12/13] submodule foreach: document variable '$displaypath'
It was observed that the variable '$displaypath' was accessible but undocumented. Hence, document it. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * Spelling mistake in the commit message was corrected. Documentation/git-submodule.txt | 6 -- t/t7407-submodule-foreach.sh| 22 +++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 8e7930ebc..0cca702cb 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -183,10 +183,12 @@ information too. foreach [--recursive] :: Evaluates an arbitrary shell command in each checked out submodule. - The command has access to the variables $name, $sm_path, $sha1 and - $toplevel: + The command has access to the variables $name, $sm_path, $displaypath, + $sha1 and $toplevel: $name is the name of the relevant submodule section in `.gitmodules`, $sm_path is the path of the submodule as recorded in the superproject, + $displaypath contains the relative path from the current working + directory to the submodules root directory, $sha1 is the commit as recorded in the superproject, and $toplevel is the absolute path to its superproject, such that $toplevel/$sm_path is the absolute path of the submodule. diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 0663622a4..6ad57e061 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -82,16 +82,16 @@ test_expect_success 'test basic "submodule foreach" usage' ' cat >expect <../../actual + git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual ) && test_i18ncmp expect actual ' @@ -206,25 +206,25 @@ submodulesha1=$(cd clone2/nested1/nested2/nested3/submodule && git rev-parse HEA cat >expect <../../actual + git submodule foreach --recursive "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual ) && test_i18ncmp expect actual ' -- 2.13.0
[GSoC][PATCH 13/13] submodule: port submodule subcommand 'foreach' from shell to C
This aims to make git-submodule foreach a builtin. This is the very first step taken in this direction. Hence, 'foreach' is ported to submodule--helper, and submodule--helper is called from git-submodule.sh. The code is split up to have one function to obtain all the list of submodules. This function acts as the front-end of git-submodule foreach subcommand. It calls the function for_each_submodule_list, which basically loops through the list and calls function fn, which in this case is runcommand_in_submodule. This third function is a calling function that takes care of running the command in that submodule, and recursively perform the same when --recursive is flagged. The first function module_foreach first parses the options present in argv, and then with the help of module_list_compute, generates the list of submodules present in the current working tree. The second function for_each_submodule_list traverses through the list, and calls function fn (which in case of submodule subcommand foreach is runcommand_in_submodule) is called for each entry. The third function runcommand_in_submodule, generates a submodule struct sub for $name, value and then later prepends name=sub->name; and other value assignment to the env argv_array structure of a child_process. Also the of submodule-foreach is push to args argv_array structure and finally, using run_command the commands are executed using a shell. The third function also takes care of the recursive flag, by creating a separate child_process structure and prepending "--super-prefix displaypath", to the args argv_array structure. Other required arguments and the input of submodule-foreach is also appended to this argv_array. Helped-by: Brandon Williams <bmw...@google.com> Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * Comment style is improved in the function runcommand_in_submodule() * Comment in added about why the variable "path" was exposed via args argv_array instead of exposing it via the env_array. * This patch exposes the various variables when argc == 1 only, just for maintaining a faithful porting. You can also find discussion about the same at [1]. [1]: https://public-inbox.org/git/came+mvusgafbn5j-_hv7qpas57hq4wgh+yz7xjmpuyqn1ga...@mail.gmail.com/ builtin/submodule--helper.c | 136 git-submodule.sh| 39 + 2 files changed, 137 insertions(+), 38 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 94438d6ce..935f56510 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -769,6 +769,141 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct cb_foreach { + int argc; + const char **argv; + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; +}; +#define CB_FOREACH_INIT { 0, NULL, NULL, 0, 0 } + +static void runcommand_in_submodule(const struct cache_entry *list_item, + void *cb_data) +{ + struct cb_foreach *info = cb_data; + const struct submodule *sub; + struct child_process cp = CHILD_PROCESS_INIT; + char *displaypath; + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + sub = submodule_from_path(null_sha1, list_item->name); + + if (!sub) + die(_("No url found for submodule path '%s' in .gitmodules"), + displaypath); + + if (!is_submodule_populated_gently(list_item->name, NULL)) + goto cleanup; + + prepare_submodule_repo_env(_array); + + /* +* For the purpose of executing in the submodule, +* separate shell is used for the purpose of running the +* child process. +*/ + cp.use_shell = 1; + cp.dir = list_item->name; + + if (info->argc == 1) { + char *toplevel = xgetcwd(); + + argv_array_pushf(_array, "name=%s", sub->name); + argv_array_pushf(_array, "sm_path=%s", list_item->name); + argv_array_pushf(_array, "displaypath=%s", displaypath); + argv_array_pushf(_array, "sha1=%s", +oid_to_hex(_item->oid)); + argv_array_pushf(_array, "toplevel=%s", toplevel); + + /* +* Since the path variable was accessible from the script +* before porting, it is also made available after porting. +* The environment variable "PATH" has a very special purpose +* on windows. And since environment
[GSoC][PATCH 09/13] submodule foreach: correct '$path' in nested submodules from a subdirectory
When running 'git submodule foreach' from a subdirectory of your repository, nested submodules get a bogus value for $sm_path: For a submodule 'sub' that contains a nested submodule 'nested', running 'git -C dir submodule foreach echo $path' would report path='../nested' for the nested submodule. The first part '../' is derived from the logic computing the relative path from $pwd to the root of the superproject. The second part is the submodule path inside the submodule. This value is of little use and is hard to document. There are two different possible solutions that have more value: (a) The path value is documented as the path from the toplevel of the superproject to the mount point of the submodule. In this case we would want to have path='sub/nested'. (b) As Ramsay noticed the documented value is wrong. For the non-nested case the path is equal to the relative path from $pwd to the submodules working directory. When following this model, the expected value would be path='../sub/nested'. The behavior for (b) was introduced in 091a6eb0fe (submodule: drop the top-level requirement, 2013-06-16) the intent for $path seemed to be relative to $cwd to the submodule worktree, but that did not work for nested submodules, as the intermittent submodules were not included in the path. If we were to fix the meaning of the $path using (a) such that "path" is "the path from the toplevel of the superproject to the mount point of the submodule", we would break any existing submodule user that runs foreach from non-root of the superproject as the non-nested submodule '../sub' would change its path to 'sub'. If we would fix the meaning of the $path using (b), such that "path" is "the relative path from $pwd to the submodule", then we would break any user that uses nested submodules (even from the root directory) as the 'nested' would become 'sub/nested'. Both groups can be found in the wild. The author has no data if one group outweighs the other by large margin, and offending each one seems equally bad at first. However in the authors imagination it is better to go with (a) as running from a sub directory sounds like it is carried out by a human rather than by some automation task. With a human on the keyboard the feedback loop is short and the changed behavior can be adapted to quickly unlike some automation that can break silently. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> Signed-off-by: Stefan Beller <sbel...@google.com> --- git-submodule.sh | 1 - t/t7407-submodule-foreach.sh | 36 ++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/git-submodule.sh b/git-submodule.sh index a427ddafd..493a64372 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -320,7 +320,6 @@ cmd_foreach() prefix="$prefix$sm_path/" sanitize_submodule_env cd "$sm_path" && - sm_path=$(git submodule--helper relative-path "$sm_path" "$wt_prefix") && # we make $path available to scripts ... path=$sm_path && if test $# -eq 1 diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 6ba5daf42..0663622a4 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -82,9 +82,9 @@ test_expect_success 'test basic "submodule foreach" usage' ' cat >expect <expect <../../actual + ) && + test_i18ncmp expect actual +' cat > expect <
[GSoC][PATCH 04/13] submodule: port submodule subcommand 'status' from shell to C
This aims to make git-submodule 'status' a built-in. Hence, the function cmd_status() is ported from shell to C. This is done by introducing three functions: module_status(), submodule_status() and print_status(). The function module_status() acts as the front-end of the subcommand. It parses subcommand's options and then calls the function module_list_compute() for computing the list of submodules. Then this functions calls for_each_submodule_list() looping through the list obtained. Then for_each_submodule_list() calls submodule_status() for each of the submodule in its list. The function submodule_status() is responsible for generating the status each submodule it is called for, and then calls print_status(). Finally, the function print_status() handles the printing of submodule's status. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * parameters passed to the function print_status() have been changed. Instead of passing char *sub_sha1, instead the object_id is being passed. * Also, since the passed parameter displaypath's value isn't changed by the function, it is passed to the funcition as const char *displaypath instead of char *displaypath. * the output type of the function handle_submodule_head_ref() is changed from strbuf to object_id, as we will use the object_id instead of the hex of sha1 being stored in a struct strbuf. * diff_files_args is cleared after using it by passing it as args in the function cmd_diff_files. * In the function status_submodule(), for checking if a submodule has merge conflicts, the patch currently checks if the value of any of the ce_flags is non-zero. Currently, I think the we aren't interested in a partiular flag, but I'm not sure on this. * Debugging leftovers and suprious new-lines are removed. * The confusion with displaypath being passed as te super-prefix in many of the ported subcommands may be a result of the fact that the function generating the displaypath: get_submodule_displaypath() uses the super-prefix as simply a path concatenated with the current submodule name to denote our current location. The function get_super_prefix() is declared in cache.h and defined in environment.c, but is majorly used in the builtin/submodule--helper.c and also in unpack-trees.c Also, for generating any submodule's displaypath, it would be important to have ".." passed to the submodule, and currently it is possible only via the super-prefix. This is also other instaces where the super-prefix contained ".." as well. One of such instance is Test 4 from t7406-submodule-update.sh Hence, maybe documenting the value of displaypath might a solution for the above problem. I'm just stating my views and would like to recieve your opinion on this matter. builtin/submodule--helper.c | 154 git-submodule.sh| 49 +- 2 files changed, 155 insertions(+), 48 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 2cb72d68e..a6e6a48cc 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -561,6 +561,159 @@ static int module_init(int argc, const char **argv, const char *prefix) return 0; } +struct status_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; + unsigned int cached: 1; +}; +#define STATUS_CB_INIT { NULL, 0, 0, 0 } + +static void print_status(struct status_cb *info, char state, const char *path, +const struct object_id *oid, const char *displaypath) +{ + if (info->quiet) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') { + struct argv_array name_rev_args = ARGV_ARRAY_INIT; + + argv_array_pushl(_rev_args, "print-name-rev", +path, oid_to_hex(oid), NULL); + print_name_rev(name_rev_args.argc, name_rev_args.argv, + info->prefix); + } else { + printf("\n"); + } +} + +static int handle_submodule_head_ref(const char *refname, +const struct object_id *oid, int flags, +void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct status_cb *info = cb_data; + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + + if (!submodule_from_path(null_sha1, list_item->name)) +
[GSoC][PATCH 03/13] submodule: port set_name_rev() from shell to C
Function set_name_rev() is ported from git-submodule to the submodule--helper builtin. The function get_name_rev() generates the value of the revision name as required, and the function print_name_rev() handles the formating and printing of the obtained revision name. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * The variable namerev from print_name_rev is now freed at the end of the function. builtin/submodule--helper.c | 64 + git-submodule.sh| 16 ++-- 2 files changed, 66 insertions(+), 14 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index e41572f7a..2cb72d68e 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -244,6 +244,69 @@ static char *get_submodule_displaypath(const char *path, const char *prefix) } } +static char *get_name_rev(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { + NULL + }; + + static const char *describe_tags[] = { + "--tags", NULL + }; + + static const char *describe_contains[] = { + "--contains", NULL + }; + + static const char *describe_all_always[] = { + "--all", "--always", NULL + }; + + static const char **describe_argv[] = { + describe_bare, describe_tags, describe_contains, + describe_all_always, NULL + }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(, "describe"); + argv_array_pushv(, *d); + argv_array_push(, object_id); + + if (!capture_command(, , 0) && sb.len) { + strbuf_strip_suffix(, "\n"); + return strbuf_detach(, NULL); + } + + } + + strbuf_release(); + return NULL; +} + +static int print_name_rev(int argc, const char **argv, const char *prefix) +{ + char *namerev; + if (argc != 3) + die("print-name-rev only accepts two arguments: "); + + namerev = get_name_rev(argv[1], argv[2]); + if (namerev && namerev[0]) + printf(" (%s)", namerev); + printf("\n"); + + free(namerev); + return 0; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -1242,6 +1305,7 @@ static struct cmd_struct commands[] = { {"relative-path", resolve_relative_path, 0}, {"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, + {"print-name-rev", print_name_rev, 0}, {"init", module_init, SUPPORT_SUPER_PREFIX}, {"remote-branch", resolve_remote_submodule_branch, 0}, {"push-check", push_check, 0}, diff --git a/git-submodule.sh b/git-submodule.sh index e131760ee..e988167e0 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -759,18 +759,6 @@ cmd_update() } } -set_name_rev () { - revname=$( ( - sanitize_submodule_env - cd "$1" && { - git describe "$2" 2>/dev/null || - git describe --tags "$2" 2>/dev/null || - git describe --contains "$2" 2>/dev/null || - git describe --all --always "$2" - } - ) ) - test -z "$revname" || revname=" ($revname)" -} # # Show commit summary for submodules in index or working tree # @@ -1042,14 +1030,14 @@ cmd_status() fi if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path" then - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper print-name-rev "$sm_path" "$sha1") say " $sha1 $displaypath$revname" else if test -z "$cached" then sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD) fi - set_name_rev "$sm_path" "$sha1" + revname=$(git submodule--helper print-name-rev "$sm_path" "$sha1") say "+$sha1 $displaypath$revname" fi -- 2.13.0
[GSoC][PATCH 02/13] submodule--helper: introduce for_each_submodule_list()
Introduce function for_each_submodule_list() and replace a loop in module_init() with a call to it. The new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 39 +-- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 7af4de09b..e41572f7a 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -14,6 +14,9 @@ #include "refs.h" #include "connect.h" +typedef void (*submodule_list_func_t)(const struct cache_entry *list_item, + void *cb_data); + static char *get_default_remote(void) { char *dest = NULL, *ret; @@ -352,17 +355,30 @@ static int module_list(int argc, const char **argv, const char *prefix) return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_submodule_list(const struct module_list list, + submodule_list_func_t fn, void *cb_data) { + int i; + for (i = 0; i < list.nr; i++) + fn(list.entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int quiet: 1; +}; +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct init_cb *info = cb_data; const struct submodule *sub; struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - /* Only loads from .gitmodules, no overlay with .git/config */ - gitmodules_config(); - displaypath = get_submodule_displaypath(path, prefix); + displaypath = get_submodule_displaypath(list_item->name, info->prefix); - sub = submodule_from_path(null_sha1, path); + sub = submodule_from_path(null_sha1, list_item->name); if (!sub) die(_("No url found for submodule path '%s' in .gitmodules"), @@ -374,7 +390,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * * Set active flag for the submodule being initialized */ - if (!is_submodule_active(the_repository, path)) { + if (!is_submodule_active(the_repository, list_item->name)) { strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); } @@ -416,7 +432,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!info->quiet) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); @@ -445,10 +461,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet) static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(, N_("Suppress output for initializing a submodule")), @@ -473,8 +489,11 @@ static int module_init(int argc, const char **argv, const char *prefix) if (!argc && git_config_get_value_multi("submodule.active")) module_list_active(); - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + info.prefix = prefix; + info.quiet = !!quiet; + + gitmodules_config(); + for_each_submodule_list(list, init_submodule, ); return 0; } -- 2.13.0
[GSoC][PATCH 01/13] submodule--helper: introduce get_submodule_displaypath()
Introduce function get_submodule_displaypath() to replace the code occurring in submodule_init() for generating displaypath of the submodule with a call to it. This new function will also be used in other parts of the system in later patches. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- builtin/submodule--helper.c | 33 ++--- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 6abdad329..7af4de09b 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -220,6 +220,27 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, )); + strbuf_release(); + return displaypath; + } else if (super_prefix) { + int len = strlen(super_prefix); + const char *format = is_dir_sep(super_prefix[len - 1]) ? "%s%s" : "%s/%s"; + return xstrfmt(format, super_prefix, path); + } else { + return xstrdup(path); + } +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -339,16 +360,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) /* Only loads from .gitmodules, no overlay with .git/config */ gitmodules_config(); - - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, )); - else if (get_super_prefix()) { - strbuf_addf(, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(null_sha1, path); @@ -363,7 +375,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * Set active flag for the submodule being initialized */ if (!is_submodule_active(the_repository, path)) { - strbuf_reset(); strbuf_addf(, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); } -- 2.13.0
[GSoC][PATCH 00/13] Update: Week-11
SUMMARY OF MY PROJECT: Git submodule subcommands are currently implemented by using shell script 'git-submodule.sh'. There are several reasons why we'll prefer not to use the shell script. My project intends to convert the subcommands into C code, thus making them builtins. This will increase Git's portability and hence the efficiency of working with the git-submodule commands. Link to the complete proposal: [1] Mentors: Stefan BellerChristian Couder UPDATES: Following are the updates about my ongoing project: * Following patches were updated after the previous reviews: - set_name_rev() submodule subcommands: - status - sync - deinit - summary - foreach * Reviews from both Christian Couder and Brandon Williams helped in improvising these patches and their suggestions were implemented. * Porting of submodule subcommand add is still underway. Its progess can be viewed at [2]. PLAN FOR WEEK-12 (1 August 2017 to 7 August 2017): * summary: One of the problems pointed out by Brandon this week was that the function print_submodule_summary() was too big to keep track of all the things that are happening. Hence, I will be splitting the function into smaller functions. * displaypath: There is some confusion produced with the way the value of displaypath is being generated, using super-prefix. [3] Via having discussion on this, I'll try to resolve the issues regarding it. In the patches following the update, I have addressed this issue as well. * add: Porting of this subcommand is still underway and will be working on to completely port this subcommand. A complete build report of these series of patches is available at: [4]. Build #145 Branch: week-11 The work is push on github and is available at: [5]. [1]: https://docs.google.com/document/d/1krxVLooWl--75Pot3dazhfygR3wCUUWZWzTXtK1 L-xU/ [2]: https://github.com/pratham-pc/git/commits/sub-add [3]: https://public-inbox.org/git/20170724213028.gb92...@google.com/ [4]: https://travis-ci.org/pratham-pc/git/builds/ [5]: https://github.com/pratham-pc/git/commits/week-11 submodule--helper: introduce get_submodule_displaypath() submodule--helper: introduce for_each_submodule_list() submodule: port set_name_rev() from shell to C submodule: port submodule subcommand 'status' from shell to C submodule: port submodule subcommand 'sync' from shell to C submodule: port submodule subcommand 'deinit' from shell to C diff: change scope of the function count_lines() submodule: port submodule subcommand 'summary' from shell to C submodule foreach: correct '$path' in nested submodules from a subdirectory submodule foreach: document '$sm_path' instead of '$path' submodule foreach: clarify the '$toplevel' variable documentation submodule foreach: document variable '$displaypath' submodule: port submodule subcommand 'foreach' from shell to C Documentation/git-submodule.txt | 15 +- builtin/submodule--helper.c | 1175 ++- diff.c |2 +- diff.h |1 + git-submodule.sh| 394 + t/t7407-submodule-foreach.sh| 38 +- 6 files changed, 1207 insertions(+), 418 deletions(-) -- 2.13.0
Re: [GSoC][PATCH v2 08/13] submodule: port submodule subcommand 'summary' from shell to C
On Sun, Jul 30, 2017 at 10:58 AM, Christian Couder <christian.cou...@gmail.com> wrote: > On Sun, Jul 30, 2017 at 12:23 AM, Prathamesh Chavan <pc44...@gmail.com> wrote: > >> +static int module_summary(int argc, const char **argv, const char *prefix) >> +{ >> + struct summary_cb info = SUMMARY_CB_INIT; >> + int cached = 0; >> + char *diff_cmd = "diff-index"; >> + int for_status = 0; >> + int quiet = 0; >> + int files = 0; >> + int summary_limits = -1; >> + struct child_process cp_rev = CHILD_PROCESS_INIT; >> + char *head; >> + struct strbuf sb = STRBUF_INIT; >> + int ret; >> + >> + struct option module_summary_options[] = { >> + OPT__QUIET(, N_("Suppress output for initializing a >> submodule")), >> + OPT_BOOL(0, "cached", , N_("Use the commit stored in >> the index instead of the submodule HEAD")), >> + OPT_BOOL(0, "files", , N_("To compares the commit in >> the index with that in the submodule HEAD")), >> + OPT_BOOL(0, "for-status", _status, N_("Skip submodules >> with 'all' ignore_config value")), >> + OPT_INTEGER('n', "summary-limits", _limits, >> N_("Limit the summary size")), >> + OPT_END() >> + }; >> + >> + const char *const git_submodule_helper_usage[] = { >> + N_("git submodule--helper summary [] [--] >> []"), >> + NULL >> + }; >> + >> + argc = parse_options(argc, argv, prefix, module_summary_options, >> +git_submodule_helper_usage, 0); >> + >> + if (!summary_limits) >> + return 0; >> + >> + cp_rev.git_cmd = 1; >> + argv_array_pushl(_rev.args, "rev-parse", "-q", "--verify", >> +argc ? argv[0] : "HEAD", NULL); >> + >> + if (!capture_command(_rev, , 0)) { >> + strbuf_strip_suffix(, "\n"); >> + if (argc) { >> + argv++; >> + argc--; >> + } >> + } else if (!argc || !strcmp(argv[0], "HEAD")) { >> + /* before the first commit: compare with an empty tree */ >> + struct stat st; >> + struct object_id oid; >> + if (fstat(0, ) < 0 || index_fd(oid.hash, 0, , 2, >> prefix, 3)) >> + die("Unable to add %s to database", oid.hash); >> + strbuf_addstr(, oid_to_hex()); >> + if (argc) { >> + argv++; >> + argc--; >> + } >> + } else { >> + strbuf_addstr(, "HEAD"); >> + } >> + >> + head = strbuf_detach(, NULL); > > I am not sure this "head" variable is really needed. > >> + if (files) { >> + if (cached) >> + die(_("The --cached option cannot be used with the >> --files option")); >> + diff_cmd = "diff-files"; >> + >> + free(head); >> + >> + head = NULL; > > If "head" isn't used, "strbuf_reset()" could be used instead. > If "head" is still needed, "FREE_AND_NULL(head)" could be used. Thank you for reviewing this patch. As suggested, here we can remove the variable head, and instead use the strbuf struct. But in the above lines, along with strbuf_reset(), later we are also required to set sb.buf to NULL. > >> + } >> + >> + info.argc = argc; >> + info.argv = argv; >> + info.prefix = prefix; >> + info.cached = !!cached; >> + info.for_status = !!for_status; >> + info.quiet = quiet; >> + info.files = files; >> + info.summary_limits = summary_limits; >> + info.diff_cmd = diff_cmd; >> + >> + ret = compute_summary_module_list(head, ); we may even remove the variable ret, but passing strbuf_detach(, NULL) here to the function compute_summary_module_list(), and then later on free(head) at the end of the function compute_summary_module_list(). Thanks, Prathamesh Chavan
[GSoC][PATCH v2 12/13] submodule foreach: document variable '$displaypath'
It was observed that the variable '$displaypath' was accessible but undocumented. Hence, document it. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * Spelling mistake in the commit message was corrected. Documentation/git-submodule.txt | 6 -- t/t7407-submodule-foreach.sh| 22 +++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 8e7930ebc..0cca702cb 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -183,10 +183,12 @@ information too. foreach [--recursive] :: Evaluates an arbitrary shell command in each checked out submodule. - The command has access to the variables $name, $sm_path, $sha1 and - $toplevel: + The command has access to the variables $name, $sm_path, $displaypath, + $sha1 and $toplevel: $name is the name of the relevant submodule section in `.gitmodules`, $sm_path is the path of the submodule as recorded in the superproject, + $displaypath contains the relative path from the current working + directory to the submodules root directory, $sha1 is the commit as recorded in the superproject, and $toplevel is the absolute path to its superproject, such that $toplevel/$sm_path is the absolute path of the submodule. diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 0663622a4..6ad57e061 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -82,16 +82,16 @@ test_expect_success 'test basic "submodule foreach" usage' ' cat >expect <../../actual + git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual ) && test_i18ncmp expect actual ' @@ -206,25 +206,25 @@ submodulesha1=$(cd clone2/nested1/nested2/nested3/submodule && git rev-parse HEA cat >expect <../../actual + git submodule foreach --recursive "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual ) && test_i18ncmp expect actual ' -- 2.13.0
[GSoC][PATCH v2 13/13] submodule: port submodule subcommand 'foreach' from shell to C
This aims to make git-submodule foreach a builtin. This is the very first step taken in this direction. Hence, 'foreach' is ported to submodule--helper, and submodule--helper is called from git-submodule.sh. The code is split up to have one function to obtain all the list of submodules. This function acts as the front-end of git-submodule foreach subcommand. It calls the function for_each_submodule_list, which basically loops through the list and calls function fn, which in this case is runcommand_in_submodule. This third function is a calling function that takes care of running the command in that submodule, and recursively perform the same when --recursive is flagged. The first function module_foreach first parses the options present in argv, and then with the help of module_list_compute, generates the list of submodules present in the current working tree. The second function for_each_submodule_list traverses through the list, and calls function fn (which in case of submodule subcommand foreach is runcommand_in_submodule) is called for each entry. The third function runcommand_in_submodule, generates a submodule struct sub for $name, value and then later prepends name=sub->name; and other value assignment to the env argv_array structure of a child_process. Also the of submodule-foreach is push to args argv_array structure and finally, using run_command the commands are executed using a shell. The third function also takes care of the recursive flag, by creating a separate child_process structure and prepending "--super-prefix displaypath", to the args argv_array structure. Other required arguments and the input of submodule-foreach is also appended to this argv_array. Helped-by: Brandon Williams <bmw...@google.com> Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * Comment style is improved in the function runcommand_in_submodule() * Comment in added about why the variable "path" was exposed via args argv_array instead of exposing it via the env_array. * This patch exposes the various variables when argc == 1 only, just for maintaining a faithful porting. You can also find discussion about the same at [1]. [1]: https://public-inbox.org/git/came+mvusgafbn5j-_hv7qpas57hq4wgh+yz7xjmpuyqn1ga...@mail.gmail.com/ builtin/submodule--helper.c | 136 git-submodule.sh| 39 + 2 files changed, 137 insertions(+), 38 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index d8bf16f1d..d5527aa93 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -772,6 +772,141 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct cb_foreach { + int argc; + const char **argv; + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; +}; +#define CB_FOREACH_INIT { 0, NULL, NULL, 0, 0 } + +static void runcommand_in_submodule(const struct cache_entry *list_item, + void *cb_data) +{ + struct cb_foreach *info = cb_data; + const struct submodule *sub; + struct child_process cp = CHILD_PROCESS_INIT; + char *displaypath; + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + sub = submodule_from_path(null_sha1, list_item->name); + + if (!sub) + die(_("No url found for submodule path '%s' in .gitmodules"), + displaypath); + + if (!is_submodule_populated_gently(list_item->name, NULL)) + goto cleanup; + + prepare_submodule_repo_env(_array); + + /* +* For the purpose of executing in the submodule, +* separate shell is used for the purpose of running the +* child process. +*/ + cp.use_shell = 1; + cp.dir = list_item->name; + + if (info->argc == 1) { + char *toplevel = xgetcwd(); + + argv_array_pushf(_array, "name=%s", sub->name); + argv_array_pushf(_array, "sm_path=%s", list_item->name); + argv_array_pushf(_array, "displaypath=%s", displaypath); + argv_array_pushf(_array, "sha1=%s", +oid_to_hex(_item->oid)); + argv_array_pushf(_array, "toplevel=%s", toplevel); + + /* +* Since the path variable was accessible from the script +* before porting, it is also made available after porting. +* The environment variable "PATH" has a very special purpose +* on windows. And since environment
[GSoC][PATCH v2 08/13] submodule: port submodule subcommand 'summary' from shell to C
The submodule subcommand 'summary' is ported in the process of making git-submodule a builtin. The function cmd_summary() from git-submodule.sh is ported to functions module_summary(), compute_summary_module_list(), prepare_submodule_summary() and print_submodule_summary(). The first function module_summary() parses the options of submodule subcommand and also acts as the front-end of this subcommand. After parsing them, it calls the compute_summary_module_list() The functions compute_summary_module_list() runs the diff_cmd, and generates the modules list, as required by the subcommand. The generation of this module list is done by the using the callback function submodule_summary_callback(), and stored in the structure module_cb. Once the module list is generated, prepare_submodule_summary() further goes through the list and filters the list, for eventually calling the print_submodule_summary() function. Finally, the print_submodule_summary() takes care of generating and printing the summary for each submodule. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * Firstly, about the function compute_summary_module_list(). This function is created to generate the list of modules, for which we will generate the summary further. Since the list is actually generated using the git-diff-files or git-diff-index command, but for porting this, we required to create a function similar to the builtin functions of the above commands. But we can't directly call cmd_diff_files() and cmd_diff_index() since we don't have to display the output and instead need to store it. Hence, this function is introduced. * Also, the module_cb_list *list is not freed since it is a non-heap object. Hence, free() can't be using on the non-heap objects. * In the function prepare_submodule_summary(), as suggested 'git_config_get_string_const' was used instead of instead of '_value' * Some variables which weren't modified throughout the function-call were passed as const. * The '!!' trick, which wasn't used in the last patch, is now used in this new version . * the variables sha1_dst and sha1_src are removed from the function print_submodule_summary(), and instead the p->oid_src and p->oid_dst are used. * The variable sm_git_dir is freed at the end of the function. builtin/submodule--helper.c | 433 git-submodule.sh| 182 +-- 2 files changed, 434 insertions(+), 181 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 038be7ee2..d8bf16f1d 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -13,6 +13,9 @@ #include "remote.h" #include "refs.h" #include "connect.h" +#include "revision.h" +#include "diffcore.h" +#include "diff.h" typedef void (*submodule_list_func_t)(const struct cache_entry *list_item, void *cb_data); @@ -769,6 +772,435 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct module_cb { + unsigned int mod_src; + unsigned int mod_dst; + struct object_id oid_src; + struct object_id oid_dst; + char status; + const char *sm_path; +}; +#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL } + +struct module_cb_list { + struct module_cb **entries; + int alloc, nr; +}; +#define MODULE_CB_LIST_INIT { NULL, 0, 0 } + +struct summary_cb { + int argc; + const char **argv; + const char *prefix; + char *diff_cmd; + unsigned int cached: 1; + unsigned int for_status: 1; + unsigned int quiet: 1; + unsigned int files: 1; + int summary_limits; +}; +#define SUMMARY_CB_INIT { 0, NULL, NULL, NULL, 0, 0, 0, 0, 0 } + +static int verify_submodule_object_name(const char *sm_path, const char *sha1) +{ + struct child_process cp_rev_parse = CHILD_PROCESS_INIT; + + cp_rev_parse.git_cmd = 1; + cp_rev_parse.no_stdout = 1; + cp_rev_parse.dir = sm_path; + prepare_submodule_repo_env(_rev_parse.env_array); + + argv_array_pushl(_rev_parse.args, "rev-parse", "-q", +"--verify", NULL); + argv_array_pushf(_rev_parse.args, "%s^0", sha1); + + if (run_command(_rev_parse)) + return 1; + + return 0; +} + +static void print_submodule_summary(struct summary_cb *info, + struct module_cb *p) +{ + int missing_src = 0; + int missing_dst = 0; + char *displaypath; + const char *sha1_abbr_src; + const char *sha1_abbr_dst; + int errmsg = 0; + int total_commits
[GSoC][PATCH v2 10/13] submodule foreach: document '$sm_path' instead of '$path'
As using a variable '$path' may be harmful to users due to capitalization issues, see 64394e3ae9 (git-submodule.sh: Don't use $path variable in eval_gettext string, 2012-04-17). Adjust the documentation to advocate for using $sm_path, which contains the same value. We still make the 'path' variable available and document it as a deprecated synonym of 'sm_path'. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- This patch is same as its previous version. Although here I'll like to add a point that we aim to slowly drop the support of the variable 'path'. Documentation/git-submodule.txt | 10 ++ 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index ff612001d..a23baef62 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -183,12 +183,14 @@ information too. foreach [--recursive] :: Evaluates an arbitrary shell command in each checked out submodule. - The command has access to the variables $name, $path, $sha1 and + The command has access to the variables $name, $sm_path, $sha1 and $toplevel: $name is the name of the relevant submodule section in `.gitmodules`, - $path is the name of the submodule directory relative to the - superproject, $sha1 is the commit as recorded in the superproject, - and $toplevel is the absolute path to the top-level of the superproject. + $sm_path is the path of the submodule as recorded in the superproject, + $sha1 is the commit as recorded in the superproject, and + $toplevel is the absolute path to the top-level of the superproject. + Note that to avoid conflicts with '$PATH' on Windows, the '$path' + variable is now a deprecated synonym of '$sm_path' variable. Any submodules defined in the superproject but not checked out are ignored by this command. Unless given `--quiet`, foreach prints the name of each submodule before evaluating the command. -- 2.13.0
[GSoC][PATCH v2 11/13] submodule foreach: clarify the '$toplevel' variable documentation
It does not contain the topmost superproject as the author assumed, but the direct superproject, such that $toplevel/$sm_path is the actual absolute path of the submodule. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- Documentation/git-submodule.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index a23baef62..8e7930ebc 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -188,7 +188,8 @@ foreach [--recursive] :: $name is the name of the relevant submodule section in `.gitmodules`, $sm_path is the path of the submodule as recorded in the superproject, $sha1 is the commit as recorded in the superproject, and - $toplevel is the absolute path to the top-level of the superproject. + $toplevel is the absolute path to its superproject, such that + $toplevel/$sm_path is the absolute path of the submodule. Note that to avoid conflicts with '$PATH' on Windows, the '$path' variable is now a deprecated synonym of '$sm_path' variable. Any submodules defined in the superproject but not checked out are -- 2.13.0
[GSoC][PATCH v2 09/13] submodule foreach: correct '$path' in nested submodules from a subdirectory
When running 'git submodule foreach' from a subdirectory of your repository, nested submodules get a bogus value for $sm_path: For a submodule 'sub' that contains a nested submodule 'nested', running 'git -C dir submodule foreach echo $path' would report path='../nested' for the nested submodule. The first part '../' is derived from the logic computing the relative path from $pwd to the root of the superproject. The second part is the submodule path inside the submodule. This value is of little use and is hard to document. There are two different possible solutions that have more value: (a) The path value is documented as the path from the toplevel of the superproject to the mount point of the submodule. In this case we would want to have path='sub/nested'. (b) As Ramsay noticed the documented value is wrong. For the non-nested case the path is equal to the relative path from $pwd to the submodules working directory. When following this model, the expected value would be path='../sub/nested'. The behavior for (b) was introduced in 091a6eb0fe (submodule: drop the top-level requirement, 2013-06-16) the intent for $path seemed to be relative to $cwd to the submodule worktree, but that did not work for nested submodules, as the intermittent submodules were not included in the path. If we were to fix the meaning of the $path using (a) such that "path" is "the path from the toplevel of the superproject to the mount point of the submodule", we would break any existing submodule user that runs foreach from non-root of the superproject as the non-nested submodule '../sub' would change its path to 'sub'. If we would fix the meaning of the $path using (b), such that "path" is "the relative path from $pwd to the submodule", then we would break any user that uses nested submodules (even from the root directory) as the 'nested' would become 'sub/nested'. Both groups can be found in the wild. The author has no data if one group outweighs the other by large margin, and offending each one seems equally bad at first. However in the authors imagination it is better to go with (a) as running from a sub directory sounds like it is carried out by a human rather than by some automation task. With a human on the keyboard the feedback loop is short and the changed behavior can be adapted to quickly unlike some automation that can break silently. Discussed-with: Ramsay Jones <ram...@ramsayjones.plus.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> Signed-off-by: Stefan Beller <sbel...@google.com> --- git-submodule.sh | 1 - t/t7407-submodule-foreach.sh | 36 ++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/git-submodule.sh b/git-submodule.sh index a427ddafd..493a64372 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -320,7 +320,6 @@ cmd_foreach() prefix="$prefix$sm_path/" sanitize_submodule_env cd "$sm_path" && - sm_path=$(git submodule--helper relative-path "$sm_path" "$wt_prefix") && # we make $path available to scripts ... path=$sm_path && if test $# -eq 1 diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 6ba5daf42..0663622a4 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -82,9 +82,9 @@ test_expect_success 'test basic "submodule foreach" usage' ' cat >expect <expect <../../actual + ) && + test_i18ncmp expect actual +' cat > expect <
[GSoC][PATCH v2 06/13] submodule: port submodule subcommand 'deinit' from shell to C
The same mechanism is used even for porting this submodule subcommand, as used in the ported subcommands till now. The function cmd_deinit in split up after porting into three functions: module_deinit(), for_each_submodule_list() and deinit_submodule(). Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * In the function deinit_submodule, since the test is_git_directory() adds an additional condition, instead is_directory() is used to check if "sm_path/.git" is a directory. * since it was possible in the previous path that the value st.st_mode passed to the function mkdir contained a garbage value, instead we intrduce a mode_t variable mode, initially containing a default mode value '0777'. This is what the default of mode is set in case, that the value is not set after the lstat call. builtin/submodule--helper.c | 144 git-submodule.sh| 55 + 2 files changed, 145 insertions(+), 54 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 877215567..038be7ee2 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -916,6 +916,149 @@ static int module_sync(int argc, const char **argv, const char *prefix) return 0; } +struct deinit_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int force: 1; + unsigned int all: 1; +}; +#define DEINIT_CB_INIT { NULL, 0, 0, 0 } + +static void deinit_submodule(const struct cache_entry *list_item, +void *cb_data) +{ + struct deinit_cb *info = cb_data; + const struct submodule *sub; + char *displaypath = NULL; + struct child_process cp_config = CHILD_PROCESS_INIT; + struct strbuf sb_config = STRBUF_INIT; + char *sub_git_dir = xstrfmt("%s/.git", list_item->name); + mode_t mode = 0777; + + sub = submodule_from_path(null_sha1, list_item->name); + + if (!sub || !sub->name) + goto cleanup; + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + /* remove the submodule work tree (unless the user already did it) */ + if (is_directory(list_item->name)) { + struct stat st; + /* protect submodules containing a .git directory */ + if (is_directory(sub_git_dir)) + die(_("Submodule work tree '%s' contains a .git " + "directory use 'rm -rf' if you really want " + "to remove it including all of its history"), + displaypath); + + if (!info->force) { + struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; + argv_array_pushl(_rm.args, "rm", "-qn", +list_item->name, NULL); + + if (run_command(_rm)) + die(_("Submodule work tree '%s' contains local " + "modifications; use '-f' to discard them"), + displaypath); + } + + if (!lstat(list_item->name, )) { + struct strbuf sb_rm = STRBUF_INIT; + const char *format; + + strbuf_addstr(_rm, list_item->name); + + if (!remove_dir_recursively(_rm, 0)) + format = _("Cleared directory '%s'\n"); + else + format = _("Could not remove submodule work tree '%s'\n"); + + if (!info->quiet) + printf(format, displaypath); + + mode = st.st_mode; + + strbuf_release(_rm); + } + } + + if (mkdir(list_item->name, mode)) + die(_("could not create empty submodule directory %s"), + displaypath); + + cp_config.git_cmd = 1; + argv_array_pushl(_config.args, "config", "--get-regexp", NULL); + argv_array_pushf(_config.args, "submodule.%s\\.", sub->name); + + /* remove the .git/config entries (unless the user already did it) */ + if (!capture_command(_config, _config, 0) && sb_config.len) { + char *sub_key = xstrfmt("submodule.%s", sub->name); + /* +* remove the whole section so we have a clean state when +* the user later d
[GSoC][PATCH v2 04/13] submodule: port submodule subcommand 'status' from shell to C
This aims to make git-submodule 'status' a built-in. Hence, the function cmd_status() is ported from shell to C. This is done by introducing three functions: module_status(), submodule_status() and print_status(). The function module_status() acts as the front-end of the subcommand. It parses subcommand's options and then calls the function module_list_compute() for computing the list of submodules. Then this functions calls for_each_submodule_list() looping through the list obtained. Then for_each_submodule_list() calls submodule_status() for each of the submodule in its list. The function submodule_status() is responsible for generating the status each submodule it is called for, and then calls print_status(). Finally, the function print_status() handles the printing of submodule's status. Mentored-by: Christian Couder <christian.cou...@gmail.com> Mentored-by: Stefan Beller <sbel...@google.com> Signed-off-by: Prathamesh Chavan <pc44...@gmail.com> --- In this new version, the following changes have been made: * parameters passed to the function print_status() have been changed. Instead of passing char *sub_sha1, instead the object_id is being passed. * Also, since the passed parameter displaypath's value isn't changed by the function, it is passed to the funcition as const char *displaypath instead of char *displaypath. * the output type of the function handle_submodule_head_ref() is changed from strbuf to object_id, as we will use the object_id instead of the hex of sha1 being stored in a struct strbuf. * diff_files_Args is cleared after using it by passing it as args in the function cmd_diff_files. * In the function status_submodule(), for checking if a submodule has merge conflicts, the patch currently checks if the value of any of the ce_flags is non-zero. Currently, I think the we aren't interested in a partiular flag, but I'm not sure on this. * The confusion with displaypath being passed as te super-prefix in many of the ported subcommands may be a result of the fact that the function generating the displaypath: get_submodule_displaypath() uses the super-prefix as simply a path concatenated with the current submodule name to denote our current location. Also, for generating any submodule's displaypath, it would be important to have ".." passed to the submodule, and currently it is possible only via the super-prefix. builtin/submodule--helper.c | 157 git-submodule.sh| 49 +- 2 files changed, 158 insertions(+), 48 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 2cb72d68e..0bd969b7c 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -561,6 +561,162 @@ static int module_init(int argc, const char **argv, const char *prefix) return 0; } +struct status_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int recursive: 1; + unsigned int cached: 1; +}; +#define STATUS_CB_INIT { NULL, 0, 0, 0 } + +static void print_status(struct status_cb *info, char state, const char *path, +const struct object_id *oid, const char *displaypath) +{ + if (info->quiet) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + + if (state == ' ' || state == '+') { + struct argv_array name_rev_args = ARGV_ARRAY_INIT; + + argv_array_pushl(_rev_args, "print-name-rev", +path, oid_to_hex(oid), NULL); + print_name_rev(name_rev_args.argc, name_rev_args.argv, + info->prefix); + } else { + printf("\n"); + } +} + +static int handle_submodule_head_ref(const char *refname, +const struct object_id *oid, int flags, +void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const struct cache_entry *list_item, void *cb_data) +{ + struct status_cb *info = cb_data; + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + + if (!submodule_from_path(null_sha1, list_item->name)) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + list_item->name); + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + trace_printf("the value of flag is %d\n", list_item->ce_flags); + if (list_item->ce_flags) { + trace_printf("for U the value of flag is %d\n", list_item->ce_flags); + print_status(info, 'U', list_item->name, +_oid, di