|
13 | 13 | #include "remote.h"
|
14 | 14 | #include "refs.h"
|
15 | 15 | #include "connect.h"
|
| 16 | +#include "revision.h" |
| 17 | +#include "diffcore.h" |
| 18 | +#include "diff.h" |
16 | 19 |
|
17 | 20 | #define OPT_QUIET (1 << 0)
|
| 21 | +#define OPT_CACHED (1 << 1) |
| 22 | +#define OPT_RECURSIVE (1 << 2) |
18 | 23 |
|
19 | 24 | typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
|
20 | 25 | void *cb_data);
|
@@ -245,6 +250,44 @@ static char *get_submodule_displaypath(const char *path, const char *prefix)
|
245 | 250 | }
|
246 | 251 | }
|
247 | 252 |
|
| 253 | +static char *compute_rev_name(const char *sub_path, const char* object_id) |
| 254 | +{ |
| 255 | + struct strbuf sb = STRBUF_INIT; |
| 256 | + const char ***d; |
| 257 | + |
| 258 | + static const char *describe_bare[] = { NULL }; |
| 259 | + |
| 260 | + static const char *describe_tags[] = { "--tags", NULL }; |
| 261 | + |
| 262 | + static const char *describe_contains[] = { "--contains", NULL }; |
| 263 | + |
| 264 | + static const char *describe_all_always[] = { "--all", "--always", NULL }; |
| 265 | + |
| 266 | + static const char **describe_argv[] = { describe_bare, describe_tags, |
| 267 | + describe_contains, |
| 268 | + describe_all_always, NULL }; |
| 269 | + |
| 270 | + for (d = describe_argv; *d; d++) { |
| 271 | + struct child_process cp = CHILD_PROCESS_INIT; |
| 272 | + prepare_submodule_repo_env(&cp.env_array); |
| 273 | + cp.dir = sub_path; |
| 274 | + cp.git_cmd = 1; |
| 275 | + cp.no_stderr = 1; |
| 276 | + |
| 277 | + argv_array_push(&cp.args, "describe"); |
| 278 | + argv_array_pushv(&cp.args, *d); |
| 279 | + argv_array_push(&cp.args, object_id); |
| 280 | + |
| 281 | + if (!capture_command(&cp, &sb, 0)) { |
| 282 | + strbuf_strip_suffix(&sb, "\n"); |
| 283 | + return strbuf_detach(&sb, NULL); |
| 284 | + } |
| 285 | + } |
| 286 | + |
| 287 | + strbuf_release(&sb); |
| 288 | + return NULL; |
| 289 | +} |
| 290 | + |
248 | 291 | struct module_list {
|
249 | 292 | const struct cache_entry **entries;
|
250 | 293 | int alloc, nr;
|
@@ -504,6 +547,160 @@ static int module_init(int argc, const char **argv, const char *prefix)
|
504 | 547 | return 0;
|
505 | 548 | }
|
506 | 549 |
|
| 550 | +struct status_cb { |
| 551 | + const char *prefix; |
| 552 | + unsigned int flags; |
| 553 | +}; |
| 554 | + |
| 555 | +#define STATUS_CB_INIT { NULL, 0 } |
| 556 | + |
| 557 | +static void print_status(unsigned int flags, char state, const char *path, |
| 558 | + const struct object_id *oid, const char *displaypath) |
| 559 | +{ |
| 560 | + if (flags & OPT_QUIET) |
| 561 | + return; |
| 562 | + |
| 563 | + printf("%c%s %s", state, oid_to_hex(oid), displaypath); |
| 564 | + |
| 565 | + if (state == ' ' || state == '+') |
| 566 | + printf(" (%s)", compute_rev_name(path, oid_to_hex(oid))); |
| 567 | + |
| 568 | + printf("\n"); |
| 569 | +} |
| 570 | + |
| 571 | +static int handle_submodule_head_ref(const char *refname, |
| 572 | + const struct object_id *oid, int flags, |
| 573 | + void *cb_data) |
| 574 | +{ |
| 575 | + struct object_id *output = cb_data; |
| 576 | + if (oid) |
| 577 | + oidcpy(output, oid); |
| 578 | + |
| 579 | + return 0; |
| 580 | +} |
| 581 | + |
| 582 | +static void status_submodule(const char *path, const struct object_id *ce_oid, |
| 583 | + unsigned int ce_flags, const char *prefix, |
| 584 | + unsigned int flags) |
| 585 | +{ |
| 586 | + char *displaypath; |
| 587 | + struct argv_array diff_files_args = ARGV_ARRAY_INIT; |
| 588 | + struct rev_info rev; |
| 589 | + int diff_files_result; |
| 590 | + |
| 591 | + if (!submodule_from_path(&null_oid, path)) |
| 592 | + die(_("no submodule mapping found in .gitmodules for path '%s'"), |
| 593 | + path); |
| 594 | + |
| 595 | + displaypath = get_submodule_displaypath(path, prefix); |
| 596 | + |
| 597 | + if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) { |
| 598 | + print_status(flags, 'U', path, &null_oid, displaypath); |
| 599 | + goto cleanup; |
| 600 | + } |
| 601 | + |
| 602 | + if (!is_submodule_active(the_repository, path)) { |
| 603 | + print_status(flags, '-', path, ce_oid, displaypath); |
| 604 | + goto cleanup; |
| 605 | + } |
| 606 | + |
| 607 | + argv_array_pushl(&diff_files_args, "diff-files", |
| 608 | + "--ignore-submodules=dirty", "--quiet", "--", |
| 609 | + path, NULL); |
| 610 | + |
| 611 | + git_config(git_diff_basic_config, NULL); |
| 612 | + init_revisions(&rev, prefix); |
| 613 | + rev.abbrev = 0; |
| 614 | + diff_files_args.argc = setup_revisions(diff_files_args.argc, |
| 615 | + diff_files_args.argv, |
| 616 | + &rev, NULL); |
| 617 | + diff_files_result = run_diff_files(&rev, 0); |
| 618 | + |
| 619 | + if (!diff_result_code(&rev.diffopt, diff_files_result)) { |
| 620 | + print_status(flags, ' ', path, ce_oid, |
| 621 | + displaypath); |
| 622 | + } else if (!(flags & OPT_CACHED)) { |
| 623 | + struct object_id oid; |
| 624 | + |
| 625 | + if (refs_head_ref(get_submodule_ref_store(path), |
| 626 | + handle_submodule_head_ref, &oid)) |
| 627 | + die(_("could not resolve HEAD ref inside the" |
| 628 | + "submodule '%s'"), path); |
| 629 | + |
| 630 | + print_status(flags, '+', path, &oid, displaypath); |
| 631 | + } else { |
| 632 | + print_status(flags, '+', path, ce_oid, displaypath); |
| 633 | + } |
| 634 | + |
| 635 | + if (flags & OPT_RECURSIVE) { |
| 636 | + struct child_process cpr = CHILD_PROCESS_INIT; |
| 637 | + |
| 638 | + cpr.git_cmd = 1; |
| 639 | + cpr.dir = path; |
| 640 | + prepare_submodule_repo_env(&cpr.env_array); |
| 641 | + |
| 642 | + argv_array_push(&cpr.args, "--super-prefix"); |
| 643 | + argv_array_pushf(&cpr.args, "%s/", displaypath); |
| 644 | + argv_array_pushl(&cpr.args, "submodule--helper", "status", |
| 645 | + "--recursive", NULL); |
| 646 | + |
| 647 | + if (flags & OPT_CACHED) |
| 648 | + argv_array_push(&cpr.args, "--cached"); |
| 649 | + |
| 650 | + if (flags & OPT_QUIET) |
| 651 | + argv_array_push(&cpr.args, "--quiet"); |
| 652 | + |
| 653 | + if (run_command(&cpr)) |
| 654 | + die(_("failed to recurse into submodule '%s'"), path); |
| 655 | + } |
| 656 | + |
| 657 | +cleanup: |
| 658 | + argv_array_clear(&diff_files_args); |
| 659 | + free(displaypath); |
| 660 | +} |
| 661 | + |
| 662 | +static void status_submodule_cb(const struct cache_entry *list_item, |
| 663 | + void *cb_data) |
| 664 | +{ |
| 665 | + struct status_cb *info = cb_data; |
| 666 | + status_submodule(list_item->name, &list_item->oid, list_item->ce_flags, |
| 667 | + info->prefix, info->flags); |
| 668 | +} |
| 669 | + |
| 670 | +static int module_status(int argc, const char **argv, const char *prefix) |
| 671 | +{ |
| 672 | + struct status_cb info = STATUS_CB_INIT; |
| 673 | + struct pathspec pathspec; |
| 674 | + struct module_list list = MODULE_LIST_INIT; |
| 675 | + int quiet = 0; |
| 676 | + |
| 677 | + struct option module_status_options[] = { |
| 678 | + OPT__QUIET(&quiet, N_("Suppress submodule status output")), |
| 679 | + OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED), |
| 680 | + OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE), |
| 681 | + OPT_END() |
| 682 | + }; |
| 683 | + |
| 684 | + const char *const git_submodule_helper_usage[] = { |
| 685 | + N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"), |
| 686 | + NULL |
| 687 | + }; |
| 688 | + |
| 689 | + argc = parse_options(argc, argv, prefix, module_status_options, |
| 690 | + git_submodule_helper_usage, 0); |
| 691 | + |
| 692 | + if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) |
| 693 | + return 1; |
| 694 | + |
| 695 | + info.prefix = prefix; |
| 696 | + if (quiet) |
| 697 | + info.flags |= OPT_QUIET; |
| 698 | + |
| 699 | + for_each_listed_submodule(&list, status_submodule_cb, &info); |
| 700 | + |
| 701 | + return 0; |
| 702 | +} |
| 703 | + |
507 | 704 | static int module_name(int argc, const char **argv, const char *prefix)
|
508 | 705 | {
|
509 | 706 | const struct submodule *sub;
|
@@ -1301,6 +1498,7 @@ static struct cmd_struct commands[] = {
|
1301 | 1498 | {"resolve-relative-url", resolve_relative_url, 0},
|
1302 | 1499 | {"resolve-relative-url-test", resolve_relative_url_test, 0},
|
1303 | 1500 | {"init", module_init, SUPPORT_SUPER_PREFIX},
|
| 1501 | + {"status", module_status, SUPPORT_SUPER_PREFIX}, |
1304 | 1502 | {"remote-branch", resolve_remote_submodule_branch, 0},
|
1305 | 1503 | {"push-check", push_check, 0},
|
1306 | 1504 | {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
|
|
0 commit comments