blame: refactor porcelain output
[gitweb.git] / builtin / apply.c
index 96246e960f9d9fb9230edc0f4e7a9a13267608f2..36e150768ba88e427e6257900d7ec4c67c8303c1 100644 (file)
@@ -204,6 +204,7 @@ struct line {
        unsigned hash : 24;
        unsigned flag : 8;
 #define LINE_COMMON     1
+#define LINE_PATCHED   2
 };
 
 /*
@@ -449,7 +450,7 @@ static char *find_name_gnu(const char *line, char *def, int p_value)
        return squash_slash(strbuf_detach(&name, NULL));
 }
 
-static size_t tz_len(const char *line, size_t len)
+static size_t sane_tz_len(const char *line, size_t len)
 {
        const char *tz, *p;
 
@@ -467,6 +468,24 @@ static size_t tz_len(const char *line, size_t len)
        return line + len - tz;
 }
 
+static size_t tz_with_colon_len(const char *line, size_t len)
+{
+       const char *tz, *p;
+
+       if (len < strlen(" +08:00") || line[len - strlen(":00")] != ':')
+               return 0;
+       tz = line + len - strlen(" +08:00");
+
+       if (tz[0] != ' ' || (tz[1] != '+' && tz[1] != '-'))
+               return 0;
+       p = tz + 2;
+       if (!isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
+           !isdigit(*p++) || !isdigit(*p++))
+               return 0;
+
+       return line + len - tz;
+}
+
 static size_t date_len(const char *line, size_t len)
 {
        const char *date, *p;
@@ -561,7 +580,9 @@ static size_t diff_timestamp_len(const char *line, size_t len)
        if (!isdigit(end[-1]))
                return 0;
 
-       n = tz_len(line, end - line);
+       n = sane_tz_len(line, end - line);
+       if (!n)
+               n = tz_with_colon_len(line, end - line);
        end -= n;
 
        n = short_time_len(line, end - line);
@@ -733,8 +754,8 @@ static int has_epoch_timestamp(const char *nameline)
                " "
                "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
                " "
-               "([-+][0-2][0-9][0-5][0-9])\n";
-       const char *timestamp = NULL, *cp;
+               "([-+][0-2][0-9]:?[0-5][0-9])\n";
+       const char *timestamp = NULL, *cp, *colon;
        static regex_t *stamp;
        regmatch_t m[10];
        int zoneoffset;
@@ -764,8 +785,11 @@ static int has_epoch_timestamp(const char *nameline)
                return 0;
        }
 
-       zoneoffset = strtol(timestamp + m[3].rm_so + 1, NULL, 10);
-       zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100);
+       zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10);
+       if (*colon == ':')
+               zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10);
+       else
+               zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100);
        if (timestamp[m[3].rm_so] == '-')
                zoneoffset = -zoneoffset;
 
@@ -919,28 +943,28 @@ static int gitdiff_newfile(const char *line, struct patch *patch)
 static int gitdiff_copysrc(const char *line, struct patch *patch)
 {
        patch->is_copy = 1;
-       patch->old_name = find_name(line, NULL, 0, 0);
+       patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
 
 static int gitdiff_copydst(const char *line, struct patch *patch)
 {
        patch->is_copy = 1;
-       patch->new_name = find_name(line, NULL, 0, 0);
+       patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
 
 static int gitdiff_renamesrc(const char *line, struct patch *patch)
 {
        patch->is_rename = 1;
-       patch->old_name = find_name(line, NULL, 0, 0);
+       patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
 
 static int gitdiff_renamedst(const char *line, struct patch *patch)
 {
        patch->is_rename = 1;
-       patch->new_name = find_name(line, NULL, 0, 0);
+       patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
 
@@ -1025,7 +1049,7 @@ static char *git_header_name(char *line, int llen)
 {
        const char *name;
        const char *second = NULL;
-       size_t len;
+       size_t len, line_len;
 
        line += strlen("diff --git ");
        llen -= strlen("diff --git ");
@@ -1125,6 +1149,10 @@ static char *git_header_name(char *line, int llen)
         * Accept a name only if it shows up twice, exactly the same
         * form.
         */
+       second = strchr(name, '\n');
+       if (!second)
+               return NULL;
+       line_len = second - name;
        for (len = 0 ; ; len++) {
                switch (name[len]) {
                default:
@@ -1132,15 +1160,11 @@ static char *git_header_name(char *line, int llen)
                case '\n':
                        return NULL;
                case '\t': case ' ':
-                       second = name+len;
-                       for (;;) {
-                               char c = *second++;
-                               if (c == '\n')
-                                       return NULL;
-                               if (c == '/')
-                                       break;
-                       }
-                       if (second[len] == '\n' && !memcmp(name, second, len)) {
+                       second = stop_at_slash(name + len, line_len - len);
+                       if (!second)
+                               return NULL;
+                       second++;
+                       if (second[len] == '\n' && !strncmp(name, second, len)) {
                                return xmemdupz(name, len);
                        }
                }
@@ -2062,7 +2086,8 @@ static int match_fragment(struct image *img,
 
        /* Quick hash check */
        for (i = 0; i < preimage_limit; i++)
-               if (preimage->line[i].hash != img->line[try_lno + i].hash)
+               if ((img->line[try_lno + i].flag & LINE_PATCHED) ||
+                   (preimage->line[i].hash != img->line[try_lno + i].hash))
                        return 0;
 
        if (preimage_limit == preimage->nr) {
@@ -2405,11 +2430,15 @@ static void update_image(struct image *img,
        memcpy(img->line + applied_pos,
               postimage->line,
               postimage->nr * sizeof(*img->line));
+       for (i = 0; i < postimage->nr; i++)
+               img->line[applied_pos + i].flag |= LINE_PATCHED;
+
        img->nr = nr;
 }
 
 static int apply_one_fragment(struct image *img, struct fragment *frag,
-                             int inaccurate_eof, unsigned ws_rule)
+                             int inaccurate_eof, unsigned ws_rule,
+                             int nth_fragment)
 {
        int match_beginning, match_end;
        const char *patch = frag->patch;
@@ -2615,6 +2644,15 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                                apply = 0;
                }
 
+               if (apply_verbosely && applied_pos != pos) {
+                       int offset = applied_pos - pos;
+                       if (apply_in_reverse)
+                               offset = 0 - offset;
+                       fprintf(stderr,
+                               "Hunk #%d succeeded at %d (offset %d lines).\n",
+                               nth_fragment, applied_pos + 1, offset);
+               }
+
                /*
                 * Warn if it was necessary to reduce the number
                 * of context lines.
@@ -2762,12 +2800,14 @@ static int apply_fragments(struct image *img, struct patch *patch)
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned ws_rule = patch->ws_rule;
        unsigned inaccurate_eof = patch->inaccurate_eof;
+       int nth = 0;
 
        if (patch->is_binary)
                return apply_binary(img, patch);
 
        while (frag) {
-               if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule)) {
+               nth++;
+               if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) {
                        error("patch failed: %s:%ld", name, frag->oldpos);
                        if (!apply_with_reject)
                                return -1;