blame: show "previous" information in --porcelain/--incremental format
authorJunio C Hamano <gitster@pobox.com>
Thu, 5 Jun 2008 05:58:40 +0000 (22:58 -0700)
committerShawn O. Pearce <spearce@spearce.org>
Sun, 12 Oct 2008 20:21:51 +0000 (13:21 -0700)
When the final blame is laid for a line to a <commit, path> pair, it also
gives a "previous" information to --porcelain and --incremental output
format. It gives the parent commit of the blamed commit, _and_ a path in
that parent commit that corresponds to the blamed path --- in short, it is
the origin that would have been blamed (or passed blame through) for the
line _if_ the blamed commit did not change that line.

This unfortunately makes sanity checking of refcount quite complex, so I
ripped it out for now.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin-blame.c
index 163d2dc34c103b0ee26b95da1946c03c1a6b44ce..e386120596efb9f0fc9ed7e2c5bd2f05f3f1df5f 100644 (file)
@@ -73,6 +73,7 @@ static unsigned blame_copy_score;
  */
 struct origin {
        int refcnt;
+       struct origin *previous;
        struct commit *commit;
        mmfile_t file;
        unsigned char blob_sha1[20];
@@ -114,6 +115,8 @@ static inline struct origin *origin_incref(struct origin *o)
 static void origin_decref(struct origin *o)
 {
        if (o && --o->refcnt <= 0) {
+               if (o->previous)
+                       origin_decref(o->previous);
                free(o->file.ptr);
                free(o);
        }
@@ -1292,6 +1295,10 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
                struct origin *porigin = sg_origin[i];
                if (!porigin)
                        continue;
+               if (!origin->previous) {
+                       origin_incref(porigin);
+                       origin->previous = porigin;
+               }
                if (pass_blame_to_parent(sb, origin, porigin))
                        goto finish;
        }
@@ -1515,6 +1522,11 @@ static int emit_one_suspect_detail(struct origin *suspect)
        printf("summary %s\n", ci.summary);
        if (suspect->commit->object.flags & UNINTERESTING)
                printf("boundary\n");
+       if (suspect->previous) {
+               struct origin *prev = suspect->previous;
+               printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
+               write_name_quoted(prev->path, stdout, '\n');
+       }
        return 1;
 }
 
@@ -1878,36 +1890,6 @@ static void sanity_check_refcnt(struct scoreboard *sb)
                        baa = 1;
                }
        }
-       for (ent = sb->ent; ent; ent = ent->next) {
-               /* Mark the ones that haven't been checked */
-               if (0 < ent->suspect->refcnt)
-                       ent->suspect->refcnt = -ent->suspect->refcnt;
-       }
-       for (ent = sb->ent; ent; ent = ent->next) {
-               /*
-                * ... then pick each and see if they have the the
-                * correct refcnt.
-                */
-               int found;
-               struct blame_entry *e;
-               struct origin *suspect = ent->suspect;
-
-               if (0 < suspect->refcnt)
-                       continue;
-               suspect->refcnt = -suspect->refcnt; /* Unmark */
-               for (found = 0, e = sb->ent; e; e = e->next) {
-                       if (e->suspect != suspect)
-                               continue;
-                       found++;
-               }
-               if (suspect->refcnt != found) {
-                       fprintf(stderr, "%s in %s has refcnt %d, not %d\n",
-                               ent->suspect->path,
-                               sha1_to_hex(ent->suspect->commit->object.sha1),
-                               ent->suspect->refcnt, found);
-                       baa = 2;
-               }
-       }
        if (baa) {
                int opt = 0160;
                find_alignment(sb, &opt);