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>

Stefan Beller committed Mar 14, 2017 at 14:46 UTC 6e3c1595c66e2b192a2a516447c5645814766339
2 files changed +144
submodule.c
+137
@@ -1247,6 +1247,143 @@ static const char *get_super_prefix_or_empty(void)
1247 return s;
1248 }
1249
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 +
1387 static int find_first_merges(struct object_array *result, const char *path,
1388 struct commit *a, struct commit *b)
1389 {
submodule.h
+7
@@ -96,6 +96,13 @@ extern int push_unpushed_submodules(struct sha1_array *commits,
96 extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
97 extern int parallel_submodules(void);
98
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 +
106 /*
107 * Prepare the "env_array" parameter of a "struct child_process" for executing
108 * a submodule by clearing any repo-specific envirionment variables, but