Skip to content

Commit 6e3c159

Browse files
stefanbellergitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 202275b commit 6e3c159

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

submodule.c

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,143 @@ static const char *get_super_prefix_or_empty(void)
12471247
return s;
12481248
}
12491249

1250+
static int submodule_has_dirty_index(const struct submodule *sub)
1251+
{
1252+
struct child_process cp = CHILD_PROCESS_INIT;
1253+
1254+
prepare_submodule_repo_env_no_git_dir(&cp.env_array);
1255+
1256+
cp.git_cmd = 1;
1257+
argv_array_pushl(&cp.args, "diff-index", "--quiet",
1258+
"--cached", "HEAD", NULL);
1259+
cp.no_stdin = 1;
1260+
cp.no_stdout = 1;
1261+
cp.dir = sub->path;
1262+
if (start_command(&cp))
1263+
die("could not recurse into submodule '%s'", sub->path);
1264+
1265+
return finish_command(&cp);
1266+
}
1267+
1268+
static void submodule_reset_index(const char *path)
1269+
{
1270+
struct child_process cp = CHILD_PROCESS_INIT;
1271+
prepare_submodule_repo_env_no_git_dir(&cp.env_array);
1272+
1273+
cp.git_cmd = 1;
1274+
cp.no_stdin = 1;
1275+
cp.dir = path;
1276+
1277+
argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
1278+
get_super_prefix_or_empty(), path);
1279+
argv_array_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
1280+
1281+
argv_array_push(&cp.args, EMPTY_TREE_SHA1_HEX);
1282+
1283+
if (run_command(&cp))
1284+
die("could not reset submodule index");
1285+
}
1286+
1287+
/**
1288+
* Moves a submodule at a given path from a given head to another new head.
1289+
* For edge cases (a submodule coming into existence or removing a submodule)
1290+
* pass NULL for old or new respectively.
1291+
*/
1292+
int submodule_move_head(const char *path,
1293+
const char *old,
1294+
const char *new,
1295+
unsigned flags)
1296+
{
1297+
int ret = 0;
1298+
struct child_process cp = CHILD_PROCESS_INIT;
1299+
const struct submodule *sub;
1300+
1301+
sub = submodule_from_path(null_sha1, path);
1302+
1303+
if (!sub)
1304+
die("BUG: could not get submodule information for '%s'", path);
1305+
1306+
if (old && !(flags & SUBMODULE_MOVE_HEAD_FORCE)) {
1307+
/* Check if the submodule has a dirty index. */
1308+
if (submodule_has_dirty_index(sub))
1309+
return error(_("submodule '%s' has dirty index"), path);
1310+
}
1311+
1312+
if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
1313+
if (old) {
1314+
if (!submodule_uses_gitfile(path))
1315+
absorb_git_dir_into_superproject("", path,
1316+
ABSORB_GITDIR_RECURSE_SUBMODULES);
1317+
} else {
1318+
struct strbuf sb = STRBUF_INIT;
1319+
strbuf_addf(&sb, "%s/modules/%s",
1320+
get_git_common_dir(), sub->name);
1321+
connect_work_tree_and_git_dir(path, sb.buf);
1322+
strbuf_release(&sb);
1323+
1324+
/* make sure the index is clean as well */
1325+
submodule_reset_index(path);
1326+
}
1327+
}
1328+
1329+
prepare_submodule_repo_env_no_git_dir(&cp.env_array);
1330+
1331+
cp.git_cmd = 1;
1332+
cp.no_stdin = 1;
1333+
cp.dir = path;
1334+
1335+
argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
1336+
get_super_prefix_or_empty(), path);
1337+
argv_array_pushl(&cp.args, "read-tree", NULL);
1338+
1339+
if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
1340+
argv_array_push(&cp.args, "-n");
1341+
else
1342+
argv_array_push(&cp.args, "-u");
1343+
1344+
if (flags & SUBMODULE_MOVE_HEAD_FORCE)
1345+
argv_array_push(&cp.args, "--reset");
1346+
else
1347+
argv_array_push(&cp.args, "-m");
1348+
1349+
argv_array_push(&cp.args, old ? old : EMPTY_TREE_SHA1_HEX);
1350+
argv_array_push(&cp.args, new ? new : EMPTY_TREE_SHA1_HEX);
1351+
1352+
if (run_command(&cp)) {
1353+
ret = -1;
1354+
goto out;
1355+
}
1356+
1357+
if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
1358+
if (new) {
1359+
struct child_process cp1 = CHILD_PROCESS_INIT;
1360+
/* also set the HEAD accordingly */
1361+
cp1.git_cmd = 1;
1362+
cp1.no_stdin = 1;
1363+
cp1.dir = path;
1364+
1365+
argv_array_pushl(&cp1.args, "update-ref", "HEAD",
1366+
new ? new : EMPTY_TREE_SHA1_HEX, NULL);
1367+
1368+
if (run_command(&cp1)) {
1369+
ret = -1;
1370+
goto out;
1371+
}
1372+
} else {
1373+
struct strbuf sb = STRBUF_INIT;
1374+
1375+
strbuf_addf(&sb, "%s/.git", path);
1376+
unlink_or_warn(sb.buf);
1377+
strbuf_release(&sb);
1378+
1379+
if (is_empty_dir(path))
1380+
rmdir_or_warn(path);
1381+
}
1382+
}
1383+
out:
1384+
return ret;
1385+
}
1386+
12501387
static int find_first_merges(struct object_array *result, const char *path,
12511388
struct commit *a, struct commit *b)
12521389
{

submodule.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ extern int push_unpushed_submodules(struct sha1_array *commits,
9696
extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
9797
extern int parallel_submodules(void);
9898

99+
#define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
100+
#define SUBMODULE_MOVE_HEAD_FORCE (1<<1)
101+
extern int submodule_move_head(const char *path,
102+
const char *old,
103+
const char *new,
104+
unsigned flags);
105+
99106
/*
100107
* Prepare the "env_array" parameter of a "struct child_process" for executing
101108
* a submodule by clearing any repo-specific envirionment variables, but

0 commit comments

Comments
 (0)