Documentation/git-fsck.txt: fix typo: unreadable -> unreachable
[gitweb.git] / log-tree.c
index 1618f3c79a3095ab88553d9ca71ce0c4024c6b97..2a1e3a94c910cd74e41303d8b7aefa539817c831 100644 (file)
 #include "reflog-walk.h"
 #include "refs.h"
 #include "string-list.h"
+#include "color.h"
 
 struct decoration name_decoration = { "object names" };
 
-static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
+enum decoration_type {
+       DECORATION_NONE = 0,
+       DECORATION_REF_LOCAL,
+       DECORATION_REF_REMOTE,
+       DECORATION_REF_TAG,
+       DECORATION_REF_STASH,
+       DECORATION_REF_HEAD,
+};
+
+static char decoration_colors[][COLOR_MAXLEN] = {
+       GIT_COLOR_RESET,
+       GIT_COLOR_BOLD_GREEN,   /* REF_LOCAL */
+       GIT_COLOR_BOLD_RED,     /* REF_REMOTE */
+       GIT_COLOR_BOLD_YELLOW,  /* REF_TAG */
+       GIT_COLOR_BOLD_MAGENTA, /* REF_STASH */
+       GIT_COLOR_BOLD_CYAN,    /* REF_HEAD */
+};
+
+static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix)
+{
+       if (decorate_use_color)
+               return decoration_colors[ix];
+       return "";
+}
+
+static int parse_decorate_color_slot(const char *slot)
+{
+       /*
+        * We're comparing with 'ignore-case' on
+        * (because config.c sets them all tolower),
+        * but let's match the letters in the literal
+        * string values here with how they are
+        * documented in Documentation/config.txt, for
+        * consistency.
+        *
+        * We love being consistent, don't we?
+        */
+       if (!strcasecmp(slot, "branch"))
+               return DECORATION_REF_LOCAL;
+       if (!strcasecmp(slot, "remoteBranch"))
+               return DECORATION_REF_REMOTE;
+       if (!strcasecmp(slot, "tag"))
+               return DECORATION_REF_TAG;
+       if (!strcasecmp(slot, "stash"))
+               return DECORATION_REF_STASH;
+       if (!strcasecmp(slot, "HEAD"))
+               return DECORATION_REF_HEAD;
+       return -1;
+}
+
+int parse_decorate_color_config(const char *var, const int ofs, const char *value)
+{
+       int slot = parse_decorate_color_slot(var + ofs);
+       if (slot < 0)
+               return 0;
+       if (!value)
+               return config_error_nonbool(var);
+       color_parse(value, var, decoration_colors[slot]);
+       return 0;
+}
+
+/*
+ * log-tree.c uses DIFF_OPT_TST for determining whether to use color
+ * for showing the commit sha1, use the same check for --decorate
+ */
+#define decorate_get_color_opt(o, ix) \
+       decorate_get_color(DIFF_OPT_TST((o), COLOR_DIFF), ix)
+
+static void add_name_decoration(enum decoration_type type, const char *name, struct object *obj)
 {
-       int plen = strlen(prefix);
        int nlen = strlen(name);
-       struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
-       memcpy(res->name, prefix, plen);
-       memcpy(res->name + plen, name, nlen + 1);
+       struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + nlen);
+       memcpy(res->name, name, nlen + 1);
+       res->type = type;
        res->next = add_decoration(&name_decoration, obj, res);
 }
 
 static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
 {
        struct object *obj = parse_object(sha1);
+       enum decoration_type type = DECORATION_NONE;
        if (!obj)
                return 0;
+
+       if (!prefixcmp(refname, "refs/heads"))
+               type = DECORATION_REF_LOCAL;
+       else if (!prefixcmp(refname, "refs/remotes"))
+               type = DECORATION_REF_REMOTE;
+       else if (!prefixcmp(refname, "refs/tags"))
+               type = DECORATION_REF_TAG;
+       else if (!prefixcmp(refname, "refs/stash"))
+               type = DECORATION_REF_STASH;
+       else if (!prefixcmp(refname, "HEAD"))
+               type = DECORATION_REF_HEAD;
+
        if (!cb_data || *(int *)cb_data == DECORATE_SHORT_REFS)
                refname = prettify_refname(refname);
-       add_name_decoration("", refname, obj);
+       add_name_decoration(type, refname, obj);
        while (obj->type == OBJ_TAG) {
                obj = ((struct tag *)obj)->tagged;
                if (!obj)
                        break;
-               add_name_decoration("tag: ", refname, obj);
+               add_name_decoration(DECORATION_REF_TAG, refname, obj);
        }
        return 0;
 }
@@ -43,6 +124,7 @@ void load_ref_decorations(int flags)
        if (!loaded) {
                loaded = 1;
                for_each_ref(add_ref_decoration, &flags);
+               head_ref(add_ref_decoration, &flags);
        }
 }
 
@@ -59,6 +141,10 @@ void show_decorations(struct rev_info *opt, struct commit *commit)
 {
        const char *prefix;
        struct name_decoration *decoration;
+       const char *color_commit =
+               diff_get_color_opt(&opt->diffopt, DIFF_COMMIT);
+       const char *color_reset =
+               decorate_get_color_opt(&opt->diffopt, DECORATION_NONE);
 
        if (opt->show_source && commit->util)
                printf("\t%s", (char *) commit->util);
@@ -69,7 +155,14 @@ void show_decorations(struct rev_info *opt, struct commit *commit)
                return;
        prefix = " (";
        while (decoration) {
-               printf("%s%s", prefix, decoration->name);
+               printf("%s", prefix);
+               fputs(decorate_get_color_opt(&opt->diffopt, decoration->type),
+                     stdout);
+               if (decoration->type == DECORATION_REF_TAG)
+                       fputs("tag: ", stdout);
+               printf("%s", decoration->name);
+               fputs(color_reset, stdout);
+               fputs(color_commit, stdout);
                prefix = ", ";
                decoration = decoration->next;
        }
@@ -178,8 +271,10 @@ void get_patch_filename(struct commit *commit, int nr, const char *suffix,
        strbuf_addf(buf, commit ? "%04d-" : "%d", nr);
        if (commit) {
                int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len;
+               struct pretty_print_context ctx = {0};
+               ctx.date_mode = DATE_NORMAL;
 
-               format_commit_message(commit, "%f", buf, DATE_NORMAL);
+               format_commit_message(commit, "%f", buf, &ctx);
                if (max_len < buf->len)
                        strbuf_setlen(buf, max_len);
                strbuf_addstr(buf, suffix);
@@ -276,27 +371,17 @@ void show_log(struct rev_info *opt)
        struct strbuf msgbuf = STRBUF_INIT;
        struct log_info *log = opt->loginfo;
        struct commit *commit = log->commit, *parent = log->parent;
-       int abbrev = opt->diffopt.abbrev;
        int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
-       const char *subject = NULL, *extra_headers = opt->extra_headers;
-       int need_8bit_cte = 0;
+       const char *extra_headers = opt->extra_headers;
+       struct pretty_print_context ctx = {0};
 
        opt->loginfo = NULL;
+       ctx.show_notes = opt->show_notes;
        if (!opt->verbose_header) {
                graph_show_commit(opt->graph);
 
-               if (!opt->graph) {
-                       if (commit->object.flags & BOUNDARY)
-                               putchar('-');
-                       else if (commit->object.flags & UNINTERESTING)
-                               putchar('^');
-                       else if (opt->left_right) {
-                               if (commit->object.flags & SYMMETRIC_LEFT)
-                                       putchar('<');
-                               else
-                                       putchar('>');
-                       }
-               }
+               if (!opt->graph)
+                       put_revision_mark(opt, commit);
                fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
                if (opt->print_parents)
                        show_parents(commit, abbrev_commit);
@@ -346,25 +431,15 @@ void show_log(struct rev_info *opt)
         */
 
        if (opt->commit_format == CMIT_FMT_EMAIL) {
-               log_write_email_headers(opt, commit, &subject, &extra_headers,
-                                       &need_8bit_cte);
+               log_write_email_headers(opt, commit, &ctx.subject, &extra_headers,
+                                       &ctx.need_8bit_cte);
        } else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
                fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
                if (opt->commit_format != CMIT_FMT_ONELINE)
                        fputs("commit ", stdout);
 
-               if (!opt->graph) {
-                       if (commit->object.flags & BOUNDARY)
-                               putchar('-');
-                       else if (commit->object.flags & UNINTERESTING)
-                               putchar('^');
-                       else if (opt->left_right) {
-                               if (commit->object.flags & SYMMETRIC_LEFT)
-                                       putchar('<');
-                               else
-                                       putchar('>');
-                       }
-               }
+               if (!opt->graph)
+                       put_revision_mark(opt, commit);
                fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit),
                      stdout);
                if (opt->print_parents)
@@ -404,11 +479,13 @@ void show_log(struct rev_info *opt)
        /*
         * And then the pretty-printed message itself
         */
-       if (need_8bit_cte >= 0)
-               need_8bit_cte = has_non_ascii(opt->add_signoff);
-       pretty_print_commit(opt->commit_format, commit, &msgbuf,
-                           abbrev, subject, extra_headers, opt->date_mode,
-                           need_8bit_cte);
+       if (ctx.need_8bit_cte >= 0)
+               ctx.need_8bit_cte = has_non_ascii(opt->add_signoff);
+       ctx.date_mode = opt->date_mode;
+       ctx.abbrev = opt->diffopt.abbrev;
+       ctx.after_subject = extra_headers;
+       ctx.reflog_info = opt->reflog_info;
+       pretty_print_commit(opt->commit_format, commit, &msgbuf, &ctx);
 
        if (opt->add_signoff)
                append_signoff(&msgbuf, opt->add_signoff);
@@ -464,6 +541,12 @@ int log_tree_diff_flush(struct rev_info *opt)
                        int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
                        if ((pch & opt->diffopt.output_format) == pch)
                                printf("---");
+                       if (opt->diffopt.output_prefix) {
+                               struct strbuf *msg = NULL;
+                               msg = opt->diffopt.output_prefix(&opt->diffopt,
+                                       opt->diffopt.output_prefix_data);
+                               fwrite(msg->buf, msg->len, 1, stdout);
+                       }
                        putchar('\n');
                }
        }
@@ -509,6 +592,16 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
                        return 0;
                else if (opt->combine_merges)
                        return do_diff_combined(opt, commit);
+               else if (opt->first_parent_only) {
+                       /*
+                        * Generate merge log entry only for the first
+                        * parent, showing summary diff of the others
+                        * we merged _in_.
+                        */
+                       diff_tree_sha1(parents->item->object.sha1, sha1, "", &opt->diffopt);
+                       log_tree_diff_flush(opt);
+                       return !opt->loginfo;
+               }
 
                /* If we show individual diffs, show the parent info */
                log->parent = parents->item;