Merge branch 'ss/submodule-summary-in-c'
Yet another subcommand of "git submodule" is getting rewritten in C. * ss/submodule-summary-in-c: submodule: port submodule subcommand 'summary' from shell to C t7421: introduce a test script for verifying 'summary' output submodule: rename helper functions to avoid ambiguity submodule: remove extra line feeds between callback struct and macro
This commit is contained in:
@ -612,7 +612,6 @@ struct init_cb {
|
||||
const char *prefix;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
#define INIT_CB_INIT { NULL, 0 }
|
||||
|
||||
static void init_submodule(const char *path, const char *prefix,
|
||||
@ -742,7 +741,6 @@ struct status_cb {
|
||||
const char *prefix;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
#define STATUS_CB_INIT { NULL, 0 }
|
||||
|
||||
static void print_status(unsigned int flags, char state, const char *path,
|
||||
@ -929,11 +927,438 @@ static int module_name(int argc, const char **argv, const char *prefix)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct module_cb {
|
||||
unsigned int mod_src;
|
||||
unsigned int mod_dst;
|
||||
struct object_id oid_src;
|
||||
struct object_id oid_dst;
|
||||
char status;
|
||||
const char *sm_path;
|
||||
};
|
||||
#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL }
|
||||
|
||||
struct module_cb_list {
|
||||
struct module_cb **entries;
|
||||
int alloc, nr;
|
||||
};
|
||||
#define MODULE_CB_LIST_INIT { NULL, 0, 0 }
|
||||
|
||||
struct summary_cb {
|
||||
int argc;
|
||||
const char **argv;
|
||||
const char *prefix;
|
||||
unsigned int cached: 1;
|
||||
unsigned int for_status: 1;
|
||||
unsigned int files: 1;
|
||||
int summary_limit;
|
||||
};
|
||||
#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0 }
|
||||
|
||||
enum diff_cmd {
|
||||
DIFF_INDEX,
|
||||
DIFF_FILES
|
||||
};
|
||||
|
||||
static char* verify_submodule_committish(const char *sm_path,
|
||||
const char *committish)
|
||||
{
|
||||
struct child_process cp_rev_parse = CHILD_PROCESS_INIT;
|
||||
struct strbuf result = STRBUF_INIT;
|
||||
|
||||
cp_rev_parse.git_cmd = 1;
|
||||
cp_rev_parse.dir = sm_path;
|
||||
prepare_submodule_repo_env(&cp_rev_parse.env_array);
|
||||
strvec_pushl(&cp_rev_parse.args, "rev-parse", "-q", "--short", NULL);
|
||||
strvec_pushf(&cp_rev_parse.args, "%s^0", committish);
|
||||
strvec_push(&cp_rev_parse.args, "--");
|
||||
|
||||
if (capture_command(&cp_rev_parse, &result, 0))
|
||||
return NULL;
|
||||
|
||||
strbuf_trim_trailing_newline(&result);
|
||||
return strbuf_detach(&result, NULL);
|
||||
}
|
||||
|
||||
static void print_submodule_summary(struct summary_cb *info, char* errmsg,
|
||||
int total_commits, const char *displaypath,
|
||||
const char *src_abbrev, const char *dst_abbrev,
|
||||
int missing_src, int missing_dst,
|
||||
struct module_cb *p)
|
||||
{
|
||||
if (p->status == 'T') {
|
||||
if (S_ISGITLINK(p->mod_dst))
|
||||
printf(_("* %s %s(blob)->%s(submodule)"),
|
||||
displaypath, src_abbrev, dst_abbrev);
|
||||
else
|
||||
printf(_("* %s %s(submodule)->%s(blob)"),
|
||||
displaypath, src_abbrev, dst_abbrev);
|
||||
} else {
|
||||
printf("* %s %s...%s",
|
||||
displaypath, src_abbrev, dst_abbrev);
|
||||
}
|
||||
|
||||
if (total_commits < 0)
|
||||
printf(":\n");
|
||||
else
|
||||
printf(" (%d):\n", total_commits);
|
||||
|
||||
if (errmsg) {
|
||||
printf(_("%s"), errmsg);
|
||||
} else if (total_commits > 0) {
|
||||
struct child_process cp_log = CHILD_PROCESS_INIT;
|
||||
|
||||
cp_log.git_cmd = 1;
|
||||
cp_log.dir = p->sm_path;
|
||||
prepare_submodule_repo_env(&cp_log.env_array);
|
||||
strvec_pushl(&cp_log.args, "log", NULL);
|
||||
|
||||
if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst)) {
|
||||
if (info->summary_limit > 0)
|
||||
strvec_pushf(&cp_log.args, "-%d",
|
||||
info->summary_limit);
|
||||
|
||||
strvec_pushl(&cp_log.args, "--pretty= %m %s",
|
||||
"--first-parent", NULL);
|
||||
strvec_pushf(&cp_log.args, "%s...%s",
|
||||
src_abbrev, dst_abbrev);
|
||||
} else if (S_ISGITLINK(p->mod_dst)) {
|
||||
strvec_pushl(&cp_log.args, "--pretty= > %s",
|
||||
"-1", dst_abbrev, NULL);
|
||||
} else {
|
||||
strvec_pushl(&cp_log.args, "--pretty= < %s",
|
||||
"-1", src_abbrev, NULL);
|
||||
}
|
||||
run_command(&cp_log);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void generate_submodule_summary(struct summary_cb *info,
|
||||
struct module_cb *p)
|
||||
{
|
||||
char *displaypath, *src_abbrev, *dst_abbrev;
|
||||
int missing_src = 0, missing_dst = 0;
|
||||
char *errmsg = NULL;
|
||||
int total_commits = -1;
|
||||
|
||||
if (!info->cached && oideq(&p->oid_dst, &null_oid)) {
|
||||
if (S_ISGITLINK(p->mod_dst)) {
|
||||
struct ref_store *refs = get_submodule_ref_store(p->sm_path);
|
||||
if (refs)
|
||||
refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst);
|
||||
} else if (S_ISLNK(p->mod_dst) || S_ISREG(p->mod_dst)) {
|
||||
struct stat st;
|
||||
int fd = open(p->sm_path, O_RDONLY);
|
||||
|
||||
if (fd < 0 || fstat(fd, &st) < 0 ||
|
||||
index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB,
|
||||
p->sm_path, 0))
|
||||
error(_("couldn't hash object from '%s'"), p->sm_path);
|
||||
} else {
|
||||
/* for a submodule removal (mode:0000000), don't warn */
|
||||
if (p->mod_dst)
|
||||
warning(_("unexpected mode %d\n"), p->mod_dst);
|
||||
}
|
||||
}
|
||||
|
||||
if (S_ISGITLINK(p->mod_src)) {
|
||||
src_abbrev = verify_submodule_committish(p->sm_path,
|
||||
oid_to_hex(&p->oid_src));
|
||||
if (!src_abbrev) {
|
||||
missing_src = 1;
|
||||
/*
|
||||
* As `rev-parse` failed, we fallback to getting
|
||||
* the abbreviated hash using oid_src. We do
|
||||
* this as we might still need the abbreviated
|
||||
* hash in cases like a submodule type change, etc.
|
||||
*/
|
||||
src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* The source does not point to a submodule.
|
||||
* So, we fallback to getting the abbreviation using
|
||||
* oid_src as we might still need the abbreviated
|
||||
* hash in cases like submodule add, etc.
|
||||
*/
|
||||
src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
|
||||
}
|
||||
|
||||
if (S_ISGITLINK(p->mod_dst)) {
|
||||
dst_abbrev = verify_submodule_committish(p->sm_path,
|
||||
oid_to_hex(&p->oid_dst));
|
||||
if (!dst_abbrev) {
|
||||
missing_dst = 1;
|
||||
/*
|
||||
* As `rev-parse` failed, we fallback to getting
|
||||
* the abbreviated hash using oid_dst. We do
|
||||
* this as we might still need the abbreviated
|
||||
* hash in cases like a submodule type change, etc.
|
||||
*/
|
||||
dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* The destination does not point to a submodule.
|
||||
* So, we fallback to getting the abbreviation using
|
||||
* oid_dst as we might still need the abbreviated
|
||||
* hash in cases like a submodule removal, etc.
|
||||
*/
|
||||
dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
|
||||
}
|
||||
|
||||
displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
|
||||
|
||||
if (!missing_src && !missing_dst) {
|
||||
struct child_process cp_rev_list = CHILD_PROCESS_INIT;
|
||||
struct strbuf sb_rev_list = STRBUF_INIT;
|
||||
|
||||
strvec_pushl(&cp_rev_list.args, "rev-list",
|
||||
"--first-parent", "--count", NULL);
|
||||
if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst))
|
||||
strvec_pushf(&cp_rev_list.args, "%s...%s",
|
||||
src_abbrev, dst_abbrev);
|
||||
else
|
||||
strvec_push(&cp_rev_list.args, S_ISGITLINK(p->mod_src) ?
|
||||
src_abbrev : dst_abbrev);
|
||||
strvec_push(&cp_rev_list.args, "--");
|
||||
|
||||
cp_rev_list.git_cmd = 1;
|
||||
cp_rev_list.dir = p->sm_path;
|
||||
prepare_submodule_repo_env(&cp_rev_list.env_array);
|
||||
|
||||
if (!capture_command(&cp_rev_list, &sb_rev_list, 0))
|
||||
total_commits = atoi(sb_rev_list.buf);
|
||||
|
||||
strbuf_release(&sb_rev_list);
|
||||
} else {
|
||||
/*
|
||||
* Don't give error msg for modification whose dst is not
|
||||
* submodule, i.e., deleted or changed to blob
|
||||
*/
|
||||
if (S_ISGITLINK(p->mod_dst)) {
|
||||
struct strbuf errmsg_str = STRBUF_INIT;
|
||||
if (missing_src && missing_dst) {
|
||||
strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commits %s and %s\n",
|
||||
displaypath, oid_to_hex(&p->oid_src),
|
||||
oid_to_hex(&p->oid_dst));
|
||||
} else {
|
||||
strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commit %s\n",
|
||||
displaypath, missing_src ?
|
||||
oid_to_hex(&p->oid_src) :
|
||||
oid_to_hex(&p->oid_dst));
|
||||
}
|
||||
errmsg = strbuf_detach(&errmsg_str, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
print_submodule_summary(info, errmsg, total_commits,
|
||||
displaypath, src_abbrev,
|
||||
dst_abbrev, missing_src,
|
||||
missing_dst, p);
|
||||
|
||||
free(displaypath);
|
||||
free(src_abbrev);
|
||||
free(dst_abbrev);
|
||||
}
|
||||
|
||||
static void prepare_submodule_summary(struct summary_cb *info,
|
||||
struct module_cb_list *list)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < list->nr; i++) {
|
||||
const struct submodule *sub;
|
||||
struct module_cb *p = list->entries[i];
|
||||
struct strbuf sm_gitdir = STRBUF_INIT;
|
||||
|
||||
if (p->status == 'D' || p->status == 'T') {
|
||||
generate_submodule_summary(info, p);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (info->for_status && p->status != 'A' &&
|
||||
(sub = submodule_from_path(the_repository,
|
||||
&null_oid, p->sm_path))) {
|
||||
char *config_key = NULL;
|
||||
const char *value;
|
||||
int ignore_all = 0;
|
||||
|
||||
config_key = xstrfmt("submodule.%s.ignore",
|
||||
sub->name);
|
||||
if (!git_config_get_string_tmp(config_key, &value))
|
||||
ignore_all = !strcmp(value, "all");
|
||||
else if (sub->ignore)
|
||||
ignore_all = !strcmp(sub->ignore, "all");
|
||||
|
||||
free(config_key);
|
||||
if (ignore_all)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Also show added or modified modules which are checked out */
|
||||
strbuf_addstr(&sm_gitdir, p->sm_path);
|
||||
if (is_nonbare_repository_dir(&sm_gitdir))
|
||||
generate_submodule_summary(info, p);
|
||||
strbuf_release(&sm_gitdir);
|
||||
}
|
||||
}
|
||||
|
||||
static void submodule_summary_callback(struct diff_queue_struct *q,
|
||||
struct diff_options *options,
|
||||
void *data)
|
||||
{
|
||||
int i;
|
||||
struct module_cb_list *list = data;
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
struct module_cb *temp;
|
||||
|
||||
if (!S_ISGITLINK(p->one->mode) && !S_ISGITLINK(p->two->mode))
|
||||
continue;
|
||||
temp = (struct module_cb*)malloc(sizeof(struct module_cb));
|
||||
temp->mod_src = p->one->mode;
|
||||
temp->mod_dst = p->two->mode;
|
||||
temp->oid_src = p->one->oid;
|
||||
temp->oid_dst = p->two->oid;
|
||||
temp->status = p->status;
|
||||
temp->sm_path = xstrdup(p->one->path);
|
||||
|
||||
ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
|
||||
list->entries[list->nr++] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *get_diff_cmd(enum diff_cmd diff_cmd)
|
||||
{
|
||||
switch (diff_cmd) {
|
||||
case DIFF_INDEX: return "diff-index";
|
||||
case DIFF_FILES: return "diff-files";
|
||||
default: BUG("bad diff_cmd value %d", diff_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static int compute_summary_module_list(struct object_id *head_oid,
|
||||
struct summary_cb *info,
|
||||
enum diff_cmd diff_cmd)
|
||||
{
|
||||
struct strvec diff_args = STRVEC_INIT;
|
||||
struct rev_info rev;
|
||||
struct module_cb_list list = MODULE_CB_LIST_INIT;
|
||||
|
||||
strvec_push(&diff_args, get_diff_cmd(diff_cmd));
|
||||
if (info->cached)
|
||||
strvec_push(&diff_args, "--cached");
|
||||
strvec_pushl(&diff_args, "--ignore-submodules=dirty", "--raw", NULL);
|
||||
if (head_oid)
|
||||
strvec_push(&diff_args, oid_to_hex(head_oid));
|
||||
strvec_push(&diff_args, "--");
|
||||
if (info->argc)
|
||||
strvec_pushv(&diff_args, info->argv);
|
||||
|
||||
git_config(git_diff_basic_config, NULL);
|
||||
init_revisions(&rev, info->prefix);
|
||||
rev.abbrev = 0;
|
||||
precompose_argv(diff_args.nr, diff_args.v);
|
||||
setup_revisions(diff_args.nr, diff_args.v, &rev, NULL);
|
||||
rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK;
|
||||
rev.diffopt.format_callback = submodule_summary_callback;
|
||||
rev.diffopt.format_callback_data = &list;
|
||||
|
||||
if (!info->cached) {
|
||||
if (diff_cmd == DIFF_INDEX)
|
||||
setup_work_tree();
|
||||
if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
} else if (read_cache() < 0) {
|
||||
perror("read_cache");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (diff_cmd == DIFF_INDEX)
|
||||
run_diff_index(&rev, info->cached);
|
||||
else
|
||||
run_diff_files(&rev, 0);
|
||||
prepare_submodule_summary(info, &list);
|
||||
strvec_clear(&diff_args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int module_summary(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct summary_cb info = SUMMARY_CB_INIT;
|
||||
int cached = 0;
|
||||
int for_status = 0;
|
||||
int files = 0;
|
||||
int summary_limit = -1;
|
||||
enum diff_cmd diff_cmd = DIFF_INDEX;
|
||||
struct object_id head_oid;
|
||||
int ret;
|
||||
|
||||
struct option module_summary_options[] = {
|
||||
OPT_BOOL(0, "cached", &cached,
|
||||
N_("use the commit stored in the index instead of the submodule HEAD")),
|
||||
OPT_BOOL(0, "files", &files,
|
||||
N_("to compare the commit in the index with that in the submodule HEAD")),
|
||||
OPT_BOOL(0, "for-status", &for_status,
|
||||
N_("skip submodules with 'ignore_config' value set to 'all'")),
|
||||
OPT_INTEGER('n', "summary-limit", &summary_limit,
|
||||
N_("limit the summary size")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
const char *const git_submodule_helper_usage[] = {
|
||||
N_("git submodule--helper summary [<options>] [commit] [--] [<path>]"),
|
||||
NULL
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, prefix, module_summary_options,
|
||||
git_submodule_helper_usage, 0);
|
||||
|
||||
if (!summary_limit)
|
||||
return 0;
|
||||
|
||||
if (!get_oid(argc ? argv[0] : "HEAD", &head_oid)) {
|
||||
if (argc) {
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
} else if (!argc || !strcmp(argv[0], "HEAD")) {
|
||||
/* before the first commit: compare with an empty tree */
|
||||
oidcpy(&head_oid, the_hash_algo->empty_tree);
|
||||
if (argc) {
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
} else {
|
||||
if (get_oid("HEAD", &head_oid))
|
||||
die(_("could not fetch a revision for HEAD"));
|
||||
}
|
||||
|
||||
if (files) {
|
||||
if (cached)
|
||||
die(_("--cached and --files are mutually exclusive"));
|
||||
diff_cmd = DIFF_FILES;
|
||||
}
|
||||
|
||||
info.argc = argc;
|
||||
info.argv = argv;
|
||||
info.prefix = prefix;
|
||||
info.cached = !!cached;
|
||||
info.files = !!files;
|
||||
info.for_status = !!for_status;
|
||||
info.summary_limit = summary_limit;
|
||||
|
||||
ret = compute_summary_module_list((diff_cmd == DIFF_INDEX) ? &head_oid : NULL,
|
||||
&info, diff_cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sync_cb {
|
||||
const char *prefix;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
#define SYNC_CB_INIT { NULL, 0 }
|
||||
|
||||
static void sync_submodule(const char *path, const char *prefix,
|
||||
@ -2344,6 +2769,7 @@ static struct cmd_struct commands[] = {
|
||||
{"print-default-remote", print_default_remote, 0},
|
||||
{"sync", module_sync, SUPPORT_SUPER_PREFIX},
|
||||
{"deinit", module_deinit, 0},
|
||||
{"summary", module_summary, SUPPORT_SUPER_PREFIX},
|
||||
{"remote-branch", resolve_remote_submodule_branch, 0},
|
||||
{"push-check", push_check, 0},
|
||||
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
|
||||
|
||||
Reference in New Issue
Block a user