Skip to content

Commit a6226fd

Browse files
tfidfwastakengitster
authored andcommitted
submodule--helper: convert the bulk of cmd_add() to C
Introduce the 'add' subcommand to `submodule--helper.c` that does all the work 'submodule add' past the parsing of flags. We also remove the constness of the sm_path field of the `add_data` struct. This is needed so that it can be modified by normalize_path_copy(). As with the previous conversions, this is meant to be a faithful conversion with no modification to the behaviour of `submodule add`. Signed-off-by: Atharva Raykar <[email protected]> Mentored-by: Christian Couder <[email protected]> Helped-by: Kaartic Sivaraam <[email protected]> Mentored-by: Shourya Shukla <[email protected]> Based-on-patch-by: Shourya Shukla <[email protected]> Based-on-patch-by: Prathamesh Chavan <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ed86301 commit a6226fd

File tree

2 files changed

+166
-95
lines changed

2 files changed

+166
-95
lines changed

builtin/submodule--helper.c

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2748,7 +2748,7 @@ struct add_data {
27482748
const char *prefix;
27492749
const char *branch;
27502750
const char *reference_path;
2751-
const char *sm_path;
2751+
char *sm_path;
27522752
const char *sm_name;
27532753
const char *repo;
27542754
const char *realrepo;
@@ -3047,6 +3047,168 @@ static int add_config(int argc, const char **argv, const char *prefix)
30473047
return 0;
30483048
}
30493049

3050+
static void die_on_index_match(const char *path, int force)
3051+
{
3052+
struct pathspec ps;
3053+
const char *args[] = { path, NULL };
3054+
parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
3055+
3056+
if (read_cache_preload(NULL) < 0)
3057+
die(_("index file corrupt"));
3058+
3059+
if (ps.nr) {
3060+
int i;
3061+
char *ps_matched = xcalloc(ps.nr, 1);
3062+
3063+
/* TODO: audit for interaction with sparse-index. */
3064+
ensure_full_index(&the_index);
3065+
3066+
/*
3067+
* Since there is only one pathspec, we just need
3068+
* need to check ps_matched[0] to know if a cache
3069+
* entry matched.
3070+
*/
3071+
for (i = 0; i < active_nr; i++) {
3072+
ce_path_match(&the_index, active_cache[i], &ps,
3073+
ps_matched);
3074+
3075+
if (ps_matched[0]) {
3076+
if (!force)
3077+
die(_("'%s' already exists in the index"),
3078+
path);
3079+
if (!S_ISGITLINK(active_cache[i]->ce_mode))
3080+
die(_("'%s' already exists in the index "
3081+
"and is not a submodule"), path);
3082+
break;
3083+
}
3084+
}
3085+
free(ps_matched);
3086+
}
3087+
}
3088+
3089+
static void die_on_repo_without_commits(const char *path)
3090+
{
3091+
struct strbuf sb = STRBUF_INIT;
3092+
strbuf_addstr(&sb, path);
3093+
if (is_nonbare_repository_dir(&sb)) {
3094+
struct object_id oid;
3095+
if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
3096+
die(_("'%s' does not have a commit checked out"), path);
3097+
}
3098+
}
3099+
3100+
static int module_add(int argc, const char **argv, const char *prefix)
3101+
{
3102+
int force = 0, quiet = 0, progress = 0, dissociate = 0;
3103+
struct add_data add_data = ADD_DATA_INIT;
3104+
3105+
struct option options[] = {
3106+
OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
3107+
N_("branch of repository to add as submodule")),
3108+
OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
3109+
PARSE_OPT_NOCOMPLETE),
3110+
OPT__QUIET(&quiet, N_("print only error messages")),
3111+
OPT_BOOL(0, "progress", &progress, N_("force cloning progress")),
3112+
OPT_STRING(0, "reference", &add_data.reference_path, N_("repository"),
3113+
N_("reference repository")),
3114+
OPT_BOOL(0, "dissociate", &dissociate, N_("borrow the objects from reference repositories")),
3115+
OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
3116+
N_("sets the submodule’s name to the given string "
3117+
"instead of defaulting to its path")),
3118+
OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")),
3119+
OPT_END()
3120+
};
3121+
3122+
const char *const usage[] = {
3123+
N_("git submodule--helper add [<options>] [--] <repository> [<path>]"),
3124+
NULL
3125+
};
3126+
3127+
argc = parse_options(argc, argv, prefix, options, usage, 0);
3128+
3129+
if (!is_writing_gitmodules_ok())
3130+
die(_("please make sure that the .gitmodules file is in the working tree"));
3131+
3132+
if (prefix && *prefix &&
3133+
add_data.reference_path && !is_absolute_path(add_data.reference_path))
3134+
add_data.reference_path = xstrfmt("%s%s", prefix, add_data.reference_path);
3135+
3136+
if (argc == 0 || argc > 2)
3137+
usage_with_options(usage, options);
3138+
3139+
add_data.repo = argv[0];
3140+
if (argc == 1)
3141+
add_data.sm_path = git_url_basename(add_data.repo, 0, 0);
3142+
else
3143+
add_data.sm_path = xstrdup(argv[1]);
3144+
3145+
if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
3146+
add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
3147+
3148+
if (starts_with_dot_dot_slash(add_data.repo) ||
3149+
starts_with_dot_slash(add_data.repo)) {
3150+
if (prefix)
3151+
die(_("Relative path can only be used from the toplevel "
3152+
"of the working tree"));
3153+
3154+
/* dereference source url relative to parent's url */
3155+
add_data.realrepo = compute_submodule_clone_url(add_data.repo, NULL, 1);
3156+
} else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
3157+
add_data.realrepo = add_data.repo;
3158+
} else {
3159+
die(_("repo URL: '%s' must be absolute or begin with ./|../"),
3160+
add_data.repo);
3161+
}
3162+
3163+
/*
3164+
* normalize path:
3165+
* multiple //; leading ./; /./; /../;
3166+
*/
3167+
normalize_path_copy(add_data.sm_path, add_data.sm_path);
3168+
strip_dir_trailing_slashes(add_data.sm_path);
3169+
3170+
die_on_index_match(add_data.sm_path, force);
3171+
die_on_repo_without_commits(add_data.sm_path);
3172+
3173+
if (!force) {
3174+
int exit_code = -1;
3175+
struct strbuf sb = STRBUF_INIT;
3176+
struct child_process cp = CHILD_PROCESS_INIT;
3177+
cp.git_cmd = 1;
3178+
cp.no_stdout = 1;
3179+
strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
3180+
"--no-warn-embedded-repo", add_data.sm_path, NULL);
3181+
if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
3182+
strbuf_complete_line(&sb);
3183+
fputs(sb.buf, stderr);
3184+
free(add_data.sm_path);
3185+
return exit_code;
3186+
}
3187+
strbuf_release(&sb);
3188+
}
3189+
3190+
if(!add_data.sm_name)
3191+
add_data.sm_name = add_data.sm_path;
3192+
3193+
if (check_submodule_name(add_data.sm_name))
3194+
die(_("'%s' is not a valid submodule name"), add_data.sm_name);
3195+
3196+
add_data.prefix = prefix;
3197+
add_data.force = !!force;
3198+
add_data.quiet = !!quiet;
3199+
add_data.progress = !!progress;
3200+
add_data.dissociate = !!dissociate;
3201+
3202+
if (add_submodule(&add_data)) {
3203+
free(add_data.sm_path);
3204+
return 1;
3205+
}
3206+
configure_added_submodule(&add_data);
3207+
free(add_data.sm_path);
3208+
3209+
return 0;
3210+
}
3211+
30503212
#define SUPPORT_SUPER_PREFIX (1<<0)
30513213

30523214
struct cmd_struct {
@@ -3061,6 +3223,7 @@ static struct cmd_struct commands[] = {
30613223
{"clone", module_clone, 0},
30623224
{"add-clone", add_clone, 0},
30633225
{"add-config", add_config, 0},
3226+
{"add", module_add, SUPPORT_SUPER_PREFIX},
30643227
{"update-module-mode", module_update_module_mode, 0},
30653228
{"update-clone", update_clone, 0},
30663229
{"ensure-core-worktree", ensure_core_worktree, 0},

git-submodule.sh

Lines changed: 2 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -145,104 +145,12 @@ cmd_add()
145145
shift
146146
done
147147

148-
if ! git submodule--helper config --check-writeable >/dev/null 2>&1
148+
if test -z "$1"
149149
then
150-
die "fatal: $(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
151-
fi
152-
153-
if test -n "$reference_path"
154-
then
155-
is_absolute_path "$reference_path" ||
156-
reference_path="$wt_prefix$reference_path"
157-
158-
reference="--reference=$reference_path"
159-
fi
160-
161-
repo=$1
162-
sm_path=$2
163-
164-
if test -z "$sm_path"; then
165-
sm_path=$(printf '%s\n' "$repo" |
166-
sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
167-
fi
168-
169-
if test -z "$repo" || test -z "$sm_path"; then
170150
usage
171151
fi
172152

173-
is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
174-
175-
# assure repo is absolute or relative to parent
176-
case "$repo" in
177-
./*|../*)
178-
test -z "$wt_prefix" ||
179-
die "fatal: $(gettext "Relative path can only be used from the toplevel of the working tree")"
180-
181-
# dereference source url relative to parent's url
182-
realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
183-
;;
184-
*:*|/*)
185-
# absolute url
186-
realrepo=$repo
187-
;;
188-
*)
189-
die "fatal: $(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
190-
;;
191-
esac
192-
193-
# normalize path:
194-
# multiple //; leading ./; /./; /../; trailing /
195-
sm_path=$(printf '%s/\n' "$sm_path" |
196-
sed -e '
197-
s|//*|/|g
198-
s|^\(\./\)*||
199-
s|/\(\./\)*|/|g
200-
:start
201-
s|\([^/]*\)/\.\./||
202-
tstart
203-
s|/*$||
204-
')
205-
if test -z "$force"
206-
then
207-
git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
208-
die "fatal: $(eval_gettext "'\$sm_path' already exists in the index")"
209-
else
210-
git ls-files -s "$sm_path" | sane_grep -v "^160000" > /dev/null 2>&1 &&
211-
die "fatal: $(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
212-
fi
213-
214-
if test -d "$sm_path" &&
215-
test -z $(git -C "$sm_path" rev-parse --show-cdup 2>/dev/null)
216-
then
217-
git -C "$sm_path" rev-parse --verify -q HEAD >/dev/null ||
218-
die "fatal: $(eval_gettext "'\$sm_path' does not have a commit checked out")"
219-
fi
220-
221-
if test -z "$force"
222-
then
223-
dryerr=$(git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" 2>&1 >/dev/null)
224-
res=$?
225-
if test $res -ne 0
226-
then
227-
echo >&2 "$dryerr"
228-
exit $res
229-
fi
230-
fi
231-
232-
if test -n "$custom_name"
233-
then
234-
sm_name="$custom_name"
235-
else
236-
sm_name="$sm_path"
237-
fi
238-
239-
if ! git submodule--helper check-name "$sm_name"
240-
then
241-
die "fatal: $(eval_gettext "'$sm_name' is not a valid submodule name")"
242-
fi
243-
244-
git submodule--helper add-clone ${GIT_QUIET:+--quiet} ${force:+"--force"} ${progress:+"--progress"} ${branch:+--branch "$branch"} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${dissociate:+"--dissociate"} ${depth:+"$depth"} || exit
245-
git submodule--helper add-config ${force:+--force} ${branch:+--branch "$branch"} --url "$repo" --resolved-url "$realrepo" --path "$sm_path" --name "$sm_name"
153+
git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper add ${GIT_QUIET:+--quiet} ${force:+--force} ${progress:+"--progress"} ${branch:+--branch "$branch"} ${reference_path:+--reference "$reference_path"} ${dissociate:+--dissociate} ${custom_name:+--name "$custom_name"} ${depth:+"$depth"} -- "$@"
246154
}
247155

248156
#

0 commit comments

Comments
 (0)