blame: output porcelain "previous" header for each file
[gitweb.git] / builtin / blame.c
index 5e5d30ecbc83cbbef4f1feadfd77163e3afbbf96..d4f3ce96a563f15d4c9af531044ecefb600f5df3 100644 (file)
@@ -56,7 +56,7 @@ static int show_progress;
 static struct date_mode blame_date_mode = { DATE_ISO8601 };
 static size_t blame_date_width;
 
-static struct string_list mailmap;
+static struct string_list mailmap = STRING_LIST_INIT_NODUP;
 
 #ifndef DEBUG
 #define DEBUG 0
@@ -598,7 +598,7 @@ static struct origin *find_origin(struct scoreboard *sb,
                            p->status);
                case 'M':
                        porigin = get_origin(sb, parent, origin->path);
-                       hashcpy(porigin->blob_sha1, p->one->sha1);
+                       hashcpy(porigin->blob_sha1, p->one->oid.hash);
                        porigin->mode = p->one->mode;
                        break;
                case 'A':
@@ -608,7 +608,7 @@ static struct origin *find_origin(struct scoreboard *sb,
                }
        }
        diff_flush(&diff_opts);
-       free_pathspec(&diff_opts.pathspec);
+       clear_pathspec(&diff_opts.pathspec);
        return porigin;
 }
 
@@ -644,13 +644,13 @@ static struct origin *find_rename(struct scoreboard *sb,
                if ((p->status == 'R' || p->status == 'C') &&
                    !strcmp(p->two->path, origin->path)) {
                        porigin = get_origin(sb, parent, p->one->path);
-                       hashcpy(porigin->blob_sha1, p->one->sha1);
+                       hashcpy(porigin->blob_sha1, p->one->oid.hash);
                        porigin->mode = p->one->mode;
                        break;
                }
        }
        diff_flush(&diff_opts);
-       free_pathspec(&diff_opts.pathspec);
+       clear_pathspec(&diff_opts.pathspec);
        return porigin;
 }
 
@@ -1308,7 +1308,7 @@ static void find_copy_in_parent(struct scoreboard *sb,
                                continue;
 
                        norigin = get_origin(sb, parent, p->one->path);
-                       hashcpy(norigin->blob_sha1, p->one->sha1);
+                       hashcpy(norigin->blob_sha1, p->one->oid.hash);
                        norigin->mode = p->one->mode;
                        fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
                        if (!file_p.ptr)
@@ -1342,7 +1342,7 @@ static void find_copy_in_parent(struct scoreboard *sb,
        } while (unblamed);
        target->suspects = reverse_blame(leftover, NULL);
        diff_flush(&diff_opts);
-       free_pathspec(&diff_opts.pathspec);
+       clear_pathspec(&diff_opts.pathspec);
 }
 
 /*
@@ -1700,13 +1700,23 @@ static void get_commit_info(struct commit *commit,
 }
 
 /*
+ * Write out any suspect information which depends on the path. This must be
+ * handled separately from emit_one_suspect_detail(), because a given commit
+ * may have changes in multiple paths. So this needs to appear each time
+ * we mention a new group.
+ *
  * To allow LF and other nonportable characters in pathnames,
  * they are c-style quoted as needed.
  */
-static void write_filename_info(const char *path)
+static void write_filename_info(struct origin *suspect)
 {
+       if (suspect->previous) {
+               struct origin *prev = suspect->previous;
+               printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
+               write_name_quoted(prev->path, stdout, '\n');
+       }
        printf("filename ");
-       write_name_quoted(path, stdout, '\n');
+       write_name_quoted(suspect->path, stdout, '\n');
 }
 
 /*
@@ -1735,11 +1745,6 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
        printf("summary %s\n", ci.summary.buf);
        if (suspect->commit->object.flags & UNINTERESTING)
                printf("boundary\n");
-       if (suspect->previous) {
-               struct origin *prev = suspect->previous;
-               printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
-               write_name_quoted(prev->path, stdout, '\n');
-       }
 
        commit_info_destroy(&ci);
 
@@ -1760,7 +1765,7 @@ static void found_guilty_entry(struct blame_entry *ent,
                       oid_to_hex(&suspect->commit->object.oid),
                       ent->s_lno + 1, ent->lno + 1, ent->num_lines);
                emit_one_suspect_detail(suspect, 0);
-               write_filename_info(suspect->path);
+               write_filename_info(suspect);
                maybe_flush_or_die(stdout, "stdout");
        }
        pi->blamed_lines += ent->num_lines;
@@ -1884,7 +1889,7 @@ static void emit_porcelain_details(struct origin *suspect, int repeat)
 {
        if (emit_one_suspect_detail(suspect, repeat) ||
            (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
-               write_filename_info(suspect->path);
+               write_filename_info(suspect);
 }
 
 static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
@@ -2111,7 +2116,7 @@ static void find_alignment(struct scoreboard *sb, int *option)
        unsigned largest_score = 0;
        struct blame_entry *e;
        int compute_auto_abbrev = (abbrev < 0);
-       int auto_abbrev = default_abbrev;
+       int auto_abbrev = DEFAULT_ABBREV;
 
        for (e = sb->ent; e; e = e->next) {
                struct origin *suspect = e->suspect;
@@ -2244,7 +2249,8 @@ static void verify_working_tree_path(struct commit *work_tree, const char *path)
        pos = cache_name_pos(path, strlen(path));
        if (pos >= 0)
                ; /* path is in the index */
-       else if (!strcmp(active_cache[-1 - pos]->name, path))
+       else if (-1 - pos < active_nr &&
+                !strcmp(active_cache[-1 - pos]->name, path))
                ; /* path is in the index, unmerged */
        else
                die("no such path '%s' in HEAD", path);
@@ -2527,12 +2533,12 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        enum object_type type;
        struct commit *final_commit = NULL;
 
-       static struct string_list range_list;
-       static int output_option = 0, opt = 0;
-       static int show_stats = 0;
-       static const char *revs_file = NULL;
-       static const char *contents_from = NULL;
-       static const struct option options[] = {
+       struct string_list range_list = STRING_LIST_INIT_NODUP;
+       int output_option = 0, opt = 0;
+       int show_stats = 0;
+       const char *revs_file = NULL;
+       const char *contents_from = NULL;
+       const struct option options[] = {
                OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
                OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
                OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
@@ -2605,9 +2611,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        } else if (show_progress < 0)
                show_progress = isatty(2);
 
-       if (0 < abbrev)
+       if (0 < abbrev && abbrev < GIT_SHA1_HEXSZ)
                /* one more abbrev length is needed for the boundary commit */
                abbrev++;
+       else if (!abbrev)
+               abbrev = GIT_SHA1_HEXSZ;
 
        if (revs_file && read_ancestry(revs_file))
                die_errno("reading graft file '%s' failed", revs_file);
@@ -2808,7 +2816,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        lno = prepare_lines(&sb);
 
        if (lno && !range_list.nr)
-               string_list_append(&range_list, xstrdup("1"));
+               string_list_append(&range_list, "1");
 
        anchor = 1;
        range_set_init(&ranges, range_list.nr);