Merge branch 'sb/submodule-embed-gitdir'
A new submodule helper "git submodule embedgitdirs" to make it easier to move embedded .git/ directory for submodules in a superproject to .git/modules/ (and point the latter with the former that is turned into a "gitdir:" file) has been added. * sb/submodule-embed-gitdir: worktree: initialize return value for submodule_uses_worktrees submodule: add absorb-git-dir function move connect_work_tree_and_git_dir to dir.h worktree: check if a submodule uses worktrees test-lib-functions.sh: teach test_commit -C <dir> submodule helper: support super prefix submodule: use absolute path for computing relative path connecting
This commit is contained in:
commit
da2b74eeec
@ -22,6 +22,7 @@ SYNOPSIS
|
|||||||
[commit] [--] [<path>...]
|
[commit] [--] [<path>...]
|
||||||
'git submodule' [--quiet] foreach [--recursive] <command>
|
'git submodule' [--quiet] foreach [--recursive] <command>
|
||||||
'git submodule' [--quiet] sync [--recursive] [--] [<path>...]
|
'git submodule' [--quiet] sync [--recursive] [--] [<path>...]
|
||||||
|
'git submodule' [--quiet] absorbgitdirs [--] [<path>...]
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
@ -245,6 +246,20 @@ sync::
|
|||||||
If `--recursive` is specified, this command will recurse into the
|
If `--recursive` is specified, this command will recurse into the
|
||||||
registered submodules, and sync any nested submodules within.
|
registered submodules, and sync any nested submodules within.
|
||||||
|
|
||||||
|
absorbgitdirs::
|
||||||
|
If a git directory of a submodule is inside the submodule,
|
||||||
|
move the git directory of the submodule into its superprojects
|
||||||
|
`$GIT_DIR/modules` path and then connect the git directory and
|
||||||
|
its working directory by setting the `core.worktree` and adding
|
||||||
|
a .git file pointing to the git directory embedded in the
|
||||||
|
superprojects git directory.
|
||||||
|
+
|
||||||
|
A repository that was cloned independently and later added as a submodule or
|
||||||
|
old setups have the submodules git directory inside the submodule instead of
|
||||||
|
embedded into the superprojects git directory.
|
||||||
|
+
|
||||||
|
This command is recursive by default.
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
-q::
|
-q::
|
||||||
|
@ -1091,21 +1091,62 @@ static int resolve_remote_submodule_branch(int argc, const char **argv,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct pathspec pathspec;
|
||||||
|
struct module_list list = MODULE_LIST_INIT;
|
||||||
|
unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
|
||||||
|
|
||||||
|
struct option embed_gitdir_options[] = {
|
||||||
|
OPT_STRING(0, "prefix", &prefix,
|
||||||
|
N_("path"),
|
||||||
|
N_("path into the working tree")),
|
||||||
|
OPT_BIT(0, "--recursive", &flags, N_("recurse into submodules"),
|
||||||
|
ABSORB_GITDIR_RECURSE_SUBMODULES),
|
||||||
|
OPT_END()
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *const git_submodule_helper_usage[] = {
|
||||||
|
N_("git submodule--helper embed-git-dir [<path>...]"),
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
argc = parse_options(argc, argv, prefix, embed_gitdir_options,
|
||||||
|
git_submodule_helper_usage, 0);
|
||||||
|
|
||||||
|
gitmodules_config();
|
||||||
|
git_config(submodule_config, NULL);
|
||||||
|
|
||||||
|
if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
for (i = 0; i < list.nr; i++)
|
||||||
|
absorb_git_dir_into_superproject(prefix,
|
||||||
|
list.entries[i]->name, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SUPPORT_SUPER_PREFIX (1<<0)
|
||||||
|
|
||||||
struct cmd_struct {
|
struct cmd_struct {
|
||||||
const char *cmd;
|
const char *cmd;
|
||||||
int (*fn)(int, const char **, const char *);
|
int (*fn)(int, const char **, const char *);
|
||||||
|
unsigned option;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct cmd_struct commands[] = {
|
static struct cmd_struct commands[] = {
|
||||||
{"list", module_list},
|
{"list", module_list, 0},
|
||||||
{"name", module_name},
|
{"name", module_name, 0},
|
||||||
{"clone", module_clone},
|
{"clone", module_clone, 0},
|
||||||
{"update-clone", update_clone},
|
{"update-clone", update_clone, 0},
|
||||||
{"relative-path", resolve_relative_path},
|
{"relative-path", resolve_relative_path, 0},
|
||||||
{"resolve-relative-url", resolve_relative_url},
|
{"resolve-relative-url", resolve_relative_url, 0},
|
||||||
{"resolve-relative-url-test", resolve_relative_url_test},
|
{"resolve-relative-url-test", resolve_relative_url_test, 0},
|
||||||
{"init", module_init},
|
{"init", module_init, 0},
|
||||||
{"remote-branch", resolve_remote_submodule_branch}
|
{"remote-branch", resolve_remote_submodule_branch, 0},
|
||||||
|
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
|
||||||
};
|
};
|
||||||
|
|
||||||
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
|
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
|
||||||
@ -1115,9 +1156,15 @@ int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
|
|||||||
die(_("submodule--helper subcommand must be "
|
die(_("submodule--helper subcommand must be "
|
||||||
"called with a subcommand"));
|
"called with a subcommand"));
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(commands); i++)
|
for (i = 0; i < ARRAY_SIZE(commands); i++) {
|
||||||
if (!strcmp(argv[1], commands[i].cmd))
|
if (!strcmp(argv[1], commands[i].cmd)) {
|
||||||
|
if (get_super_prefix() &&
|
||||||
|
!(commands[i].option & SUPPORT_SUPER_PREFIX))
|
||||||
|
die(_("%s doesn't support --super-prefix"),
|
||||||
|
commands[i].cmd);
|
||||||
return commands[i].fn(argc - 1, argv + 1, prefix);
|
return commands[i].fn(argc - 1, argv + 1, prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
die(_("'%s' is not a valid submodule--helper "
|
die(_("'%s' is not a valid submodule--helper "
|
||||||
"subcommand"), argv[1]);
|
"subcommand"), argv[1]);
|
||||||
|
37
dir.c
37
dir.c
@ -2748,3 +2748,40 @@ void untracked_cache_add_to_index(struct index_state *istate,
|
|||||||
{
|
{
|
||||||
untracked_cache_invalidate_path(istate, path);
|
untracked_cache_invalidate_path(istate, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update gitfile and core.worktree setting to connect work tree and git dir */
|
||||||
|
void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
|
||||||
|
{
|
||||||
|
struct strbuf file_name = STRBUF_INIT;
|
||||||
|
struct strbuf rel_path = STRBUF_INIT;
|
||||||
|
char *git_dir = xstrdup(real_path(git_dir_));
|
||||||
|
char *work_tree = xstrdup(real_path(work_tree_));
|
||||||
|
|
||||||
|
/* Update gitfile */
|
||||||
|
strbuf_addf(&file_name, "%s/.git", work_tree);
|
||||||
|
write_file(file_name.buf, "gitdir: %s",
|
||||||
|
relative_path(git_dir, work_tree, &rel_path));
|
||||||
|
|
||||||
|
/* Update core.worktree setting */
|
||||||
|
strbuf_reset(&file_name);
|
||||||
|
strbuf_addf(&file_name, "%s/config", git_dir);
|
||||||
|
git_config_set_in_file(file_name.buf, "core.worktree",
|
||||||
|
relative_path(work_tree, git_dir, &rel_path));
|
||||||
|
|
||||||
|
strbuf_release(&file_name);
|
||||||
|
strbuf_release(&rel_path);
|
||||||
|
free(work_tree);
|
||||||
|
free(git_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Migrate the git directory of the given path from old_git_dir to new_git_dir.
|
||||||
|
*/
|
||||||
|
void relocate_gitdir(const char *path, const char *old_git_dir, const char *new_git_dir)
|
||||||
|
{
|
||||||
|
if (rename(old_git_dir, new_git_dir) < 0)
|
||||||
|
die_errno(_("could not migrate git directory from '%s' to '%s'"),
|
||||||
|
old_git_dir, new_git_dir);
|
||||||
|
|
||||||
|
connect_work_tree_and_git_dir(path, new_git_dir);
|
||||||
|
}
|
||||||
|
4
dir.h
4
dir.h
@ -335,4 +335,8 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
|
|||||||
void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked);
|
void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked);
|
||||||
void add_untracked_cache(struct index_state *istate);
|
void add_untracked_cache(struct index_state *istate);
|
||||||
void remove_untracked_cache(struct index_state *istate);
|
void remove_untracked_cache(struct index_state *istate);
|
||||||
|
extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
|
||||||
|
extern void relocate_gitdir(const char *path,
|
||||||
|
const char *old_git_dir,
|
||||||
|
const char *new_git_dir);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1127,6 +1127,11 @@ cmd_sync()
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd_absorbgitdirs()
|
||||||
|
{
|
||||||
|
git submodule--helper absorb-git-dirs --prefix "$wt_prefix" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
# This loop parses the command line arguments to find the
|
# This loop parses the command line arguments to find the
|
||||||
# subcommand name to dispatch. Parsing of the subcommand specific
|
# subcommand name to dispatch. Parsing of the subcommand specific
|
||||||
# options are primarily done by the subcommand implementations.
|
# options are primarily done by the subcommand implementations.
|
||||||
@ -1136,7 +1141,7 @@ cmd_sync()
|
|||||||
while test $# != 0 && test -z "$command"
|
while test $# != 0 && test -z "$command"
|
||||||
do
|
do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
add | foreach | init | deinit | update | status | summary | sync)
|
add | foreach | init | deinit | update | status | summary | sync | absorbgitdirs)
|
||||||
command=$1
|
command=$1
|
||||||
;;
|
;;
|
||||||
-q|--quiet)
|
-q|--quiet)
|
||||||
|
2
git.c
2
git.c
@ -493,7 +493,7 @@ static struct cmd_struct commands[] = {
|
|||||||
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
|
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
|
||||||
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
|
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
|
||||||
{ "stripspace", cmd_stripspace },
|
{ "stripspace", cmd_stripspace },
|
||||||
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP },
|
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX},
|
||||||
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
|
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
|
||||||
{ "tag", cmd_tag, RUN_SETUP },
|
{ "tag", cmd_tag, RUN_SETUP },
|
||||||
{ "unpack-file", cmd_unpack_file, RUN_SETUP },
|
{ "unpack-file", cmd_unpack_file, RUN_SETUP },
|
||||||
|
127
submodule.c
127
submodule.c
@ -14,6 +14,7 @@
|
|||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "thread-utils.h"
|
#include "thread-utils.h"
|
||||||
#include "quote.h"
|
#include "quote.h"
|
||||||
|
#include "worktree.h"
|
||||||
|
|
||||||
static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
|
static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
|
||||||
static int parallel_jobs = 1;
|
static int parallel_jobs = 1;
|
||||||
@ -1296,30 +1297,6 @@ int merge_submodule(unsigned char result[20], const char *path,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update gitfile and core.worktree setting to connect work tree and git dir */
|
|
||||||
void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
|
|
||||||
{
|
|
||||||
struct strbuf file_name = STRBUF_INIT;
|
|
||||||
struct strbuf rel_path = STRBUF_INIT;
|
|
||||||
const char *real_work_tree = xstrdup(real_path(work_tree));
|
|
||||||
|
|
||||||
/* Update gitfile */
|
|
||||||
strbuf_addf(&file_name, "%s/.git", work_tree);
|
|
||||||
write_file(file_name.buf, "gitdir: %s",
|
|
||||||
relative_path(git_dir, real_work_tree, &rel_path));
|
|
||||||
|
|
||||||
/* Update core.worktree setting */
|
|
||||||
strbuf_reset(&file_name);
|
|
||||||
strbuf_addf(&file_name, "%s/config", git_dir);
|
|
||||||
git_config_set_in_file(file_name.buf, "core.worktree",
|
|
||||||
relative_path(real_work_tree, git_dir,
|
|
||||||
&rel_path));
|
|
||||||
|
|
||||||
strbuf_release(&file_name);
|
|
||||||
strbuf_release(&rel_path);
|
|
||||||
free((void *)real_work_tree);
|
|
||||||
}
|
|
||||||
|
|
||||||
int parallel_submodules(void)
|
int parallel_submodules(void)
|
||||||
{
|
{
|
||||||
return parallel_jobs;
|
return parallel_jobs;
|
||||||
@ -1335,3 +1312,105 @@ void prepare_submodule_repo_env(struct argv_array *out)
|
|||||||
}
|
}
|
||||||
argv_array_push(out, "GIT_DIR=.git");
|
argv_array_push(out, "GIT_DIR=.git");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Embeds a single submodules git directory into the superprojects git dir,
|
||||||
|
* non recursively.
|
||||||
|
*/
|
||||||
|
static void relocate_single_git_dir_into_superproject(const char *prefix,
|
||||||
|
const char *path)
|
||||||
|
{
|
||||||
|
char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
|
||||||
|
const char *new_git_dir;
|
||||||
|
const struct submodule *sub;
|
||||||
|
|
||||||
|
if (submodule_uses_worktrees(path))
|
||||||
|
die(_("relocate_gitdir for submodule '%s' with "
|
||||||
|
"more than one worktree not supported"), path);
|
||||||
|
|
||||||
|
old_git_dir = xstrfmt("%s/.git", path);
|
||||||
|
if (read_gitfile(old_git_dir))
|
||||||
|
/* If it is an actual gitfile, it doesn't need migration. */
|
||||||
|
return;
|
||||||
|
|
||||||
|
real_old_git_dir = xstrdup(real_path(old_git_dir));
|
||||||
|
|
||||||
|
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 = xstrdup(real_path(new_git_dir));
|
||||||
|
|
||||||
|
if (!prefix)
|
||||||
|
prefix = get_super_prefix();
|
||||||
|
|
||||||
|
fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
|
||||||
|
prefix ? prefix : "", path,
|
||||||
|
real_old_git_dir, real_new_git_dir);
|
||||||
|
|
||||||
|
relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
|
||||||
|
|
||||||
|
free(old_git_dir);
|
||||||
|
free(real_old_git_dir);
|
||||||
|
free(real_new_git_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Migrate the git directory of the submodule given by path from
|
||||||
|
* having its git directory within the working tree to the git dir nested
|
||||||
|
* in its superprojects git dir under modules/.
|
||||||
|
*/
|
||||||
|
void absorb_git_dir_into_superproject(const char *prefix,
|
||||||
|
const char *path,
|
||||||
|
unsigned flags)
|
||||||
|
{
|
||||||
|
const char *sub_git_dir, *v;
|
||||||
|
char *real_sub_git_dir = NULL, *real_common_git_dir = NULL;
|
||||||
|
struct strbuf gitdir = STRBUF_INIT;
|
||||||
|
|
||||||
|
strbuf_addf(&gitdir, "%s/.git", path);
|
||||||
|
sub_git_dir = resolve_gitdir(gitdir.buf);
|
||||||
|
|
||||||
|
/* Not populated? */
|
||||||
|
if (!sub_git_dir)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Is it already absorbed into the superprojects git dir? */
|
||||||
|
real_sub_git_dir = xstrdup(real_path(sub_git_dir));
|
||||||
|
real_common_git_dir = xstrdup(real_path(get_git_common_dir()));
|
||||||
|
if (!skip_prefix(real_sub_git_dir, real_common_git_dir, &v))
|
||||||
|
relocate_single_git_dir_into_superproject(prefix, path);
|
||||||
|
|
||||||
|
if (flags & ABSORB_GITDIR_RECURSE_SUBMODULES) {
|
||||||
|
struct child_process cp = CHILD_PROCESS_INIT;
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
|
||||||
|
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, path);
|
||||||
|
strbuf_addch(&sb, '/');
|
||||||
|
|
||||||
|
cp.dir = path;
|
||||||
|
cp.git_cmd = 1;
|
||||||
|
cp.no_stdin = 1;
|
||||||
|
argv_array_pushl(&cp.args, "--super-prefix", sb.buf,
|
||||||
|
"submodule--helper",
|
||||||
|
"absorb-git-dirs", NULL);
|
||||||
|
prepare_submodule_repo_env(&cp.env_array);
|
||||||
|
if (run_command(&cp))
|
||||||
|
die(_("could not recurse into submodule '%s'"), path);
|
||||||
|
|
||||||
|
strbuf_release(&sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
strbuf_release(&gitdir);
|
||||||
|
free(real_sub_git_dir);
|
||||||
|
free(real_common_git_dir);
|
||||||
|
}
|
||||||
|
@ -68,7 +68,6 @@ int find_unpushed_submodules(struct sha1_array *commits, const char *remotes_nam
|
|||||||
extern int push_unpushed_submodules(struct sha1_array *commits,
|
extern int push_unpushed_submodules(struct sha1_array *commits,
|
||||||
const char *remotes_name,
|
const char *remotes_name,
|
||||||
int dry_run);
|
int dry_run);
|
||||||
void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
|
|
||||||
int parallel_submodules(void);
|
int parallel_submodules(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -78,4 +77,8 @@ int parallel_submodules(void);
|
|||||||
*/
|
*/
|
||||||
void prepare_submodule_repo_env(struct argv_array *out);
|
void prepare_submodule_repo_env(struct argv_array *out);
|
||||||
|
|
||||||
|
#define ABSORB_GITDIR_RECURSE_SUBMODULES (1<<0)
|
||||||
|
extern void absorb_git_dir_into_superproject(const char *prefix,
|
||||||
|
const char *path,
|
||||||
|
unsigned flags);
|
||||||
#endif
|
#endif
|
||||||
|
101
t/t7412-submodule-absorbgitdirs.sh
Executable file
101
t/t7412-submodule-absorbgitdirs.sh
Executable file
@ -0,0 +1,101 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='Test submodule absorbgitdirs
|
||||||
|
|
||||||
|
This test verifies that `git submodue absorbgitdirs` moves a submodules git
|
||||||
|
directory into the superproject.
|
||||||
|
'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success 'setup a real submodule' '
|
||||||
|
git init sub1 &&
|
||||||
|
test_commit -C sub1 first &&
|
||||||
|
git submodule add ./sub1 &&
|
||||||
|
test_tick &&
|
||||||
|
git commit -m superproject
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'absorb the git dir' '
|
||||||
|
>expect.1 &&
|
||||||
|
>expect.2 &&
|
||||||
|
>actual.1 &&
|
||||||
|
>actual.2 &&
|
||||||
|
git status >expect.1 &&
|
||||||
|
git -C sub1 rev-parse HEAD >expect.2 &&
|
||||||
|
git submodule absorbgitdirs &&
|
||||||
|
git fsck &&
|
||||||
|
test -f sub1/.git &&
|
||||||
|
test -d .git/modules/sub1 &&
|
||||||
|
git status >actual.1 &&
|
||||||
|
git -C sub1 rev-parse HEAD >actual.2 &&
|
||||||
|
test_cmp expect.1 actual.1 &&
|
||||||
|
test_cmp expect.2 actual.2
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'absorbing does not fail for deinitalized submodules' '
|
||||||
|
test_when_finished "git submodule update --init" &&
|
||||||
|
git submodule deinit --all &&
|
||||||
|
git submodule absorbgitdirs &&
|
||||||
|
test -d .git/modules/sub1 &&
|
||||||
|
test -d sub1 &&
|
||||||
|
! test -e sub1/.git
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'setup nested submodule' '
|
||||||
|
git init sub1/nested &&
|
||||||
|
test_commit -C sub1/nested first_nested &&
|
||||||
|
git -C sub1 submodule add ./nested &&
|
||||||
|
test_tick &&
|
||||||
|
git -C sub1 commit -m "add nested" &&
|
||||||
|
git add sub1 &&
|
||||||
|
git commit -m "sub1 to include nested submodule"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'absorb the git dir in a nested submodule' '
|
||||||
|
git status >expect.1 &&
|
||||||
|
git -C sub1/nested rev-parse HEAD >expect.2 &&
|
||||||
|
git submodule absorbgitdirs &&
|
||||||
|
test -f sub1/nested/.git &&
|
||||||
|
test -d .git/modules/sub1/modules/nested &&
|
||||||
|
git status >actual.1 &&
|
||||||
|
git -C sub1/nested rev-parse HEAD >actual.2 &&
|
||||||
|
test_cmp expect.1 actual.1 &&
|
||||||
|
test_cmp expect.2 actual.2
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'setup a gitlink with missing .gitmodules entry' '
|
||||||
|
git init sub2 &&
|
||||||
|
test_commit -C sub2 first &&
|
||||||
|
git add sub2 &&
|
||||||
|
git commit -m superproject
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'absorbing the git dir fails for incomplete submodules' '
|
||||||
|
git status >expect.1 &&
|
||||||
|
git -C sub2 rev-parse HEAD >expect.2 &&
|
||||||
|
test_must_fail git submodule absorbgitdirs &&
|
||||||
|
git -C sub2 fsck &&
|
||||||
|
test -d sub2/.git &&
|
||||||
|
git status >actual &&
|
||||||
|
git -C sub2 rev-parse HEAD >actual.2 &&
|
||||||
|
test_cmp expect.1 actual.1 &&
|
||||||
|
test_cmp expect.2 actual.2
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'setup a submodule with multiple worktrees' '
|
||||||
|
# first create another unembedded git dir in a new submodule
|
||||||
|
git init sub3 &&
|
||||||
|
test_commit -C sub3 first &&
|
||||||
|
git submodule add ./sub3 &&
|
||||||
|
test_tick &&
|
||||||
|
git commit -m "add another submodule" &&
|
||||||
|
git -C sub3 worktree add ../sub3_second_work_tree
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'absorbing fails for a submodule with multiple worktrees' '
|
||||||
|
test_must_fail git submodule absorbgitdirs sub3 2>error &&
|
||||||
|
test_i18ngrep "not supported" error
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
@ -157,16 +157,21 @@ debug () {
|
|||||||
GIT_TEST_GDB=1 "$@"
|
GIT_TEST_GDB=1 "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Call test_commit with the arguments "<message> [<file> [<contents> [<tag>]]]"
|
# Call test_commit with the arguments
|
||||||
|
# [-C <directory>] <message> [<file> [<contents> [<tag>]]]"
|
||||||
#
|
#
|
||||||
# This will commit a file with the given contents and the given commit
|
# This will commit a file with the given contents and the given commit
|
||||||
# message, and tag the resulting commit with the given tag name.
|
# message, and tag the resulting commit with the given tag name.
|
||||||
#
|
#
|
||||||
# <file>, <contents>, and <tag> all default to <message>.
|
# <file>, <contents>, and <tag> all default to <message>.
|
||||||
|
#
|
||||||
|
# If the first argument is "-C", the second argument is used as a path for
|
||||||
|
# the git invocations.
|
||||||
|
|
||||||
test_commit () {
|
test_commit () {
|
||||||
notick= &&
|
notick= &&
|
||||||
signoff= &&
|
signoff= &&
|
||||||
|
indir= &&
|
||||||
while test $# != 0
|
while test $# != 0
|
||||||
do
|
do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@ -176,21 +181,26 @@ test_commit () {
|
|||||||
--signoff)
|
--signoff)
|
||||||
signoff="$1"
|
signoff="$1"
|
||||||
;;
|
;;
|
||||||
|
-C)
|
||||||
|
indir="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done &&
|
done &&
|
||||||
|
indir=${indir:+"$indir"/} &&
|
||||||
file=${2:-"$1.t"} &&
|
file=${2:-"$1.t"} &&
|
||||||
echo "${3-$1}" > "$file" &&
|
echo "${3-$1}" > "$indir$file" &&
|
||||||
git add "$file" &&
|
git ${indir:+ -C "$indir"} add "$file" &&
|
||||||
if test -z "$notick"
|
if test -z "$notick"
|
||||||
then
|
then
|
||||||
test_tick
|
test_tick
|
||||||
fi &&
|
fi &&
|
||||||
git commit $signoff -m "$1" &&
|
git ${indir:+ -C "$indir"} commit $signoff -m "$1" &&
|
||||||
git tag "${4:-$1}"
|
git ${indir:+ -C "$indir"} tag "${4:-$1}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Call test_merge with the arguments "<message> <commit>", where <commit>
|
# Call test_merge with the arguments "<message> <commit>", where <commit>
|
||||||
|
50
worktree.c
50
worktree.c
@ -380,3 +380,53 @@ const struct worktree *find_shared_symref(const char *symref,
|
|||||||
|
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int submodule_uses_worktrees(const char *path)
|
||||||
|
{
|
||||||
|
char *submodule_gitdir;
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *d;
|
||||||
|
int ret = 0;
|
||||||
|
struct repository_format format;
|
||||||
|
|
||||||
|
submodule_gitdir = git_pathdup_submodule(path, "%s", "");
|
||||||
|
if (!submodule_gitdir)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* The env would be set for the superproject. */
|
||||||
|
get_common_dir_noenv(&sb, submodule_gitdir);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The check below is only known to be good for repository format
|
||||||
|
* version 0 at the time of writing this code.
|
||||||
|
*/
|
||||||
|
strbuf_addstr(&sb, "/config");
|
||||||
|
read_repository_format(&format, sb.buf);
|
||||||
|
if (format.version != 0) {
|
||||||
|
strbuf_release(&sb);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replace config by worktrees. */
|
||||||
|
strbuf_setlen(&sb, sb.len - strlen("config"));
|
||||||
|
strbuf_addstr(&sb, "worktrees");
|
||||||
|
|
||||||
|
/* See if there is any file inside the worktrees directory. */
|
||||||
|
dir = opendir(sb.buf);
|
||||||
|
strbuf_release(&sb);
|
||||||
|
free(submodule_gitdir);
|
||||||
|
|
||||||
|
if (!dir)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while ((d = readdir(dir)) != NULL) {
|
||||||
|
if (is_dot_or_dotdot(d->d_name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -27,6 +27,11 @@ struct worktree {
|
|||||||
*/
|
*/
|
||||||
extern struct worktree **get_worktrees(unsigned flags);
|
extern struct worktree **get_worktrees(unsigned flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns 1 if linked worktrees exist, 0 otherwise.
|
||||||
|
*/
|
||||||
|
extern int submodule_uses_worktrees(const char *path);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return git dir of the worktree. Note that the path may be relative.
|
* Return git dir of the worktree. Note that the path may be relative.
|
||||||
* If wt is NULL, git dir of current worktree is returned.
|
* If wt is NULL, git dir of current worktree is returned.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user