summaryrefslogtreecommitdiff
path: root/builtin-log.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin-log.c')
-rw-r--r--builtin-log.c247
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);