user-manual: fix directory name in git-archive example
[gitweb.git] / commit.c
index 718e568855a37586f99f20bcfafd44cf3aa2b657..5632e32685478dcce34e479e8e916b0ccd3a3b13 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -4,6 +4,8 @@
 #include "pkt-line.h"
 #include "utf8.h"
 #include "interpolate.h"
+#include "diff.h"
+#include "revision.h"
 
 int save_commit_buffer = 1;
 
@@ -96,12 +98,8 @@ struct commit *lookup_commit_reference(const unsigned char *sha1)
 struct commit *lookup_commit(const unsigned char *sha1)
 {
        struct object *obj = lookup_object(sha1);
-       if (!obj) {
-               struct commit *ret = alloc_commit_node();
-               created_object(sha1, &ret->object);
-               ret->object.type = OBJ_COMMIT;
-               return ret;
-       }
+       if (!obj)
+               return create_object(sha1, OBJ_COMMIT, alloc_commit_node());
        if (!obj->type)
                obj->type = OBJ_COMMIT;
        return check_commit(obj, sha1, 0);
@@ -513,12 +511,16 @@ static int add_rfc2047(char *buf, const char *line, int len,
        bp += i;
        for (i = 0; i < len; i++) {
                unsigned ch = line[i] & 0xFF;
-               if (is_rfc2047_special(ch)) {
+               /*
+                * We encode ' ' using '=20' even though rfc2047
+                * allows using '_' for readability.  Unfortunately,
+                * many programs do not understand this and just
+                * leave the underscore in place.
+                */
+               if (is_rfc2047_special(ch) || ch == ' ') {
                        sprintf(bp, "=%02X", ch);
                        bp += 3;
                }
-               else if (ch == ' ')
-                       *bp++ = '_';
                else
                        *bp++ = ch;
        }
@@ -528,7 +530,7 @@ static int add_rfc2047(char *buf, const char *line, int len,
 }
 
 static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
-                        const char *line, int relative_date,
+                        const char *line, enum date_mode dmode,
                         const char *encoding)
 {
        char *date;
@@ -571,7 +573,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
        switch (fmt) {
        case CMIT_FMT_MEDIUM:
                ret += sprintf(buf + ret, "Date:   %s\n",
-                              show_date(time, tz, relative_date));
+                              show_date(time, tz, dmode));
                break;
        case CMIT_FMT_EMAIL:
                ret += sprintf(buf + ret, "Date: %s\n",
@@ -579,7 +581,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
                break;
        case CMIT_FMT_FULLER:
                ret += sprintf(buf + ret, "%sDate: %s\n", what,
-                              show_date(time, tz, relative_date));
+                              show_date(time, tz, dmode));
                break;
        default:
                /* notin' */
@@ -640,7 +642,9 @@ static char *get_header(const struct commit *commit, const char *key)
                        next = NULL;
                } else
                        next = eol + 1;
-               if (!strncmp(line, key, key_len) && line[key_len] == ' ') {
+               if (eol - line > key_len &&
+                   !strncmp(line, key, key_len) &&
+                   line[key_len] == ' ') {
                        int len = eol - line - key_len;
                        char *ret = xmalloc(len);
                        memcpy(ret, line + key_len + 1, len - 1);
@@ -654,6 +658,7 @@ static char *get_header(const struct commit *commit, const char *key)
 static char *replace_encoding_header(char *buf, const char *encoding)
 {
        char *encoding_header = strstr(buf, "\nencoding ");
+       char *header_end = strstr(buf, "\n\n");
        char *end_of_encoding_header;
        int encoding_header_pos;
        int encoding_header_len;
@@ -661,8 +666,10 @@ static char *replace_encoding_header(char *buf, const char *encoding)
        int need_len;
        int buflen = strlen(buf) + 1;
 
-       if (!encoding_header)
-               return buf; /* should not happen but be defensive */
+       if (!header_end)
+               header_end = buf + buflen;
+       if (!encoding_header || encoding_header >= header_end)
+               return buf;
        encoding_header++;
        end_of_encoding_header = strchr(encoding_header, '\n');
        if (!end_of_encoding_header)
@@ -717,14 +724,6 @@ 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;
@@ -760,7 +759,7 @@ static void fill_person(struct interp *table, const char *msg, int len)
        if (msg + start == ep)
                return;
 
-       table[5].value = xstrndup(msg + start, ep - msg + start);
+       table[5].value = xstrndup(msg + start, ep - (msg + start));
 
        /* parse tz */
        for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
@@ -805,7 +804,8 @@ static long format_commit_message(const struct commit *commit,
                { "%Cgreen" },  /* green */
                { "%Cblue" },   /* blue */
                { "%Creset" },  /* reset color */
-               { "%n" }        /* newline */
+               { "%n" },       /* newline */
+               { "%m" },       /* left/right/bottom */
        };
        enum interp_index {
                IHASH = 0, IHASH_ABBREV,
@@ -821,14 +821,15 @@ static long format_commit_message(const struct commit *commit,
                ISUBJECT,
                IBODY,
                IRED, IGREEN, IBLUE, IRESET_COLOR,
-               INEWLINE
+               INEWLINE,
+               ILEFT_RIGHT,
        };
        struct commit_list *p;
        char parents[1024];
        int i;
        enum { HEADER, SUBJECT, BODY } state;
 
-       if (INEWLINE + 1 != ARRAY_SIZE(table))
+       if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
                die("invalid interp table!");
 
        /* these are independent of the commit */
@@ -849,19 +850,29 @@ static long format_commit_message(const struct commit *commit,
        interp_set_entry(table, ITREE_ABBREV,
                        find_unique_abbrev(commit->tree->object.sha1,
                                DEFAULT_ABBREV));
+       interp_set_entry(table, ILEFT_RIGHT,
+                        (commit->object.flags & BOUNDARY)
+                        ? "-"
+                        : (commit->object.flags & SYMMETRIC_LEFT)
+                        ? "<"
+                        : ">");
+
+       parents[1] = 0;
        for (i = 0, p = commit->parents;
                        p && i < sizeof(parents) - 1;
                        p = p->next)
-               i += snprintf(parents + i, sizeof(parents) - i - 1, "%s ",
+               i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
                        sha1_to_hex(p->item->object.sha1));
-       interp_set_entry(table, IPARENTS, parents);
+       interp_set_entry(table, IPARENTS, parents + 1);
+
+       parents[1] = 0;
        for (i = 0, p = commit->parents;
                        p && i < sizeof(parents) - 1;
                        p = p->next)
-               i += snprintf(parents + i, sizeof(parents) - i - 1, "%s ",
+               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);
+       interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
 
        for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
                int eol;
@@ -884,7 +895,8 @@ static long format_commit_message(const struct commit *commit,
                        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);
+                       table[IENCODING].value =
+                               xstrndup(msg + i + 9, eol - i - 9);
                i = eol;
        }
        if (msg[i])
@@ -905,7 +917,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
                                  char *buf, unsigned long space,
                                  int abbrev, const char *subject,
                                  const char *after_subject,
-                                 int relative_date)
+                                 enum date_mode dmode)
 {
        int hdr = 1, body = 0, seen_title = 0;
        unsigned long offset = 0;
@@ -1009,14 +1021,14 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
                                offset += add_user_info("Author", fmt,
                                                        buf + offset,
                                                        line + 7,
-                                                       relative_date,
+                                                       dmode,
                                                        encoding);
                        if (!memcmp(line, "committer ", 10) &&
                            (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER))
                                offset += add_user_info("Commit", fmt,
                                                        buf + offset,
                                                        line + 10,
-                                                       relative_date,
+                                                       dmode,
                                                        encoding);
                        continue;
                }
@@ -1055,6 +1067,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
                        int sz;
                        char header[512];
                        const char *header_fmt =
+                               "MIME-Version: 1.0\n"
                                "Content-Type: text/plain; charset=%s\n"
                                "Content-Transfer-Encoding: 8bit\n";
                        sz = snprintf(header, sizeof(header), header_fmt,