push: support pushing HEAD to real branch name
[gitweb.git] / builtin-log.c
index a381c75f3c3d8645eabc46264660361c403e86bf..e1f1cf67143721933010065130adaba8723b272b 100644 (file)
@@ -55,13 +55,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 +77,134 @@ 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;
 
+       if (rev->early_output)
+               setup_early_output(rev);
+
        prepare_revision_walk(rev);
+
+       if (rev->early_output)
+               finish_early_output(rev);
+
        while ((commit = get_revision(rev)) != NULL) {
                log_tree_commit(rev, commit);
                if (!rev->reflog_info) {
@@ -185,11 +308,9 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                        struct tag *t = (struct tag *)o;
 
                        printf("%stag %s%s\n\n",
-                                       diff_get_color(rev.diffopt.color_diff,
-                                               DIFF_COMMIT),
+                                       diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        t->tag,
-                                       diff_get_color(rev.diffopt.color_diff,
-                                               DIFF_RESET));
+                                       diff_get_color_opt(&rev.diffopt, DIFF_RESET));
                        ret = show_object(o->sha1, 1);
                        objects[i].item = (struct object *)t->tagged;
                        i--;
@@ -197,11 +318,9 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                }
                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;
@@ -273,6 +392,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)
 {
@@ -297,6 +418,15 @@ static int git_format_config(const char *var, const char *value)
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
                return 0;
        }
+       if (!strcmp(var, "format.numbered")) {
+               if (!strcasecmp(value, "auto")) {
+                       auto_number = 1;
+                       return 0;
+               }
+
+               numbered = git_config_bool(var, value);
+               return 0;
+       }
 
        return git_log_config(var, value);
 }
@@ -437,6 +567,28 @@ static void gen_message_id(char *dest, unsigned int length, char *base)
                 (int)(email_end - email_start - 1), email_start + 1);
 }
 
+static const char *clean_message_id(const char *msg_id)
+{
+       char ch;
+       const char *a, *z, *m;
+
+       m = msg_id;
+       while ((ch = *m) && (isspace(ch) || (ch == '<')))
+               m++;
+       a = m;
+       z = NULL;
+       while ((ch = *m)) {
+               if (!isspace(ch) && (ch != '>'))
+                       z = m;
+               m++;
+       }
+       if (!z)
+               die("insane in-reply-to: %s", msg_id);
+       if (++z == m)
+               return a;
+       return xmemdupz(a, z - a);
+}
+
 int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
        struct commit *commit;
@@ -444,7 +596,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 */
@@ -465,7 +616,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;
@@ -481,6 +632,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"))
@@ -513,9 +669,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        endpos = strchr(committer, '>');
                        if (!endpos)
                                die("bogos committer info %s\n", committer);
-                       add_signoff = xmalloc(endpos - committer + 2);
-                       memcpy(add_signoff, committer, endpos - committer + 1);
-                       add_signoff[endpos - committer + 1] = 0;
+                       add_signoff = xmemdupz(committer, endpos - committer + 1);
                }
                else if (!strcmp(argv[i], "--attach")) {
                        rev.mime_boundary = git_version_string;
@@ -570,8 +724,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;
@@ -585,12 +739,19 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        }
 
        if (rev.pending.nr == 1) {
-               if (rev.max_count < 0) {
+               if (rev.max_count < 0 && !rev.show_root_diff) {
+                       /*
+                        * This is traditional behaviour of "git format-patch
+                        * origin" that prepares what the origin side still
+                        * does not have.
+                        */
                        rev.pending.objects[0].item->flags |= UNINTERESTING;
                        add_head(&rev);
                }
-               /* Otherwise, it is "format-patch -22 HEAD", and
-                * get_revision() would return only the specified count.
+               /*
+                * Otherwise, it is "format-patch -22 HEAD", and/or
+                * "format-patch --root HEAD".  The user wants
+                * get_revision() to do the usual traversal.
                 */
        }
 
@@ -615,10 +776,13 @@ 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;
-       rev.ref_message_id = in_reply_to;
+       if (in_reply_to)
+               rev.ref_message_id = clean_message_id(in_reply_to);
        while (0 <= --nr) {
                int shown;
                commit = list[nr];
@@ -719,7 +883,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);
@@ -756,13 +920,13 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
                        sign = '-';
 
                if (verbose) {
-                       char *buf = NULL;
-                       unsigned long buflen = 0;
-                       pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
-                                           &buf, &buflen, 0, NULL, NULL, 0);
+                       struct strbuf buf;
+                       strbuf_init(&buf, 0);
+                       pretty_print_commit(CMIT_FMT_ONELINE, commit,
+                                           &buf, 0, NULL, NULL, 0, 0);
                        printf("%c %s %s\n", sign,
-                              sha1_to_hex(commit->object.sha1), buf);
-                       free(buf);
+                              sha1_to_hex(commit->object.sha1), buf.buf);
+                       strbuf_release(&buf);
                }
                else {
                        printf("%c %s\n", sign,