]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'js/commit-format'
authorJunio C Hamano <junkio@cox.net>
Fri, 2 Mar 2007 08:37:12 +0000 (00:37 -0800)
committerJunio C Hamano <junkio@cox.net>
Fri, 2 Mar 2007 08:37:12 +0000 (00:37 -0800)
* js/commit-format:
  show_date(): rename the "relative" parameter to "mode"
  Actually make print_wrapped_text() useful
  pretty-formats: add 'format:<string>'

Documentation/pretty-formats.txt
cache.h
commit.c
commit.h
date.c
log-tree.c
utf8.c
utf8.h

index fb0b0b95829e0fc1aa98019554ce12e9b45d1f63..2fe6c319675926afe1609ad7c221dceb42c82310 100644 (file)
@@ -77,9 +77,53 @@ displayed in full, regardless of whether --abbrev or
 true parent commits, without taking grafts nor history
 simplification into account.
 
+       * 'format:'
++
+The 'format:' format allows you to specify which information
+you want to show. It works a little bit like printf format,
+with the notable exception that you get a newline with '%n'
+instead of '\n'.
+
+E.g, 'format:"The author of %h was %an, %ar%nThe title was >>%s<<"'
+would show something like this:
+
+The author of fe6e0ee was Junio C Hamano, 23 hours ago
+The title was >>t4119: test autocomputing -p<n> for traditional diff input.<<
+
+The placeholders are:
+
+- '%H': commit hash
+- '%h': abbreviated commit hash
+- '%T': tree hash
+- '%t': abbreviated tree hash
+- '%P': parent hashes
+- '%p': abbreviated parent hashes
+- '%an': author name
+- '%ae': author email
+- '%ad': author date
+- '%aD': author date, RFC2822 style
+- '%ar': author date, relative
+- '%at': author date, UNIX timestamp
+- '%cn': committer name
+- '%ce': committer email
+- '%cd': committer date
+- '%cD': committer date, RFC2822 style
+- '%cr': committer date, relative
+- '%ct': committer date, UNIX timestamp
+- '%e': encoding
+- '%s': subject
+- '%b': body
+- '%Cred': switch color to red
+- '%Cgreen': switch color to green
+- '%Cblue': switch color to blue
+- '%Creset': reset color
+- '%n': newline
+
+
 --encoding[=<encoding>]::
        The commit objects record the encoding used for the log message
        in their encoding header; this option can be used to tell the
        command to re-code the commit log message in the encoding
        preferred by the user.  For non plumbing commands this
        defaults to UTF-8.
+
diff --git a/cache.h b/cache.h
index b84e3decfcd38a349243dc8e3a88e1b5151ced6c..8018b2cd3b20533ea03ce503bc88b8bd0155432e 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -327,7 +327,8 @@ extern void *read_object_with_reference(const unsigned char *sha1,
                                        unsigned long *size,
                                        unsigned char *sha1_ret);
 
-const char *show_date(unsigned long time, int timezone, int relative);
+enum date_mode { DATE_NORMAL = 0, DATE_RELATIVE, DATE_SHORT };
+const char *show_date(unsigned long time, int timezone, enum date_mode mode);
 const char *show_rfc2822_date(unsigned long time, int timezone);
 int parse_date(const char *date, char *buf, int bufsize);
 void datestamp(char *buf, int bufsize);
index da515a49738c3c8e3065da08e468a93bf976f1e4..555252734298540ffc5678ac5d80888fd23dd4e5 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -3,6 +3,7 @@
 #include "commit.h"
 #include "pkt-line.h"
 #include "utf8.h"
+#include "interpolate.h"
 
 int save_commit_buffer = 1;
 
@@ -36,8 +37,11 @@ struct cmt_fmt_map {
        { "full",       5,      CMIT_FMT_FULL },
        { "fuller",     5,      CMIT_FMT_FULLER },
        { "oneline",    1,      CMIT_FMT_ONELINE },
+       { "format:",    7,      CMIT_FMT_USERFORMAT},
 };
 
+static char *user_format;
+
 enum cmit_fmt get_commit_format(const char *arg)
 {
        int i;
@@ -46,6 +50,12 @@ enum cmit_fmt get_commit_format(const char *arg)
                return CMIT_FMT_DEFAULT;
        if (*arg == '=')
                arg++;
+       if (!prefixcmp(arg, "format:")) {
+               if (user_format)
+                       free(user_format);
+               user_format = xstrdup(arg + 7);
+               return CMIT_FMT_USERFORMAT;
+       }
        for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
                if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
                    !strncmp(arg, cmt_fmts[i].n, strlen(arg)))
@@ -710,6 +720,188 @@ static char *logmsg_reencode(const struct commit *commit,
        return out;
 }
 
+static char *xstrndup(const char *text, int len)
+{
+       char *result = xmalloc(len + 1);
+       memcpy(result, text, len);
+       result[len] = '\0';
+       return result;
+}
+
+static void fill_person(struct interp *table, const char *msg, int len)
+{
+       int start, end, tz = 0;
+       unsigned long date;
+       char *ep;
+
+       /* parse name */
+       for (end = 0; end < len && msg[end] != '<'; end++)
+               ; /* do nothing */
+       start = end + 1;
+       while (end > 0 && isspace(msg[end - 1]))
+               end--;
+       table[0].value = xstrndup(msg, end);
+
+       if (start >= len)
+               return;
+
+       /* parse email */
+       for (end = start + 1; end < len && msg[end] != '>'; end++)
+               ; /* do nothing */
+
+       if (end >= len)
+               return;
+
+       table[1].value = xstrndup(msg + start, end - start);
+
+       /* parse date */
+       for (start = end + 1; start < len && isspace(msg[start]); start++)
+               ; /* do nothing */
+       if (start >= len)
+               return;
+       date = strtoul(msg + start, &ep, 10);
+       if (msg + start == ep)
+               return;
+
+       table[5].value = xstrndup(msg + start, ep - msg + start);
+
+       /* parse tz */
+       for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
+               ; /* do nothing */
+       if (start + 1 < len) {
+               tz = strtoul(msg + start + 1, NULL, 10);
+               if (msg[start] == '-')
+                       tz = -tz;
+       }
+
+       interp_set_entry(table, 2, show_date(date, tz, 0));
+       interp_set_entry(table, 3, show_rfc2822_date(date, tz));
+       interp_set_entry(table, 4, show_date(date, tz, 1));
+}
+
+static long format_commit_message(const struct commit *commit,
+               const char *msg, char *buf, unsigned long space)
+{
+       struct interp table[] = {
+               { "%H" },       /* commit hash */
+               { "%h" },       /* abbreviated commit hash */
+               { "%T" },       /* tree hash */
+               { "%t" },       /* abbreviated tree hash */
+               { "%P" },       /* parent hashes */
+               { "%p" },       /* abbreviated parent hashes */
+               { "%an" },      /* author name */
+               { "%ae" },      /* author email */
+               { "%ad" },      /* author date */
+               { "%aD" },      /* author date, RFC2822 style */
+               { "%ar" },      /* author date, relative */
+               { "%at" },      /* author date, UNIX timestamp */
+               { "%cn" },      /* committer name */
+               { "%ce" },      /* committer email */
+               { "%cd" },      /* committer date */
+               { "%cD" },      /* committer date, RFC2822 style */
+               { "%cr" },      /* committer date, relative */
+               { "%ct" },      /* committer date, UNIX timestamp */
+               { "%e" },       /* encoding */
+               { "%s" },       /* subject */
+               { "%b" },       /* body */
+               { "%Cred" },    /* red */
+               { "%Cgreen" },  /* green */
+               { "%Cblue" },   /* blue */
+               { "%Creset" },  /* reset color */
+               { "%n" }        /* newline */
+       };
+       enum interp_index {
+               IHASH = 0, IHASH_ABBREV,
+               ITREE, ITREE_ABBREV,
+               IPARENTS, IPARENTS_ABBREV,
+               IAUTHOR_NAME, IAUTHOR_EMAIL,
+               IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
+               IAUTHOR_TIMESTAMP,
+               ICOMMITTER_NAME, ICOMMITTER_EMAIL,
+               ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
+               ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
+               IENCODING,
+               ISUBJECT,
+               IBODY,
+               IRED, IGREEN, IBLUE, IRESET_COLOR,
+               INEWLINE
+       };
+       struct commit_list *p;
+       char parents[1024];
+       int i;
+       enum { HEADER, SUBJECT, BODY } state;
+
+       if (INEWLINE + 1 != ARRAY_SIZE(table))
+               die("invalid interp table!");
+
+       /* these are independent of the commit */
+       interp_set_entry(table, IRED, "\033[31m");
+       interp_set_entry(table, IGREEN, "\033[32m");
+       interp_set_entry(table, IBLUE, "\033[34m");
+       interp_set_entry(table, IRESET_COLOR, "\033[m");
+       interp_set_entry(table, INEWLINE, "\n");
+
+       /* these depend on the commit */
+       if (!commit->object.parsed)
+               parse_object(commit->object.sha1);
+       interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1));
+       interp_set_entry(table, IHASH_ABBREV,
+                       find_unique_abbrev(commit->object.sha1,
+                               DEFAULT_ABBREV));
+       interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1));
+       interp_set_entry(table, ITREE_ABBREV,
+                       find_unique_abbrev(commit->tree->object.sha1,
+                               DEFAULT_ABBREV));
+       for (i = 0, p = commit->parents;
+                       p && i < sizeof(parents) - 1;
+                       p = p->next)
+               i += snprintf(parents + i, sizeof(parents) - i - 1, "%s ",
+                       sha1_to_hex(p->item->object.sha1));
+       interp_set_entry(table, IPARENTS, parents);
+       for (i = 0, p = commit->parents;
+                       p && i < sizeof(parents) - 1;
+                       p = p->next)
+               i += snprintf(parents + i, sizeof(parents) - i - 1, "%s ",
+                       find_unique_abbrev(p->item->object.sha1,
+                               DEFAULT_ABBREV));
+       interp_set_entry(table, IPARENTS_ABBREV, parents);
+
+       for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
+               int eol;
+               for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
+                       ; /* do nothing */
+
+               if (state == SUBJECT) {
+                       table[ISUBJECT].value = xstrndup(msg + i, eol - i);
+                       i = eol;
+               }
+               if (i == eol) {
+                       state++;
+                       /* strip empty lines */
+                       while (msg[eol + 1] == '\n')
+                               eol++;
+               } else if (!prefixcmp(msg + i, "author "))
+                       fill_person(table + IAUTHOR_NAME,
+                                       msg + i + 7, eol - i - 7);
+               else if (!prefixcmp(msg + i, "committer "))
+                       fill_person(table + ICOMMITTER_NAME,
+                                       msg + i + 10, eol - i - 10);
+               else if (!prefixcmp(msg + i, "encoding "))
+                       table[IENCODING].value = xstrndup(msg + i, eol - i);
+               i = eol;
+       }
+       if (msg[i])
+               table[IBODY].value = xstrdup(msg + i);
+       for (i = 0; i < ARRAY_SIZE(table); i++)
+               if (!table[i].value)
+                       interp_set_entry(table, i, "<unknown>");
+
+       interpolate(buf, space, user_format, table, ARRAY_SIZE(table));
+       interp_clear_table(table, ARRAY_SIZE(table));
+
+       return strlen(buf);
+}
+
 unsigned long pretty_print_commit(enum cmit_fmt fmt,
                                  const struct commit *commit,
                                  unsigned long len,
@@ -727,6 +919,9 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
        char *reencoded;
        char *encoding;
 
+       if (fmt == CMIT_FMT_USERFORMAT)
+               return format_commit_message(commit, msg, buf, space);
+
        encoding = (git_log_output_encoding
                    ? git_log_output_encoding
                    : git_commit_encoding);
index c73744463ca55ce0df3623dffbb994bd4a86d76e..83507a07e4cbaab81f130891a64b78539d1beede 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -47,6 +47,7 @@ enum cmit_fmt {
        CMIT_FMT_FULLER,
        CMIT_FMT_ONELINE,
        CMIT_FMT_EMAIL,
+       CMIT_FMT_USERFORMAT,
 
        CMIT_FMT_UNSPECIFIED,
 };
diff --git a/date.c b/date.c
index 542c004c2e8d2a9f731a1f303cc3393d88f5a6e8..0ceccbe03401faa67836577b9bdbe139fe025dd5 100644 (file)
--- a/date.c
+++ b/date.c
@@ -55,12 +55,12 @@ static struct tm *time_to_tm(unsigned long time, int tz)
        return gmtime(&t);
 }
 
-const char *show_date(unsigned long time, int tz, int relative)
+const char *show_date(unsigned long time, int tz, enum date_mode mode)
 {
        struct tm *tm;
        static char timebuf[200];
 
-       if (relative) {
+       if (mode == DATE_RELATIVE) {
                unsigned long diff;
                struct timeval now;
                gettimeofday(&now, NULL);
@@ -105,12 +105,16 @@ const char *show_date(unsigned long time, int tz, int relative)
        tm = time_to_tm(time, tz);
        if (!tm)
                return NULL;
-       sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d",
-               weekday_names[tm->tm_wday],
-               month_names[tm->tm_mon],
-               tm->tm_mday,
-               tm->tm_hour, tm->tm_min, tm->tm_sec,
-               tm->tm_year + 1900, tz);
+       if (mode == DATE_SHORT)
+               sprintf(timebuf, "%04d-%02d-%02d", tm->tm_year + 1900,
+                               tm->tm_mon + 1, tm->tm_mday);
+       else
+               sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d",
+                               weekday_names[tm->tm_wday],
+                               month_names[tm->tm_mon],
+                               tm->tm_mday,
+                               tm->tm_hour, tm->tm_min, tm->tm_sec,
+                               tm->tm_year + 1900, tz);
        return timebuf;
 }
 
index ac8619404797633436258dbf9d76f9f4aa323395..6ce239d8f92837ccce7326dab1951f74360eef88 100644 (file)
@@ -211,7 +211,7 @@ void show_log(struct rev_info *opt, const char *sep)
                                 sha1, sha1);
                        opt->diffopt.stat_sep = buffer;
                }
-       } else {
+       } else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
                fputs(diff_get_color(opt->diffopt.color_diff, DIFF_COMMIT),
                      stdout);
                if (opt->commit_format != CMIT_FMT_ONELINE)
diff --git a/utf8.c b/utf8.c
index 7c80eeccb4537ab6d4387941c6262c89cab30d33..ea23a6e5dc60bc3683c98a01f9a4e7dd8c7efb18 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -235,12 +235,19 @@ static void print_spaces(int count)
 /*
  * Wrap the text, if necessary. The variable indent is the indent for the
  * first line, indent2 is the indent for all other lines.
+ * If indent is negative, assume that already -indent columns have been
+ * consumed (and no extra indent is necessary for the first line).
  */
-void print_wrapped_text(const char *text, int indent, int indent2, int width)
+int print_wrapped_text(const char *text, int indent, int indent2, int width)
 {
        int w = indent, assume_utf8 = is_utf8(text);
        const char *bol = text, *space = NULL;
 
+       if (indent < 0) {
+               w = -indent;
+               space = text;
+       }
+
        for (;;) {
                char c = *text;
                if (!c || isspace(c)) {
@@ -251,10 +258,9 @@ void print_wrapped_text(const char *text, int indent, int indent2, int width)
                                else
                                        print_spaces(indent);
                                fwrite(start, text - start, 1, stdout);
-                               if (!c) {
-                                       putchar('\n');
-                                       return;
-                               } else if (c == '\t')
+                               if (!c)
+                                       return w;
+                               else if (c == '\t')
                                        w |= 0x07;
                                space = text;
                                w++;
@@ -275,6 +281,7 @@ void print_wrapped_text(const char *text, int indent, int indent2, int width)
                        text++;
                }
        }
+       return w;
 }
 
 int is_encoding_utf8(const char *name)
diff --git a/utf8.h b/utf8.h
index a07c5a88af63d41ae963f230510a33d78b68a525..15db6f1f27ef7a1f056d5b6adca901f092036f82 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -5,7 +5,7 @@ int utf8_width(const char **start);
 int is_utf8(const char *text);
 int is_encoding_utf8(const char *name);
 
-void print_wrapped_text(const char *text, int indent, int indent2, int len);
+int print_wrapped_text(const char *text, int indent, int indent2, int len);
 
 #ifndef NO_ICONV
 char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding);