@ -25,7 +25,6 @@
@@ -25,7 +25,6 @@
#include "commit-reach.h"
static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
static struct string_list changed_submodule_names = STRING_LIST_INIT_DUP;
static int initialized_fetch_ref_tips;
static struct oid_array ref_tips_before_fetch;
static struct oid_array ref_tips_after_fetch;
@ -495,6 +494,12 @@ void prepare_submodule_repo_env(struct argv_array *out)
@@ -495,6 +494,12 @@ void prepare_submodule_repo_env(struct argv_array *out)
DEFAULT_GIT_DIR_ENVIRONMENT);
}
static void prepare_submodule_repo_env_in_gitdir(struct argv_array *out)
{
prepare_submodule_repo_env_no_git_dir(out);
argv_array_pushf(out, "%s=.", GIT_DIR_ENVIRONMENT);
}
/* Helper function to display the submodule header line prior to the full
* summary output. If it can locate the submodule objects directory it will
* attempt to lookup both the left and right commits and put them into the
@ -1136,11 +1141,11 @@ void check_for_new_submodule_commits(struct object_id *oid)
@@ -1136,11 +1141,11 @@ void check_for_new_submodule_commits(struct object_id *oid)
oid_array_append(&ref_tips_after_fetch, oid);
}
static void calculate_changed_submodule_paths(struct repository *r)
static void calculate_changed_submodule_paths(struct repository *r,
struct string_list *changed_submodule_names)
{
struct argv_array argv = ARGV_ARRAY_INIT;
struct string_list changed_submodules = STRING_LIST_INIT_DUP;
const struct string_list_item *name;
struct string_list_item *name;
/* No need to check if there are no submodules configured */
if (!submodule_from_path(r, NULL, NULL))
@ -1157,9 +1162,9 @@ static void calculate_changed_submodule_paths(struct repository *r)
@@ -1157,9 +1162,9 @@ static void calculate_changed_submodule_paths(struct repository *r)
* Collect all submodules (whether checked out or not) for which new
* commits have been recorded upstream in "changed_submodule_names".
*/
collect_changed_submodules(r, &changed_submodules, &argv);
collect_changed_submodules(r, changed_submodule_names, &argv);
for_each_string_list_item(name, &changed_submodules) {
for_each_string_list_item(name, changed_submodule_names) {
struct oid_array *commits = name->util;
const struct submodule *submodule;
const char *path = NULL;
@ -1173,11 +1178,14 @@ static void calculate_changed_submodule_paths(struct repository *r)
@@ -1173,11 +1178,14 @@ static void calculate_changed_submodule_paths(struct repository *r)
if (!path)
continue;
if (!submodule_has_commits(r, path, commits))
string_list_append(&changed_submodule_names, name->string);
if (submodule_has_commits(r, path, commits)) {
oid_array_clear(commits);
*name->string = '\0';
}
}
free_submodules_oids(&changed_submodules);
string_list_remove_empty_items(changed_submodule_names, 1);
argv_array_clear(&argv);
oid_array_clear(&ref_tips_before_fetch);
oid_array_clear(&ref_tips_after_fetch);
@ -1221,8 +1229,16 @@ struct submodule_parallel_fetch {
@@ -1221,8 +1229,16 @@ struct submodule_parallel_fetch {
int default_option;
int quiet;
int result;
struct string_list changed_submodule_names;
/* Pending fetches by OIDs */
struct fetch_task **oid_fetch_tasks;
int oid_fetch_tasks_nr, oid_fetch_tasks_alloc;
};
#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0}
#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0, \
STRING_LIST_INIT_DUP, \
NULL, 0, 0}
static int get_fetch_recurse_config(const struct submodule *submodule,
struct submodule_parallel_fetch *spf)
@ -1249,40 +1265,126 @@ static int get_fetch_recurse_config(const struct submodule *submodule,
@@ -1249,40 +1265,126 @@ static int get_fetch_recurse_config(const struct submodule *submodule,
return spf->default_option;
}
/*
* Fetch in progress (if callback data) or
* pending (if in oid_fetch_tasks in struct submodule_parallel_fetch)
*/
struct fetch_task {
struct repository *repo;
const struct submodule *sub;
unsigned free_sub : 1; /* Do we need to free the submodule? */
struct oid_array *commits; /* Ensure these commits are fetched */
};
/**
* When a submodule is not defined in .gitmodules, we cannot access it
* via the regular submodule-config. Create a fake submodule, which we can
* work on.
*/
static const struct submodule *get_non_gitmodules_submodule(const char *path)
{
struct submodule *ret = NULL;
const char *name = default_name_or_path(path);
if (!name)
return NULL;
ret = xmalloc(sizeof(*ret));
memset(ret, 0, sizeof(*ret));
ret->path = name;
ret->name = name;
return (const struct submodule *) ret;
}
static struct fetch_task *fetch_task_create(struct repository *r,
const char *path)
{
struct fetch_task *task = xmalloc(sizeof(*task));
memset(task, 0, sizeof(*task));
task->sub = submodule_from_path(r, &null_oid, path);
if (!task->sub) {
/*
* No entry in .gitmodules? Technically not a submodule,
* but historically we supported repositories that happen to be
* in-place where a gitlink is. Keep supporting them.
*/
task->sub = get_non_gitmodules_submodule(path);
if (!task->sub) {
free(task);
return NULL;
}
task->free_sub = 1;
}
return task;
}
static void fetch_task_release(struct fetch_task *p)
{
if (p->free_sub)
free((void*)p->sub);
p->free_sub = 0;
p->sub = NULL;
if (p->repo)
repo_clear(p->repo);
FREE_AND_NULL(p->repo);
}
static struct repository *get_submodule_repo_for(struct repository *r,
const struct submodule *sub)
{
struct repository *ret = xmalloc(sizeof(*ret));
if (repo_submodule_init(ret, r, sub)) {
/*
* No entry in .gitmodules? Technically not a submodule,
* but historically we supported repositories that happen to be
* in-place where a gitlink is. Keep supporting them.
*/
struct strbuf gitdir = STRBUF_INIT;
strbuf_repo_worktree_path(&gitdir, r, "%s/.git", sub->path);
if (repo_init(ret, gitdir.buf, NULL)) {
strbuf_release(&gitdir);
free(ret);
return NULL;
}
strbuf_release(&gitdir);
}
return ret;
}
static int get_next_submodule(struct child_process *cp,
struct strbuf *err, void *data, void **task_cb)
{
int ret = 0;
struct submodule_parallel_fetch *spf = data;
for (; spf->count < spf->r->index->cache_nr; spf->count++) {
struct strbuf submodule_path = STRBUF_INIT;
struct strbuf submodule_git_dir = STRBUF_INIT;
struct strbuf submodule_prefix = STRBUF_INIT;
const struct cache_entry *ce = spf->r->index->cache[spf->count];
const char *git_dir, *default_argv;
const struct submodule *submodule;
struct submodule default_submodule = SUBMODULE_INIT;
const char *default_argv;
struct fetch_task *task;
if (!S_ISGITLINK(ce->ce_mode))
continue;
submodule = submodule_from_path(spf->r, &null_oid, ce->name);
if (!submodule) {
const char *name = default_name_or_path(ce->name);
if (name) {
default_submodule.path = default_submodule.name = name;
submodule = &default_submodule;
}
}
task = fetch_task_create(spf->r, ce->name);
if (!task)
continue;
switch (get_fetch_recurse_config(submodule, spf))
switch (get_fetch_recurse_config(task->sub, spf))
{
default:
case RECURSE_SUBMODULES_DEFAULT:
case RECURSE_SUBMODULES_ON_DEMAND:
if (!submodule || !unsorted_string_list_lookup(&changed_submodule_names,
submodule->name))
if (!task->sub ||
!string_list_lookup(
&spf->changed_submodule_names,
task->sub->name))
continue;
default_argv = "on-demand";
break;
@ -1293,16 +1395,12 @@ static int get_next_submodule(struct child_process *cp,
@@ -1293,16 +1395,12 @@ static int get_next_submodule(struct child_process *cp,
continue;
}
strbuf_repo_worktree_path(&submodule_path, spf->r, "%s", ce->name);
strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf);
strbuf_addf(&submodule_prefix, "%s%s/", spf->prefix, ce->name);
git_dir = read_gitfile(submodule_git_dir.buf);
if (!git_dir)
git_dir = submodule_git_dir.buf;
if (is_directory(git_dir)) {
task->repo = get_submodule_repo_for(spf->r, task->sub);
if (task->repo) {
struct strbuf submodule_prefix = STRBUF_INIT;
child_process_init(cp);
cp->dir = strbuf_detach(&submodule_path, NULL);
prepare_submodule_repo_env(&cp->env_array);
cp->dir = task->repo->gitdir;
prepare_submodule_repo_env_in_gitdir(&cp->env_array);
cp->git_cmd = 1;
if (!spf->quiet)
strbuf_addf(err, "Fetching submodule %s%s\n",
@ -1311,17 +1409,66 @@ static int get_next_submodule(struct child_process *cp,
@@ -1311,17 +1409,66 @@ static int get_next_submodule(struct child_process *cp,
argv_array_pushv(&cp->args, spf->args.argv);
argv_array_push(&cp->args, default_argv);
argv_array_push(&cp->args, "--submodule-prefix");
strbuf_addf(&submodule_prefix, "%s%s/",
spf->prefix,
task->sub->path);
argv_array_push(&cp->args, submodule_prefix.buf);
ret = 1;
}
strbuf_release(&submodule_path);
strbuf_release(&submodule_git_dir);
strbuf_release(&submodule_prefix);
if (ret) {
spf->count++;
*task_cb = task;
strbuf_release(&submodule_prefix);
return 1;
} else {
fetch_task_release(task);
free(task);
/*
* An empty directory is normal,
* the submodule is not initialized
*/
if (S_ISGITLINK(ce->ce_mode) &&
!is_empty_dir(ce->name)) {
spf->result = 1;
strbuf_addf(err,
_("Could not access submodule '%s'"),
ce->name);
}
}
}
if (spf->oid_fetch_tasks_nr) {
struct fetch_task *task =
spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr - 1];
struct strbuf submodule_prefix = STRBUF_INIT;
spf->oid_fetch_tasks_nr--;
strbuf_addf(&submodule_prefix, "%s%s/",
spf->prefix, task->sub->path);
child_process_init(cp);
prepare_submodule_repo_env_in_gitdir(&cp->env_array);
cp->git_cmd = 1;
cp->dir = task->repo->gitdir;
argv_array_init(&cp->args);
argv_array_pushv(&cp->args, spf->args.argv);
argv_array_push(&cp->args, "on-demand");
argv_array_push(&cp->args, "--submodule-prefix");
argv_array_push(&cp->args, submodule_prefix.buf);
/* NEEDSWORK: have get_default_remote from submodule--helper */
argv_array_push(&cp->args, "origin");
oid_array_for_each_unique(task->commits,
append_oid_to_argv, &cp->args);
*task_cb = task;
strbuf_release(&submodule_prefix);
return 1;
}
return 0;
}
@ -1329,20 +1476,66 @@ static int fetch_start_failure(struct strbuf *err,
@@ -1329,20 +1476,66 @@ static int fetch_start_failure(struct strbuf *err,
void *cb, void *task_cb)
{
struct submodule_parallel_fetch *spf = cb;
struct fetch_task *task = task_cb;
spf->result = 1;
fetch_task_release(task);
return 0;
}
static int commit_missing_in_sub(const struct object_id *oid, void *data)
{
struct repository *subrepo = data;
enum object_type type = oid_object_info(subrepo, oid, NULL);
return type != OBJ_COMMIT;
}
static int fetch_finish(int retvalue, struct strbuf *err,
void *cb, void *task_cb)
{
struct submodule_parallel_fetch *spf = cb;
struct fetch_task *task = task_cb;
struct string_list_item *it;
struct oid_array *commits;
if (retvalue)
spf->result = 1;
if (!task || !task->sub)
BUG("callback cookie bogus");
/* Is this the second time we process this submodule? */
if (task->commits)
goto out;
it = string_list_lookup(&spf->changed_submodule_names, task->sub->name);
if (!it)
/* Could be an unchanged submodule, not contained in the list */
goto out;
commits = it->util;
oid_array_filter(commits,
commit_missing_in_sub,
task->repo);
/* Are there commits we want, but do not exist? */
if (commits->nr) {
task->commits = commits;
ALLOC_GROW(spf->oid_fetch_tasks,
spf->oid_fetch_tasks_nr + 1,
spf->oid_fetch_tasks_alloc);
spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr] = task;
spf->oid_fetch_tasks_nr++;
return 0;
}
out:
fetch_task_release(task);
return 0;
}
@ -1373,7 +1566,8 @@ int fetch_populated_submodules(struct repository *r,
@@ -1373,7 +1566,8 @@ int fetch_populated_submodules(struct repository *r,
argv_array_push(&spf.args, "--recurse-submodules-default");
/* default value, "--submodule-prefix" and its value are added later */
calculate_changed_submodule_paths(r);
calculate_changed_submodule_paths(r, &spf.changed_submodule_names);
string_list_sort(&spf.changed_submodule_names);
run_processes_parallel(max_parallel_jobs,
get_next_submodule,
fetch_start_failure,
@ -1382,7 +1576,7 @@ int fetch_populated_submodules(struct repository *r,
@@ -1382,7 +1576,7 @@ int fetch_populated_submodules(struct repository *r,
argv_array_clear(&spf.args);
out:
string_list_clear(&changed_submodule_names, 1);
free_submodules_oids(&spf.changed_submodule_names);
return spf.result;
}