submodule: rewrite `module_list` shell function in C

Most of the submodule operations work on a set of submodules. Calculating and using this set is usually done via: module_list "$@" | { while read mode sha1 stage sm_path do # the actual operation done } Currently the function `module_list` is implemented in the git-submodule.sh as a shell script wrapping a perl script. The rewrite is in C, such that it is faster and can later be easily adapted when other functions are rewritten in C. git-submodule.sh, similar to the builtin commands, will navigate to the top-most directory of the repository and keep the subdirectory as a variable. As the helper is called from within the git-submodule.sh script, we are already navigated to the root level, but the path arguments are still relative to the subdirectory we were in when calling git-submodule.sh. That's why there is a `--prefix` option pointing to an alternative path which to anchor relative path arguments. Signed-off-by: Stefan Beller <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Stefan Beller committed Sep 2, 2015 at 14:42 UTC 74703a1e4dfc5affcb8944e78b53f0817b492246
6 files changed +138 -48
.gitignore
+1
@@ -155,6 +155,7 @@
155 /git-status
156 /git-stripspace
157 /git-submodule
158 +/git-submodule--helper
159 /git-svn
160 /git-symbolic-ref
161 /git-tag
Makefile
+1
@@ -901,6 +901,7 @@ BUILTIN_OBJS += builtin/shortlog.o
901 BUILTIN_OBJS += builtin/show-branch.o
902 BUILTIN_OBJS += builtin/show-ref.o
903 BUILTIN_OBJS += builtin/stripspace.o
904 +BUILTIN_OBJS += builtin/submodule--helper.o
905 BUILTIN_OBJS += builtin/symbolic-ref.o
906 BUILTIN_OBJS += builtin/tag.o
907 BUILTIN_OBJS += builtin/unpack-file.o
builtin.h
+1
@@ -119,6 +119,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);
119 extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
120 extern int cmd_status(int argc, const char **argv, const char *prefix);
121 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
122 +extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
123 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
124 extern int cmd_tag(int argc, const char **argv, const char *prefix);
125 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
builtin/submodule--helper.c new
+128
@@ -0,0 +1,128 @@
1 +#include "builtin.h"
2 +#include "cache.h"
3 +#include "parse-options.h"
4 +#include "quote.h"
5 +#include "pathspec.h"
6 +#include "dir.h"
7 +#include "utf8.h"
8 +
9 +struct module_list {
10 + const struct cache_entry **entries;
11 + int alloc, nr;
12 +};
13 +#define MODULE_LIST_INIT { NULL, 0, 0 }
14 +
15 +static int module_list_compute(int argc, const char **argv,
16 + const char *prefix,
17 + struct pathspec *pathspec,
18 + struct module_list *list)
19 +{
20 + int i, result = 0;
21 + char *max_prefix, *ps_matched = NULL;
22 + int max_prefix_len;
23 + parse_pathspec(pathspec, 0,
24 + PATHSPEC_PREFER_FULL |
25 + PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
26 + prefix, argv);
27 +
28 + /* Find common prefix for all pathspec's */
29 + max_prefix = common_prefix(pathspec);
30 + max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
31 +
32 + if (pathspec->nr)
33 + ps_matched = xcalloc(pathspec->nr, 1);
34 +
35 + if (read_cache() < 0)
36 + die(_("index file corrupt"));
37 +
38 + for (i = 0; i < active_nr; i++) {
39 + const struct cache_entry *ce = active_cache[i];
40 +
41 + if (!S_ISGITLINK(ce->ce_mode) ||
42 + !match_pathspec(pathspec, ce->name, ce_namelen(ce),
43 + max_prefix_len, ps_matched, 1))
44 + continue;
45 +
46 + ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
47 + list->entries[list->nr++] = ce;
48 + while (i + 1 < active_nr &&
49 + !strcmp(ce->name, active_cache[i + 1]->name))
50 + /*
51 + * Skip entries with the same name in different stages
52 + * to make sure an entry is returned only once.
53 + */
54 + i++;
55 + }
56 + free(max_prefix);
57 +
58 + if (ps_matched && report_path_error(ps_matched, pathspec, prefix))
59 + result = -1;
60 +
61 + free(ps_matched);
62 +
63 + return result;
64 +}
65 +
66 +static int module_list(int argc, const char **argv, const char *prefix)
67 +{
68 + int i;
69 + struct pathspec pathspec;
70 + struct module_list list = MODULE_LIST_INIT;
71 +
72 + struct option module_list_options[] = {
73 + OPT_STRING(0, "prefix", &prefix,
74 + N_("path"),
75 + N_("alternative anchor for relative paths")),
76 + OPT_END()
77 + };
78 +
79 + const char *const git_submodule_helper_usage[] = {
80 + N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
81 + NULL
82 + };
83 +
84 + argc = parse_options(argc, argv, prefix, module_list_options,
85 + git_submodule_helper_usage, 0);
86 +
87 + if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
88 + printf("#unmatched\n");
89 + return 1;
90 + }
91 +
92 + for (i = 0; i < list.nr; i++) {
93 + const struct cache_entry *ce = list.entries[i];
94 +
95 + if (ce_stage(ce))
96 + printf("%06o %s U\t", ce->ce_mode, sha1_to_hex(null_sha1));
97 + else
98 + printf("%06o %s %d\t", ce->ce_mode, sha1_to_hex(ce->sha1), ce_stage(ce));
99 +
100 + utf8_fprintf(stdout, "%s\n", ce->name);
101 + }
102 + return 0;
103 +}
104 +
105 +
106 +struct cmd_struct {
107 + const char *cmd;
108 + int (*fn)(int, const char **, const char *);
109 +};
110 +
111 +static struct cmd_struct commands[] = {
112 + {"list", module_list},
113 +};
114 +
115 +int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
116 +{
117 + int i;
118 + if (argc < 2)
119 + die(_("fatal: submodule--helper subcommand must be "
120 + "called with a subcommand"));
121 +
122 + for (i = 0; i < ARRAY_SIZE(commands); i++)
123 + if (!strcmp(argv[1], commands[i].cmd))
124 + return commands[i].fn(argc - 1, argv + 1, prefix);
125 +
126 + die(_("fatal: '%s' is not a valid submodule--helper "
127 + "subcommand"), argv[1]);
128 +}
git-submodule.sh
+6 -48
@@ -145,48 +145,6 @@ relative_path ()
145 echo "$result$target"
146 }
147
148 -#
149 -# Get submodule info for registered submodules
150 -# $@ = path to limit submodule list
151 -#
152 -module_list()
153 -{
154 - eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
155 - (
156 - git ls-files -z --error-unmatch --stage -- "$@" ||
157 - echo "unmatched pathspec exists"
158 - ) |
159 - @@PERL@@ -e '
160 - my %unmerged = ();
161 - my ($null_sha1) = ("0" x 40);
162 - my @out = ();
163 - my $unmatched = 0;
164 - $/ = "\0";
165 - while (<STDIN>) {
166 - if (/^unmatched pathspec/) {
167 - $unmatched = 1;
168 - next;
169 - }
170 - chomp;
171 - my ($mode, $sha1, $stage, $path) =
172 - /^([0-7]+) ([0-9a-f]{40}) ([0-3])\t(.*)$/;
173 - next unless $mode eq "160000";
174 - if ($stage ne "0") {
175 - if (!$unmerged{$path}++) {
176 - push @out, "$mode $null_sha1 U\t$path\n";
177 - }
178 - next;
179 - }
180 - push @out, "$_\n";
181 - }
182 - if ($unmatched) {
183 - print "#unmatched\n";
184 - } else {
185 - print for (@out);
186 - }
187 - '
188 -}
189 -
148 die_if_unmatched ()
149 {
150 if test "$1" = "#unmatched"
@@ -532,7 +490,7 @@ cmd_foreach()
490 # command in the subshell (and a recursive call to this function)
491 exec 3<&0
492
535 - module_list |
493 + git submodule--helper list --prefix "$wt_prefix"|
494 while read mode sha1 stage sm_path
495 do
496 die_if_unmatched "$mode"
@@ -592,7 +550,7 @@ cmd_init()
550 shift
551 done
552
595 - module_list "$@" |
553 + git submodule--helper list --prefix "$wt_prefix" "$@" |
554 while read mode sha1 stage sm_path
555 do
556 die_if_unmatched "$mode"
@@ -674,7 +632,7 @@ cmd_deinit()
632 die "$(eval_gettext "Use '.' if you really want to deinitialize all submodules")"
633 fi
634
677 - module_list "$@" |
635 + git submodule--helper list --prefix "$wt_prefix" "$@" |
636 while read mode sha1 stage sm_path
637 do
638 die_if_unmatched "$mode"
@@ -790,7 +748,7 @@ cmd_update()
748 fi
749
750 cloned_modules=
793 - module_list "$@" | {
751 + git submodule--helper list --prefix "$wt_prefix" "$@" | {
752 err=
753 while read mode sha1 stage sm_path
754 do
@@ -1222,7 +1180,7 @@ cmd_status()
1180 shift
1181 done
1182
1225 - module_list "$@" |
1183 + git submodule--helper list --prefix "$wt_prefix" "$@" |
1184 while read mode sha1 stage sm_path
1185 do
1186 die_if_unmatched "$mode"
@@ -1299,7 +1257,7 @@ cmd_sync()
1257 esac
1258 done
1259 cd_to_toplevel
1302 - module_list "$@" |
1260 + git submodule--helper list --prefix "$wt_prefix" "$@" |
1261 while read mode sha1 stage sm_path
1262 do
1263 die_if_unmatched "$mode"
git.c
+1
@@ -469,6 +469,7 @@ static struct cmd_struct commands[] = {
469 { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
470 { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
471 { "stripspace", cmd_stripspace },
472 + { "submodule--helper", cmd_submodule__helper, RUN_SETUP },
473 { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
474 { "tag", cmd_tag, RUN_SETUP },
475 { "unpack-file", cmd_unpack_file, RUN_SETUP },