@matheusbsbs / matheus-bagliano / commits / e83e3333b5
Fork 0

submodule: port submodule subcommand 'summary' from shell to C

Convert submodule subcommand 'summary' to a builtin and call it via 'git-submodule.sh'. The shell version had to call $diff_cmd twice, once to find the modified modules cared by the user and then again, with that list of modules to do various operations for computing the summary of those modules. On the other hand, the C version does not need a second call to $diff_cmd since it reuses the module list from the first call to do the aforementioned tasks. In the C version, we use the combination of setting a child process' working directory to the submodule path and then calling 'prepare_submodule_repo_env()' which also sets the 'GIT_DIR' to '.git', so that we can be certain that those spawned processes will not access the superproject's ODB by mistake. A behavioural difference between the C and the shell version is that the shell version outputs two line feeds after the 'git log' output when run outside of the tests while the C version outputs one line feed in any case. The reason for this is that the shell version calls log with '--pretty=format:<fmt>' whose output is followed by two echo calls; 'format' does not have "terminator" semantics like its 'tformat' counterpart. So, the log output is terminated by a newline only when invoked by the user and not when invoked from the scripts. This results in the one & two line feed differences in the shell version. On the other hand, the C version calls log with '--pretty=<fmt>' which is equivalent to '--pretty:tformat:<fmt>' which is then followed by a 'printf("\n")'. Due to its "terminator" semantics the log output is always terminated by newline and hence one line feed in any case. Also, when we try to pass an option-like argument after a non-option argument, for instance: git submodule summary HEAD --foo-bar (or) git submodule summary HEAD --cached That argument would be treated like a path to the submodule for which the user is requesting a summary. So, the option ends up having no effect. Though, passing '--quiet' is an exception to this: git submodule summary HEAD --quiet While 'summary' doesn't support '--quiet', we don't get an output for the above command as '--quiet' is treated as a path which means we get an output only if a submodule whose path is '--quiet' exists. The error message in case of computing a summary for non-existent submodules in the C version is different from that of the shell version. Since the new error message is not marked for translation, change the 'test_i18ngrep' in t7421.4 to 'grep'. Mentored-by: Christian Couder <chriscool@tuxfamily.org> Mentored-by: Stefan Beller <stefanbeller@gmail.com> Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com> Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de> Signed-off-by: Prathamesh Chavan <pc44800@gmail.com> Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Prathamesh Chavan committed Aug 13, 2020 at 01:14 UTC e83e3333b5770815e9c2e3aee48c305257385bbb
3 files changed +431 -186
builtin/submodule--helper.c
+429
@@ -927,6 +927,434 @@ static int module_name(int argc, const char **argv, const char *prefix)
927 return 0;
928 }
929
930 +struct module_cb {
931 + unsigned int mod_src;
932 + unsigned int mod_dst;
933 + struct object_id oid_src;
934 + struct object_id oid_dst;
935 + char status;
936 + const char *sm_path;
937 +};
938 +#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL }
939 +
940 +struct module_cb_list {
941 + struct module_cb **entries;
942 + int alloc, nr;
943 +};
944 +#define MODULE_CB_LIST_INIT { NULL, 0, 0 }
945 +
946 +struct summary_cb {
947 + int argc;
948 + const char **argv;
949 + const char *prefix;
950 + unsigned int cached: 1;
951 + unsigned int for_status: 1;
952 + unsigned int files: 1;
953 + int summary_limit;
954 +};
955 +#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0 }
956 +
957 +enum diff_cmd {
958 + DIFF_INDEX,
959 + DIFF_FILES
960 +};
961 +
962 +static char* verify_submodule_committish(const char *sm_path,
963 + const char *committish)
964 +{
965 + struct child_process cp_rev_parse = CHILD_PROCESS_INIT;
966 + struct strbuf result = STRBUF_INIT;
967 +
968 + cp_rev_parse.git_cmd = 1;
969 + cp_rev_parse.dir = sm_path;
970 + prepare_submodule_repo_env(&cp_rev_parse.env_array);
971 + strvec_pushl(&cp_rev_parse.args, "rev-parse", "-q", "--short", NULL);
972 + strvec_pushf(&cp_rev_parse.args, "%s^0", committish);
973 + strvec_push(&cp_rev_parse.args, "--");
974 +
975 + if (capture_command(&cp_rev_parse, &result, 0))
976 + return NULL;
977 +
978 + strbuf_trim_trailing_newline(&result);
979 + return strbuf_detach(&result, NULL);
980 +}
981 +
982 +static void print_submodule_summary(struct summary_cb *info, char* errmsg,
983 + int total_commits, const char *displaypath,
984 + const char *src_abbrev, const char *dst_abbrev,
985 + int missing_src, int missing_dst,
986 + struct module_cb *p)
987 +{
988 + if (p->status == 'T') {
989 + if (S_ISGITLINK(p->mod_dst))
990 + printf(_("* %s %s(blob)->%s(submodule)"),
991 + displaypath, src_abbrev, dst_abbrev);
992 + else
993 + printf(_("* %s %s(submodule)->%s(blob)"),
994 + displaypath, src_abbrev, dst_abbrev);
995 + } else {
996 + printf("* %s %s...%s",
997 + displaypath, src_abbrev, dst_abbrev);
998 + }
999 +
1000 + if (total_commits < 0)
1001 + printf(":\n");
1002 + else
1003 + printf(" (%d):\n", total_commits);
1004 +
1005 + if (errmsg) {
1006 + printf(_("%s"), errmsg);
1007 + } else if (total_commits > 0) {
1008 + struct child_process cp_log = CHILD_PROCESS_INIT;
1009 +
1010 + cp_log.git_cmd = 1;
1011 + cp_log.dir = p->sm_path;
1012 + prepare_submodule_repo_env(&cp_log.env_array);
1013 + strvec_pushl(&cp_log.args, "log", NULL);
1014 +
1015 + if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst)) {
1016 + if (info->summary_limit > 0)
1017 + strvec_pushf(&cp_log.args, "-%d",
1018 + info->summary_limit);
1019 +
1020 + strvec_pushl(&cp_log.args, "--pretty= %m %s",
1021 + "--first-parent", NULL);
1022 + strvec_pushf(&cp_log.args, "%s...%s",
1023 + src_abbrev, dst_abbrev);
1024 + } else if (S_ISGITLINK(p->mod_dst)) {
1025 + strvec_pushl(&cp_log.args, "--pretty= > %s",
1026 + "-1", dst_abbrev, NULL);
1027 + } else {
1028 + strvec_pushl(&cp_log.args, "--pretty= < %s",
1029 + "-1", src_abbrev, NULL);
1030 + }
1031 + run_command(&cp_log);
1032 + }
1033 + printf("\n");
1034 +}
1035 +
1036 +static void generate_submodule_summary(struct summary_cb *info,
1037 + struct module_cb *p)
1038 +{
1039 + char *displaypath, *src_abbrev, *dst_abbrev;
1040 + int missing_src = 0, missing_dst = 0;
1041 + char *errmsg = NULL;
1042 + int total_commits = -1;
1043 +
1044 + if (!info->cached && oideq(&p->oid_dst, &null_oid)) {
1045 + if (S_ISGITLINK(p->mod_dst)) {
1046 + struct ref_store *refs = get_submodule_ref_store(p->sm_path);
1047 + if (refs)
1048 + refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst);
1049 + } else if (S_ISLNK(p->mod_dst) || S_ISREG(p->mod_dst)) {
1050 + struct stat st;
1051 + int fd = open(p->sm_path, O_RDONLY);
1052 +
1053 + if (fd < 0 || fstat(fd, &st) < 0 ||
1054 + index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB,
1055 + p->sm_path, 0))
1056 + error(_("couldn't hash object from '%s'"), p->sm_path);
1057 + } else {
1058 + /* for a submodule removal (mode:0000000), don't warn */
1059 + if (p->mod_dst)
1060 + warning(_("unexpected mode %d\n"), p->mod_dst);
1061 + }
1062 + }
1063 +
1064 + if (S_ISGITLINK(p->mod_src)) {
1065 + src_abbrev = verify_submodule_committish(p->sm_path,
1066 + oid_to_hex(&p->oid_src));
1067 + if (!src_abbrev) {
1068 + missing_src = 1;
1069 + /*
1070 + * As `rev-parse` failed, we fallback to getting
1071 + * the abbreviated hash using oid_src. We do
1072 + * this as we might still need the abbreviated
1073 + * hash in cases like a submodule type change, etc.
1074 + */
1075 + src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
1076 + }
1077 + } else {
1078 + /*
1079 + * The source does not point to a submodule.
1080 + * So, we fallback to getting the abbreviation using
1081 + * oid_src as we might still need the abbreviated
1082 + * hash in cases like submodule add, etc.
1083 + */
1084 + src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
1085 + }
1086 +
1087 + if (S_ISGITLINK(p->mod_dst)) {
1088 + dst_abbrev = verify_submodule_committish(p->sm_path,
1089 + oid_to_hex(&p->oid_dst));
1090 + if (!dst_abbrev) {
1091 + missing_dst = 1;
1092 + /*
1093 + * As `rev-parse` failed, we fallback to getting
1094 + * the abbreviated hash using oid_dst. We do
1095 + * this as we might still need the abbreviated
1096 + * hash in cases like a submodule type change, etc.
1097 + */
1098 + dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
1099 + }
1100 + } else {
1101 + /*
1102 + * The destination does not point to a submodule.
1103 + * So, we fallback to getting the abbreviation using
1104 + * oid_dst as we might still need the abbreviated
1105 + * hash in cases like a submodule removal, etc.
1106 + */
1107 + dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
1108 + }
1109 +
1110 + displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
1111 +
1112 + if (!missing_src && !missing_dst) {
1113 + struct child_process cp_rev_list = CHILD_PROCESS_INIT;
1114 + struct strbuf sb_rev_list = STRBUF_INIT;
1115 +
1116 + strvec_pushl(&cp_rev_list.args, "rev-list",
1117 + "--first-parent", "--count", NULL);
1118 + if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst))
1119 + strvec_pushf(&cp_rev_list.args, "%s...%s",
1120 + src_abbrev, dst_abbrev);
1121 + else
1122 + strvec_push(&cp_rev_list.args, S_ISGITLINK(p->mod_src) ?
1123 + src_abbrev : dst_abbrev);
1124 + strvec_push(&cp_rev_list.args, "--");
1125 +
1126 + cp_rev_list.git_cmd = 1;
1127 + cp_rev_list.dir = p->sm_path;
1128 + prepare_submodule_repo_env(&cp_rev_list.env_array);
1129 +
1130 + if (!capture_command(&cp_rev_list, &sb_rev_list, 0))
1131 + total_commits = atoi(sb_rev_list.buf);
1132 +
1133 + strbuf_release(&sb_rev_list);
1134 + } else {
1135 + /*
1136 + * Don't give error msg for modification whose dst is not
1137 + * submodule, i.e., deleted or changed to blob
1138 + */
1139 + if (S_ISGITLINK(p->mod_dst)) {
1140 + struct strbuf errmsg_str = STRBUF_INIT;
1141 + if (missing_src && missing_dst) {
1142 + strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commits %s and %s\n",
1143 + displaypath, oid_to_hex(&p->oid_src),
1144 + oid_to_hex(&p->oid_dst));
1145 + } else {
1146 + strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commit %s\n",
1147 + displaypath, missing_src ?
1148 + oid_to_hex(&p->oid_src) :
1149 + oid_to_hex(&p->oid_dst));
1150 + }
1151 + errmsg = strbuf_detach(&errmsg_str, NULL);
1152 + }
1153 + }
1154 +
1155 + print_submodule_summary(info, errmsg, total_commits,
1156 + displaypath, src_abbrev,
1157 + dst_abbrev, missing_src,
1158 + missing_dst, p);
1159 +
1160 + free(displaypath);
1161 + free(src_abbrev);
1162 + free(dst_abbrev);
1163 +}
1164 +
1165 +static void prepare_submodule_summary(struct summary_cb *info,
1166 + struct module_cb_list *list)
1167 +{
1168 + int i;
1169 + for (i = 0; i < list->nr; i++) {
1170 + const struct submodule *sub;
1171 + struct module_cb *p = list->entries[i];
1172 + struct strbuf sm_gitdir = STRBUF_INIT;
1173 +
1174 + if (p->status == 'D' || p->status == 'T') {
1175 + generate_submodule_summary(info, p);
1176 + continue;
1177 + }
1178 +
1179 + if (info->for_status && p->status != 'A' &&
1180 + (sub = submodule_from_path(the_repository,
1181 + &null_oid, p->sm_path))) {
1182 + char *config_key = NULL;
1183 + const char *value;
1184 + int ignore_all = 0;
1185 +
1186 + config_key = xstrfmt("submodule.%s.ignore",
1187 + sub->name);
1188 + if (!git_config_get_string_const(config_key, &value))
1189 + ignore_all = !strcmp(value, "all");
1190 + else if (sub->ignore)
1191 + ignore_all = !strcmp(sub->ignore, "all");
1192 +
1193 + free(config_key);
1194 + if (ignore_all)
1195 + continue;
1196 + }
1197 +
1198 + /* Also show added or modified modules which are checked out */
1199 + strbuf_addstr(&sm_gitdir, p->sm_path);
1200 + if (is_nonbare_repository_dir(&sm_gitdir))
1201 + generate_submodule_summary(info, p);
1202 + strbuf_release(&sm_gitdir);
1203 + }
1204 +}
1205 +
1206 +static void submodule_summary_callback(struct diff_queue_struct *q,
1207 + struct diff_options *options,
1208 + void *data)
1209 +{
1210 + int i;
1211 + struct module_cb_list *list = data;
1212 + for (i = 0; i < q->nr; i++) {
1213 + struct diff_filepair *p = q->queue[i];
1214 + struct module_cb *temp;
1215 +
1216 + if (!S_ISGITLINK(p->one->mode) && !S_ISGITLINK(p->two->mode))
1217 + continue;
1218 + temp = (struct module_cb*)malloc(sizeof(struct module_cb));
1219 + temp->mod_src = p->one->mode;
1220 + temp->mod_dst = p->two->mode;
1221 + temp->oid_src = p->one->oid;
1222 + temp->oid_dst = p->two->oid;
1223 + temp->status = p->status;
1224 + temp->sm_path = xstrdup(p->one->path);
1225 +
1226 + ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
1227 + list->entries[list->nr++] = temp;
1228 + }
1229 +}
1230 +
1231 +static const char *get_diff_cmd(enum diff_cmd diff_cmd)
1232 +{
1233 + switch (diff_cmd) {
1234 + case DIFF_INDEX: return "diff-index";
1235 + case DIFF_FILES: return "diff-files";
1236 + default: BUG("bad diff_cmd value %d", diff_cmd);
1237 + }
1238 +}
1239 +
1240 +static int compute_summary_module_list(struct object_id *head_oid,
1241 + struct summary_cb *info,
1242 + enum diff_cmd diff_cmd)
1243 +{
1244 + struct strvec diff_args = STRVEC_INIT;
1245 + struct rev_info rev;
1246 + struct module_cb_list list = MODULE_CB_LIST_INIT;
1247 +
1248 + strvec_push(&diff_args, get_diff_cmd(diff_cmd));
1249 + if (info->cached)
1250 + strvec_push(&diff_args, "--cached");
1251 + strvec_pushl(&diff_args, "--ignore-submodules=dirty", "--raw", NULL);
1252 + if (head_oid)
1253 + strvec_push(&diff_args, oid_to_hex(head_oid));
1254 + strvec_push(&diff_args, "--");
1255 + if (info->argc)
1256 + strvec_pushv(&diff_args, info->argv);
1257 +
1258 + git_config(git_diff_basic_config, NULL);
1259 + init_revisions(&rev, info->prefix);
1260 + rev.abbrev = 0;
1261 + precompose_argv(diff_args.nr, diff_args.v);
1262 + setup_revisions(diff_args.nr, diff_args.v, &rev, NULL);
1263 + rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK;
1264 + rev.diffopt.format_callback = submodule_summary_callback;
1265 + rev.diffopt.format_callback_data = &list;
1266 +
1267 + if (!info->cached) {
1268 + if (diff_cmd == DIFF_INDEX)
1269 + setup_work_tree();
1270 + if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
1271 + perror("read_cache_preload");
1272 + return -1;
1273 + }
1274 + } else if (read_cache() < 0) {
1275 + perror("read_cache");
1276 + return -1;
1277 + }
1278 +
1279 + if (diff_cmd == DIFF_INDEX)
1280 + run_diff_index(&rev, info->cached);
1281 + else
1282 + run_diff_files(&rev, 0);
1283 + prepare_submodule_summary(info, &list);
1284 + strvec_clear(&diff_args);
1285 + return 0;
1286 +}
1287 +
1288 +static int module_summary(int argc, const char **argv, const char *prefix)
1289 +{
1290 + struct summary_cb info = SUMMARY_CB_INIT;
1291 + int cached = 0;
1292 + int for_status = 0;
1293 + int files = 0;
1294 + int summary_limit = -1;
1295 + enum diff_cmd diff_cmd = DIFF_INDEX;
1296 + struct object_id head_oid;
1297 + int ret;
1298 +
1299 + struct option module_summary_options[] = {
1300 + OPT_BOOL(0, "cached", &cached,
1301 + N_("use the commit stored in the index instead of the submodule HEAD")),
1302 + OPT_BOOL(0, "files", &files,
1303 + N_("to compare the commit in the index with that in the submodule HEAD")),
1304 + OPT_BOOL(0, "for-status", &for_status,
1305 + N_("skip submodules with 'ignore_config' value set to 'all'")),
1306 + OPT_INTEGER('n', "summary-limit", &summary_limit,
1307 + N_("limit the summary size")),
1308 + OPT_END()
1309 + };
1310 +
1311 + const char *const git_submodule_helper_usage[] = {
1312 + N_("git submodule--helper summary [<options>] [commit] [--] [<path>]"),
1313 + NULL
1314 + };
1315 +
1316 + argc = parse_options(argc, argv, prefix, module_summary_options,
1317 + git_submodule_helper_usage, 0);
1318 +
1319 + if (!summary_limit)
1320 + return 0;
1321 +
1322 + if (!get_oid(argc ? argv[0] : "HEAD", &head_oid)) {
1323 + if (argc) {
1324 + argv++;
1325 + argc--;
1326 + }
1327 + } else if (!argc || !strcmp(argv[0], "HEAD")) {
1328 + /* before the first commit: compare with an empty tree */
1329 + oidcpy(&head_oid, the_hash_algo->empty_tree);
1330 + if (argc) {
1331 + argv++;
1332 + argc--;
1333 + }
1334 + } else {
1335 + if (get_oid("HEAD", &head_oid))
1336 + die(_("could not fetch a revision for HEAD"));
1337 + }
1338 +
1339 + if (files) {
1340 + if (cached)
1341 + die(_("--cached and --files are mutually exclusive"));
1342 + diff_cmd = DIFF_FILES;
1343 + }
1344 +
1345 + info.argc = argc;
1346 + info.argv = argv;
1347 + info.prefix = prefix;
1348 + info.cached = !!cached;
1349 + info.files = !!files;
1350 + info.for_status = !!for_status;
1351 + info.summary_limit = summary_limit;
1352 +
1353 + ret = compute_summary_module_list((diff_cmd == DIFF_INDEX) ? &head_oid : NULL,
1354 + &info, diff_cmd);
1355 + return ret;
1356 +}
1357 +
1358 struct sync_cb {
1359 const char *prefix;
1360 unsigned int flags;
@@ -2341,6 +2769,7 @@ static struct cmd_struct commands[] = {
2769 {"print-default-remote", print_default_remote, 0},
2770 {"sync", module_sync, SUPPORT_SUPER_PREFIX},
2771 {"deinit", module_deinit, 0},
2772 + {"summary", module_summary, SUPPORT_SUPER_PREFIX},
2773 {"remote-branch", resolve_remote_submodule_branch, 0},
2774 {"push-check", push_check, 0},
2775 {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
git-submodule.sh
+1 -185
@@ -59,31 +59,6 @@ die_if_unmatched ()
59 fi
60 }
61
62 -#
63 -# Print a submodule configuration setting
64 -#
65 -# $1 = submodule name
66 -# $2 = option name
67 -# $3 = default value
68 -#
69 -# Checks in the usual git-config places first (for overrides),
70 -# otherwise it falls back on .gitmodules. This allows you to
71 -# distribute project-wide defaults in .gitmodules, while still
72 -# customizing individual repositories if necessary. If the option is
73 -# not in .gitmodules either, print a default value.
74 -#
75 -get_submodule_config () {
76 - name="$1"
77 - option="$2"
78 - default="$3"
79 - value=$(git config submodule."$name"."$option")
80 - if test -z "$value"
81 - then
82 - value=$(git submodule--helper config submodule."$name"."$option")
83 - fi
84 - printf '%s' "${value:-$default}"
85 -}
86 -
62 isnumber()
63 {
64 n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
@@ -831,166 +806,7 @@ cmd_summary() {
806 shift
807 done
808
834 - test $summary_limit = 0 && return
835 -
836 - if rev=$(git rev-parse -q --verify --default HEAD ${1+"$1"})
837 - then
838 - head=$rev
839 - test $# = 0 || shift
840 - elif test -z "$1" || test "$1" = "HEAD"
841 - then
842 - # before the first commit: compare with an empty tree
843 - head=$(git hash-object -w -t tree --stdin </dev/null)
844 - test -z "$1" || shift
845 - else
846 - head="HEAD"
847 - fi
848 -
849 - if [ -n "$files" ]
850 - then
851 - test -n "$cached" &&
852 - die "$(gettext "The --cached option cannot be used with the --files option")"
853 - diff_cmd=diff-files
854 - head=
855 - fi
856 -
857 - cd_to_toplevel
858 - eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
859 - # Get modified modules cared by user
860 - modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
861 - sane_egrep '^:([0-7]* )?160000' |
862 - while read -r mod_src mod_dst sha1_src sha1_dst status sm_path
863 - do
864 - # Always show modules deleted or type-changed (blob<->module)
865 - if test "$status" = D || test "$status" = T
866 - then
867 - printf '%s\n' "$sm_path"
868 - continue
869 - fi
870 - # Respect the ignore setting for --for-status.
871 - if test -n "$for_status"
872 - then
873 - name=$(git submodule--helper name "$sm_path")
874 - ignore_config=$(get_submodule_config "$name" ignore none)
875 - test $status != A && test $ignore_config = all && continue
876 - fi
877 - # Also show added or modified modules which are checked out
878 - GIT_DIR="$sm_path/.git" git rev-parse --git-dir >/dev/null 2>&1 &&
879 - printf '%s\n' "$sm_path"
880 - done
881 - )
882 -
883 - test -z "$modules" && return
884 -
885 - git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- $modules |
886 - sane_egrep '^:([0-7]* )?160000' |
887 - cut -c2- |
888 - while read -r mod_src mod_dst sha1_src sha1_dst status name
889 - do
890 - if test -z "$cached" &&
891 - is_zero_oid $sha1_dst
892 - then
893 - case "$mod_dst" in
894 - 160000)
895 - sha1_dst=$(GIT_DIR="$name/.git" git rev-parse HEAD)
896 - ;;
897 - 100644 | 100755 | 120000)
898 - sha1_dst=$(git hash-object $name)
899 - ;;
900 - 000000)
901 - ;; # removed
902 - *)
903 - # unexpected type
904 - eval_gettextln "unexpected mode \$mod_dst" >&2
905 - continue ;;
906 - esac
907 - fi
908 - missing_src=
909 - missing_dst=
910 -
911 - test $mod_src = 160000 &&
912 - ! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_src^0 >/dev/null &&
913 - missing_src=t
914 -
915 - test $mod_dst = 160000 &&
916 - ! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_dst^0 >/dev/null &&
917 - missing_dst=t
918 -
919 - display_name=$(git submodule--helper relative-path "$name" "$wt_prefix")
920 -
921 - total_commits=
922 - case "$missing_src,$missing_dst" in
923 - t,)
924 - errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commit \$sha1_src")"
925 - ;;
926 - ,t)
927 - errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commit \$sha1_dst")"
928 - ;;
929 - t,t)
930 - errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commits \$sha1_src and \$sha1_dst")"
931 - ;;
932 - *)
933 - errmsg=
934 - total_commits=$(
935 - if test $mod_src = 160000 && test $mod_dst = 160000
936 - then
937 - range="$sha1_src...$sha1_dst"
938 - elif test $mod_src = 160000
939 - then
940 - range=$sha1_src
941 - else
942 - range=$sha1_dst
943 - fi
944 - GIT_DIR="$name/.git" \
945 - git rev-list --first-parent $range -- | wc -l
946 - )
947 - total_commits=" ($(($total_commits + 0)))"
948 - ;;
949 - esac
950 -
951 - sha1_abbr_src=$(GIT_DIR="$name/.git" git rev-parse --short $sha1_src 2>/dev/null ||
952 - echo $sha1_src | cut -c1-7)
953 - sha1_abbr_dst=$(GIT_DIR="$name/.git" git rev-parse --short $sha1_dst 2>/dev/null ||
954 - echo $sha1_dst | cut -c1-7)
955 -
956 - if test $status = T
957 - then
958 - blob="$(gettext "blob")"
959 - submodule="$(gettext "submodule")"
960 - if test $mod_dst = 160000
961 - then
962 - echo "* $display_name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
963 - else
964 - echo "* $display_name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
965 - fi
966 - else
967 - echo "* $display_name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
968 - fi
969 - if test -n "$errmsg"
970 - then
971 - # Don't give error msg for modification whose dst is not submodule
972 - # i.e. deleted or changed to blob
973 - test $mod_dst = 160000 && echo "$errmsg"
974 - else
975 - if test $mod_src = 160000 && test $mod_dst = 160000
976 - then
977 - limit=
978 - test $summary_limit -gt 0 && limit="-$summary_limit"
979 - GIT_DIR="$name/.git" \
980 - git log $limit --pretty='format: %m %s' \
981 - --first-parent $sha1_src...$sha1_dst
982 - elif test $mod_dst = 160000
983 - then
984 - GIT_DIR="$name/.git" \
985 - git log --pretty='format: > %s' -1 $sha1_dst
986 - else
987 - GIT_DIR="$name/.git" \
988 - git log --pretty='format: < %s' -1 $sha1_src
989 - fi
990 - echo
991 - fi
992 - echo
993 - done
809 + git ${wt_prefix:+-C "$wt_prefix"} submodule--helper summary ${prefix:+--prefix "$prefix"} ${files:+--files} ${cached:+--cached} ${for_status:+--for-status} ${summary_limit:+-n $summary_limit} -- "$@"
810 }
811 #
812 # List all submodules, prefixed with:
t/t7421-submodule-summary-add.sh
+1 -1
@@ -58,7 +58,7 @@ test_expect_success 'submodule summary output for submodules with changed paths'
58 git commit -m "change submodule path" &&
59 rev=$(git -C sm rev-parse --short HEAD^) &&
60 git submodule summary HEAD^^ -- my-subm >actual 2>err &&
61 - test_i18ngrep "fatal:.*my-subm" err &&
61 + grep "fatal:.*my-subm" err &&
62 cat >expected <<-EOF &&
63 * my-subm ${rev}...0000000:
64