blame: move no_whole_file_rename flag to scoreboard
[gitweb.git] / builtin / blame.c
index f7aa95f4babaab904443784eccdaa3b219bd2b08..90c643c7da671235ad51b563d93db7e5fa56b431 100644 (file)
@@ -8,7 +8,6 @@
 #include "cache.h"
 #include "refs.h"
 #include "builtin.h"
-#include "blob.h"
 #include "commit.h"
 #include "tag.h"
 #include "tree-walk.h"
@@ -62,20 +61,11 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP;
 #define DEBUG 0
 #endif
 
-/* stats */
-static int num_read_blob;
-static int num_get_patch;
-static int num_commits;
-
 #define PICKAXE_BLAME_MOVE             01
 #define PICKAXE_BLAME_COPY             02
 #define PICKAXE_BLAME_COPY_HARDER      04
 #define PICKAXE_BLAME_COPY_HARDEST     010
 
-/*
- * blame for a blame_entry with score lower than these thresholds
- * is not passed to the parent using move/copy logic.
- */
 static unsigned blame_move_score;
 static unsigned blame_copy_score;
 #define BLAME_DEFAULT_MOVE_SCORE       20
@@ -88,10 +78,10 @@ static unsigned blame_copy_score;
 /*
  * One blob in a commit that is being suspected
  */
-struct origin {
+struct blame_origin {
        int refcnt;
        /* Record preceding blame record for this blob */
-       struct origin *previous;
+       struct blame_origin *previous;
        /* origins are put in a list linked via `next' hanging off the
         * corresponding commit's util field in order to make finding
         * them fast.  The presence in this chain does not count
@@ -109,7 +99,7 @@ struct origin {
         * us get tripped up by this case, it certainly does not seem
         * worth optimizing for.
         */
-       struct origin *next;
+       struct blame_origin *next;
        struct commit *commit;
        /* `suspects' contains blame entries that may be attributed to
         * this origin's commit or to parent commits.  When a commit
@@ -135,7 +125,7 @@ struct progress_info {
 };
 
 static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,
-                     xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
+                     xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts)
 {
        xpparam_t xpp = {0};
        xdemitconf_t xecfg = {0};
@@ -147,46 +137,18 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,
        return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
 }
 
-/*
- * Prepare diff_filespec and convert it using diff textconv API
- * if the textconv driver exists.
- * Return 1 if the conversion succeeds, 0 otherwise.
- */
-int textconv_object(const char *path,
-                   unsigned mode,
-                   const struct object_id *oid,
-                   int oid_valid,
-                   char **buf,
-                   unsigned long *buf_size)
-{
-       struct diff_filespec *df;
-       struct userdiff_driver *textconv;
-
-       df = alloc_filespec(path);
-       fill_filespec(df, oid->hash, oid_valid, mode);
-       textconv = get_textconv(df);
-       if (!textconv) {
-               free_filespec(df);
-               return 0;
-       }
-
-       *buf_size = fill_textconv(textconv, df, buf);
-       free_filespec(df);
-       return 1;
-}
-
 /*
  * Given an origin, prepare mmfile_t structure to be used by the
  * diff machinery
  */
 static void fill_origin_blob(struct diff_options *opt,
-                            struct origin *o, mmfile_t *file)
+                            struct blame_origin *o, mmfile_t *file, int *num_read_blob)
 {
        if (!o->file.ptr) {
                enum object_type type;
                unsigned long file_size;
 
-               num_read_blob++;
+               (*num_read_blob)++;
                if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
                    textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
                        ;
@@ -209,19 +171,19 @@ static void fill_origin_blob(struct diff_options *opt,
  * Origin is refcounted and usually we keep the blob contents to be
  * reused.
  */
-static inline struct origin *origin_incref(struct origin *o)
+static inline struct blame_origin *blame_origin_incref(struct blame_origin *o)
 {
        if (o)
                o->refcnt++;
        return o;
 }
 
-static void origin_decref(struct origin *o)
+static void blame_origin_decref(struct blame_origin *o)
 {
        if (o && --o->refcnt <= 0) {
-               struct origin *p, *l = NULL;
+               struct blame_origin *p, *l = NULL;
                if (o->previous)
-                       origin_decref(o->previous);
+                       blame_origin_decref(o->previous);
                free(o->file.ptr);
                /* Should be present exactly once in commit chain */
                for (p = o->commit->util; p; l = p, p = p->next) {
@@ -234,11 +196,11 @@ static void origin_decref(struct origin *o)
                                return;
                        }
                }
-               die("internal error in blame::origin_decref");
+               die("internal error in blame_origin_decref");
        }
 }
 
-static void drop_origin_blob(struct origin *o)
+static void drop_origin_blob(struct blame_origin *o)
 {
        if (o->file.ptr) {
                free(o->file.ptr);
@@ -267,7 +229,7 @@ struct blame_entry {
        int num_lines;
 
        /* the commit that introduced this group into the final image */
-       struct origin *suspect;
+       struct blame_origin *suspect;
 
        /* the line number of the first line of this group in the
         * suspect's file; internally all line numbers are 0 based.
@@ -382,7 +344,7 @@ static int compare_commits_by_reverse_commit_date(const void *a,
 /*
  * The current state of the blame assignment.
  */
-struct scoreboard {
+struct blame_scoreboard {
        /* the final commit (i.e. where we started digging from) */
        struct commit *final;
        /* Priority queue for commits with unassigned blame records */
@@ -404,16 +366,37 @@ struct scoreboard {
        /* look-up a line in the final buffer */
        int num_lines;
        int *lineno;
+
+       /* stats */
+       int num_read_blob;
+       int num_get_patch;
+       int num_commits;
+
+       /*
+        * blame for a blame_entry with score lower than these thresholds
+        * is not passed to the parent using move/copy logic.
+        */
+       unsigned move_score;
+       unsigned copy_score;
+
+       /* use this file's contents as the final image */
+       const char *contents_from;
+
+       /* flags */
+       int reverse;
+       int show_root;
+       int xdl_opts;
+       int no_whole_file_rename;
 };
 
-static void sanity_check_refcnt(struct scoreboard *);
+static void sanity_check_refcnt(struct blame_scoreboard *);
 
 /*
  * If two blame entries that are next to each other came from
  * contiguous lines in the same origin (i.e. <commit, path> pair),
  * merge them together.
  */
-static void coalesce(struct scoreboard *sb)
+static void blame_coalesce(struct blame_scoreboard *sb)
 {
        struct blame_entry *ent, *next;
 
@@ -422,7 +405,7 @@ static void coalesce(struct scoreboard *sb)
                    ent->s_lno + ent->num_lines == next->s_lno) {
                        ent->num_lines += next->num_lines;
                        ent->next = next->next;
-                       origin_decref(next->suspect);
+                       blame_origin_decref(next->suspect);
                        free(next);
                        ent->score = 0;
                        next = ent; /* again */
@@ -439,13 +422,13 @@ static void coalesce(struct scoreboard *sb)
  * the commit priority queue of the score board.
  */
 
-static void queue_blames(struct scoreboard *sb, struct origin *porigin,
+static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porigin,
                         struct blame_entry *sorted)
 {
        if (porigin->suspects)
                porigin->suspects = blame_merge(porigin->suspects, sorted);
        else {
-               struct origin *o;
+               struct blame_origin *o;
                for (o = porigin->commit->util; o; o = o->next) {
                        if (o->suspects) {
                                porigin->suspects = sorted;
@@ -463,9 +446,9 @@ static void queue_blames(struct scoreboard *sb, struct origin *porigin,
  * get_origin() to obtain shared, refcounted copy instead of calling
  * this function directly.
  */
-static struct origin *make_origin(struct commit *commit, const char *path)
+static struct blame_origin *make_origin(struct commit *commit, const char *path)
 {
-       struct origin *o;
+       struct blame_origin *o;
        FLEX_ALLOC_STR(o, path, path);
        o->commit = commit;
        o->refcnt = 1;
@@ -478,11 +461,9 @@ static struct origin *make_origin(struct commit *commit, const char *path)
  * Locate an existing origin or create a new one.
  * This moves the origin to front position in the commit util list.
  */
-static struct origin *get_origin(struct scoreboard *sb,
-                                struct commit *commit,
-                                const char *path)
+static struct blame_origin *get_origin(struct commit *commit, const char *path)
 {
-       struct origin *o, *l;
+       struct blame_origin *o, *l;
 
        for (o = commit->util, l = NULL; o; l = o, o = o->next) {
                if (!strcmp(o->path, path)) {
@@ -492,7 +473,7 @@ static struct origin *get_origin(struct scoreboard *sb,
                                o->next = commit->util;
                                commit->util = o;
                        }
-                       return origin_incref(o);
+                       return blame_origin_incref(o);
                }
        }
        return make_origin(commit, path);
@@ -507,7 +488,7 @@ static struct origin *get_origin(struct scoreboard *sb,
  *
  * This also fills origin->mode for corresponding tree path.
  */
-static int fill_blob_sha1_and_mode(struct origin *origin)
+static int fill_blob_sha1_and_mode(struct blame_origin *origin)
 {
        if (!is_null_oid(&origin->blob_oid))
                return 0;
@@ -528,11 +509,10 @@ static int fill_blob_sha1_and_mode(struct origin *origin)
  * We have an origin -- check if the same path exists in the
  * parent and return an origin structure to represent it.
  */
-static struct origin *find_origin(struct scoreboard *sb,
-                                 struct commit *parent,
-                                 struct origin *origin)
+static struct blame_origin *find_origin(struct commit *parent,
+                                 struct blame_origin *origin)
 {
-       struct origin *porigin;
+       struct blame_origin *porigin;
        struct diff_options diff_opts;
        const char *paths[2];
 
@@ -543,7 +523,7 @@ static struct origin *find_origin(struct scoreboard *sb,
                         * The same path between origin and its parent
                         * without renaming -- the most common case.
                         */
-                       return origin_incref (porigin);
+                       return blame_origin_incref (porigin);
                }
 
        /* See if the origin->path is different between parent
@@ -572,7 +552,7 @@ static struct origin *find_origin(struct scoreboard *sb,
 
        if (!diff_queued_diff.nr) {
                /* The path is the same as parent */
-               porigin = get_origin(sb, parent, origin->path);
+               porigin = get_origin(parent, origin->path);
                oidcpy(&porigin->blob_oid, &origin->blob_oid);
                porigin->mode = origin->mode;
        } else {
@@ -598,7 +578,7 @@ static struct origin *find_origin(struct scoreboard *sb,
                        die("internal error in blame::find_origin (%c)",
                            p->status);
                case 'M':
-                       porigin = get_origin(sb, parent, origin->path);
+                       porigin = get_origin(parent, origin->path);
                        oidcpy(&porigin->blob_oid, &p->one->oid);
                        porigin->mode = p->one->mode;
                        break;
@@ -617,11 +597,10 @@ static struct origin *find_origin(struct scoreboard *sb,
  * We have an origin -- find the path that corresponds to it in its
  * parent and return an origin structure to represent it.
  */
-static struct origin *find_rename(struct scoreboard *sb,
-                                 struct commit *parent,
-                                 struct origin *origin)
+static struct blame_origin *find_rename(struct commit *parent,
+                                 struct blame_origin *origin)
 {
-       struct origin *porigin = NULL;
+       struct blame_origin *porigin = NULL;
        struct diff_options diff_opts;
        int i;
 
@@ -644,7 +623,7 @@ static struct origin *find_rename(struct scoreboard *sb,
                struct diff_filepair *p = diff_queued_diff.queue[i];
                if ((p->status == 'R' || p->status == 'C') &&
                    !strcmp(p->two->path, origin->path)) {
-                       porigin = get_origin(sb, parent, p->one->path);
+                       porigin = get_origin(parent, p->one->path);
                        oidcpy(&porigin->blob_oid, &p->one->oid);
                        porigin->mode = p->one->mode;
                        break;
@@ -663,7 +642,7 @@ static void add_blame_entry(struct blame_entry ***queue,
 {
        struct blame_entry *e = xmalloc(sizeof(*e));
        memcpy(e, src, sizeof(*e));
-       origin_incref(e->suspect);
+       blame_origin_incref(e->suspect);
 
        e->next = **queue;
        **queue = e;
@@ -678,22 +657,22 @@ static void add_blame_entry(struct blame_entry ***queue,
 static void dup_entry(struct blame_entry ***queue,
                      struct blame_entry *dst, struct blame_entry *src)
 {
-       origin_incref(src->suspect);
-       origin_decref(dst->suspect);
+       blame_origin_incref(src->suspect);
+       blame_origin_decref(dst->suspect);
        memcpy(dst, src, sizeof(*src));
        dst->next = **queue;
        **queue = dst;
        *queue = &dst->next;
 }
 
-static const char *nth_line(struct scoreboard *sb, long lno)
+static const char *blame_nth_line(struct blame_scoreboard *sb, long lno)
 {
        return sb->final_buf + sb->lineno[lno];
 }
 
 static const char *nth_line_cb(void *data, long lno)
 {
-       return nth_line((struct scoreboard *)data, lno);
+       return blame_nth_line((struct blame_scoreboard *)data, lno);
 }
 
 /*
@@ -713,14 +692,14 @@ static const char *nth_line_cb(void *data, long lno)
 static void split_overlap(struct blame_entry *split,
                          struct blame_entry *e,
                          int tlno, int plno, int same,
-                         struct origin *parent)
+                         struct blame_origin *parent)
 {
        int chunk_end_lno;
        memset(split, 0, sizeof(struct blame_entry [3]));
 
        if (e->s_lno < tlno) {
                /* there is a pre-chunk part not blamed on parent */
-               split[0].suspect = origin_incref(e->suspect);
+               split[0].suspect = blame_origin_incref(e->suspect);
                split[0].lno = e->lno;
                split[0].s_lno = e->s_lno;
                split[0].num_lines = tlno - e->s_lno;
@@ -734,7 +713,7 @@ static void split_overlap(struct blame_entry *split,
 
        if (same < e->s_lno + e->num_lines) {
                /* there is a post-chunk part not blamed on parent */
-               split[2].suspect = origin_incref(e->suspect);
+               split[2].suspect = blame_origin_incref(e->suspect);
                split[2].lno = e->lno + (same - e->s_lno);
                split[2].s_lno = e->s_lno + (same - e->s_lno);
                split[2].num_lines = e->s_lno + e->num_lines - same;
@@ -750,7 +729,7 @@ static void split_overlap(struct blame_entry *split,
         */
        if (split[1].num_lines < 1)
                return;
-       split[1].suspect = origin_incref(parent);
+       split[1].suspect = blame_origin_incref(parent);
 }
 
 /*
@@ -800,7 +779,7 @@ static void decref_split(struct blame_entry *split)
        int i;
 
        for (i = 0; i < 3; i++)
-               origin_decref(split[i].suspect);
+               blame_origin_decref(split[i].suspect);
 }
 
 /*
@@ -837,7 +816,7 @@ static struct blame_entry *reverse_blame(struct blame_entry *head,
  */
 static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,
                        int tlno, int offset, int same,
-                       struct origin *parent)
+                       struct blame_origin *parent)
 {
        struct blame_entry *e = **srcq;
        struct blame_entry *samep = NULL, *diffp = NULL;
@@ -863,10 +842,10 @@ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,
                        n->next = diffp;
                        diffp = n;
                } else
-                       origin_decref(e->suspect);
+                       blame_origin_decref(e->suspect);
                /* Pass blame for everything before the differing
                 * chunk to the parent */
-               e->suspect = origin_incref(parent);
+               e->suspect = blame_origin_incref(parent);
                e->s_lno += offset;
                e->next = samep;
                samep = e;
@@ -907,7 +886,7 @@ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,
                         */
                        int len = same - e->s_lno;
                        struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
-                       n->suspect = origin_incref(e->suspect);
+                       n->suspect = blame_origin_incref(e->suspect);
                        n->lno = e->lno + len;
                        n->s_lno = e->s_lno + len;
                        n->num_lines = e->num_lines - len;
@@ -928,7 +907,7 @@ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,
 }
 
 struct blame_chunk_cb_data {
-       struct origin *parent;
+       struct blame_origin *parent;
        long offset;
        struct blame_entry **dstq;
        struct blame_entry **srcq;
@@ -952,9 +931,9 @@ static int blame_chunk_cb(long start_a, long count_a,
  * for the lines it is suspected to its parent.  Run diff to find
  * which lines came from parent and pass blame for them.
  */
-static void pass_blame_to_parent(struct scoreboard *sb,
-                                struct origin *target,
-                                struct origin *parent)
+static void pass_blame_to_parent(struct blame_scoreboard *sb,
+                                struct blame_origin *target,
+                                struct blame_origin *parent)
 {
        mmfile_t file_p, file_o;
        struct blame_chunk_cb_data d;
@@ -967,11 +946,11 @@ static void pass_blame_to_parent(struct scoreboard *sb,
        d.offset = 0;
        d.dstq = &newdest; d.srcq = &target->suspects;
 
-       fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
-       fill_origin_blob(&sb->revs->diffopt, target, &file_o);
-       num_get_patch++;
+       fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);
+       fill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob);
+       sb->num_get_patch++;
 
-       if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d))
+       if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts))
                die("unable to generate diff (%s -> %s)",
                    oid_to_hex(&parent->commit->object.oid),
                    oid_to_hex(&target->commit->object.oid));
@@ -992,7 +971,7 @@ static void pass_blame_to_parent(struct scoreboard *sb,
  *
  * Compute how trivial the lines in the blame_entry are.
  */
-static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)
+static unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e)
 {
        unsigned score;
        const char *cp, *ep;
@@ -1001,8 +980,8 @@ static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)
                return e->score;
 
        score = 1;
-       cp = nth_line(sb, e->lno);
-       ep = nth_line(sb, e->lno + e->num_lines);
+       cp = blame_nth_line(sb, e->lno);
+       ep = blame_nth_line(sb, e->lno + e->num_lines);
        while (cp < ep) {
                unsigned ch = *((unsigned char *)cp);
                if (isalnum(ch))
@@ -1019,7 +998,7 @@ static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)
  * so far, by comparing this and best_so_far and copying this into
  * bst_so_far as needed.
  */
-static void copy_split_if_better(struct scoreboard *sb,
+static void copy_split_if_better(struct blame_scoreboard *sb,
                                 struct blame_entry *best_so_far,
                                 struct blame_entry *this)
 {
@@ -1028,12 +1007,12 @@ static void copy_split_if_better(struct scoreboard *sb,
        if (!this[1].suspect)
                return;
        if (best_so_far[1].suspect) {
-               if (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1]))
+               if (blame_entry_score(sb, &this[1]) < blame_entry_score(sb, &best_so_far[1]))
                        return;
        }
 
        for (i = 0; i < 3; i++)
-               origin_incref(this[i].suspect);
+               blame_origin_incref(this[i].suspect);
        decref_split(best_so_far);
        memcpy(best_so_far, this, sizeof(struct blame_entry [3]));
 }
@@ -1053,10 +1032,10 @@ static void copy_split_if_better(struct scoreboard *sb,
  *
  * All line numbers are 0-based.
  */
-static void handle_split(struct scoreboard *sb,
+static void handle_split(struct blame_scoreboard *sb,
                         struct blame_entry *ent,
                         int tlno, int plno, int same,
-                        struct origin *parent,
+                        struct blame_origin *parent,
                         struct blame_entry *split)
 {
        if (ent->num_lines <= tlno)
@@ -1072,9 +1051,9 @@ static void handle_split(struct scoreboard *sb,
 }
 
 struct handle_split_cb_data {
-       struct scoreboard *sb;
+       struct blame_scoreboard *sb;
        struct blame_entry *ent;
-       struct origin *parent;
+       struct blame_origin *parent;
        struct blame_entry *split;
        long plno;
        long tlno;
@@ -1096,9 +1075,9 @@ static int handle_split_cb(long start_a, long count_a,
  * we can pass blames to it.  file_p has the blob contents for
  * the parent.
  */
-static void find_copy_in_blob(struct scoreboard *sb,
+static void find_copy_in_blob(struct blame_scoreboard *sb,
                              struct blame_entry *ent,
-                             struct origin *parent,
+                             struct blame_origin *parent,
                              struct blame_entry *split,
                              mmfile_t *file_p)
 {
@@ -1111,16 +1090,16 @@ static void find_copy_in_blob(struct scoreboard *sb,
        /*
         * Prepare mmfile that contains only the lines in ent.
         */
-       cp = nth_line(sb, ent->lno);
+       cp = blame_nth_line(sb, ent->lno);
        file_o.ptr = (char *) cp;
-       file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp;
+       file_o.size = blame_nth_line(sb, ent->lno + ent->num_lines) - cp;
 
        /*
         * file_o is a part of final image we are annotating.
         * file_p partially may match that image.
         */
        memset(split, 0, sizeof(struct blame_entry [3]));
-       if (diff_hunks(file_p, &file_o, handle_split_cb, &d))
+       if (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts))
                die("unable to generate diff (%s)",
                    oid_to_hex(&parent->commit->object.oid));
        /* remainder, if any, all match the preimage */
@@ -1132,7 +1111,7 @@ static void find_copy_in_blob(struct scoreboard *sb,
  * Returns a pointer to the link pointing to the old head of the small list.
  */
 
-static struct blame_entry **filter_small(struct scoreboard *sb,
+static struct blame_entry **filter_small(struct blame_scoreboard *sb,
                                         struct blame_entry **small,
                                         struct blame_entry **source,
                                         unsigned score_min)
@@ -1140,7 +1119,7 @@ static struct blame_entry **filter_small(struct scoreboard *sb,
        struct blame_entry *p = *source;
        struct blame_entry *oldsmall = *small;
        while (p) {
-               if (ent_score(sb, p) <= score_min) {
+               if (blame_entry_score(sb, p) <= score_min) {
                        *small = p;
                        small = &p->next;
                        p = *small;
@@ -1159,11 +1138,11 @@ static struct blame_entry **filter_small(struct scoreboard *sb,
  * See if lines currently target is suspected for can be attributed to
  * parent.
  */
-static void find_move_in_parent(struct scoreboard *sb,
+static void find_move_in_parent(struct blame_scoreboard *sb,
                                struct blame_entry ***blamed,
                                struct blame_entry **toosmall,
-                               struct origin *target,
-                               struct origin *parent)
+                               struct blame_origin *target,
+                               struct blame_origin *parent)
 {
        struct blame_entry *e, split[3];
        struct blame_entry *unblamed = target->suspects;
@@ -1173,7 +1152,7 @@ static void find_move_in_parent(struct scoreboard *sb,
        if (!unblamed)
                return; /* nothing remains for this target */
 
-       fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
+       fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);
        if (!file_p.ptr)
                return;
 
@@ -1189,7 +1168,7 @@ static void find_move_in_parent(struct scoreboard *sb,
                        next = e->next;
                        find_copy_in_blob(sb, e, parent, split, &file_p);
                        if (split[1].suspect &&
-                           blame_move_score < ent_score(sb, &split[1])) {
+                           sb->move_score < blame_entry_score(sb, &split[1])) {
                                split_blame(blamed, &unblamedtail, split, e);
                        } else {
                                e->next = leftover;
@@ -1198,7 +1177,7 @@ static void find_move_in_parent(struct scoreboard *sb,
                        decref_split(split);
                }
                *unblamedtail = NULL;
-               toosmall = filter_small(sb, toosmall, &unblamed, blame_move_score);
+               toosmall = filter_small(sb, toosmall, &unblamed, sb->move_score);
        } while (unblamed);
        target->suspects = reverse_blame(leftover, NULL);
 }
@@ -1235,12 +1214,12 @@ static struct blame_list *setup_blame_list(struct blame_entry *unblamed,
  * across file boundary from the parent commit.  porigin is the path
  * in the parent we already tried.
  */
-static void find_copy_in_parent(struct scoreboard *sb,
+static void find_copy_in_parent(struct blame_scoreboard *sb,
                                struct blame_entry ***blamed,
                                struct blame_entry **toosmall,
-                               struct origin *target,
+                               struct blame_origin *target,
                                struct commit *parent,
-                               struct origin *porigin,
+                               struct blame_origin *porigin,
                                int opt)
 {
        struct diff_options diff_opts;
@@ -1287,7 +1266,7 @@ static void find_copy_in_parent(struct scoreboard *sb,
 
                for (i = 0; i < diff_queued_diff.nr; i++) {
                        struct diff_filepair *p = diff_queued_diff.queue[i];
-                       struct origin *norigin;
+                       struct blame_origin *norigin;
                        mmfile_t file_p;
                        struct blame_entry this[3];
 
@@ -1299,10 +1278,10 @@ static void find_copy_in_parent(struct scoreboard *sb,
                                /* find_move already dealt with this path */
                                continue;
 
-                       norigin = get_origin(sb, parent, p->one->path);
+                       norigin = get_origin(parent, p->one->path);
                        oidcpy(&norigin->blob_oid, &p->one->oid);
                        norigin->mode = p->one->mode;
-                       fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
+                       fill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob);
                        if (!file_p.ptr)
                                continue;
 
@@ -1313,13 +1292,13 @@ static void find_copy_in_parent(struct scoreboard *sb,
                                                     this);
                                decref_split(this);
                        }
-                       origin_decref(norigin);
+                       blame_origin_decref(norigin);
                }
 
                for (j = 0; j < num_ents; j++) {
                        struct blame_entry *split = blame_list[j].split;
                        if (split[1].suspect &&
-                           blame_copy_score < ent_score(sb, &split[1])) {
+                           sb->copy_score < blame_entry_score(sb, &split[1])) {
                                split_blame(blamed, &unblamedtail, split,
                                            blame_list[j].ent);
                        } else {
@@ -1330,7 +1309,7 @@ static void find_copy_in_parent(struct scoreboard *sb,
                }
                free(blame_list);
                *unblamedtail = NULL;
-               toosmall = filter_small(sb, toosmall, &unblamed, blame_copy_score);
+               toosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score);
        } while (unblamed);
        target->suspects = reverse_blame(leftover, NULL);
        diff_flush(&diff_opts);
@@ -1341,8 +1320,8 @@ static void find_copy_in_parent(struct scoreboard *sb,
  * The blobs of origin and porigin exactly match, so everything
  * origin is suspected for can be blamed on the parent.
  */
-static void pass_whole_blame(struct scoreboard *sb,
-                            struct origin *origin, struct origin *porigin)
+static void pass_whole_blame(struct blame_scoreboard *sb,
+                            struct blame_origin *origin, struct blame_origin *porigin)
 {
        struct blame_entry *e, *suspects;
 
@@ -1354,8 +1333,8 @@ static void pass_whole_blame(struct scoreboard *sb,
        suspects = origin->suspects;
        origin->suspects = NULL;
        for (e = suspects; e; e = e->next) {
-               origin_incref(porigin);
-               origin_decref(e->suspect);
+               blame_origin_incref(porigin);
+               blame_origin_decref(e->suspect);
                e->suspect = porigin;
        }
        queue_blames(sb, porigin, suspects);
@@ -1366,7 +1345,8 @@ static void pass_whole_blame(struct scoreboard *sb,
  * "parent" (and "porigin"), but what we mean is to find scapegoat to
  * exonerate ourselves.
  */
-static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
+static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit,
+                                          int reverse)
 {
        if (!reverse) {
                if (revs->first_parent_only &&
@@ -1380,21 +1360,21 @@ static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit
        return lookup_decoration(&revs->children, &commit->object);
 }
 
-static int num_scapegoats(struct rev_info *revs, struct commit *commit)
+static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse)
 {
-       struct commit_list *l = first_scapegoat(revs, commit);
+       struct commit_list *l = first_scapegoat(revs, commit, reverse);
        return commit_list_count(l);
 }
 
 /* Distribute collected unsorted blames to the respected sorted lists
  * in the various origins.
  */
-static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)
+static void distribute_blame(struct blame_scoreboard *sb, struct blame_entry *blamed)
 {
        blamed = blame_sort(blamed, compare_blame_suspect);
        while (blamed)
        {
-               struct origin *porigin = blamed->suspect;
+               struct blame_origin *porigin = blamed->suspect;
                struct blame_entry *suspects = NULL;
                do {
                        struct blame_entry *next = blamed->next;
@@ -1409,18 +1389,18 @@ static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)
 
 #define MAXSG 16
 
-static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
+static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, int opt)
 {
        struct rev_info *revs = sb->revs;
        int i, pass, num_sg;
        struct commit *commit = origin->commit;
        struct commit_list *sg;
-       struct origin *sg_buf[MAXSG];
-       struct origin *porigin, **sg_origin = sg_buf;
+       struct blame_origin *sg_buf[MAXSG];
+       struct blame_origin *porigin, **sg_origin = sg_buf;
        struct blame_entry *toosmall = NULL;
        struct blame_entry *blames, **blametail = &blames;
 
-       num_sg = num_scapegoats(revs, commit);
+       num_sg = num_scapegoats(revs, commit, sb->reverse);
        if (!num_sg)
                goto finish;
        else if (num_sg < ARRAY_SIZE(sg_buf))
@@ -1432,12 +1412,11 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
         * The first pass looks for unrenamed path to optimize for
         * common cases, then we look for renames in the second pass.
         */
-       for (pass = 0; pass < 2 - no_whole_file_rename; pass++) {
-               struct origin *(*find)(struct scoreboard *,
-                                      struct commit *, struct origin *);
+       for (pass = 0; pass < 2 - sb->no_whole_file_rename; pass++) {
+               struct blame_origin *(*find)(struct commit *, struct blame_origin *);
                find = pass ? find_rename : find_origin;
 
-               for (i = 0, sg = first_scapegoat(revs, commit);
+               for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
                     i < num_sg && sg;
                     sg = sg->next, i++) {
                        struct commit *p = sg->item;
@@ -1447,12 +1426,12 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
                                continue;
                        if (parse_commit(p))
                                continue;
-                       porigin = find(sb, p, origin);
+                       porigin = find(p, origin);
                        if (!porigin)
                                continue;
                        if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {
                                pass_whole_blame(sb, origin, porigin);
-                               origin_decref(porigin);
+                               blame_origin_decref(porigin);
                                goto finish;
                        }
                        for (j = same = 0; j < i; j++)
@@ -1464,19 +1443,19 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
                        if (!same)
                                sg_origin[i] = porigin;
                        else
-                               origin_decref(porigin);
+                               blame_origin_decref(porigin);
                }
        }
 
-       num_commits++;
-       for (i = 0, sg = first_scapegoat(revs, commit);
+       sb->num_commits++;
+       for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
             i < num_sg && sg;
             sg = sg->next, i++) {
-               struct origin *porigin = sg_origin[i];
+               struct blame_origin *porigin = sg_origin[i];
                if (!porigin)
                        continue;
                if (!origin->previous) {
-                       origin_incref(porigin);
+                       blame_origin_incref(porigin);
                        origin->previous = porigin;
                }
                pass_blame_to_parent(sb, origin, porigin);
@@ -1488,12 +1467,12 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
         * Optionally find moves in parents' files.
         */
        if (opt & PICKAXE_BLAME_MOVE) {
-               filter_small(sb, &toosmall, &origin->suspects, blame_move_score);
+               filter_small(sb, &toosmall, &origin->suspects, sb->move_score);
                if (origin->suspects) {
-                       for (i = 0, sg = first_scapegoat(revs, commit);
+                       for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
                             i < num_sg && sg;
                             sg = sg->next, i++) {
-                               struct origin *porigin = sg_origin[i];
+                               struct blame_origin *porigin = sg_origin[i];
                                if (!porigin)
                                        continue;
                                find_move_in_parent(sb, &blametail, &toosmall, origin, porigin);
@@ -1507,20 +1486,20 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
         * Optionally find copies from parents' files.
         */
        if (opt & PICKAXE_BLAME_COPY) {
-               if (blame_copy_score > blame_move_score)
-                       filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
-               else if (blame_copy_score < blame_move_score) {
+               if (sb->copy_score > sb->move_score)
+                       filter_small(sb, &toosmall, &origin->suspects, sb->copy_score);
+               else if (sb->copy_score < sb->move_score) {
                        origin->suspects = blame_merge(origin->suspects, toosmall);
                        toosmall = NULL;
-                       filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
+                       filter_small(sb, &toosmall, &origin->suspects, sb->copy_score);
                }
                if (!origin->suspects)
                        goto finish;
 
-               for (i = 0, sg = first_scapegoat(revs, commit);
+               for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
                     i < num_sg && sg;
                     sg = sg->next, i++) {
-                       struct origin *porigin = sg_origin[i];
+                       struct blame_origin *porigin = sg_origin[i];
                        find_copy_in_parent(sb, &blametail, &toosmall,
                                            origin, sg->item, porigin, opt);
                        if (!origin->suspects)
@@ -1547,7 +1526,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
        for (i = 0; i < num_sg; i++) {
                if (sg_origin[i]) {
                        drop_origin_blob(sg_origin[i]);
-                       origin_decref(sg_origin[i]);
+                       blame_origin_decref(sg_origin[i]);
                }
        }
        drop_origin_blob(origin);
@@ -1561,13 +1540,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
 struct commit_info {
        struct strbuf author;
        struct strbuf author_mail;
-       unsigned long author_time;
+       timestamp_t author_time;
        struct strbuf author_tz;
 
        /* filled only when asked for details */
        struct strbuf committer;
        struct strbuf committer_mail;
-       unsigned long committer_time;
+       timestamp_t committer_time;
        struct strbuf committer_tz;
 
        struct strbuf summary;
@@ -1578,7 +1557,7 @@ struct commit_info {
  */
 static void get_ac_line(const char *inbuf, const char *what,
        struct strbuf *name, struct strbuf *mail,
-       unsigned long *time, struct strbuf *tz)
+       timestamp_t *time, struct strbuf *tz)
 {
        struct ident_split ident;
        size_t len, maillen, namelen;
@@ -1699,10 +1678,10 @@ static void get_commit_info(struct commit *commit,
  * To allow LF and other nonportable characters in pathnames,
  * they are c-style quoted as needed.
  */
-static void write_filename_info(struct origin *suspect)
+static void write_filename_info(struct blame_origin *suspect)
 {
        if (suspect->previous) {
-               struct origin *prev = suspect->previous;
+               struct blame_origin *prev = suspect->previous;
                printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
                write_name_quoted(prev->path, stdout, '\n');
        }
@@ -1716,7 +1695,7 @@ static void write_filename_info(struct origin *suspect)
  * the first time each commit appears in the output (unless the
  * user has specifically asked for us to repeat).
  */
-static int emit_one_suspect_detail(struct origin *suspect, int repeat)
+static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat)
 {
        struct commit_info ci;
 
@@ -1727,11 +1706,11 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
        get_commit_info(suspect->commit, &ci, 1);
        printf("author %s\n", ci.author.buf);
        printf("author-mail %s\n", ci.author_mail.buf);
-       printf("author-time %lu\n", ci.author_time);
+       printf("author-time %"PRItime"\n", ci.author_time);
        printf("author-tz %s\n", ci.author_tz.buf);
        printf("committer %s\n", ci.committer.buf);
        printf("committer-mail %s\n", ci.committer_mail.buf);
-       printf("committer-time %lu\n", ci.committer_time);
+       printf("committer-time %"PRItime"\n", ci.committer_time);
        printf("committer-tz %s\n", ci.committer_tz.buf);
        printf("summary %s\n", ci.summary.buf);
        if (suspect->commit->object.flags & UNINTERESTING)
@@ -1750,7 +1729,7 @@ static void found_guilty_entry(struct blame_entry *ent,
                           struct progress_info *pi)
 {
        if (incremental) {
-               struct origin *suspect = ent->suspect;
+               struct blame_origin *suspect = ent->suspect;
 
                printf("%s %d %d %d\n",
                       oid_to_hex(&suspect->commit->object.oid),
@@ -1767,7 +1746,7 @@ static void found_guilty_entry(struct blame_entry *ent,
  * The main loop -- while we have blobs with lines whose true origin
  * is still unknown, pick one blob, and allow its lines to pass blames
  * to its parents. */
-static void assign_blame(struct scoreboard *sb, int opt)
+static void assign_blame(struct blame_scoreboard *sb, int opt)
 {
        struct rev_info *revs = sb->revs;
        struct commit *commit = prio_queue_get(&sb->commits);
@@ -1779,7 +1758,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
 
        while (commit) {
                struct blame_entry *ent;
-               struct origin *suspect = commit->util;
+               struct blame_origin *suspect = commit->util;
 
                /* find one suspect to break down */
                while (suspect && !suspect->suspects)
@@ -1796,9 +1775,9 @@ static void assign_blame(struct scoreboard *sb, int opt)
                 * We will use this suspect later in the loop,
                 * so hold onto it in the meantime.
                 */
-               origin_incref(suspect);
+               blame_origin_incref(suspect);
                parse_commit(commit);
-               if (reverse ||
+               if (sb->reverse ||
                    (!(commit->object.flags & UNINTERESTING) &&
                     !(revs->max_age != -1 && commit->date < revs->max_age)))
                        pass_blame(sb, suspect, opt);
@@ -1808,7 +1787,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
                                mark_parents_uninteresting(commit);
                }
                /* treat root commit as boundary */
-               if (!commit->parents && !show_root)
+               if (!commit->parents && !sb->show_root)
                        commit->object.flags |= UNINTERESTING;
 
                /* Take responsibility for the remaining entries */
@@ -1828,7 +1807,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
                                break;
                        }
                }
-               origin_decref(suspect);
+               blame_origin_decref(suspect);
 
                if (DEBUG) /* sanity */
                        sanity_check_refcnt(sb);
@@ -1837,14 +1816,14 @@ static void assign_blame(struct scoreboard *sb, int opt)
        stop_progress(&pi.progress);
 }
 
-static const char *format_time(unsigned long time, const char *tz_str,
+static const char *format_time(timestamp_t time, const char *tz_str,
                               int show_raw_time)
 {
        static struct strbuf time_buf = STRBUF_INIT;
 
        strbuf_reset(&time_buf);
        if (show_raw_time) {
-               strbuf_addf(&time_buf, "%lu %s", time, tz_str);
+               strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str);
        }
        else {
                const char *time_str;
@@ -1876,21 +1855,21 @@ static const char *format_time(unsigned long time, const char *tz_str,
 #define OUTPUT_SHOW_EMAIL      0400
 #define OUTPUT_LINE_PORCELAIN 01000
 
-static void emit_porcelain_details(struct origin *suspect, int repeat)
+static void emit_porcelain_details(struct blame_origin *suspect, int repeat)
 {
        if (emit_one_suspect_detail(suspect, repeat) ||
            (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
                write_filename_info(suspect);
 }
 
-static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
+static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
                           int opt)
 {
        int repeat = opt & OUTPUT_LINE_PORCELAIN;
        int cnt;
        const char *cp;
-       struct origin *suspect = ent->suspect;
-       char hex[GIT_SHA1_HEXSZ + 1];
+       struct blame_origin *suspect = ent->suspect;
+       char hex[GIT_MAX_HEXSZ + 1];
 
        oid_to_hex_r(hex, &suspect->commit->object.oid);
        printf("%s %d %d %d\n",
@@ -1900,7 +1879,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
               ent->num_lines);
        emit_porcelain_details(suspect, repeat);
 
-       cp = nth_line(sb, ent->lno);
+       cp = blame_nth_line(sb, ent->lno);
        for (cnt = 0; cnt < ent->num_lines; cnt++) {
                char ch;
                if (cnt) {
@@ -1922,19 +1901,19 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
                putchar('\n');
 }
 
-static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
+static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt)
 {
        int cnt;
        const char *cp;
-       struct origin *suspect = ent->suspect;
+       struct blame_origin *suspect = ent->suspect;
        struct commit_info ci;
-       char hex[GIT_SHA1_HEXSZ + 1];
+       char hex[GIT_MAX_HEXSZ + 1];
        int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
 
        get_commit_info(suspect->commit, &ci, 1);
        oid_to_hex_r(hex, &suspect->commit->object.oid);
 
-       cp = nth_line(sb, ent->lno);
+       cp = blame_nth_line(sb, ent->lno);
        for (cnt = 0; cnt < ent->num_lines; cnt++) {
                char ch;
                int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;
@@ -2001,14 +1980,14 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
        commit_info_destroy(&ci);
 }
 
-static void output(struct scoreboard *sb, int option)
+static void output(struct blame_scoreboard *sb, int option)
 {
        struct blame_entry *ent;
 
        if (option & OUTPUT_PORCELAIN) {
                for (ent = sb->ent; ent; ent = ent->next) {
                        int count = 0;
-                       struct origin *suspect;
+                       struct blame_origin *suspect;
                        struct commit *commit = ent->suspect->commit;
                        if (commit->object.flags & MORE_THAN_ONE_PATH)
                                continue;
@@ -2040,7 +2019,7 @@ static const char *get_next_line(const char *start, const char *end)
  * To allow quick access to the contents of nth line in the
  * final image, prepare an index in the scoreboard.
  */
-static int prepare_lines(struct scoreboard *sb)
+static int prepare_lines(struct blame_scoreboard *sb)
 {
        const char *buf = sb->final_buf;
        unsigned long len = sb->final_buf_size;
@@ -2086,7 +2065,7 @@ static int read_ancestry(const char *graft_file)
        return 0;
 }
 
-static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
+static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect)
 {
        const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash,
                                              auto_abbrev);
@@ -2100,7 +2079,7 @@ static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
  * How many columns do we need to show line numbers, authors,
  * and filenames?
  */
-static void find_alignment(struct scoreboard *sb, int *option)
+static void find_alignment(struct blame_scoreboard *sb, int *option)
 {
        int longest_src_lines = 0;
        int longest_dst_lines = 0;
@@ -2110,7 +2089,7 @@ static void find_alignment(struct scoreboard *sb, int *option)
        int auto_abbrev = DEFAULT_ABBREV;
 
        for (e = sb->ent; e; e = e->next) {
-               struct origin *suspect = e->suspect;
+               struct blame_origin *suspect = e->suspect;
                int num;
 
                if (compute_auto_abbrev)
@@ -2138,8 +2117,8 @@ static void find_alignment(struct scoreboard *sb, int *option)
                num = e->lno + e->num_lines;
                if (longest_dst_lines < num)
                        longest_dst_lines = num;
-               if (largest_score < ent_score(sb, e))
-                       largest_score = ent_score(sb, e);
+               if (largest_score < blame_entry_score(sb, e))
+                       largest_score = blame_entry_score(sb, e);
        }
        max_orig_digits = decimal_width(longest_src_lines);
        max_digits = decimal_width(longest_dst_lines);
@@ -2154,7 +2133,7 @@ static void find_alignment(struct scoreboard *sb, int *option)
  * For debugging -- origin is refcounted, and this asserts that
  * we do not underflow.
  */
-static void sanity_check_refcnt(struct scoreboard *sb)
+static void sanity_check_refcnt(struct blame_scoreboard *sb)
 {
        int baa = 0;
        struct blame_entry *ent;
@@ -2302,7 +2281,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
                                               const char *contents_from)
 {
        struct commit *commit;
-       struct origin *origin;
+       struct blame_origin *origin;
        struct commit_list **parent_tail, *parent;
        struct object_id head_oid;
        struct strbuf buf = STRBUF_INIT;
@@ -2445,14 +2424,14 @@ static struct commit *find_single_final(struct rev_info *revs,
        return found;
 }
 
-static char *prepare_final(struct scoreboard *sb)
+static char *prepare_final(struct blame_scoreboard *sb)
 {
        const char *name;
        sb->final = find_single_final(sb->revs, &name);
        return xstrdup_or_null(name);
 }
 
-static const char *dwim_reverse_initial(struct scoreboard *sb)
+static const char *dwim_reverse_initial(struct blame_scoreboard *sb)
 {
        /*
         * DWIM "git blame --reverse ONE -- PATH" as
@@ -2487,7 +2466,7 @@ static const char *dwim_reverse_initial(struct scoreboard *sb)
        return sb->revs->pending.objects[0].name;
 }
 
-static char *prepare_initial(struct scoreboard *sb)
+static char *prepare_initial(struct blame_scoreboard *sb)
 {
        int i;
        const char *final_commit_name = NULL;
@@ -2556,8 +2535,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
        const char *path;
-       struct scoreboard sb;
-       struct origin *o;
+       struct blame_scoreboard sb;
+       struct blame_origin *o;
        struct blame_entry *ent = NULL;
        long dashdash_pos, lno;
        char *final_commit_name = NULL;
@@ -2709,11 +2688,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
                        PICKAXE_BLAME_COPY_HARDER);
 
-       if (!blame_move_score)
-               blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
-       if (!blame_copy_score)
-               blame_copy_score = BLAME_DEFAULT_COPY_SCORE;
-
        /*
         * We have collected options unknown to us in argv[1..unk]
         * which are to be passed to revision machinery if we are
@@ -2767,8 +2741,12 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        revs.disable_stdin = 1;
        setup_revisions(argc, argv, &revs, NULL);
        memset(&sb, 0, sizeof(sb));
+       sb.move_score = BLAME_DEFAULT_MOVE_SCORE;
+       sb.copy_score = BLAME_DEFAULT_COPY_SCORE;
 
        sb.revs = &revs;
+       sb.contents_from = contents_from;
+       sb.reverse = reverse;
        if (!reverse) {
                final_commit_name = prepare_final(&sb);
                sb.commits.compare = compare_commits_by_commit_date;
@@ -2835,7 +2813,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                sb.final_buf_size = o->file.size;
        }
        else {
-               o = get_origin(&sb, sb.final, path);
+               o = get_origin(sb.final, path);
                if (fill_blob_sha1_and_mode(o))
                        die(_("no such path %s in %s"), path, final_commit_name);
 
@@ -2852,7 +2830,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                            oid_to_hex(&o->blob_oid),
                            path);
        }
-       num_read_blob++;
+       sb.num_read_blob++;
        lno = prepare_lines(&sb);
 
        if (lno && !range_list.nr)
@@ -2891,13 +2869,13 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                ent->suspect = o;
                ent->s_lno = bottom;
                ent->next = next;
-               origin_incref(o);
+               blame_origin_incref(o);
        }
 
        o->suspects = ent;
        prio_queue_put(&sb.commits, o->commit);
 
-       origin_decref(o);
+       blame_origin_decref(o);
 
        range_set_release(&ranges);
        string_list_clear(&range_list, 0);
@@ -2905,6 +2883,15 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        sb.ent = NULL;
        sb.path = path;
 
+       if (blame_move_score)
+               sb.move_score = blame_move_score;
+       if (blame_copy_score)
+               sb.copy_score = blame_copy_score;
+
+       sb.show_root = show_root;
+       sb.xdl_opts = xdl_opts;
+       sb.no_whole_file_rename = no_whole_file_rename;
+
        read_mailmap(&mailmap, NULL);
 
        assign_blame(&sb, opt);
@@ -2919,7 +2906,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
        sb.ent = blame_sort(sb.ent, compare_blame_final);
 
-       coalesce(&sb);
+       blame_coalesce(&sb);
 
        if (!(output_option & OUTPUT_PORCELAIN))
                find_alignment(&sb, &output_option);
@@ -2933,9 +2920,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        }
 
        if (show_stats) {
-               printf("num read blob: %d\n", num_read_blob);
-               printf("num get patch: %d\n", num_get_patch);
-               printf("num commits: %d\n", num_commits);
+               printf("num read blob: %d\n", sb.num_read_blob);
+               printf("num get patch: %d\n", sb.num_get_patch);
+               printf("num commits: %d\n", sb.num_commits);
        }
        return 0;
 }