Merge branch 'sb/checkout-recurse-submodules'
"git checkout" is taught the "--recurse-submodules" option. * sb/checkout-recurse-submodules: builtin/read-tree: add --recurse-submodules switch builtin/checkout: add --recurse-submodules switch entry.c: create submodules when interesting unpack-trees: check if we can perform the operation for submodules unpack-trees: pass old oid to verify_clean_submodule update submodules: add submodule_move_head submodule.c: get_super_prefix_or_empty update submodules: move up prepare_submodule_repo_env submodules: introduce check to see whether to touch a submodule update submodules: add a config option to determine if submodules are updated update submodules: add submodule config parsing make is_submodule_populated gently lib-submodule-update.sh: define tests for recursing into submodules lib-submodule-update.sh: replace sha1 by hash lib-submodule-update: teach test_submodule_content the -C <dir> flag lib-submodule-update.sh: do not use ./. as submodule remote lib-submodule-update.sh: reorder create_lib_submodule_repo submodule--helper.c: remove duplicate code connect_work_tree_and_git_dir: safely create leading directories
This commit is contained in:
222
submodule.c
222
submodule.c
@ -17,6 +17,7 @@
|
||||
#include "worktree.h"
|
||||
|
||||
static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
|
||||
static int config_update_recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
|
||||
static int parallel_jobs = 1;
|
||||
static struct string_list changed_submodule_paths = STRING_LIST_INIT_NODUP;
|
||||
static int initialized_fetch_ref_tips;
|
||||
@ -234,15 +235,12 @@ int is_submodule_initialized(const char *path)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if a submodule has been populated at a given 'path'
|
||||
*/
|
||||
int is_submodule_populated(const char *path)
|
||||
int is_submodule_populated_gently(const char *path, int *return_error_code)
|
||||
{
|
||||
int ret = 0;
|
||||
char *gitdir = xstrfmt("%s/.git", path);
|
||||
|
||||
if (resolve_gitdir(gitdir))
|
||||
if (resolve_gitdir_gently(gitdir, return_error_code))
|
||||
ret = 1;
|
||||
|
||||
free(gitdir);
|
||||
@ -358,6 +356,23 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
|
||||
strbuf_release(&sb);
|
||||
}
|
||||
|
||||
static void prepare_submodule_repo_env_no_git_dir(struct argv_array *out)
|
||||
{
|
||||
const char * const *var;
|
||||
|
||||
for (var = local_repo_env; *var; var++) {
|
||||
if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
|
||||
argv_array_push(out, *var);
|
||||
}
|
||||
}
|
||||
|
||||
void prepare_submodule_repo_env(struct argv_array *out)
|
||||
{
|
||||
prepare_submodule_repo_env_no_git_dir(out);
|
||||
argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
|
||||
DEFAULT_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
|
||||
@ -545,6 +560,27 @@ void set_config_fetch_recurse_submodules(int value)
|
||||
config_fetch_recurse_submodules = value;
|
||||
}
|
||||
|
||||
void set_config_update_recurse_submodules(int value)
|
||||
{
|
||||
config_update_recurse_submodules = value;
|
||||
}
|
||||
|
||||
int should_update_submodules(void)
|
||||
{
|
||||
return config_update_recurse_submodules == RECURSE_SUBMODULES_ON;
|
||||
}
|
||||
|
||||
const struct submodule *submodule_from_ce(const struct cache_entry *ce)
|
||||
{
|
||||
if (!S_ISGITLINK(ce->ce_mode))
|
||||
return NULL;
|
||||
|
||||
if (!should_update_submodules())
|
||||
return NULL;
|
||||
|
||||
return submodule_from_path(null_sha1, ce->name);
|
||||
}
|
||||
|
||||
static int has_remote(const char *refname, const struct object_id *oid,
|
||||
int flags, void *cb_data)
|
||||
{
|
||||
@ -1203,6 +1239,151 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *get_super_prefix_or_empty(void)
|
||||
{
|
||||
const char *s = get_super_prefix();
|
||||
if (!s)
|
||||
s = "";
|
||||
return s;
|
||||
}
|
||||
|
||||
static int submodule_has_dirty_index(const struct submodule *sub)
|
||||
{
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
|
||||
prepare_submodule_repo_env_no_git_dir(&cp.env_array);
|
||||
|
||||
cp.git_cmd = 1;
|
||||
argv_array_pushl(&cp.args, "diff-index", "--quiet",
|
||||
"--cached", "HEAD", NULL);
|
||||
cp.no_stdin = 1;
|
||||
cp.no_stdout = 1;
|
||||
cp.dir = sub->path;
|
||||
if (start_command(&cp))
|
||||
die("could not recurse into submodule '%s'", sub->path);
|
||||
|
||||
return finish_command(&cp);
|
||||
}
|
||||
|
||||
static void submodule_reset_index(const char *path)
|
||||
{
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
prepare_submodule_repo_env_no_git_dir(&cp.env_array);
|
||||
|
||||
cp.git_cmd = 1;
|
||||
cp.no_stdin = 1;
|
||||
cp.dir = path;
|
||||
|
||||
argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
|
||||
get_super_prefix_or_empty(), path);
|
||||
argv_array_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
|
||||
|
||||
argv_array_push(&cp.args, EMPTY_TREE_SHA1_HEX);
|
||||
|
||||
if (run_command(&cp))
|
||||
die("could not reset submodule index");
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a submodule at a given path from a given head to another new head.
|
||||
* For edge cases (a submodule coming into existence or removing a submodule)
|
||||
* pass NULL for old or new respectively.
|
||||
*/
|
||||
int submodule_move_head(const char *path,
|
||||
const char *old,
|
||||
const char *new,
|
||||
unsigned flags)
|
||||
{
|
||||
int ret = 0;
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
const struct submodule *sub;
|
||||
|
||||
sub = submodule_from_path(null_sha1, path);
|
||||
|
||||
if (!sub)
|
||||
die("BUG: could not get submodule information for '%s'", path);
|
||||
|
||||
if (old && !(flags & SUBMODULE_MOVE_HEAD_FORCE)) {
|
||||
/* Check if the submodule has a dirty index. */
|
||||
if (submodule_has_dirty_index(sub))
|
||||
return error(_("submodule '%s' has dirty index"), path);
|
||||
}
|
||||
|
||||
if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
|
||||
if (old) {
|
||||
if (!submodule_uses_gitfile(path))
|
||||
absorb_git_dir_into_superproject("", path,
|
||||
ABSORB_GITDIR_RECURSE_SUBMODULES);
|
||||
} else {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
strbuf_addf(&sb, "%s/modules/%s",
|
||||
get_git_common_dir(), sub->name);
|
||||
connect_work_tree_and_git_dir(path, sb.buf);
|
||||
strbuf_release(&sb);
|
||||
|
||||
/* make sure the index is clean as well */
|
||||
submodule_reset_index(path);
|
||||
}
|
||||
}
|
||||
|
||||
prepare_submodule_repo_env_no_git_dir(&cp.env_array);
|
||||
|
||||
cp.git_cmd = 1;
|
||||
cp.no_stdin = 1;
|
||||
cp.dir = path;
|
||||
|
||||
argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
|
||||
get_super_prefix_or_empty(), path);
|
||||
argv_array_pushl(&cp.args, "read-tree", NULL);
|
||||
|
||||
if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
|
||||
argv_array_push(&cp.args, "-n");
|
||||
else
|
||||
argv_array_push(&cp.args, "-u");
|
||||
|
||||
if (flags & SUBMODULE_MOVE_HEAD_FORCE)
|
||||
argv_array_push(&cp.args, "--reset");
|
||||
else
|
||||
argv_array_push(&cp.args, "-m");
|
||||
|
||||
argv_array_push(&cp.args, old ? old : EMPTY_TREE_SHA1_HEX);
|
||||
argv_array_push(&cp.args, new ? new : EMPTY_TREE_SHA1_HEX);
|
||||
|
||||
if (run_command(&cp)) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
|
||||
if (new) {
|
||||
struct child_process cp1 = CHILD_PROCESS_INIT;
|
||||
/* also set the HEAD accordingly */
|
||||
cp1.git_cmd = 1;
|
||||
cp1.no_stdin = 1;
|
||||
cp1.dir = path;
|
||||
|
||||
argv_array_pushl(&cp1.args, "update-ref", "HEAD",
|
||||
new ? new : EMPTY_TREE_SHA1_HEX, NULL);
|
||||
|
||||
if (run_command(&cp1)) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
strbuf_addf(&sb, "%s/.git", path);
|
||||
unlink_or_warn(sb.buf);
|
||||
strbuf_release(&sb);
|
||||
|
||||
if (is_empty_dir(path))
|
||||
rmdir_or_warn(path);
|
||||
}
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int find_first_merges(struct object_array *result, const char *path,
|
||||
struct commit *a, struct commit *b)
|
||||
{
|
||||
@ -1371,18 +1552,6 @@ int parallel_submodules(void)
|
||||
return parallel_jobs;
|
||||
}
|
||||
|
||||
void prepare_submodule_repo_env(struct argv_array *out)
|
||||
{
|
||||
const char * const *var;
|
||||
|
||||
for (var = local_repo_env; *var; var++) {
|
||||
if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
|
||||
argv_array_push(out, *var);
|
||||
}
|
||||
argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
|
||||
DEFAULT_GIT_DIR_ENVIRONMENT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Embeds a single submodules git directory into the superprojects git dir,
|
||||
* non recursively.
|
||||
@ -1414,11 +1583,8 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
|
||||
die(_("could not create directory '%s'"), new_git_dir);
|
||||
real_new_git_dir = real_pathdup(new_git_dir, 1);
|
||||
|
||||
if (!prefix)
|
||||
prefix = get_super_prefix();
|
||||
|
||||
fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
|
||||
prefix ? prefix : "", path,
|
||||
get_super_prefix_or_empty(), path,
|
||||
real_old_git_dir, real_new_git_dir);
|
||||
|
||||
relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
|
||||
@ -1445,8 +1611,6 @@ void absorb_git_dir_into_superproject(const char *prefix,
|
||||
|
||||
/* Not populated? */
|
||||
if (!sub_git_dir) {
|
||||
char *real_new_git_dir;
|
||||
const char *new_git_dir;
|
||||
const struct submodule *sub;
|
||||
|
||||
if (err_code == READ_GITFILE_ERR_STAT_FAILED) {
|
||||
@ -1469,13 +1633,8 @@ void absorb_git_dir_into_superproject(const char *prefix,
|
||||
sub = submodule_from_path(null_sha1, path);
|
||||
if (!sub)
|
||||
die(_("could not lookup name for submodule '%s'"), path);
|
||||
new_git_dir = git_path("modules/%s", sub->name);
|
||||
if (safe_create_leading_directories_const(new_git_dir) < 0)
|
||||
die(_("could not create directory '%s'"), new_git_dir);
|
||||
real_new_git_dir = real_pathdup(new_git_dir, 1);
|
||||
connect_work_tree_and_git_dir(path, real_new_git_dir);
|
||||
|
||||
free(real_new_git_dir);
|
||||
connect_work_tree_and_git_dir(path,
|
||||
git_path("modules/%s", sub->name));
|
||||
} else {
|
||||
/* Is it already absorbed into the superprojects git dir? */
|
||||
char *real_sub_git_dir = real_pathdup(sub_git_dir, 1);
|
||||
@ -1496,8 +1655,7 @@ void absorb_git_dir_into_superproject(const char *prefix,
|
||||
if (flags & ~ABSORB_GITDIR_RECURSE_SUBMODULES)
|
||||
die("BUG: we don't know how to pass the flags down?");
|
||||
|
||||
if (get_super_prefix())
|
||||
strbuf_addstr(&sb, get_super_prefix());
|
||||
strbuf_addstr(&sb, get_super_prefix_or_empty());
|
||||
strbuf_addstr(&sb, path);
|
||||
strbuf_addch(&sb, '/');
|
||||
|
||||
|
Reference in New Issue
Block a user