Merge branch 'ar/decorate-color'
authorJunio C Hamano <gitster@pobox.com>
Wed, 30 Jun 2010 18:55:40 +0000 (11:55 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 30 Jun 2010 18:55:40 +0000 (11:55 -0700)
* ar/decorate-color:
Add test for correct coloring of git log --decoration
Allow customizable commit decorations colors
log --decorate: Colorize commit decorations
log-tree.c: Use struct name_decoration's type for classifying decoration
commit.h: add 'type' to struct name_decoration

Documentation/config.txt
builtin/log.c
commit.h
log-tree.c
log-tree.h
t/t4207-log-decoration-colors.sh [new file with mode: 0755]
index 4c491045c9fa8fba7e154af85686557598e90320..72949e71ac0996c3a6af7ff98c4de5bb4a457625 100644 (file)
@@ -690,6 +690,11 @@ color.diff.<slot>::
        (highlighting whitespace errors). The values of these variables may be
        specified as in color.branch.<slot>.
 
+color.decorate.<slot>::
+       Use customized color for 'git log --decorate' output.  `<slot>` is one
+       of `branch`, `remoteBranch`, `tag`, `stash` or `HEAD` for local
+       branches, remote tracking branches, tags, stash and HEAD, respectively.
+
 color.grep::
        When set to `always`, always highlight matches.  When `false` (or
        `never`), never.  When set to `true` or `auto`, use color only
index 7cb9317af7d7de95197244b60cb90fbac8ddd947..08b872263cd646ec9d7dc8ad91273c71b1af2b69 100644 (file)
@@ -296,6 +296,9 @@ static int git_log_config(const char *var, const char *value, void *cb)
                default_show_root = git_config_bool(var, value);
                return 0;
        }
+       if (!prefixcmp(var, "color.decorate."))
+               return parse_decorate_color_config(var, 15, value);
+
        return git_diff_ui_config(var, value, cb);
 }
 
index e958a7c3dfbb3ff76410dc83e47d0ffa950af9ea..eb2b8ac3cd5f375e70354e8c364abd036b0966ed 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -28,6 +28,7 @@ extern const char *commit_type;
 extern struct decoration name_decoration;
 struct name_decoration {
        struct name_decoration *next;
+       int type;
        char name[1];
 };
 
index 2e2be7c40f996949a69f597168b620976cf55b2f..b46ed3baef7d9d9971a3886d700059217fbe974a 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;
 }
@@ -60,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);
@@ -70,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;
        }
index 3f7b40027b7203985f018e43ab72a6cfc233e8d7..5c4cf7cac3327e2ef6000dff414680cd5c4c0e1e 100644 (file)
@@ -7,6 +7,7 @@ struct log_info {
        struct commit *commit, *parent;
 };
 
+int parse_decorate_color_config(const char *var, const int ofs, const char *value);
 void init_log_tree_opt(struct rev_info *);
 int log_tree_diff_flush(struct rev_info *);
 int log_tree_commit(struct rev_info *, struct commit *);
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
new file mode 100755 (executable)
index 0000000..bbde31b
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Nazri Ramliy
+#
+
+test_description='Test for "git log --decorate" colors'
+
+. ./test-lib.sh
+
+get_color ()
+{
+       git config --get-color no.such.slot "$1"
+}
+
+test_expect_success setup '
+       git config diff.color.commit yellow &&
+       git config color.decorate.branch green &&
+       git config color.decorate.remoteBranch red &&
+       git config color.decorate.tag "reverse bold yellow" &&
+       git config color.decorate.stash magenta &&
+       git config color.decorate.HEAD cyan &&
+
+       c_reset=$(get_color reset) &&
+
+       c_commit=$(get_color yellow) &&
+       c_branch=$(get_color green) &&
+       c_remoteBranch=$(get_color red) &&
+       c_tag=$(get_color "reverse bold yellow") &&
+       c_stash=$(get_color magenta) &&
+       c_HEAD=$(get_color cyan) &&
+
+       test_commit A &&
+       git clone . other &&
+       (
+               cd other &&
+               test_commit A1
+       ) &&
+
+       git remote add -f other ./other &&
+       test_commit B &&
+       git tag v1.0 &&
+       echo >>A.t &&
+       git stash save Changes to A.t
+'
+
+cat >expected <<EOF
+${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\
+ ${c_tag}tag: v1.0${c_reset}${c_commit},\
+ ${c_tag}tag: B${c_reset}${c_commit},\
+ ${c_branch}master${c_reset}${c_commit})${c_reset} B
+${c_commit}COMMIT_ID (${c_tag}tag: A1${c_reset}${c_commit},\
+ ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1
+${c_commit}COMMIT_ID (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\
+ On master: Changes to A.t
+${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+EOF
+
+# We want log to show all, but the second parent to refs/stash is irrelevant
+# to this test since it does not contain any decoration, hence --first-parent
+test_expect_success 'Commit Decorations Colored Correctly' '
+       git log --first-parent --abbrev=10 --all --decorate --oneline --color=always |
+       sed "s/[0-9a-f]\{10,10\}/COMMIT_ID/" >out &&
+       test_cmp expected out
+'
+
+test_done