git-p4: Clean up git-p4 submit's log message handling.
[gitweb.git] / builtin-log.c
index 197f6eec03df49d4fac044377afd8d0c50e90c81..c67d63cb1cf588877fa10a7546757f2cd249c5c6 100644 (file)
@@ -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);
@@ -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,16 +333,16 @@ 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",
+                       printf("%stag %s%s\n",
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        t->tag,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
-                       ret = show_object(o->sha1, 1);
+                       ret = show_object(o->sha1, 1, &rev);
                        objects[i].item = (struct object *)t->tagged;
                        i--;
                        break;
@@ -223,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;
@@ -251,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);
@@ -288,7 +450,7 @@ 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;
        }
@@ -296,11 +458,10 @@ static int git_format_config(const char *var, const char *value)
                return 0;
        }
        if (!strcmp(var, "format.numbered")) {
-               if (!strcasecmp(value, "auto")) {
+               if (value && !strcasecmp(value, "auto")) {
                        auto_number = 1;
                        return 0;
                }
-
                numbered = git_config_bool(var, value);
                return 0;
        }
@@ -413,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 */
@@ -434,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)
@@ -542,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);
@@ -623,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
@@ -638,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)
@@ -780,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)