Skip to content

Commit a1bf46e

Browse files
committed
Merge branch 'pc/submodule-helper'
GSoC. * pc/submodule-helper: submodule: port submodule subcommand 'status' from shell to C submodule--helper: introduce for_each_listed_submodule() submodule--helper: introduce get_submodule_displaypath()
2 parents 5faa27a + a9f8a37 commit a1bf46e

File tree

2 files changed

+257
-77
lines changed

2 files changed

+257
-77
lines changed

builtin/submodule--helper.c

Lines changed: 256 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@
1313
#include "remote.h"
1414
#include "refs.h"
1515
#include "connect.h"
16+
#include "revision.h"
17+
#include "diffcore.h"
18+
#include "diff.h"
19+
20+
#define OPT_QUIET (1 << 0)
21+
#define OPT_CACHED (1 << 1)
22+
#define OPT_RECURSIVE (1 << 2)
23+
24+
typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
25+
void *cb_data);
1626

1727
static char *get_default_remote(void)
1828
{
@@ -219,6 +229,64 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr
219229
return 0;
220230
}
221231

232+
/* the result should be freed by the caller. */
233+
static char *get_submodule_displaypath(const char *path, const char *prefix)
234+
{
235+
const char *super_prefix = get_super_prefix();
236+
237+
if (prefix && super_prefix) {
238+
BUG("cannot have prefix '%s' and superprefix '%s'",
239+
prefix, super_prefix);
240+
} else if (prefix) {
241+
struct strbuf sb = STRBUF_INIT;
242+
char *displaypath = xstrdup(relative_path(path, prefix, &sb));
243+
strbuf_release(&sb);
244+
return displaypath;
245+
} else if (super_prefix) {
246+
return xstrfmt("%s%s", super_prefix, path);
247+
} else {
248+
return xstrdup(path);
249+
}
250+
}
251+
252+
static char *compute_rev_name(const char *sub_path, const char* object_id)
253+
{
254+
struct strbuf sb = STRBUF_INIT;
255+
const char ***d;
256+
257+
static const char *describe_bare[] = { NULL };
258+
259+
static const char *describe_tags[] = { "--tags", NULL };
260+
261+
static const char *describe_contains[] = { "--contains", NULL };
262+
263+
static const char *describe_all_always[] = { "--all", "--always", NULL };
264+
265+
static const char **describe_argv[] = { describe_bare, describe_tags,
266+
describe_contains,
267+
describe_all_always, NULL };
268+
269+
for (d = describe_argv; *d; d++) {
270+
struct child_process cp = CHILD_PROCESS_INIT;
271+
prepare_submodule_repo_env(&cp.env_array);
272+
cp.dir = sub_path;
273+
cp.git_cmd = 1;
274+
cp.no_stderr = 1;
275+
276+
argv_array_push(&cp.args, "describe");
277+
argv_array_pushv(&cp.args, *d);
278+
argv_array_push(&cp.args, object_id);
279+
280+
if (!capture_command(&cp, &sb, 0)) {
281+
strbuf_strip_suffix(&sb, "\n");
282+
return strbuf_detach(&sb, NULL);
283+
}
284+
}
285+
286+
strbuf_release(&sb);
287+
return NULL;
288+
}
289+
222290
struct module_list {
223291
const struct cache_entry **entries;
224292
int alloc, nr;
@@ -328,21 +396,29 @@ static int module_list(int argc, const char **argv, const char *prefix)
328396
return 0;
329397
}
330398

331-
static void init_submodule(const char *path, const char *prefix, int quiet)
399+
static void for_each_listed_submodule(const struct module_list *list,
400+
each_submodule_fn fn, void *cb_data)
401+
{
402+
int i;
403+
for (i = 0; i < list->nr; i++)
404+
fn(list->entries[i], cb_data);
405+
}
406+
407+
struct init_cb {
408+
const char *prefix;
409+
unsigned int flags;
410+
};
411+
412+
#define INIT_CB_INIT { NULL, 0 }
413+
414+
static void init_submodule(const char *path, const char *prefix,
415+
unsigned int flags)
332416
{
333417
const struct submodule *sub;
334418
struct strbuf sb = STRBUF_INIT;
335419
char *upd = NULL, *url = NULL, *displaypath;
336420

337-
if (prefix && get_super_prefix())
338-
die("BUG: cannot have prefix and superprefix");
339-
else if (prefix)
340-
displaypath = xstrdup(relative_path(path, prefix, &sb));
341-
else if (get_super_prefix()) {
342-
strbuf_addf(&sb, "%s%s", get_super_prefix(), path);
343-
displaypath = strbuf_detach(&sb, NULL);
344-
} else
345-
displaypath = xstrdup(path);
421+
displaypath = get_submodule_displaypath(path, prefix);
346422

347423
sub = submodule_from_path(&null_oid, path);
348424

@@ -357,17 +433,16 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
357433
* Set active flag for the submodule being initialized
358434
*/
359435
if (!is_submodule_active(the_repository, path)) {
360-
strbuf_reset(&sb);
361436
strbuf_addf(&sb, "submodule.%s.active", sub->name);
362437
git_config_set_gently(sb.buf, "true");
438+
strbuf_reset(&sb);
363439
}
364440

365441
/*
366442
* Copy url setting when it is not set yet.
367443
* To look up the url in .git/config, we must not fall back to
368444
* .gitmodules, so look it up directly.
369445
*/
370-
strbuf_reset(&sb);
371446
strbuf_addf(&sb, "submodule.%s.url", sub->name);
372447
if (git_config_get_string(sb.buf, &url)) {
373448
if (!sub->url)
@@ -399,14 +474,14 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
399474
if (git_config_set_gently(sb.buf, url))
400475
die(_("Failed to register url for submodule path '%s'"),
401476
displaypath);
402-
if (!quiet)
477+
if (!(flags & OPT_QUIET))
403478
fprintf(stderr,
404479
_("Submodule '%s' (%s) registered for path '%s'\n"),
405480
sub->name, url, displaypath);
406481
}
482+
strbuf_reset(&sb);
407483

408484
/* Copy "update" setting when it is not set yet */
409-
strbuf_reset(&sb);
410485
strbuf_addf(&sb, "submodule.%s.update", sub->name);
411486
if (git_config_get_string(sb.buf, &upd) &&
412487
sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
@@ -426,12 +501,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
426501
free(upd);
427502
}
428503

504+
static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data)
505+
{
506+
struct init_cb *info = cb_data;
507+
init_submodule(list_item->name, info->prefix, info->flags);
508+
}
509+
429510
static int module_init(int argc, const char **argv, const char *prefix)
430511
{
512+
struct init_cb info = INIT_CB_INIT;
431513
struct pathspec pathspec;
432514
struct module_list list = MODULE_LIST_INIT;
433515
int quiet = 0;
434-
int i;
435516

436517
struct option module_init_options[] = {
437518
OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
@@ -456,8 +537,165 @@ static int module_init(int argc, const char **argv, const char *prefix)
456537
if (!argc && git_config_get_value_multi("submodule.active"))
457538
module_list_active(&list);
458539

459-
for (i = 0; i < list.nr; i++)
460-
init_submodule(list.entries[i]->name, prefix, quiet);
540+
info.prefix = prefix;
541+
if (quiet)
542+
info.flags |= OPT_QUIET;
543+
544+
for_each_listed_submodule(&list, init_submodule_cb, &info);
545+
546+
return 0;
547+
}
548+
549+
struct status_cb {
550+
const char *prefix;
551+
unsigned int flags;
552+
};
553+
554+
#define STATUS_CB_INIT { NULL, 0 }
555+
556+
static void print_status(unsigned int flags, char state, const char *path,
557+
const struct object_id *oid, const char *displaypath)
558+
{
559+
if (flags & OPT_QUIET)
560+
return;
561+
562+
printf("%c%s %s", state, oid_to_hex(oid), displaypath);
563+
564+
if (state == ' ' || state == '+')
565+
printf(" (%s)", compute_rev_name(path, oid_to_hex(oid)));
566+
567+
printf("\n");
568+
}
569+
570+
static int handle_submodule_head_ref(const char *refname,
571+
const struct object_id *oid, int flags,
572+
void *cb_data)
573+
{
574+
struct object_id *output = cb_data;
575+
if (oid)
576+
oidcpy(output, oid);
577+
578+
return 0;
579+
}
580+
581+
static void status_submodule(const char *path, const struct object_id *ce_oid,
582+
unsigned int ce_flags, const char *prefix,
583+
unsigned int flags)
584+
{
585+
char *displaypath;
586+
struct argv_array diff_files_args = ARGV_ARRAY_INIT;
587+
struct rev_info rev;
588+
int diff_files_result;
589+
590+
if (!submodule_from_path(&null_oid, path))
591+
die(_("no submodule mapping found in .gitmodules for path '%s'"),
592+
path);
593+
594+
displaypath = get_submodule_displaypath(path, prefix);
595+
596+
if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
597+
print_status(flags, 'U', path, &null_oid, displaypath);
598+
goto cleanup;
599+
}
600+
601+
if (!is_submodule_active(the_repository, path)) {
602+
print_status(flags, '-', path, ce_oid, displaypath);
603+
goto cleanup;
604+
}
605+
606+
argv_array_pushl(&diff_files_args, "diff-files",
607+
"--ignore-submodules=dirty", "--quiet", "--",
608+
path, NULL);
609+
610+
git_config(git_diff_basic_config, NULL);
611+
init_revisions(&rev, prefix);
612+
rev.abbrev = 0;
613+
diff_files_args.argc = setup_revisions(diff_files_args.argc,
614+
diff_files_args.argv,
615+
&rev, NULL);
616+
diff_files_result = run_diff_files(&rev, 0);
617+
618+
if (!diff_result_code(&rev.diffopt, diff_files_result)) {
619+
print_status(flags, ' ', path, ce_oid,
620+
displaypath);
621+
} else if (!(flags & OPT_CACHED)) {
622+
struct object_id oid;
623+
624+
if (refs_head_ref(get_submodule_ref_store(path),
625+
handle_submodule_head_ref, &oid))
626+
die(_("could not resolve HEAD ref inside the"
627+
"submodule '%s'"), path);
628+
629+
print_status(flags, '+', path, &oid, displaypath);
630+
} else {
631+
print_status(flags, '+', path, ce_oid, displaypath);
632+
}
633+
634+
if (flags & OPT_RECURSIVE) {
635+
struct child_process cpr = CHILD_PROCESS_INIT;
636+
637+
cpr.git_cmd = 1;
638+
cpr.dir = path;
639+
prepare_submodule_repo_env(&cpr.env_array);
640+
641+
argv_array_push(&cpr.args, "--super-prefix");
642+
argv_array_pushf(&cpr.args, "%s/", displaypath);
643+
argv_array_pushl(&cpr.args, "submodule--helper", "status",
644+
"--recursive", NULL);
645+
646+
if (flags & OPT_CACHED)
647+
argv_array_push(&cpr.args, "--cached");
648+
649+
if (flags & OPT_QUIET)
650+
argv_array_push(&cpr.args, "--quiet");
651+
652+
if (run_command(&cpr))
653+
die(_("failed to recurse into submodule '%s'"), path);
654+
}
655+
656+
cleanup:
657+
argv_array_clear(&diff_files_args);
658+
free(displaypath);
659+
}
660+
661+
static void status_submodule_cb(const struct cache_entry *list_item,
662+
void *cb_data)
663+
{
664+
struct status_cb *info = cb_data;
665+
status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
666+
info->prefix, info->flags);
667+
}
668+
669+
static int module_status(int argc, const char **argv, const char *prefix)
670+
{
671+
struct status_cb info = STATUS_CB_INIT;
672+
struct pathspec pathspec;
673+
struct module_list list = MODULE_LIST_INIT;
674+
int quiet = 0;
675+
676+
struct option module_status_options[] = {
677+
OPT__QUIET(&quiet, N_("Suppress submodule status output")),
678+
OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
679+
OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
680+
OPT_END()
681+
};
682+
683+
const char *const git_submodule_helper_usage[] = {
684+
N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
685+
NULL
686+
};
687+
688+
argc = parse_options(argc, argv, prefix, module_status_options,
689+
git_submodule_helper_usage, 0);
690+
691+
if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
692+
return 1;
693+
694+
info.prefix = prefix;
695+
if (quiet)
696+
info.flags |= OPT_QUIET;
697+
698+
for_each_listed_submodule(&list, status_submodule_cb, &info);
461699

462700
return 0;
463701
}
@@ -1259,6 +1497,7 @@ static struct cmd_struct commands[] = {
12591497
{"resolve-relative-url", resolve_relative_url, 0},
12601498
{"resolve-relative-url-test", resolve_relative_url_test, 0},
12611499
{"init", module_init, SUPPORT_SUPER_PREFIX},
1500+
{"status", module_status, SUPPORT_SUPER_PREFIX},
12621501
{"remote-branch", resolve_remote_submodule_branch, 0},
12631502
{"push-check", push_check, 0},
12641503
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},

0 commit comments

Comments
 (0)