Merge branch 'ye/doc-http-proto' into maint
[gitweb.git] / builtin / blame.c
index e44a6bb30a5a5299c6d57150dd2f23454b25891a..6a284ce46b4f3b46a47d033fca607cae710806bc 100644 (file)
@@ -74,7 +74,7 @@ static unsigned blame_copy_score;
 #define BLAME_DEFAULT_MOVE_SCORE       20
 #define BLAME_DEFAULT_COPY_SCORE       40
 
-/* bits #0..7 in revision.h, #8..11 used for merge_bases() in commit.c */
+/* Remember to update object flag allocation in object.h */
 #define METAINFO_SHOWN         (1u<<12)
 #define MORE_THAN_ONE_PATH     (1u<<13)
 
@@ -197,7 +197,6 @@ static void drop_origin_blob(struct origin *o)
  * scoreboard structure, sorted by the target line number.
  */
 struct blame_entry {
-       struct blame_entry *prev;
        struct blame_entry *next;
 
        /* the first line of this group in the final image;
@@ -256,15 +255,6 @@ struct scoreboard {
        int *lineno;
 };
 
-static inline int same_suspect(struct origin *a, struct origin *b)
-{
-       if (a == b)
-               return 1;
-       if (a->commit != b->commit)
-               return 0;
-       return !strcmp(a->path, b->path);
-}
-
 static void sanity_check_refcnt(struct scoreboard *);
 
 /*
@@ -277,13 +267,11 @@ static void coalesce(struct scoreboard *sb)
        struct blame_entry *ent, *next;
 
        for (ent = sb->ent; ent && (next = ent->next); ent = next) {
-               if (same_suspect(ent->suspect, next->suspect) &&
+               if (ent->suspect == next->suspect &&
                    ent->guilty == next->guilty &&
                    ent->s_lno + ent->num_lines == next->s_lno) {
                        ent->num_lines += next->num_lines;
                        ent->next = next->next;
-                       if (ent->next)
-                               ent->next->prev = ent;
                        origin_decref(next->suspect);
                        free(next);
                        ent->score = 0;
@@ -534,7 +522,7 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
                prev = ent;
 
        /* prev, if not NULL, is the last one that is below e */
-       e->prev = prev;
+
        if (prev) {
                e->next = prev->next;
                prev->next = e;
@@ -543,8 +531,6 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
                e->next = sb->ent;
                sb->ent = e;
        }
-       if (e->next)
-               e->next->prev = e;
 }
 
 /*
@@ -555,14 +541,12 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
  */
 static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
 {
-       struct blame_entry *p, *n;
+       struct blame_entry *n;
 
-       p = dst->prev;
        n = dst->next;
        origin_incref(src->suspect);
        origin_decref(dst->suspect);
        memcpy(dst, src, sizeof(*src));
-       dst->prev = p;
        dst->next = n;
        dst->score = 0;
 }
@@ -742,7 +726,7 @@ static int find_last_in_target(struct scoreboard *sb, struct origin *target)
        int last_in_target = -1;
 
        for (e = sb->ent; e; e = e->next) {
-               if (e->guilty || !same_suspect(e->suspect, target))
+               if (e->guilty || e->suspect != target)
                        continue;
                if (last_in_target < e->s_lno + e->num_lines)
                        last_in_target = e->s_lno + e->num_lines;
@@ -762,7 +746,7 @@ static void blame_chunk(struct scoreboard *sb,
        struct blame_entry *e;
 
        for (e = sb->ent; e; e = e->next) {
-               if (e->guilty || !same_suspect(e->suspect, target))
+               if (e->guilty || e->suspect != target)
                        continue;
                if (same <= e->s_lno)
                        continue;
@@ -939,7 +923,6 @@ static void find_copy_in_blob(struct scoreboard *sb,
                              mmfile_t *file_p)
 {
        const char *cp;
-       int cnt;
        mmfile_t file_o;
        struct handle_split_cb_data d;
 
@@ -950,13 +933,7 @@ static void find_copy_in_blob(struct scoreboard *sb,
         */
        cp = nth_line(sb, ent->lno);
        file_o.ptr = (char *) cp;
-       cnt = ent->num_lines;
-
-       while (cnt && cp < sb->final_buf + sb->final_buf_size) {
-               if (*cp++ == '\n')
-                       cnt--;
-       }
-       file_o.size = cp - file_o.ptr;
+       file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp;
 
        /*
         * file_o is a part of final image we are annotating.
@@ -992,7 +969,7 @@ static int find_move_in_parent(struct scoreboard *sb,
        while (made_progress) {
                made_progress = 0;
                for (e = sb->ent; e; e = e->next) {
-                       if (e->guilty || !same_suspect(e->suspect, target) ||
+                       if (e->guilty || e->suspect != target ||
                            ent_score(sb, e) < blame_move_score)
                                continue;
                        find_copy_in_blob(sb, e, parent, split, &file_p);
@@ -1027,14 +1004,14 @@ static struct blame_list *setup_blame_list(struct scoreboard *sb,
 
        for (e = sb->ent, num_ents = 0; e; e = e->next)
                if (!e->scanned && !e->guilty &&
-                   same_suspect(e->suspect, target) &&
+                   e->suspect == target &&
                    min_score < ent_score(sb, e))
                        num_ents++;
        if (num_ents) {
                blame_list = xcalloc(num_ents, sizeof(struct blame_list));
                for (e = sb->ent, i = 0; e; e = e->next)
                        if (!e->scanned && !e->guilty &&
-                           same_suspect(e->suspect, target) &&
+                           e->suspect == target &&
                            min_score < ent_score(sb, e))
                                blame_list[i++].ent = e;
        }
@@ -1178,7 +1155,7 @@ static void pass_whole_blame(struct scoreboard *sb,
                origin->file.ptr = NULL;
        }
        for (e = sb->ent; e; e = e->next) {
-               if (!same_suspect(e->suspect, origin))
+               if (e->suspect != origin)
                        continue;
                origin_incref(porigin);
                origin_decref(e->suspect);
@@ -1428,7 +1405,7 @@ static void get_commit_info(struct commit *commit,
 {
        int len;
        const char *subject, *encoding;
-       char *message;
+       const char *message;
 
        commit_info_init(ret);
 
@@ -1439,7 +1416,7 @@ static void get_commit_info(struct commit *commit,
                    &ret->author_time, &ret->author_tz);
 
        if (!detailed) {
-               logmsg_free(message, commit);
+               unuse_commit_buffer(commit, message);
                return;
        }
 
@@ -1453,7 +1430,7 @@ static void get_commit_info(struct commit *commit,
        else
                strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1));
 
-       logmsg_free(message, commit);
+       unuse_commit_buffer(commit, message);
 }
 
 /*
@@ -1567,7 +1544,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
 
                /* Take responsibility for the remaining entries */
                for (ent = sb->ent; ent; ent = ent->next)
-                       if (same_suspect(ent->suspect, suspect))
+                       if (ent->suspect == suspect)
                                found_guilty_entry(ent);
                origin_decref(suspect);
 
@@ -1579,22 +1556,29 @@ static void assign_blame(struct scoreboard *sb, int opt)
 static const char *format_time(unsigned long time, const char *tz_str,
                               int show_raw_time)
 {
-       static char time_buf[128];
-       const char *time_str;
-       int time_len;
-       int tz;
+       static struct strbuf time_buf = STRBUF_INIT;
 
+       strbuf_reset(&time_buf);
        if (show_raw_time) {
-               snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
+               strbuf_addf(&time_buf, "%lu %s", time, tz_str);
        }
        else {
+               const char *time_str;
+               size_t time_width;
+               int tz;
                tz = atoi(tz_str);
                time_str = show_date(time, tz, blame_date_mode);
-               time_len = strlen(time_str);
-               memcpy(time_buf, time_str, time_len);
-               memset(time_buf + time_len, ' ', blame_date_width - time_len);
+               strbuf_addstr(&time_buf, time_str);
+               /*
+                * Add space paddings to time_buf to display a fixed width
+                * string, and use time_width for display width calibration.
+                */
+               for (time_width = utf8_strwidth(time_str);
+                    time_width < blame_date_width;
+                    time_width++)
+                       strbuf_addch(&time_buf, ' ');
        }
-       return time_buf;
+       return time_buf.buf;
 }
 
 #define OUTPUT_ANNOTATE_COMPAT 001
@@ -1772,25 +1756,41 @@ static int prepare_lines(struct scoreboard *sb)
 {
        const char *buf = sb->final_buf;
        unsigned long len = sb->final_buf_size;
-       int num = 0, incomplete = 0, bol = 1;
+       const char *end = buf + len;
+       const char *p;
+       int *lineno;
+       int num = 0, incomplete = 0;
 
-       if (len && buf[len-1] != '\n')
-               incomplete++; /* incomplete line at the end */
-       while (len--) {
-               if (bol) {
-                       sb->lineno = xrealloc(sb->lineno,
-                                             sizeof(int *) * (num + 1));
-                       sb->lineno[num] = buf - sb->final_buf;
-                       bol = 0;
-               }
-               if (*buf++ == '\n') {
+       for (p = buf;;) {
+               p = memchr(p, '\n', end - p);
+               if (p) {
+                       p++;
                        num++;
-                       bol = 1;
+                       continue;
                }
+               break;
        }
-       sb->lineno = xrealloc(sb->lineno,
-                             sizeof(int *) * (num + incomplete + 1));
-       sb->lineno[num + incomplete] = buf - sb->final_buf;
+
+       if (len && end[-1] != '\n')
+               incomplete++; /* incomplete line at the end */
+
+       sb->lineno = xmalloc(sizeof(*sb->lineno) * (num + incomplete + 1));
+       lineno = sb->lineno;
+
+       *lineno++ = 0;
+       for (p = buf;;) {
+               p = memchr(p, '\n', end - p);
+               if (p) {
+                       p++;
+                       *lineno++ = p - buf;
+                       continue;
+               }
+               break;
+       }
+
+       if (incomplete)
+               *lineno++ = len;
+
        sb->num_lines = num + incomplete;
        return sb->num_lines;
 }
@@ -2005,6 +2005,18 @@ static void append_merge_parents(struct commit_list **tail)
        strbuf_release(&line);
 }
 
+/*
+ * This isn't as simple as passing sb->buf and sb->len, because we
+ * want to transfer ownership of the buffer to the commit (so we
+ * must use detach).
+ */
+static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
+{
+       size_t len;
+       void *buf = strbuf_detach(sb, &len);
+       set_commit_buffer(c, buf, len);
+}
+
 /*
  * Prepare a dummy commit that represents the work tree (or staged) item.
  * Note that annotating work tree item never works in the reverse.
@@ -2026,7 +2038,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
        struct strbuf msg = STRBUF_INIT;
 
        time(&now);
-       commit = xcalloc(1, sizeof(*commit));
+       commit = alloc_commit_node();
        commit->object.parsed = 1;
        commit->date = now;
        commit->object.type = OBJ_COMMIT;
@@ -2053,7 +2065,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
                    ident, ident, path,
                    (!contents_from ? path :
                     (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
-       commit->buffer = strbuf_detach(&msg, NULL);
+       set_commit_buffer_from_strbuf(commit, &msg);
 
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
@@ -2095,7 +2107,6 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
                if (strbuf_read(&buf, 0, 0) < 0)
                        die_errno("failed to read from stdin");
        }
-       convert_to_git(path, buf.buf, buf.len, &buf, 0);
        origin->file.ptr = buf.buf;
        origin->file.size = buf.len;
        pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
@@ -2338,7 +2349,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                blame_date_width = sizeof("2006-10-19");
                break;
        case DATE_RELATIVE:
-               /* "normal" is used as the fallback for "relative" */
+               /* TRANSLATORS: This string is used to tell us the maximum
+                  display width for a relative timestamp in "git blame"
+                  output.  For C locale, "4 years, 11 months ago", which
+                  takes 22 places, is the longest among various forms of
+                  relative timestamps, but your language may need more or
+                  fewer display columns. */
+               blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
+               break;
        case DATE_LOCAL:
        case DATE_NORMAL:
                blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
@@ -2440,11 +2458,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                die("revision walk setup failed");
 
        if (is_null_sha1(sb.final->object.sha1)) {
-               char *buf;
                o = sb.final->util;
-               buf = xmalloc(o->file.size + 1);
-               memcpy(buf, o->file.ptr, o->file.size + 1);
-               sb.final_buf = buf;
+               sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
                sb.final_buf_size = o->file.size;
        }
        else {
@@ -2502,8 +2517,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                ent->suspect = o;
                ent->s_lno = bottom;
                ent->next = next;
-               if (next)
-                       next->prev = ent;
                origin_incref(o);
        }
        origin_decref(o);