Skip to content

Commit 74703a1

Browse files
stefanbellergitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 5a1ba6b commit 74703a1

File tree

6 files changed

+138
-48
lines changed

6 files changed

+138
-48
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@
155155
/git-status
156156
/git-stripspace
157157
/git-submodule
158+
/git-submodule--helper
158159
/git-svn
159160
/git-symbolic-ref
160161
/git-tag

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,7 @@ BUILTIN_OBJS += builtin/shortlog.o
901901
BUILTIN_OBJS += builtin/show-branch.o
902902
BUILTIN_OBJS += builtin/show-ref.o
903903
BUILTIN_OBJS += builtin/stripspace.o
904+
BUILTIN_OBJS += builtin/submodule--helper.o
904905
BUILTIN_OBJS += builtin/symbolic-ref.o
905906
BUILTIN_OBJS += builtin/tag.o
906907
BUILTIN_OBJS += builtin/unpack-file.o

builtin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);
119119
extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
120120
extern int cmd_status(int argc, const char **argv, const char *prefix);
121121
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);
122123
extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
123124
extern int cmd_tag(int argc, const char **argv, const char *prefix);
124125
extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);

builtin/submodule--helper.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 6 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -145,48 +145,6 @@ relative_path ()
145145
echo "$result$target"
146146
}
147147

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-
190148
die_if_unmatched ()
191149
{
192150
if test "$1" = "#unmatched"
@@ -532,7 +490,7 @@ cmd_foreach()
532490
# command in the subshell (and a recursive call to this function)
533491
exec 3<&0
534492

535-
module_list |
493+
git submodule--helper list --prefix "$wt_prefix"|
536494
while read mode sha1 stage sm_path
537495
do
538496
die_if_unmatched "$mode"
@@ -592,7 +550,7 @@ cmd_init()
592550
shift
593551
done
594552

595-
module_list "$@" |
553+
git submodule--helper list --prefix "$wt_prefix" "$@" |
596554
while read mode sha1 stage sm_path
597555
do
598556
die_if_unmatched "$mode"
@@ -674,7 +632,7 @@ cmd_deinit()
674632
die "$(eval_gettext "Use '.' if you really want to deinitialize all submodules")"
675633
fi
676634

677-
module_list "$@" |
635+
git submodule--helper list --prefix "$wt_prefix" "$@" |
678636
while read mode sha1 stage sm_path
679637
do
680638
die_if_unmatched "$mode"
@@ -790,7 +748,7 @@ cmd_update()
790748
fi
791749

792750
cloned_modules=
793-
module_list "$@" | {
751+
git submodule--helper list --prefix "$wt_prefix" "$@" | {
794752
err=
795753
while read mode sha1 stage sm_path
796754
do
@@ -1222,7 +1180,7 @@ cmd_status()
12221180
shift
12231181
done
12241182

1225-
module_list "$@" |
1183+
git submodule--helper list --prefix "$wt_prefix" "$@" |
12261184
while read mode sha1 stage sm_path
12271185
do
12281186
die_if_unmatched "$mode"
@@ -1299,7 +1257,7 @@ cmd_sync()
12991257
esac
13001258
done
13011259
cd_to_toplevel
1302-
module_list "$@" |
1260+
git submodule--helper list --prefix "$wt_prefix" "$@" |
13031261
while read mode sha1 stage sm_path
13041262
do
13051263
die_if_unmatched "$mode"

git.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ static struct cmd_struct commands[] = {
469469
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
470470
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
471471
{ "stripspace", cmd_stripspace },
472+
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP },
472473
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
473474
{ "tag", cmd_tag, RUN_SETUP },
474475
{ "unpack-file", cmd_unpack_file, RUN_SETUP },

0 commit comments

Comments
 (0)