Skip to content

Commit da2b74e

Browse files
committed
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
2 parents 2ced5f2 + 7c4be45 commit da2b74e

File tree

12 files changed

+399
-43
lines changed

12 files changed

+399
-43
lines changed

Documentation/git-submodule.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ SYNOPSIS
2222
[commit] [--] [<path>...]
2323
'git submodule' [--quiet] foreach [--recursive] <command>
2424
'git submodule' [--quiet] sync [--recursive] [--] [<path>...]
25+
'git submodule' [--quiet] absorbgitdirs [--] [<path>...]
2526

2627

2728
DESCRIPTION
@@ -245,6 +246,20 @@ sync::
245246
If `--recursive` is specified, this command will recurse into the
246247
registered submodules, and sync any nested submodules within.
247248

249+
absorbgitdirs::
250+
If a git directory of a submodule is inside the submodule,
251+
move the git directory of the submodule into its superprojects
252+
`$GIT_DIR/modules` path and then connect the git directory and
253+
its working directory by setting the `core.worktree` and adding
254+
a .git file pointing to the git directory embedded in the
255+
superprojects git directory.
256+
+
257+
A repository that was cloned independently and later added as a submodule or
258+
old setups have the submodules git directory inside the submodule instead of
259+
embedded into the superprojects git directory.
260+
+
261+
This command is recursive by default.
262+
248263
OPTIONS
249264
-------
250265
-q::

builtin/submodule--helper.c

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,21 +1091,62 @@ static int resolve_remote_submodule_branch(int argc, const char **argv,
10911091
return 0;
10921092
}
10931093

1094+
static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
1095+
{
1096+
int i;
1097+
struct pathspec pathspec;
1098+
struct module_list list = MODULE_LIST_INIT;
1099+
unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
1100+
1101+
struct option embed_gitdir_options[] = {
1102+
OPT_STRING(0, "prefix", &prefix,
1103+
N_("path"),
1104+
N_("path into the working tree")),
1105+
OPT_BIT(0, "--recursive", &flags, N_("recurse into submodules"),
1106+
ABSORB_GITDIR_RECURSE_SUBMODULES),
1107+
OPT_END()
1108+
};
1109+
1110+
const char *const git_submodule_helper_usage[] = {
1111+
N_("git submodule--helper embed-git-dir [<path>...]"),
1112+
NULL
1113+
};
1114+
1115+
argc = parse_options(argc, argv, prefix, embed_gitdir_options,
1116+
git_submodule_helper_usage, 0);
1117+
1118+
gitmodules_config();
1119+
git_config(submodule_config, NULL);
1120+
1121+
if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
1122+
return 1;
1123+
1124+
for (i = 0; i < list.nr; i++)
1125+
absorb_git_dir_into_superproject(prefix,
1126+
list.entries[i]->name, flags);
1127+
1128+
return 0;
1129+
}
1130+
1131+
#define SUPPORT_SUPER_PREFIX (1<<0)
1132+
10941133
struct cmd_struct {
10951134
const char *cmd;
10961135
int (*fn)(int, const char **, const char *);
1136+
unsigned option;
10971137
};
10981138

10991139
static struct cmd_struct commands[] = {
1100-
{"list", module_list},
1101-
{"name", module_name},
1102-
{"clone", module_clone},
1103-
{"update-clone", update_clone},
1104-
{"relative-path", resolve_relative_path},
1105-
{"resolve-relative-url", resolve_relative_url},
1106-
{"resolve-relative-url-test", resolve_relative_url_test},
1107-
{"init", module_init},
1108-
{"remote-branch", resolve_remote_submodule_branch}
1140+
{"list", module_list, 0},
1141+
{"name", module_name, 0},
1142+
{"clone", module_clone, 0},
1143+
{"update-clone", update_clone, 0},
1144+
{"relative-path", resolve_relative_path, 0},
1145+
{"resolve-relative-url", resolve_relative_url, 0},
1146+
{"resolve-relative-url-test", resolve_relative_url_test, 0},
1147+
{"init", module_init, 0},
1148+
{"remote-branch", resolve_remote_submodule_branch, 0},
1149+
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
11091150
};
11101151

11111152
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)
11151156
die(_("submodule--helper subcommand must be "
11161157
"called with a subcommand"));
11171158

1118-
for (i = 0; i < ARRAY_SIZE(commands); i++)
1119-
if (!strcmp(argv[1], commands[i].cmd))
1159+
for (i = 0; i < ARRAY_SIZE(commands); i++) {
1160+
if (!strcmp(argv[1], commands[i].cmd)) {
1161+
if (get_super_prefix() &&
1162+
!(commands[i].option & SUPPORT_SUPER_PREFIX))
1163+
die(_("%s doesn't support --super-prefix"),
1164+
commands[i].cmd);
11201165
return commands[i].fn(argc - 1, argv + 1, prefix);
1166+
}
1167+
}
11211168

11221169
die(_("'%s' is not a valid submodule--helper "
11231170
"subcommand"), argv[1]);

dir.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2748,3 +2748,40 @@ void untracked_cache_add_to_index(struct index_state *istate,
27482748
{
27492749
untracked_cache_invalidate_path(istate, path);
27502750
}
2751+
2752+
/* Update gitfile and core.worktree setting to connect work tree and git dir */
2753+
void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
2754+
{
2755+
struct strbuf file_name = STRBUF_INIT;
2756+
struct strbuf rel_path = STRBUF_INIT;
2757+
char *git_dir = xstrdup(real_path(git_dir_));
2758+
char *work_tree = xstrdup(real_path(work_tree_));
2759+
2760+
/* Update gitfile */
2761+
strbuf_addf(&file_name, "%s/.git", work_tree);
2762+
write_file(file_name.buf, "gitdir: %s",
2763+
relative_path(git_dir, work_tree, &rel_path));
2764+
2765+
/* Update core.worktree setting */
2766+
strbuf_reset(&file_name);
2767+
strbuf_addf(&file_name, "%s/config", git_dir);
2768+
git_config_set_in_file(file_name.buf, "core.worktree",
2769+
relative_path(work_tree, git_dir, &rel_path));
2770+
2771+
strbuf_release(&file_name);
2772+
strbuf_release(&rel_path);
2773+
free(work_tree);
2774+
free(git_dir);
2775+
}
2776+
2777+
/*
2778+
* Migrate the git directory of the given path from old_git_dir to new_git_dir.
2779+
*/
2780+
void relocate_gitdir(const char *path, const char *old_git_dir, const char *new_git_dir)
2781+
{
2782+
if (rename(old_git_dir, new_git_dir) < 0)
2783+
die_errno(_("could not migrate git directory from '%s' to '%s'"),
2784+
old_git_dir, new_git_dir);
2785+
2786+
connect_work_tree_and_git_dir(path, new_git_dir);
2787+
}

dir.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,4 +335,8 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
335335
void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked);
336336
void add_untracked_cache(struct index_state *istate);
337337
void remove_untracked_cache(struct index_state *istate);
338+
extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
339+
extern void relocate_gitdir(const char *path,
340+
const char *old_git_dir,
341+
const char *new_git_dir);
338342
#endif

git-submodule.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,11 @@ cmd_sync()
11271127
done
11281128
}
11291129

1130+
cmd_absorbgitdirs()
1131+
{
1132+
git submodule--helper absorb-git-dirs --prefix "$wt_prefix" "$@"
1133+
}
1134+
11301135
# This loop parses the command line arguments to find the
11311136
# subcommand name to dispatch. Parsing of the subcommand specific
11321137
# options are primarily done by the subcommand implementations.
@@ -1136,7 +1141,7 @@ cmd_sync()
11361141
while test $# != 0 && test -z "$command"
11371142
do
11381143
case "$1" in
1139-
add | foreach | init | deinit | update | status | summary | sync)
1144+
add | foreach | init | deinit | update | status | summary | sync | absorbgitdirs)
11401145
command=$1
11411146
;;
11421147
-q|--quiet)

git.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ static struct cmd_struct commands[] = {
493493
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
494494
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
495495
{ "stripspace", cmd_stripspace },
496-
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP },
496+
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX},
497497
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
498498
{ "tag", cmd_tag, RUN_SETUP },
499499
{ "unpack-file", cmd_unpack_file, RUN_SETUP },

submodule.c

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "blob.h"
1515
#include "thread-utils.h"
1616
#include "quote.h"
17+
#include "worktree.h"
1718

1819
static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
1920
static int parallel_jobs = 1;
@@ -1296,30 +1297,6 @@ int merge_submodule(unsigned char result[20], const char *path,
12961297
return 0;
12971298
}
12981299

1299-
/* Update gitfile and core.worktree setting to connect work tree and git dir */
1300-
void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
1301-
{
1302-
struct strbuf file_name = STRBUF_INIT;
1303-
struct strbuf rel_path = STRBUF_INIT;
1304-
const char *real_work_tree = xstrdup(real_path(work_tree));
1305-
1306-
/* Update gitfile */
1307-
strbuf_addf(&file_name, "%s/.git", work_tree);
1308-
write_file(file_name.buf, "gitdir: %s",
1309-
relative_path(git_dir, real_work_tree, &rel_path));
1310-
1311-
/* Update core.worktree setting */
1312-
strbuf_reset(&file_name);
1313-
strbuf_addf(&file_name, "%s/config", git_dir);
1314-
git_config_set_in_file(file_name.buf, "core.worktree",
1315-
relative_path(real_work_tree, git_dir,
1316-
&rel_path));
1317-
1318-
strbuf_release(&file_name);
1319-
strbuf_release(&rel_path);
1320-
free((void *)real_work_tree);
1321-
}
1322-
13231300
int parallel_submodules(void)
13241301
{
13251302
return parallel_jobs;
@@ -1335,3 +1312,105 @@ void prepare_submodule_repo_env(struct argv_array *out)
13351312
}
13361313
argv_array_push(out, "GIT_DIR=.git");
13371314
}
1315+
1316+
/*
1317+
* Embeds a single submodules git directory into the superprojects git dir,
1318+
* non recursively.
1319+
*/
1320+
static void relocate_single_git_dir_into_superproject(const char *prefix,
1321+
const char *path)
1322+
{
1323+
char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
1324+
const char *new_git_dir;
1325+
const struct submodule *sub;
1326+
1327+
if (submodule_uses_worktrees(path))
1328+
die(_("relocate_gitdir for submodule '%s' with "
1329+
"more than one worktree not supported"), path);
1330+
1331+
old_git_dir = xstrfmt("%s/.git", path);
1332+
if (read_gitfile(old_git_dir))
1333+
/* If it is an actual gitfile, it doesn't need migration. */
1334+
return;
1335+
1336+
real_old_git_dir = xstrdup(real_path(old_git_dir));
1337+
1338+
sub = submodule_from_path(null_sha1, path);
1339+
if (!sub)
1340+
die(_("could not lookup name for submodule '%s'"), path);
1341+
1342+
new_git_dir = git_path("modules/%s", sub->name);
1343+
if (safe_create_leading_directories_const(new_git_dir) < 0)
1344+
die(_("could not create directory '%s'"), new_git_dir);
1345+
real_new_git_dir = xstrdup(real_path(new_git_dir));
1346+
1347+
if (!prefix)
1348+
prefix = get_super_prefix();
1349+
1350+
fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
1351+
prefix ? prefix : "", path,
1352+
real_old_git_dir, real_new_git_dir);
1353+
1354+
relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
1355+
1356+
free(old_git_dir);
1357+
free(real_old_git_dir);
1358+
free(real_new_git_dir);
1359+
}
1360+
1361+
/*
1362+
* Migrate the git directory of the submodule given by path from
1363+
* having its git directory within the working tree to the git dir nested
1364+
* in its superprojects git dir under modules/.
1365+
*/
1366+
void absorb_git_dir_into_superproject(const char *prefix,
1367+
const char *path,
1368+
unsigned flags)
1369+
{
1370+
const char *sub_git_dir, *v;
1371+
char *real_sub_git_dir = NULL, *real_common_git_dir = NULL;
1372+
struct strbuf gitdir = STRBUF_INIT;
1373+
1374+
strbuf_addf(&gitdir, "%s/.git", path);
1375+
sub_git_dir = resolve_gitdir(gitdir.buf);
1376+
1377+
/* Not populated? */
1378+
if (!sub_git_dir)
1379+
goto out;
1380+
1381+
/* Is it already absorbed into the superprojects git dir? */
1382+
real_sub_git_dir = xstrdup(real_path(sub_git_dir));
1383+
real_common_git_dir = xstrdup(real_path(get_git_common_dir()));
1384+
if (!skip_prefix(real_sub_git_dir, real_common_git_dir, &v))
1385+
relocate_single_git_dir_into_superproject(prefix, path);
1386+
1387+
if (flags & ABSORB_GITDIR_RECURSE_SUBMODULES) {
1388+
struct child_process cp = CHILD_PROCESS_INIT;
1389+
struct strbuf sb = STRBUF_INIT;
1390+
1391+
if (flags & ~ABSORB_GITDIR_RECURSE_SUBMODULES)
1392+
die("BUG: we don't know how to pass the flags down?");
1393+
1394+
if (get_super_prefix())
1395+
strbuf_addstr(&sb, get_super_prefix());
1396+
strbuf_addstr(&sb, path);
1397+
strbuf_addch(&sb, '/');
1398+
1399+
cp.dir = path;
1400+
cp.git_cmd = 1;
1401+
cp.no_stdin = 1;
1402+
argv_array_pushl(&cp.args, "--super-prefix", sb.buf,
1403+
"submodule--helper",
1404+
"absorb-git-dirs", NULL);
1405+
prepare_submodule_repo_env(&cp.env_array);
1406+
if (run_command(&cp))
1407+
die(_("could not recurse into submodule '%s'"), path);
1408+
1409+
strbuf_release(&sb);
1410+
}
1411+
1412+
out:
1413+
strbuf_release(&gitdir);
1414+
free(real_sub_git_dir);
1415+
free(real_common_git_dir);
1416+
}

submodule.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ int find_unpushed_submodules(struct sha1_array *commits, const char *remotes_nam
6868
extern int push_unpushed_submodules(struct sha1_array *commits,
6969
const char *remotes_name,
7070
int dry_run);
71-
void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
7271
int parallel_submodules(void);
7372

7473
/*
@@ -78,4 +77,8 @@ int parallel_submodules(void);
7877
*/
7978
void prepare_submodule_repo_env(struct argv_array *out);
8079

80+
#define ABSORB_GITDIR_RECURSE_SUBMODULES (1<<0)
81+
extern void absorb_git_dir_into_superproject(const char *prefix,
82+
const char *path,
83+
unsigned flags);
8184
#endif

0 commit comments

Comments
 (0)