Merge branch 'jk/chopped-ident'
authorJunio C Hamano <gitster@pobox.com>
Mon, 22 Apr 2013 18:11:36 +0000 (11:11 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 22 Apr 2013 18:11:36 +0000 (11:11 -0700)
A commit object whose author or committer ident are malformed
crashed some code that trusted that a name, an email and an
timestamp can always be found in it.

* jk/chopped-ident:
blame: handle broken commit headers gracefully
pretty: handle broken commit headers gracefully
cat-file: print tags raw for "cat-file -p"

builtin/blame.c
builtin/cat-file.c
pretty.c
t/t1006-cat-file.sh
t/t4212-log-corrupt.sh [new file with mode: 0755]
index 86100e96627e4f839fb5b627bc09ed4264c36176..77707819afa03703b3ee110345a79eec3d3d9d6f 100644 (file)
@@ -1375,10 +1375,15 @@ static void get_ac_line(const char *inbuf, const char *what,
        maillen = ident.mail_end - ident.mail_begin;
        mailbuf = ident.mail_begin;
 
-       *time = strtoul(ident.date_begin, NULL, 10);
+       if (ident.date_begin && ident.date_end)
+               *time = strtoul(ident.date_begin, NULL, 10);
+       else
+               *time = 0;
 
-       len = ident.tz_end - ident.tz_begin;
-       strbuf_add(tz, ident.tz_begin, len);
+       if (ident.tz_begin && ident.tz_end)
+               strbuf_add(tz, ident.tz_begin, ident.tz_end - ident.tz_begin);
+       else
+               strbuf_addstr(tz, "(unknown)");
 
        /*
         * Now, convert both name and e-mail using mailmap
index 40f87b4649aa97ca1f33a02f09f5938ce5f41f74..045cee7bce0cfc130d6964bab941a44558ec35a6 100644 (file)
 #define BATCH 1
 #define BATCH_CHECK 2
 
-static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size)
-{
-       /* the parser in tag.c is useless here. */
-       const char *endp = buf + size;
-       const char *cp = buf;
-
-       while (cp < endp) {
-               char c = *cp++;
-               if (c != '\n')
-                       continue;
-               if (7 <= endp - cp && !memcmp("tagger ", cp, 7)) {
-                       const char *tagger = cp;
-
-                       /* Found the tagger line.  Copy out the contents
-                        * of the buffer so far.
-                        */
-                       write_or_die(1, buf, cp - buf);
-
-                       /*
-                        * Do something intelligent, like pretty-printing
-                        * the date.
-                        */
-                       while (cp < endp) {
-                               if (*cp++ == '\n') {
-                                       /* tagger to cp is a line
-                                        * that has ident and time.
-                                        */
-                                       const char *sp = tagger;
-                                       char *ep;
-                                       unsigned long date;
-                                       long tz;
-                                       while (sp < cp && *sp != '>')
-                                               sp++;
-                                       if (sp == cp) {
-                                               /* give up */
-                                               write_or_die(1, tagger,
-                                                            cp - tagger);
-                                               break;
-                                       }
-                                       while (sp < cp &&
-                                              !('0' <= *sp && *sp <= '9'))
-                                               sp++;
-                                       write_or_die(1, tagger, sp - tagger);
-                                       date = strtoul(sp, &ep, 10);
-                                       tz = strtol(ep, NULL, 10);
-                                       sp = show_date(date, tz, 0);
-                                       write_or_die(1, sp, strlen(sp));
-                                       xwrite(1, "\n", 1);
-                                       break;
-                               }
-                       }
-                       break;
-               }
-               if (cp < endp && *cp == '\n')
-                       /* end of header */
-                       break;
-       }
-       /* At this point, we have copied out the header up to the end of
-        * the tagger line and cp points at one past \n.  It could be the
-        * next header line after the tagger line, or it could be another
-        * \n that marks the end of the headers.  We need to copy out the
-        * remainder as is.
-        */
-       if (cp < endp)
-               write_or_die(1, cp, endp - cp);
-}
-
 static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
 {
        unsigned char sha1[20];
@@ -133,10 +66,6 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
                buf = read_sha1_file(sha1, &type, &size);
                if (!buf)
                        die("Cannot read object %s", obj_name);
-               if (type == OBJ_TAG) {
-                       pprint_tag(sha1, buf, size);
-                       return 0;
-               }
 
                /* otherwise just spit out the data */
                break;
index d3a82d22d398ae7234f8917f4d8b0770ba4d0c47..c11624832bf09d9b4630d8a9b5e213fe2de69c7f 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -393,6 +393,19 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
        strbuf_addstr(sb, "?=");
 }
 
+static const char *show_ident_date(const struct ident_split *ident,
+                                  enum date_mode mode)
+{
+       unsigned long date = 0;
+       int tz = 0;
+
+       if (ident->date_begin && ident->date_end)
+               date = strtoul(ident->date_begin, NULL, 10);
+       if (ident->tz_begin && ident->tz_end)
+               tz = strtol(ident->tz_begin, NULL, 10);
+       return show_date(date, tz, mode);
+}
+
 void pp_user_info(const struct pretty_print_context *pp,
                  const char *what, struct strbuf *sb,
                  const char *line, const char *encoding)
@@ -401,12 +414,10 @@ void pp_user_info(const struct pretty_print_context *pp,
        struct strbuf mail;
        struct ident_split ident;
        int linelen;
-       char *line_end, *date;
+       char *line_end;
        const char *mailbuf, *namebuf;
        size_t namelen, maillen;
        int max_length = 78; /* per rfc2822 */
-       unsigned long time;
-       int tz;
 
        if (pp->fmt == CMIT_FMT_ONELINE)
                return;
@@ -438,8 +449,6 @@ void pp_user_info(const struct pretty_print_context *pp,
        strbuf_add(&name, namebuf, namelen);
 
        namelen = name.len + mail.len + 3; /* ' ' + '<' + '>' */
-       time = strtoul(ident.date_begin, &date, 10);
-       tz = strtol(date, NULL, 10);
 
        if (pp->fmt == CMIT_FMT_EMAIL) {
                strbuf_addstr(sb, "From: ");
@@ -472,13 +481,16 @@ void pp_user_info(const struct pretty_print_context *pp,
 
        switch (pp->fmt) {
        case CMIT_FMT_MEDIUM:
-               strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, pp->date_mode));
+               strbuf_addf(sb, "Date:   %s\n",
+                           show_ident_date(&ident, pp->date_mode));
                break;
        case CMIT_FMT_EMAIL:
-               strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
+               strbuf_addf(sb, "Date: %s\n",
+                           show_ident_date(&ident, DATE_RFC2822));
                break;
        case CMIT_FMT_FULLER:
-               strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, pp->date_mode));
+               strbuf_addf(sb, "%sDate: %s\n", what,
+                           show_ident_date(&ident, pp->date_mode));
                break;
        default:
                /* notin' */
@@ -688,8 +700,6 @@ static size_t format_person_part(struct strbuf *sb, char part,
 {
        /* currently all placeholders have same length */
        const int placeholder_len = 2;
-       int tz;
-       unsigned long date = 0;
        struct ident_split s;
        const char *name, *mail;
        size_t maillen, namelen;
@@ -716,30 +726,23 @@ static size_t format_person_part(struct strbuf *sb, char part,
        if (!s.date_begin)
                goto skip;
 
-       date = strtoul(s.date_begin, NULL, 10);
-
        if (part == 't') {      /* date, UNIX timestamp */
                strbuf_add(sb, s.date_begin, s.date_end - s.date_begin);
                return placeholder_len;
        }
 
-       /* parse tz */
-       tz = strtoul(s.tz_begin + 1, NULL, 10);
-       if (*s.tz_begin == '-')
-               tz = -tz;
-
        switch (part) {
        case 'd':       /* date */
-               strbuf_addstr(sb, show_date(date, tz, dmode));
+               strbuf_addstr(sb, show_ident_date(&s, dmode));
                return placeholder_len;
        case 'D':       /* date, RFC2822 style */
-               strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822));
+               strbuf_addstr(sb, show_ident_date(&s, DATE_RFC2822));
                return placeholder_len;
        case 'r':       /* date, relative */
-               strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE));
+               strbuf_addstr(sb, show_ident_date(&s, DATE_RELATIVE));
                return placeholder_len;
        case 'i':       /* date, ISO 8601 */
-               strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601));
+               strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601));
                return placeholder_len;
        }
 
index 9820f70c84d93b9461ae6663e6ab5b3cd735e3d5..9cc5c6bf4dda488428f6b4389e4e494c6c63983c 100755 (executable)
@@ -134,15 +134,12 @@ tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
 tag_description="This is a tag"
 tag_content="$tag_header_without_timestamp 0000000000 +0000
 
-$tag_description"
-tag_pretty_content="$tag_header_without_timestamp Thu Jan 1 00:00:00 1970 +0000
-
 $tag_description"
 
 tag_sha1=$(echo_without_newline "$tag_content" | git mktag)
 tag_size=$(strlen "$tag_content")
 
-run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_pretty_content" 1
+run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content" 1
 
 test_expect_success \
     "Reach a blob from a tag pointing to it" \
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
new file mode 100755 (executable)
index 0000000..ec5099b
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='git log with invalid commit headers'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit foo &&
+
+       git cat-file commit HEAD |
+       sed "/^author /s/>/>-<>/" >broken_email.commit &&
+       git hash-object -w -t commit broken_email.commit >broken_email.hash &&
+       git update-ref refs/heads/broken_email $(cat broken_email.hash)
+'
+
+test_expect_success 'git log with broken author email' '
+       {
+               echo commit $(cat broken_email.hash)
+               echo "Author: A U Thor <author@example.com>"
+               echo "Date:   Thu Jan 1 00:00:00 1970 +0000"
+               echo
+               echo "    foo"
+       } >expect.out &&
+       : >expect.err &&
+
+       git log broken_email >actual.out 2>actual.err &&
+
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
+'
+
+test_expect_success 'git log --format with broken author email' '
+       echo "A U Thor+author@example.com+" >expect.out &&
+       : >expect.err &&
+
+       git log --format="%an+%ae+%ad" broken_email >actual.out 2>actual.err &&
+
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
+'
+
+test_done