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