Merge branch 'sb/submodule-update-in-c'

"git submodule update" is getting rewritten piece-by-piece into C.

* sb/submodule-update-in-c:
  submodule--helper: introduce new update-module-mode helper
  submodule--helper: replace connect-gitdir-workingtree by ensure-core-worktree
  builtin/submodule--helper: factor out method to update a single submodule
  builtin/submodule--helper: store update_clone information in a struct
  builtin/submodule--helper: factor out submodule updating
  git-submodule.sh: rename unused variables
  git-submodule.sh: align error reporting for update mode to use path
This commit is contained in:
Junio C Hamano 2018-09-17 13:53:51 -07:00
Родитель 39006893f9 ee69b2a90c
Коммит 4d6d6ef1fc
2 изменённых файлов: 164 добавлений и 52 удалений

Просмотреть файл

@ -1443,6 +1443,72 @@ static int module_clone(int argc, const char **argv, const char *prefix)
return 0; return 0;
} }
static void determine_submodule_update_strategy(struct repository *r,
int just_cloned,
const char *path,
const char *update,
struct submodule_update_strategy *out)
{
const struct submodule *sub = submodule_from_path(r, &null_oid, path);
char *key;
const char *val;
key = xstrfmt("submodule.%s.update", sub->name);
if (update) {
trace_printf("parsing update");
if (parse_submodule_update_strategy(update, out) < 0)
die(_("Invalid update mode '%s' for submodule path '%s'"),
update, path);
} else if (!repo_config_get_string_const(r, key, &val)) {
if (parse_submodule_update_strategy(val, out) < 0)
die(_("Invalid update mode '%s' configured for submodule path '%s'"),
val, path);
} else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
trace_printf("loaded thing");
out->type = sub->update_strategy.type;
out->command = sub->update_strategy.command;
} else
out->type = SM_UPDATE_CHECKOUT;
if (just_cloned &&
(out->type == SM_UPDATE_MERGE ||
out->type == SM_UPDATE_REBASE ||
out->type == SM_UPDATE_NONE))
out->type = SM_UPDATE_CHECKOUT;
free(key);
}
static int module_update_module_mode(int argc, const char **argv, const char *prefix)
{
const char *path, *update = NULL;
int just_cloned;
struct submodule_update_strategy update_strategy = { .type = SM_UPDATE_CHECKOUT };
if (argc < 3 || argc > 4)
die("submodule--helper update-module-clone expects <just-cloned> <path> [<update>]");
just_cloned = git_config_int("just_cloned", argv[1]);
path = argv[2];
if (argc == 4)
update = argv[3];
determine_submodule_update_strategy(the_repository,
just_cloned, path, update,
&update_strategy);
fputs(submodule_strategy_to_string(&update_strategy), stdout);
return 0;
}
struct update_clone_data {
const struct submodule *sub;
struct object_id oid;
unsigned just_cloned;
};
struct submodule_update_clone { struct submodule_update_clone {
/* index into 'list', the list of submodules to look into for cloning */ /* index into 'list', the list of submodules to look into for cloning */
int current; int current;
@ -1462,8 +1528,9 @@ struct submodule_update_clone {
const char *recursive_prefix; const char *recursive_prefix;
const char *prefix; const char *prefix;
/* Machine-readable status lines to be consumed by git-submodule.sh */ /* to be consumed by git-submodule.sh */
struct string_list projectlines; struct update_clone_data *update_clone;
int update_clone_nr; int update_clone_alloc;
/* If we want to stop as fast as possible and return an error */ /* If we want to stop as fast as possible and return an error */
unsigned quickstop : 1; unsigned quickstop : 1;
@ -1471,11 +1538,13 @@ struct submodule_update_clone {
/* failed clones to be retried again */ /* failed clones to be retried again */
const struct cache_entry **failed_clones; const struct cache_entry **failed_clones;
int failed_clones_nr, failed_clones_alloc; int failed_clones_nr, failed_clones_alloc;
int max_jobs;
}; };
#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \ #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \ SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
NULL, NULL, NULL, \ NULL, NULL, NULL, \
STRING_LIST_INIT_DUP, 0, NULL, 0, 0} NULL, 0, 0, 0, NULL, 0, 0, 0}
static void next_submodule_warn_missing(struct submodule_update_clone *suc, static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@ -1569,11 +1638,12 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
strbuf_addf(&sb, "%s/.git", ce->name); strbuf_addf(&sb, "%s/.git", ce->name);
needs_cloning = !file_exists(sb.buf); needs_cloning = !file_exists(sb.buf);
strbuf_reset(&sb); ALLOC_GROW(suc->update_clone, suc->update_clone_nr + 1,
strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode, suc->update_clone_alloc);
oid_to_hex(&ce->oid), ce_stage(ce), oidcpy(&suc->update_clone[suc->update_clone_nr].oid, &ce->oid);
needs_cloning, ce->name); suc->update_clone[suc->update_clone_nr].just_cloned = needs_cloning;
string_list_append(&suc->projectlines, sb.buf); suc->update_clone[suc->update_clone_nr].sub = sub;
suc->update_clone_nr++;
if (!needs_cloning) if (!needs_cloning)
goto cleanup; goto cleanup;
@ -1714,11 +1784,44 @@ static int git_update_clone_config(const char *var, const char *value,
return 0; return 0;
} }
static void update_submodule(struct update_clone_data *ucd)
{
fprintf(stdout, "dummy %s %d\t%s\n",
oid_to_hex(&ucd->oid),
ucd->just_cloned,
ucd->sub->path);
}
static int update_submodules(struct submodule_update_clone *suc)
{
int i;
run_processes_parallel(suc->max_jobs,
update_clone_get_next_task,
update_clone_start_failure,
update_clone_task_finished,
suc);
/*
* We saved the output and put it out all at once now.
* That means:
* - the listener does not have to interleave their (checkout)
* work with our fetching. The writes involved in a
* checkout involve more straightforward sequential I/O.
* - the listener can avoid doing any work if fetching failed.
*/
if (suc->quickstop)
return 1;
for (i = 0; i < suc->update_clone_nr; i++)
update_submodule(&suc->update_clone[i]);
return 0;
}
static int update_clone(int argc, const char **argv, const char *prefix) static int update_clone(int argc, const char **argv, const char *prefix)
{ {
const char *update = NULL; const char *update = NULL;
int max_jobs = 1;
struct string_list_item *item;
struct pathspec pathspec; struct pathspec pathspec;
struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT; struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
@ -1740,7 +1843,7 @@ static int update_clone(int argc, const char **argv, const char *prefix)
OPT_STRING(0, "depth", &suc.depth, "<depth>", OPT_STRING(0, "depth", &suc.depth, "<depth>",
N_("Create a shallow clone truncated to the " N_("Create a shallow clone truncated to the "
"specified number of revisions")), "specified number of revisions")),
OPT_INTEGER('j', "jobs", &max_jobs, OPT_INTEGER('j', "jobs", &suc.max_jobs,
N_("parallel jobs")), N_("parallel jobs")),
OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow, OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
N_("whether the initial clone should follow the shallow recommendation")), N_("whether the initial clone should follow the shallow recommendation")),
@ -1756,8 +1859,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
}; };
suc.prefix = prefix; suc.prefix = prefix;
update_clone_config_from_gitmodules(&max_jobs); update_clone_config_from_gitmodules(&suc.max_jobs);
git_config(git_update_clone_config, &max_jobs); git_config(git_update_clone_config, &suc.max_jobs);
argc = parse_options(argc, argv, prefix, module_update_clone_options, argc = parse_options(argc, argv, prefix, module_update_clone_options,
git_submodule_helper_usage, 0); git_submodule_helper_usage, 0);
@ -1772,27 +1875,7 @@ static int update_clone(int argc, const char **argv, const char *prefix)
if (pathspec.nr) if (pathspec.nr)
suc.warn_if_uninitialized = 1; suc.warn_if_uninitialized = 1;
run_processes_parallel(max_jobs, return update_submodules(&suc);
update_clone_get_next_task,
update_clone_start_failure,
update_clone_task_finished,
&suc);
/*
* We saved the output and put it out all at once now.
* That means:
* - the listener does not have to interleave their (checkout)
* work with our fetching. The writes involved in a
* checkout involve more straightforward sequential I/O.
* - the listener can avoid doing any work if fetching failed.
*/
if (suc.quickstop)
return 1;
for_each_string_list_item(item, &suc.projectlines)
fprintf(stdout, "%s", item->string);
return 0;
} }
static int resolve_relative_path(int argc, const char **argv, const char *prefix) static int resolve_relative_path(int argc, const char **argv, const char *prefix)
@ -1938,6 +2021,45 @@ static int push_check(int argc, const char **argv, const char *prefix)
return 0; return 0;
} }
static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
{
const struct submodule *sub;
const char *path;
char *cw;
struct repository subrepo;
if (argc != 2)
BUG("submodule--helper connect-gitdir-workingtree <name> <path>");
path = argv[1];
sub = submodule_from_path(the_repository, &null_oid, path);
if (!sub)
BUG("We could get the submodule handle before?");
if (repo_submodule_init(&subrepo, the_repository, path))
die(_("could not get a repository handle for submodule '%s'"), path);
if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) {
char *cfg_file, *abs_path;
const char *rel_path;
struct strbuf sb = STRBUF_INIT;
cfg_file = repo_git_path(&subrepo, "config");
abs_path = absolute_pathdup(path);
rel_path = relative_path(abs_path, subrepo.gitdir, &sb);
git_config_set_in_file(cfg_file, "core.worktree", rel_path);
free(cfg_file);
free(abs_path);
strbuf_release(&sb);
}
return 0;
}
static int absorb_git_dirs(int argc, const char **argv, const char *prefix) static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
{ {
int i; int i;
@ -2015,7 +2137,9 @@ static struct cmd_struct commands[] = {
{"list", module_list, 0}, {"list", module_list, 0},
{"name", module_name, 0}, {"name", module_name, 0},
{"clone", module_clone, 0}, {"clone", module_clone, 0},
{"update-module-mode", module_update_module_mode, 0},
{"update-clone", update_clone, 0}, {"update-clone", update_clone, 0},
{"ensure-core-worktree", ensure_core_worktree, 0},
{"relative-path", resolve_relative_path, 0}, {"relative-path", resolve_relative_path, 0},
{"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url", resolve_relative_url, 0},
{"resolve-relative-url-test", resolve_relative_url_test, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0},

Просмотреть файл

@ -534,31 +534,19 @@ cmd_update()
"$@" || echo "#unmatched" $? "$@" || echo "#unmatched" $?
} | { } | {
err= err=
while read -r mode sha1 stage just_cloned sm_path while read -r quickabort sha1 just_cloned sm_path
do do
die_if_unmatched "$mode" "$sha1" die_if_unmatched "$quickabort" "$sha1"
name=$(git submodule--helper name "$sm_path") || exit git submodule--helper ensure-core-worktree "$sm_path"
if ! test -z "$update"
then update_module=$(git submodule--helper update-module-mode $just_cloned "$sm_path" $update)
update_module=$update
else
update_module=$(git config submodule."$name".update)
if test -z "$update_module"
then
update_module="checkout"
fi
fi
displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix") displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
if test $just_cloned -eq 1 if test $just_cloned -eq 1
then then
subsha1= subsha1=
case "$update_module" in
merge | rebase | none)
update_module=checkout ;;
esac
else else
subsha1=$(sanitize_submodule_env; cd "$sm_path" && subsha1=$(sanitize_submodule_env; cd "$sm_path" &&
git rev-parse --verify HEAD) || git rev-parse --verify HEAD) ||
@ -630,7 +618,7 @@ cmd_update()
must_die_on_failure=yes must_die_on_failure=yes
;; ;;
*) *)
die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")" die "$(eval_gettext "Invalid update mode '$update_module' for submodule path '$path'")"
esac esac
if (sanitize_submodule_env; cd "$sm_path" && $command "$sha1") if (sanitize_submodule_env; cd "$sm_path" && $command "$sha1")