diff options
Diffstat (limited to 'builtin-log.c')
-rw-r--r-- | builtin-log.c | 247 |
1 files changed, 212 insertions, 35 deletions
diff --git a/builtin-log.c b/builtin-log.c index e8b982db7c..c67d63cb1c 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -5,6 +5,7 @@ * 2006 Junio Hamano */ #include "cache.h" +#include "color.h" #include "commit.h" #include "diff.h" #include "revision.h" @@ -18,9 +19,6 @@ static int default_show_root = 1; static const char *fmt_patch_subject_prefix = "PATCH"; -/* this is in builtin-diff.c */ -void add_head(struct rev_info *revs); - static void add_name_decoration(const char *prefix, const char *name, struct object *obj) { int plen = strlen(prefix); @@ -55,13 +53,13 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, rev->abbrev = DEFAULT_ABBREV; rev->commit_format = CMIT_FMT_DEFAULT; rev->verbose_header = 1; - rev->diffopt.recursive = 1; + DIFF_OPT_SET(&rev->diffopt, RECURSIVE); rev->show_root_diff = default_show_root; rev->subject_prefix = fmt_patch_subject_prefix; argc = setup_revisions(argc, argv, rev, "HEAD"); if (rev->diffopt.pickaxe || rev->diffopt.filter) rev->always_show_header = 0; - if (rev->diffopt.follow_renames) { + if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) { rev->always_show_header = 0; if (rev->diffopt.nr_paths != 1) usage("git logs can only follow renames on one pathname at a time"); @@ -77,11 +75,135 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, } } +/* + * This gives a rough estimate for how many commits we + * will print out in the list. + */ +static int estimate_commit_count(struct rev_info *rev, struct commit_list *list) +{ + int n = 0; + + while (list) { + struct commit *commit = list->item; + unsigned int flags = commit->object.flags; + list = list->next; + if (!(flags & (TREESAME | UNINTERESTING))) + n++; + } + return n; +} + +static void show_early_header(struct rev_info *rev, const char *stage, int nr) +{ + if (rev->shown_one) { + rev->shown_one = 0; + if (rev->commit_format != CMIT_FMT_ONELINE) + putchar(rev->diffopt.line_termination); + } + printf("Final output: %d %s\n", nr, stage); +} + +struct itimerval early_output_timer; + +static void log_show_early(struct rev_info *revs, struct commit_list *list) +{ + int i = revs->early_output; + int show_header = 1; + + sort_in_topological_order(&list, revs->lifo); + while (list && i) { + struct commit *commit = list->item; + switch (simplify_commit(revs, commit)) { + case commit_show: + if (show_header) { + int n = estimate_commit_count(revs, list); + show_early_header(revs, "incomplete", n); + show_header = 0; + } + log_tree_commit(revs, commit); + i--; + break; + case commit_ignore: + break; + case commit_error: + return; + } + list = list->next; + } + + /* Did we already get enough commits for the early output? */ + if (!i) + return; + + /* + * ..if no, then repeat it twice a second until we + * do. + * + * NOTE! We don't use "it_interval", because if the + * reader isn't listening, we want our output to be + * throttled by the writing, and not have the timer + * trigger every second even if we're blocked on a + * reader! + */ + early_output_timer.it_value.tv_sec = 0; + early_output_timer.it_value.tv_usec = 500000; + setitimer(ITIMER_REAL, &early_output_timer, NULL); +} + +static void early_output(int signal) +{ + show_early_output = log_show_early; +} + +static void setup_early_output(struct rev_info *rev) +{ + struct sigaction sa; + + /* + * Set up the signal handler, minimally intrusively: + * we only set a single volatile integer word (not + * using sigatomic_t - trying to avoid unnecessary + * system dependencies and headers), and using + * SA_RESTART. + */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = early_output; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGALRM, &sa, NULL); + + /* + * If we can get the whole output in less than a + * tenth of a second, don't even bother doing the + * early-output thing.. + * + * This is a one-time-only trigger. + */ + early_output_timer.it_value.tv_sec = 0; + early_output_timer.it_value.tv_usec = 100000; + setitimer(ITIMER_REAL, &early_output_timer, NULL); +} + +static void finish_early_output(struct rev_info *rev) +{ + int n = estimate_commit_count(rev, rev->commits); + signal(SIGALRM, SIG_IGN); + show_early_header(rev, "done", n); +} + static int cmd_log_walk(struct rev_info *rev) { struct commit *commit; - prepare_revision_walk(rev); + if (rev->early_output) + setup_early_output(rev); + + if (prepare_revision_walk(rev)) + die("revision walk setup failed"); + + if (rev->early_output) + finish_early_output(rev); + while ((commit = get_revision(rev)) != NULL) { log_tree_commit(rev, commit); if (!rev->reflog_info) { @@ -99,7 +221,7 @@ static int git_log_config(const char *var, const char *value) { if (!strcmp(var, "format.subjectprefix")) { if (!value) - die("format.subjectprefix without value"); + config_error_nonbool(var); fmt_patch_subject_prefix = xstrdup(value); return 0; } @@ -115,6 +237,10 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix) struct rev_info rev; git_config(git_log_config); + + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; + init_revisions(&rev, prefix); rev.diff = 1; rev.simplify_history = 0; @@ -124,7 +250,28 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix) return cmd_log_walk(&rev); } -static int show_object(const unsigned char *sha1, int suppress_header) +static void show_tagger(char *buf, int len, struct rev_info *rev) +{ + char *email_end, *p; + unsigned long date; + int tz; + + email_end = memchr(buf, '>', len); + if (!email_end) + return; + p = ++email_end; + while (isspace(*p)) + p++; + date = strtoul(p, &p, 10); + while (isspace(*p)) + p++; + tz = (int)strtol(p, NULL, 10); + printf("Tagger: %.*s\nDate: %s\n", (int)(email_end - buf), buf, + show_date(date, tz, rev->date_mode)); +} + +static int show_object(const unsigned char *sha1, int show_tag_object, + struct rev_info *rev) { unsigned long size; enum object_type type; @@ -134,11 +281,14 @@ static int show_object(const unsigned char *sha1, int suppress_header) if (!buf) return error("Could not read object %s", sha1_to_hex(sha1)); - if (suppress_header) - while (offset < size && buf[offset++] != '\n') { - int new_offset = offset; + if (show_tag_object) + while (offset < size && buf[offset] != '\n') { + int new_offset = offset + 1; while (new_offset < size && buf[new_offset++] != '\n') ; /* do nothing */ + if (!prefixcmp(buf + offset, "tagger ")) + show_tagger(buf + offset + 7, + new_offset - offset - 7, rev); offset = new_offset; } @@ -163,6 +313,10 @@ int cmd_show(int argc, const char **argv, const char *prefix) int i, count, ret = 0; git_config(git_log_config); + + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; + init_revisions(&rev, prefix); rev.diff = 1; rev.combine_merges = 1; @@ -179,29 +333,25 @@ int cmd_show(int argc, const char **argv, const char *prefix) const char *name = objects[i].name; switch (o->type) { case OBJ_BLOB: - ret = show_object(o->sha1, 0); + ret = show_object(o->sha1, 0, NULL); break; case OBJ_TAG: { struct tag *t = (struct tag *)o; - printf("%stag %s%s\n\n", - diff_get_color(rev.diffopt.color_diff, - DIFF_COMMIT), + printf("%stag %s%s\n", + diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), t->tag, - diff_get_color(rev.diffopt.color_diff, - DIFF_RESET)); - ret = show_object(o->sha1, 1); + diff_get_color_opt(&rev.diffopt, DIFF_RESET)); + ret = show_object(o->sha1, 1, &rev); objects[i].item = (struct object *)t->tagged; i--; break; } case OBJ_TREE: printf("%stree %s%s\n\n", - diff_get_color(rev.diffopt.color_diff, - DIFF_COMMIT), + diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), name, - diff_get_color(rev.diffopt.color_diff, - DIFF_RESET)); + diff_get_color_opt(&rev.diffopt, DIFF_RESET)); read_tree_recursive((struct tree *)o, "", 0, 0, NULL, show_tree_object); break; @@ -227,6 +377,10 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) struct rev_info rev; git_config(git_log_config); + + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; + init_revisions(&rev, prefix); init_reflog_walk(&rev.reflog_info); rev.abbrev_commit = 1; @@ -255,6 +409,10 @@ int cmd_log(int argc, const char **argv, const char *prefix) struct rev_info rev; git_config(git_log_config); + + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; + init_revisions(&rev, prefix); rev.always_show_header = 1; cmd_log_init(argc, argv, prefix, &rev); @@ -273,6 +431,8 @@ static int istitlechar(char c) static char *extra_headers = NULL; static int extra_headers_size = 0; static const char *fmt_patch_suffix = ".patch"; +static int numbered = 0; +static int auto_number = 0; static int git_format_config(const char *var, const char *value) { @@ -290,13 +450,21 @@ static int git_format_config(const char *var, const char *value) } if (!strcmp(var, "format.suffix")) { if (!value) - die("format.suffix without value"); + return config_error_nonbool(var); fmt_patch_suffix = xstrdup(value); return 0; } if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { return 0; } + if (!strcmp(var, "format.numbered")) { + if (value && !strcasecmp(value, "auto")) { + auto_number = 1; + return 0; + } + numbered = git_config_bool(var, value); + return 0; + } return git_log_config(var, value); } @@ -406,7 +574,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha o2->flags ^= UNINTERESTING; add_pending_object(&check_rev, o1, "o1"); add_pending_object(&check_rev, o2, "o2"); - prepare_revision_walk(&check_rev); + if (prepare_revision_walk(&check_rev)) + die("revision walk setup failed"); while ((commit = get_revision(&check_rev)) != NULL) { /* ignore merges */ @@ -427,7 +596,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha static void gen_message_id(char *dest, unsigned int length, char *base) { - const char *committer = git_committer_info(-1); + const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME); const char *email_start = strrchr(committer, '<'); const char *email_end = strrchr(committer, '>'); if(!email_start || !email_end || email_start > email_end - 1) @@ -466,7 +635,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct rev_info rev; int nr = 0, total, i, j; int use_stdout = 0; - int numbered = 0; int start_number = -1; int keep_subject = 0; int numbered_files = 0; /* _just_ numbers */ @@ -487,7 +655,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.combine_merges = 0; rev.ignore_merges = 1; rev.diffopt.msg_sep = ""; - rev.diffopt.recursive = 1; + DIFF_OPT_SET(&rev.diffopt, RECURSIVE); rev.subject_prefix = fmt_patch_subject_prefix; rev.extra_headers = extra_headers; @@ -503,6 +671,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--numbered")) numbered = 1; + else if (!strcmp(argv[i], "-N") || + !strcmp(argv[i], "--no-numbered")) { + numbered = 0; + auto_number = 0; + } else if (!prefixcmp(argv[i], "--start-number=")) start_number = strtol(argv[i] + 15, NULL, 10); else if (!strcmp(argv[i], "--numbered-files")) @@ -531,7 +704,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) !strcmp(argv[i], "-s")) { const char *committer; const char *endpos; - committer = git_committer_info(1); + committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); endpos = strchr(committer, '>'); if (!endpos) die("bogos committer info %s\n", committer); @@ -590,8 +763,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH; - if (!rev.diffopt.text) - rev.diffopt.binary = 1; + if (!DIFF_OPT_TST(&rev.diffopt, TEXT)) + DIFF_OPT_SET(&rev.diffopt, BINARY); if (!output_directory && !use_stdout) output_directory = prefix; @@ -612,7 +785,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) * does not have. */ rev.pending.objects[0].item->flags |= UNINTERESTING; - add_head(&rev); + add_head_to_pending(&rev); } /* * Otherwise, it is "format-patch -22 HEAD", and/or @@ -627,7 +800,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (!use_stdout) realstdout = xfdopen(xdup(1), "w"); - prepare_revision_walk(&rev); + if (prepare_revision_walk(&rev)) + die("revision walk setup failed"); while ((commit = get_revision(&rev)) != NULL) { /* ignore merges */ if (commit->parents && commit->parents->next) @@ -642,6 +816,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) list[nr - 1] = commit; } total = nr; + if (!keep_subject && auto_number && total > 1) + numbered = 1; if (numbered) rev.total = total + start_number - 1; rev.add_signoff = add_signoff; @@ -747,7 +923,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) revs.diff = 1; revs.combine_merges = 0; revs.ignore_merges = 1; - revs.diffopt.recursive = 1; + DIFF_OPT_SET(&revs.diffopt, RECURSIVE); if (add_pending_commit(head, &revs, 0)) die("Unknown commit %s", head); @@ -767,7 +943,8 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) die("Unknown commit %s", limit); /* reverse the list of commits */ - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); while ((commit = get_revision(&revs)) != NULL) { /* ignore merges */ if (commit->parents && commit->parents->next) @@ -787,7 +964,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) struct strbuf buf; strbuf_init(&buf, 0); pretty_print_commit(CMIT_FMT_ONELINE, commit, - &buf, 0, NULL, NULL, 0); + &buf, 0, NULL, NULL, 0, 0); printf("%c %s %s\n", sign, sha1_to_hex(commit->object.sha1), buf.buf); strbuf_release(&buf); |