Do linear-time/space rename logic for exact renames
[gitweb.git] / builtin-blame.c
index 590533321a99bf4d00b872a839527f00616ed593..8432b823e6ef6020a897838e7a561c3919c31f0b 100644 (file)
@@ -20,7 +20,7 @@
 #include "mailmap.h"
 
 static char blame_usage[] =
-"git-blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-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"
@@ -30,6 +30,7 @@ static char blame_usage[] =
 "  -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"
@@ -45,6 +46,7 @@ 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
@@ -96,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
@@ -515,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));
@@ -1382,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 ",
@@ -1421,8 +1430,7 @@ static void get_commit_info(struct commit *commit,
 static void write_filename_info(const char *path)
 {
        printf("filename ");
-       write_name_quoted(NULL, 0, path, 1, stdout);
-       putchar('\n');
+       write_name_quoted(path, stdout, '\n');
 }
 
 /*
@@ -1457,6 +1465,7 @@ static void found_guilty_entry(struct blame_entry *ent)
                                printf("boundary\n");
                }
                write_filename_info(suspect->path);
+               maybe_flush_or_die(stdout, "stdout");
        }
 }
 
@@ -1991,11 +2000,9 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
        struct commit *commit;
        struct origin *origin;
        unsigned char head_sha1[20];
-       char *buf;
+       struct strbuf buf;
        const char *ident;
-       int fd;
        time_t now;
-       unsigned long fin_size;
        int size, len;
        struct cache_entry *ce;
        unsigned mode;
@@ -2013,9 +2020,11 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
 
        origin = make_origin(commit, path);
 
+       strbuf_init(&buf, 0);
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
                const char *read_from;
+               unsigned long fin_size;
 
                if (contents_from) {
                        if (stat(contents_from, &st) < 0)
@@ -2028,19 +2037,16 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
                        read_from = path;
                }
                fin_size = xsize_t(st.st_size);
-               buf = xmalloc(fin_size+1);
                mode = canon_mode(st.st_mode);
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
-                       fd = open(read_from, O_RDONLY);
-                       if (fd < 0)
-                               die("cannot open %s", read_from);
-                       if (read_in_full(fd, buf, fin_size) != fin_size)
-                               die("cannot read %s", read_from);
+                       if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
+                               die("cannot open or read %s", read_from);
                        break;
                case S_IFLNK:
-                       if (readlink(read_from, buf, fin_size+1) != fin_size)
+                       if (readlink(read_from, buf.buf, buf.alloc) != fin_size)
                                die("cannot readlink %s", read_from);
+                       buf.len = fin_size;
                        break;
                default:
                        die("unsupported file type %s", read_from);
@@ -2049,26 +2055,14 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
        else {
                /* Reading from stdin */
                contents_from = "standard input";
-               buf = NULL;
-               fin_size = 0;
                mode = 0;
-               while (1) {
-                       ssize_t cnt = 8192;
-                       buf = xrealloc(buf, fin_size + cnt);
-                       cnt = xread(0, buf + fin_size, cnt);
-                       if (cnt < 0)
-                               die("read error %s from stdin",
-                                   strerror(errno));
-                       if (!cnt)
-                               break;
-                       fin_size += cnt;
-               }
-               buf = xrealloc(buf, fin_size + 1);
+               if (strbuf_read(&buf, 0, 0) < 0)
+                       die("read error %s from stdin", strerror(errno));
        }
-       buf[fin_size] = 0;
-       origin->file.ptr = buf;
-       origin->file.size = fin_size;
-       pretend_sha1_file(buf, fin_size, OBJ_BLOB, origin->blob_sha1);
+       convert_to_git(path, buf.buf, buf.len, &buf);
+       origin->file.ptr = buf.buf;
+       origin->file.size = buf.len;
+       pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
        commit->util = origin;
 
        /*
@@ -2159,6 +2153,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                        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")) {
@@ -2377,6 +2373,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);