Documentation / grammer nit
[gitweb.git] / builtin-blame.c
index 65d029a773691f994711478bc2475681093e0088..dc88a953a519e975f426f6158b73542a2ea120dd 100644 (file)
 #include "quote.h"
 #include "xdiff-interface.h"
 #include "cache-tree.h"
+#include "path-list.h"
+#include "mailmap.h"
 
 static char blame_usage[] =
-"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
+"git-blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
 "  -c                  Use the same output mode as git-annotate (Default: off)\n"
 "  -b                  Show blank SHA-1 for boundary commits (Default: off)\n"
 "  -l                  Show long commit SHA1 (Default: off)\n"
@@ -26,7 +28,9 @@ static char blame_usage[] =
 "  -t                  Show raw timestamp (Default: off)\n"
 "  -f, --show-name     Show original filename (Default: auto)\n"
 "  -n, --show-number   Show original linenumber (Default: off)\n"
+"  -s                  Suppress author name and timestamp (Default: off)\n"
 "  -p, --porcelain     Show in a format designed for machine consumption\n"
+"  -w                  Ignore whitespace differences\n"
 "  -L n,m              Process only line range n,m, counting from 1\n"
 "  -M, -C              Find line movements within and across files\n"
 "  --incremental       Show blame entries as we find them, incrementally\n"
@@ -42,6 +46,8 @@ static int show_root;
 static int blank_boundary;
 static int incremental;
 static int cmd_is_annotate;
+static int xdl_opts = XDF_NEED_MINIMAL;
+static struct path_list mailmap;
 
 #ifndef DEBUG
 #define DEBUG 0
@@ -92,6 +98,10 @@ static char *fill_origin_blob(struct origin *o, mmfile_t *file)
                num_read_blob++;
                file->ptr = read_sha1_file(o->blob_sha1, &type,
                                           (unsigned long *)(&(file->size)));
+               if (!file->ptr)
+                       die("Cannot read blob %s for path %s",
+                           sha1_to_hex(o->blob_sha1),
+                           o->path);
                o->file = *file;
        }
        else
@@ -511,9 +521,9 @@ static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o,
        xdemitconf_t xecfg;
        xdemitcb_t ecb;
 
-       xpp.flags = XDF_NEED_MINIMAL;
+       xpp.flags = xdl_opts;
+       memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = context;
-       xecfg.flags = 0;
        ecb.outf = xdiff_outf;
        ecb.priv = &state;
        memset(&state, 0, sizeof(state));
@@ -1294,8 +1304,8 @@ static void get_ac_line(const char *inbuf, const char *what,
                        int bufsz, char *person, const char **mail,
                        unsigned long *time, const char **tz)
 {
-       int len;
-       char *tmp, *endp;
+       int len, tzlen, maillen;
+       char *tmp, *endp, *timepos;
 
        tmp = strstr(inbuf, what);
        if (!tmp)
@@ -1321,17 +1331,42 @@ static void get_ac_line(const char *inbuf, const char *what,
        while (*tmp != ' ')
                tmp--;
        *tz = tmp+1;
+       tzlen = (person+len)-(tmp+1);
 
        *tmp = 0;
        while (*tmp != ' ')
                tmp--;
        *time = strtoul(tmp, NULL, 10);
+       timepos = tmp;
 
        *tmp = 0;
        while (*tmp != ' ')
                tmp--;
        *mail = tmp + 1;
        *tmp = 0;
+       maillen = timepos - tmp;
+
+       if (!mailmap.nr)
+               return;
+
+       /*
+        * mailmap expansion may make the name longer.
+        * make room by pushing stuff down.
+        */
+       tmp = person + bufsz - (tzlen + 1);
+       memmove(tmp, *tz, tzlen);
+       tmp[tzlen] = 0;
+       *tz = tmp;
+
+       tmp = tmp - (maillen + 1);
+       memmove(tmp, *mail, maillen);
+       tmp[maillen] = 0;
+       *mail = tmp;
+
+       /*
+        * Now, convert e-mail using mailmap
+        */
+       map_email(&mailmap, tmp + 1, person, tmp-person-1);
 }
 
 static void get_commit_info(struct commit *commit,
@@ -1353,6 +1388,9 @@ static void get_commit_info(struct commit *commit,
                unsigned long size;
                commit->buffer =
                        read_sha1_file(commit->object.sha1, &type, &size);
+               if (!commit->buffer)
+                       die("Cannot read commit %s",
+                           sha1_to_hex(commit->object.sha1));
        }
        ret->author = author_buf;
        get_ac_line(commit->buffer, "\nauthor ",
@@ -1428,6 +1466,7 @@ static void found_guilty_entry(struct blame_entry *ent)
                                printf("boundary\n");
                }
                write_filename_info(suspect->path);
+               maybe_flush_or_die(stdout, "stdout");
        }
 }
 
@@ -1513,6 +1552,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
 #define OUTPUT_SHOW_NAME       020
 #define OUTPUT_SHOW_NUMBER     040
 #define OUTPUT_SHOW_SCORE      0100
+#define OUTPUT_NO_AUTHOR       0200
 
 static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
 {
@@ -1607,10 +1647,15 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
                        if (opt & OUTPUT_SHOW_NUMBER)
                                printf(" %*d", max_orig_digits,
                                       ent->s_lno + 1 + cnt);
-                       printf(" (%-*.*s %10s %*d) ",
-                              longest_author, longest_author, ci.author,
-                              format_time(ci.author_time, ci.author_tz,
-                                          show_raw_time),
+
+                       if (!(opt & OUTPUT_NO_AUTHOR))
+                               printf(" (%-*.*s %10s",
+                                      longest_author, longest_author,
+                                      ci.author,
+                                      format_time(ci.author_time,
+                                                  ci.author_tz,
+                                                  show_raw_time));
+                       printf(" %*d) ",
                               max_digits, ent->lno + 1 + cnt);
                }
                do {
@@ -1709,11 +1754,11 @@ static int read_ancestry(const char *graft_file)
  */
 static int lineno_width(int lines)
 {
-        int i, width;
+       int i, width;
 
-        for (width = 1, i = 10; i <= lines + 1; width++)
-                i *= 10;
-        return width;
+       for (width = 1, i = 10; i <= lines + 1; width++)
+               i *= 10;
+       return width;
 }
 
 /*
@@ -2122,6 +2167,10 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                        output_option |= OUTPUT_RAW_TIMESTAMP;
                else if (!strcmp("-l", arg))
                        output_option |= OUTPUT_LONG_OBJECT_NAME;
+               else if (!strcmp("-s", arg))
+                       output_option |= OUTPUT_NO_AUTHOR;
+               else if (!strcmp("-w", arg))
+                       xdl_opts |= XDF_IGNORE_WHITESPACE;
                else if (!strcmp("-S", arg) && ++i < argc)
                        revs_file = argv[i];
                else if (!prefixcmp(arg, "-M")) {
@@ -2340,6 +2389,10 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
                sb.final_buf = read_sha1_file(o->blob_sha1, &type,
                                              &sb.final_buf_size);
+               if (!sb.final_buf)
+                       die("Cannot read blob %s for path %s",
+                           sha1_to_hex(o->blob_sha1),
+                           path);
        }
        num_read_blob++;
        lno = prepare_lines(&sb);
@@ -2372,6 +2425,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                die("reading graft file %s failed: %s",
                    revs_file, strerror(errno));
 
+       read_mailmap(&mailmap, ".mailmap", NULL);
+
        assign_blame(&sb, &revs, opt);
 
        if (incremental)