Note that git-branch will not automatically checkout the new branch
[gitweb.git] / builtin-blame.c
index 3442d282aab208720ba11cebce30b89c55238ddd..dc88a953a519e975f426f6158b73542a2ea120dd 100644 (file)
 #include "mailmap.h"
 
 static char blame_usage[] =
-"git-blame [-c] [-b] [-l] [--root] [-x] [-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"
 "  --root              Do not treat root commits as boundaries (Default: off)\n"
 "  -t                  Show raw timestamp (Default: off)\n"
-"  -x                  Do not use .mailmap file\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"
@@ -46,7 +46,7 @@ static int show_root;
 static int blank_boundary;
 static int incremental;
 static int cmd_is_annotate;
-static int no_mailmap;
+static int xdl_opts = XDF_NEED_MINIMAL;
 static struct path_list mailmap;
 
 #ifndef DEBUG
@@ -61,6 +61,7 @@ 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
@@ -97,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
@@ -516,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));
@@ -896,6 +901,39 @@ static void copy_split_if_better(struct scoreboard *sb,
        memcpy(best_so_far, this, sizeof(struct blame_entry [3]));
 }
 
+/*
+ * We are looking at a part of the final image represented by
+ * ent (tlno and same are offset by ent->s_lno).
+ * tlno is where we are looking at in the final image.
+ * up to (but not including) same match preimage.
+ * plno is where we are looking at in the preimage.
+ *
+ * <-------------- final image ---------------------->
+ *       <------ent------>
+ *         ^tlno ^same
+ *    <---------preimage----->
+ *         ^plno
+ *
+ * All line numbers are 0-based.
+ */
+static void handle_split(struct scoreboard *sb,
+                        struct blame_entry *ent,
+                        int tlno, int plno, int same,
+                        struct origin *parent,
+                        struct blame_entry *split)
+{
+       if (ent->num_lines <= tlno)
+               return;
+       if (tlno < same) {
+               struct blame_entry this[3];
+               tlno += ent->s_lno;
+               same += ent->s_lno;
+               split_overlap(this, ent, tlno, plno, same, parent);
+               copy_split_if_better(sb, split, this);
+               decref_split(this);
+       }
+}
+
 /*
  * Find the lines from parent that are the same as ent so that
  * we can pass blames to it.  file_p has the blob contents for
@@ -928,26 +966,21 @@ static void find_copy_in_blob(struct scoreboard *sb,
 
        patch = compare_buffer(file_p, &file_o, 1);
 
+       /*
+        * 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]));
        plno = tlno = 0;
        for (i = 0; i < patch->num; i++) {
                struct chunk *chunk = &patch->chunks[i];
 
-               /* tlno to chunk->same are the same as ent */
-               if (ent->num_lines <= tlno)
-                       break;
-               if (tlno < chunk->same) {
-                       struct blame_entry this[3];
-                       split_overlap(this, ent,
-                                     tlno + ent->s_lno, plno,
-                                     chunk->same + ent->s_lno,
-                                     parent);
-                       copy_split_if_better(sb, split, this);
-                       decref_split(this);
-               }
+               handle_split(sb, ent, tlno, plno, chunk->same, parent, split);
                plno = chunk->p_next;
                tlno = chunk->t_next;
        }
+       /* remainder, if any, all match the preimage */
+       handle_split(sb, ent, tlno, plno, ent->num_lines, parent, split);
        free_patch(patch);
 }
 
@@ -1057,8 +1090,9 @@ static int find_copy_in_parent(struct scoreboard *sb,
         * and this code needs to be after diff_setup_done(), which
         * usually makes find-copies-harder imply copy detection.
         */
-       if ((opt & PICKAXE_BLAME_COPY_HARDER) &&
-           (!porigin || strcmp(target->path, porigin->path)))
+       if ((opt & PICKAXE_BLAME_COPY_HARDEST)
+           || ((opt & PICKAXE_BLAME_COPY_HARDER)
+               && (!porigin || strcmp(target->path, porigin->path))))
                diff_opts.find_copies_harder = 1;
 
        if (is_null_sha1(target->commit->object.sha1))
@@ -1354,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 ",
@@ -1429,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");
        }
 }
 
@@ -1716,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;
 }
 
 /*
@@ -2131,6 +2169,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")) {
@@ -2138,6 +2178,15 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                        blame_move_score = parse_score(arg+2);
                }
                else if (!prefixcmp(arg, "-C")) {
+                       /*
+                        * -C enables copy from removed files;
+                        * -C -C enables copy from existing files, but only
+                        *       when blaming a new file;
+                        * -C -C -C enables copy from existing files for
+                        *          everybody
+                        */
+                       if (opt & PICKAXE_BLAME_COPY_HARDER)
+                               opt |= PICKAXE_BLAME_COPY_HARDEST;
                        if (opt & PICKAXE_BLAME_COPY)
                                opt |= PICKAXE_BLAME_COPY_HARDER;
                        opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
@@ -2173,9 +2222,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                else if (!strcmp("-p", arg) ||
                         !strcmp("--porcelain", arg))
                        output_option |= OUTPUT_PORCELAIN;
-               else if (!strcmp("-x", arg) ||
-                        !strcmp("--no-mailmap", arg))
-                       no_mailmap = 1;
                else if (!strcmp("--", arg)) {
                        seen_dashdash = 1;
                        i++;
@@ -2343,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);
@@ -2375,8 +2425,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                die("reading graft file %s failed: %s",
                    revs_file, strerror(errno));
 
-       if (!no_mailmap)
-               read_mailmap(&mailmap, ".mailmap", NULL);
+       read_mailmap(&mailmap, ".mailmap", NULL);
 
        assign_blame(&sb, &revs, opt);