update submodules: add submodule_move_head
In later patches we introduce the options and flag for commands that modify the working directory, e.g. git-checkout. This piece of code will be used universally for all these working tree modifications as it * supports dry run to answer the question: "Is it safe to change the submodule to this new state?" e.g. is it overwriting untracked files or are there local changes that would be overwritten? * supports a force flag that can be used for resetting the tree. Signed-off-by: Stefan Beller <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
202275b96b
commit
6e3c1595c6
137
submodule.c
137
submodule.c
@ -1247,6 +1247,143 @@ static const char *get_super_prefix_or_empty(void)
|
||||
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)
|
||||
{
|
||||
|
@ -96,6 +96,13 @@ extern int push_unpushed_submodules(struct sha1_array *commits,
|
||||
extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
|
||||
extern int parallel_submodules(void);
|
||||
|
||||
#define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
|
||||
#define SUBMODULE_MOVE_HEAD_FORCE (1<<1)
|
||||
extern int submodule_move_head(const char *path,
|
||||
const char *old,
|
||||
const char *new,
|
||||
unsigned flags);
|
||||
|
||||
/*
|
||||
* Prepare the "env_array" parameter of a "struct child_process" for executing
|
||||
* a submodule by clearing any repo-specific envirionment variables, but
|
||||
|
Loading…
Reference in New Issue
Block a user