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 <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Stefan Beller committed
Sep 8, 2015 at 11:57 UTC
ee8838d157761acf4cc38f2378277dc894c10eb0
2 files changed
+134
-76
builtin/submodule--helper.c
+132
@@ -8,6 +8,7 @@
8
#include "submodule.h"
9
#include "submodule-config.h"
10
#include "string-list.h"
11
+#include "run-command.h"
12
13
struct module_list {
14
const struct cache_entry **entries;
@@ -123,6 +124,136 @@ static int module_name(int argc, const char **argv, const char *prefix)
124
125
return 0;
126
}
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
+}
257
258
struct cmd_struct {
259
const char *cmd;
@@ -132,6 +263,7 @@ struct cmd_struct {
263
static struct cmd_struct commands[] = {
264
{"list", module_list},
265
{"name", module_name},
266
+ {"clone", module_clone},
267
};
268
269
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
git-submodule.sh
+2
-76
@@ -178,80 +178,6 @@ get_submodule_config () {
178
printf '%s' "${value:-$default}"
179
}
180
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
-
181
isnumber()
182
{
183
n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
@@ -412,7 +338,7 @@ Use -f if you really want to add it." >&2
338
echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
339
fi
340
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
342
(
343
clear_local_git_env
344
cd "$sm_path" &&
@@ -774,7 +700,7 @@ Maybe you want to use 'update --init'?")"
700
701
if ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git
702
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
704
cloned_modules="$cloned_modules;$name"
705
subsha1=
706
else