Skip to content

Commit ee8838d

Browse files
stefanbellergitster
authored andcommitted
submodule: rewrite module_clone shell function in C
This reimplements the helper function `module_clone` in shell in C as `clone`. This functionality is needed for converting `git submodule update` later on, which we want to add threading to. Signed-off-by: Stefan Beller <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 0ea306e commit ee8838d

File tree

2 files changed

+134
-76
lines changed

2 files changed

+134
-76
lines changed

builtin/submodule--helper.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "submodule.h"
99
#include "submodule-config.h"
1010
#include "string-list.h"
11+
#include "run-command.h"
1112

1213
struct module_list {
1314
const struct cache_entry **entries;
@@ -123,6 +124,136 @@ static int module_name(int argc, const char **argv, const char *prefix)
123124

124125
return 0;
125126
}
127+
static int clone_submodule(const char *path, const char *gitdir, const char *url,
128+
const char *depth, const char *reference, int quiet)
129+
{
130+
struct child_process cp;
131+
child_process_init(&cp);
132+
133+
argv_array_push(&cp.args, "clone");
134+
argv_array_push(&cp.args, "--no-checkout");
135+
if (quiet)
136+
argv_array_push(&cp.args, "--quiet");
137+
if (depth && *depth)
138+
argv_array_pushl(&cp.args, "--depth", depth, NULL);
139+
if (reference && *reference)
140+
argv_array_pushl(&cp.args, "--reference", reference, NULL);
141+
if (gitdir && *gitdir)
142+
argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
143+
144+
argv_array_push(&cp.args, url);
145+
argv_array_push(&cp.args, path);
146+
147+
cp.git_cmd = 1;
148+
cp.env = local_repo_env;
149+
cp.no_stdin = 1;
150+
151+
return run_command(&cp);
152+
}
153+
154+
static int module_clone(int argc, const char **argv, const char *prefix)
155+
{
156+
const char *path = NULL, *name = NULL, *url = NULL;
157+
const char *reference = NULL, *depth = NULL;
158+
int quiet = 0;
159+
FILE *submodule_dot_git;
160+
char *sm_gitdir, *cwd, *p;
161+
struct strbuf rel_path = STRBUF_INIT;
162+
struct strbuf sb = STRBUF_INIT;
163+
164+
struct option module_clone_options[] = {
165+
OPT_STRING(0, "prefix", &prefix,
166+
N_("path"),
167+
N_("alternative anchor for relative paths")),
168+
OPT_STRING(0, "path", &path,
169+
N_("path"),
170+
N_("where the new submodule will be cloned to")),
171+
OPT_STRING(0, "name", &name,
172+
N_("string"),
173+
N_("name of the new submodule")),
174+
OPT_STRING(0, "url", &url,
175+
N_("string"),
176+
N_("url where to clone the submodule from")),
177+
OPT_STRING(0, "reference", &reference,
178+
N_("string"),
179+
N_("reference repository")),
180+
OPT_STRING(0, "depth", &depth,
181+
N_("string"),
182+
N_("depth for shallow clones")),
183+
OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
184+
OPT_END()
185+
};
186+
187+
const char *const git_submodule_helper_usage[] = {
188+
N_("git submodule--helper clone [--prefix=<path>] [--quiet] "
189+
"[--reference <repository>] [--name <name>] [--url <url>]"
190+
"[--depth <depth>] [--] [<path>...]"),
191+
NULL
192+
};
193+
194+
argc = parse_options(argc, argv, prefix, module_clone_options,
195+
git_submodule_helper_usage, 0);
196+
197+
strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
198+
sm_gitdir = strbuf_detach(&sb, NULL);
199+
200+
if (!file_exists(sm_gitdir)) {
201+
if (safe_create_leading_directories_const(sm_gitdir) < 0)
202+
die(_("could not create directory '%s'"), sm_gitdir);
203+
if (clone_submodule(path, sm_gitdir, url, depth, reference, quiet))
204+
die(_("clone of '%s' into submodule path '%s' failed"),
205+
url, path);
206+
} else {
207+
if (safe_create_leading_directories_const(path) < 0)
208+
die(_("could not create directory '%s'"), path);
209+
strbuf_addf(&sb, "%s/index", sm_gitdir);
210+
unlink_or_warn(sb.buf);
211+
strbuf_reset(&sb);
212+
}
213+
214+
/* Write a .git file in the submodule to redirect to the superproject. */
215+
if (safe_create_leading_directories_const(path) < 0)
216+
die(_("could not create directory '%s'"), path);
217+
218+
if (path && *path)
219+
strbuf_addf(&sb, "%s/.git", path);
220+
else
221+
strbuf_addstr(&sb, ".git");
222+
223+
if (safe_create_leading_directories_const(sb.buf) < 0)
224+
die(_("could not create leading directories of '%s'"), sb.buf);
225+
submodule_dot_git = fopen(sb.buf, "w");
226+
if (!submodule_dot_git)
227+
die_errno(_("cannot open file '%s'"), sb.buf);
228+
229+
fprintf(submodule_dot_git, "gitdir: %s\n",
230+
relative_path(sm_gitdir, path, &rel_path));
231+
if (fclose(submodule_dot_git))
232+
die(_("could not close file %s"), sb.buf);
233+
strbuf_reset(&sb);
234+
strbuf_reset(&rel_path);
235+
236+
cwd = xgetcwd();
237+
/* Redirect the worktree of the submodule in the superproject's config */
238+
if (!is_absolute_path(sm_gitdir)) {
239+
strbuf_addf(&sb, "%s/%s", cwd, sm_gitdir);
240+
free(sm_gitdir);
241+
sm_gitdir = strbuf_detach(&sb, NULL);
242+
}
243+
244+
strbuf_addf(&sb, "%s/%s", cwd, path);
245+
p = git_pathdup_submodule(path, "config");
246+
if (!p)
247+
die(_("could not get submodule directory for '%s'"), path);
248+
git_config_set_in_file(p, "core.worktree",
249+
relative_path(sb.buf, sm_gitdir, &rel_path));
250+
strbuf_release(&sb);
251+
strbuf_release(&rel_path);
252+
free(sm_gitdir);
253+
free(cwd);
254+
free(p);
255+
return 0;
256+
}
126257

127258
struct cmd_struct {
128259
const char *cmd;
@@ -132,6 +263,7 @@ struct cmd_struct {
132263
static struct cmd_struct commands[] = {
133264
{"list", module_list},
134265
{"name", module_name},
266+
{"clone", module_clone},
135267
};
136268

137269
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)

git-submodule.sh

Lines changed: 2 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -178,80 +178,6 @@ get_submodule_config () {
178178
printf '%s' "${value:-$default}"
179179
}
180180

181-
#
182-
# Clone a submodule
183-
#
184-
# $1 = submodule path
185-
# $2 = submodule name
186-
# $3 = URL to clone
187-
# $4 = reference repository to reuse (empty for independent)
188-
# $5 = depth argument for shallow clones (empty for deep)
189-
#
190-
# Prior to calling, cmd_update checks that a possibly existing
191-
# path is not a git repository.
192-
# Likewise, cmd_add checks that path does not exist at all,
193-
# since it is the location of a new submodule.
194-
#
195-
module_clone()
196-
{
197-
sm_path=$1
198-
name=$2
199-
url=$3
200-
reference="$4"
201-
depth="$5"
202-
quiet=
203-
if test -n "$GIT_QUIET"
204-
then
205-
quiet=-q
206-
fi
207-
208-
gitdir=
209-
gitdir_base=
210-
base_name=$(dirname "$name")
211-
212-
gitdir=$(git rev-parse --git-dir)
213-
gitdir_base="$gitdir/modules/$base_name"
214-
gitdir="$gitdir/modules/$name"
215-
216-
if test -d "$gitdir"
217-
then
218-
mkdir -p "$sm_path"
219-
rm -f "$gitdir/index"
220-
else
221-
mkdir -p "$gitdir_base"
222-
(
223-
clear_local_git_env
224-
git clone $quiet ${depth:+"$depth"} -n ${reference:+"$reference"} \
225-
--separate-git-dir "$gitdir" "$url" "$sm_path"
226-
) ||
227-
die "$(eval_gettext "Clone of '\$url' into submodule path '\$sm_path' failed")"
228-
fi
229-
230-
# We already are at the root of the work tree but cd_to_toplevel will
231-
# resolve any symlinks that might be present in $PWD
232-
a=$(cd_to_toplevel && cd "$gitdir" && pwd)/
233-
b=$(cd_to_toplevel && cd "$sm_path" && pwd)/
234-
# Remove all common leading directories after a sanity check
235-
if test "${a#$b}" != "$a" || test "${b#$a}" != "$b"; then
236-
die "$(eval_gettext "Gitdir '\$a' is part of the submodule path '\$b' or vice versa")"
237-
fi
238-
while test "${a%%/*}" = "${b%%/*}"
239-
do
240-
a=${a#*/}
241-
b=${b#*/}
242-
done
243-
# Now chop off the trailing '/'s that were added in the beginning
244-
a=${a%/}
245-
b=${b%/}
246-
247-
# Turn each leading "*/" component into "../"
248-
rel=$(printf '%s\n' "$b" | sed -e 's|[^/][^/]*|..|g')
249-
printf '%s\n' "gitdir: $rel/$a" >"$sm_path/.git"
250-
251-
rel=$(printf '%s\n' "$a" | sed -e 's|[^/][^/]*|..|g')
252-
(clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b")
253-
}
254-
255181
isnumber()
256182
{
257183
n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
@@ -412,7 +338,7 @@ Use -f if you really want to add it." >&2
412338
echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
413339
fi
414340
fi
415-
module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" "$depth" || exit
341+
git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" "$reference" "$depth" || exit
416342
(
417343
clear_local_git_env
418344
cd "$sm_path" &&
@@ -774,7 +700,7 @@ Maybe you want to use 'update --init'?")"
774700

775701
if ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git
776702
then
777-
module_clone "$sm_path" "$name" "$url" "$reference" "$depth" || exit
703+
git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$prefix" --path "$sm_path" --name "$name" --url "$url" "$reference" "$depth" || exit
778704
cloned_modules="$cloned_modules;$name"
779705
subsha1=
780706
else

0 commit comments

Comments
 (0)