submodule: port submodule subcommand 'foreach' from shell to C
This aims to make git-submodule foreach a builtin. 'foreach' is ported to the submodule--helper, and submodule--helper is called from git-submodule.sh. Helped-by: Brandon Williams <bmwill@google.com> Mentored-by: Christian Couder <christian.couder@gmail.com> Mentored-by: Stefan Beller <sbeller@google.com> Signed-off-by: Prathamesh Chavan <pc44800@gmail.com> Signed-off-by: Stefan Beller <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Prathamesh Chavan committed
May 10, 2018 at 14:25 UTC
fc1b9243cd5d51ae455272f1f953d4002294edee
2 files changed
+145
-38
builtin/submodule--helper.c
+144
@@ -439,6 +439,149 @@ static void for_each_listed_submodule(const struct module_list *list,
439
fn(list->entries[i], cb_data);
440
}
441
442
+struct cb_foreach {
443
+ int argc;
444
+ const char **argv;
445
+ const char *prefix;
446
+ int quiet;
447
+ int recursive;
448
+};
449
+#define CB_FOREACH_INIT { 0 }
450
+
451
+static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
452
+ void *cb_data)
453
+{
454
+ struct cb_foreach *info = cb_data;
455
+ const char *path = list_item->name;
456
+ const struct object_id *ce_oid = &list_item->oid;
457
+
458
+ const struct submodule *sub;
459
+ struct child_process cp = CHILD_PROCESS_INIT;
460
+ char *displaypath;
461
+
462
+ displaypath = get_submodule_displaypath(path, info->prefix);
463
+
464
+ sub = submodule_from_path(the_repository, &null_oid, path);
465
+
466
+ if (!sub)
467
+ die(_("No url found for submodule path '%s' in .gitmodules"),
468
+ displaypath);
469
+
470
+ if (!is_submodule_populated_gently(path, NULL))
471
+ goto cleanup;
472
+
473
+ prepare_submodule_repo_env(&cp.env_array);
474
+
475
+ /*
476
+ * For the purpose of executing <command> in the submodule,
477
+ * separate shell is used for the purpose of running the
478
+ * child process.
479
+ */
480
+ cp.use_shell = 1;
481
+ cp.dir = path;
482
+
483
+ /*
484
+ * NEEDSWORK: the command currently has access to the variables $name,
485
+ * $sm_path, $displaypath, $sha1 and $toplevel only when the command
486
+ * contains a single argument. This is done for maintaining a faithful
487
+ * translation from shell script.
488
+ */
489
+ if (info->argc == 1) {
490
+ char *toplevel = xgetcwd();
491
+ struct strbuf sb = STRBUF_INIT;
492
+
493
+ argv_array_pushf(&cp.env_array, "name=%s", sub->name);
494
+ argv_array_pushf(&cp.env_array, "sm_path=%s", path);
495
+ argv_array_pushf(&cp.env_array, "displaypath=%s", displaypath);
496
+ argv_array_pushf(&cp.env_array, "sha1=%s",
497
+ oid_to_hex(ce_oid));
498
+ argv_array_pushf(&cp.env_array, "toplevel=%s", toplevel);
499
+
500
+ /*
501
+ * Since the path variable was accessible from the script
502
+ * before porting, it is also made available after porting.
503
+ * The environment variable "PATH" has a very special purpose
504
+ * on windows. And since environment variables are
505
+ * case-insensitive in windows, it interferes with the
506
+ * existing PATH variable. Hence, to avoid that, we expose
507
+ * path via the args argv_array and not via env_array.
508
+ */
509
+ sq_quote_buf(&sb, path);
510
+ argv_array_pushf(&cp.args, "path=%s; %s",
511
+ sb.buf, info->argv[0]);
512
+ strbuf_release(&sb);
513
+ free(toplevel);
514
+ } else {
515
+ argv_array_pushv(&cp.args, info->argv);
516
+ }
517
+
518
+ if (!info->quiet)
519
+ printf(_("Entering '%s'\n"), displaypath);
520
+
521
+ if (info->argv[0] && run_command(&cp))
522
+ die(_("run_command returned non-zero status for %s\n."),
523
+ displaypath);
524
+
525
+ if (info->recursive) {
526
+ struct child_process cpr = CHILD_PROCESS_INIT;
527
+
528
+ cpr.git_cmd = 1;
529
+ cpr.dir = path;
530
+ prepare_submodule_repo_env(&cpr.env_array);
531
+
532
+ argv_array_pushl(&cpr.args, "--super-prefix", NULL);
533
+ argv_array_pushf(&cpr.args, "%s/", displaypath);
534
+ argv_array_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
535
+ NULL);
536
+
537
+ if (info->quiet)
538
+ argv_array_push(&cpr.args, "--quiet");
539
+
540
+ argv_array_pushv(&cpr.args, info->argv);
541
+
542
+ if (run_command(&cpr))
543
+ die(_("run_command returned non-zero status while"
544
+ "recursing in the nested submodules of %s\n."),
545
+ displaypath);
546
+ }
547
+
548
+cleanup:
549
+ free(displaypath);
550
+}
551
+
552
+static int module_foreach(int argc, const char **argv, const char *prefix)
553
+{
554
+ struct cb_foreach info = CB_FOREACH_INIT;
555
+ struct pathspec pathspec;
556
+ struct module_list list = MODULE_LIST_INIT;
557
+
558
+ struct option module_foreach_options[] = {
559
+ OPT__QUIET(&info.quiet, N_("Suppress output of entering each submodule command")),
560
+ OPT_BOOL(0, "recursive", &info.recursive,
561
+ N_("Recurse into nested submodules")),
562
+ OPT_END()
563
+ };
564
+
565
+ const char *const git_submodule_helper_usage[] = {
566
+ N_("git submodule--helper foreach [--quiet] [--recursive] <command>"),
567
+ NULL
568
+ };
569
+
570
+ argc = parse_options(argc, argv, prefix, module_foreach_options,
571
+ git_submodule_helper_usage, PARSE_OPT_KEEP_UNKNOWN);
572
+
573
+ if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
574
+ return 1;
575
+
576
+ info.argc = argc;
577
+ info.argv = argv;
578
+ info.prefix = prefix;
579
+
580
+ for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
581
+
582
+ return 0;
583
+}
584
+
585
struct init_cb {
586
const char *prefix;
587
unsigned int flags;
@@ -1841,6 +1984,7 @@ static struct cmd_struct commands[] = {
1984
{"relative-path", resolve_relative_path, 0},
1985
{"resolve-relative-url", resolve_relative_url, 0},
1986
{"resolve-relative-url-test", resolve_relative_url_test, 0},
1987
+ {"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
1988
{"init", module_init, SUPPORT_SUPER_PREFIX},
1989
{"status", module_status, SUPPORT_SUPER_PREFIX},
1990
{"print-default-remote", print_default_remote, 0},
git-submodule.sh
+1
-38
@@ -323,44 +323,7 @@ cmd_foreach()
323
shift
324
done
325
326
- toplevel=$(pwd)
327
-
328
- # dup stdin so that it can be restored when running the external
329
- # command in the subshell (and a recursive call to this function)
330
- exec 3<&0
331
-
332
- {
333
- git submodule--helper list --prefix "$wt_prefix" ||
334
- echo "#unmatched" $?
335
- } |
336
- while read -r mode sha1 stage sm_path
337
- do
338
- die_if_unmatched "$mode" "$sha1"
339
- if test -e "$sm_path"/.git
340
- then
341
- displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
342
- say "$(eval_gettext "Entering '\$displaypath'")"
343
- name=$(git submodule--helper name "$sm_path")
344
- (
345
- prefix="$prefix$sm_path/"
346
- sanitize_submodule_env
347
- cd "$sm_path" &&
348
- # we make $path available to scripts ...
349
- path=$sm_path &&
350
- if test $# -eq 1
351
- then
352
- eval "$1"
353
- else
354
- "$@"
355
- fi &&
356
- if test -n "$recursive"
357
- then
358
- cmd_foreach "--recursive" "$@"
359
- fi
360
- ) <&3 3<&- ||
361
- die "$(eval_gettext "Stopping at '\$displaypath'; script returned non-zero status.")"
362
- fi
363
- done
326
+ git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper foreach ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@"
327
}
328
329
#