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;
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},