Merge branch 'jn/cherry-revert-message-clean-up'
[gitweb.git] / builtin / blame.c
index 729b43058a8afe01b03a463d9c11f5d74d9c8d43..101535448f2b0405c280387b63f7ce82fb7dd350 100644 (file)
@@ -20,6 +20,7 @@
 #include "mailmap.h"
 #include "parse-options.h"
 #include "utf8.h"
+#include "userdiff.h"
 
 static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
 
@@ -85,17 +86,51 @@ struct origin {
        char path[FLEX_ARRAY];
 };
 
+/*
+ * Prepare diff_filespec and convert it using diff textconv API
+ * if the textconv driver exists.
+ * Return 1 if the conversion succeeds, 0 otherwise.
+ */
+int textconv_object(const char *path,
+                   const unsigned char *sha1,
+                   char **buf,
+                   unsigned long *buf_size)
+{
+       struct diff_filespec *df;
+       struct userdiff_driver *textconv;
+
+       df = alloc_filespec(path);
+       fill_filespec(df, sha1, S_IFREG | 0664);
+       textconv = get_textconv(df);
+       if (!textconv) {
+               free_filespec(df);
+               return 0;
+       }
+
+       *buf_size = fill_textconv(textconv, df, buf);
+       free_filespec(df);
+       return 1;
+}
+
 /*
  * Given an origin, prepare mmfile_t structure to be used by the
  * diff machinery
  */
-static void fill_origin_blob(struct origin *o, mmfile_t *file)
+static void fill_origin_blob(struct diff_options *opt,
+                            struct origin *o, mmfile_t *file)
 {
        if (!o->file.ptr) {
                enum object_type type;
+               unsigned long file_size;
+
                num_read_blob++;
-               file->ptr = read_sha1_file(o->blob_sha1, &type,
-                                          (unsigned long *)(&(file->size)));
+               if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+                   textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size))
+                       ;
+               else
+                       file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
+               file->size = file_size;
+
                if (!file->ptr)
                        die("Cannot read blob %s for path %s",
                            sha1_to_hex(o->blob_sha1),
@@ -282,7 +317,6 @@ static struct origin *get_origin(struct scoreboard *sb,
 static int fill_blob_sha1(struct origin *origin)
 {
        unsigned mode;
-
        if (!is_null_sha1(origin->blob_sha1))
                return 0;
        if (get_tree_entry(origin->commit->object.sha1,
@@ -742,8 +776,8 @@ static int pass_blame_to_parent(struct scoreboard *sb,
        if (last_in_target < 0)
                return 1; /* nothing remains for this target */
 
-       fill_origin_blob(parent, &file_p);
-       fill_origin_blob(target, &file_o);
+       fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
+       fill_origin_blob(&sb->revs->diffopt, target, &file_o);
        num_get_patch++;
 
        memset(&xpp, 0, sizeof(xpp));
@@ -924,7 +958,7 @@ static int find_move_in_parent(struct scoreboard *sb,
        if (last_in_target < 0)
                return 1; /* nothing remains for this target */
 
-       fill_origin_blob(parent, &file_p);
+       fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
        if (!file_p.ptr)
                return 0;
 
@@ -1065,7 +1099,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
 
                        norigin = get_origin(sb, parent, p->one->path);
                        hashcpy(norigin->blob_sha1, p->one->sha1);
-                       fill_origin_blob(norigin, &file_p);
+                       fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
                        if (!file_p.ptr)
                                continue;
 
@@ -1373,7 +1407,8 @@ static void get_commit_info(struct commit *commit,
                            int detailed)
 {
        int len;
-       char *tmp, *endp, *reencoded, *message;
+       const char *subject;
+       char *reencoded, *message;
        static char author_name[1024];
        static char author_mail[1024];
        static char committer_name[1024];
@@ -1415,22 +1450,13 @@ static void get_commit_info(struct commit *commit,
                    &ret->committer_time, &ret->committer_tz);
 
        ret->summary = summary_buf;
-       tmp = strstr(message, "\n\n");
-       if (!tmp) {
-       error_out:
+       len = find_commit_subject(message, &subject);
+       if (len && len < sizeof(summary_buf)) {
+               memcpy(summary_buf, subject, len);
+               summary_buf[len] = 0;
+       } else {
                sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
-               free(reencoded);
-               return;
        }
-       tmp += 2;
-       endp = strchr(tmp, '\n');
-       if (!endp)
-               endp = tmp + strlen(tmp);
-       len = endp - tmp;
-       if (len >= sizeof(summary_buf) || len == 0)
-               goto error_out;
-       memcpy(summary_buf, tmp, len);
-       summary_buf[len] = 0;
        free(reencoded);
 }
 
@@ -1985,6 +2011,16 @@ static int git_blame_config(const char *var, const char *value, void *cb)
                blame_date_mode = parse_date_format(value);
                return 0;
        }
+
+       switch (userdiff_config(var, value)) {
+       case 0:
+               break;
+       case -1:
+               return -1;
+       default:
+               return 0;
+       }
+
        return git_default_config(var, value, cb);
 }
 
@@ -1992,7 +2028,9 @@ static int git_blame_config(const char *var, const char *value, void *cb)
  * Prepare a dummy commit that represents the work tree (or staged) item.
  * Note that annotating work tree item never works in the reverse.
  */
-static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
+static struct commit *fake_working_tree_commit(struct diff_options *opt,
+                                              const char *path,
+                                              const char *contents_from)
 {
        struct commit *commit;
        struct origin *origin;
@@ -2020,6 +2058,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
                const char *read_from;
+               unsigned long buf_len;
 
                if (contents_from) {
                        if (stat(contents_from, &st) < 0)
@@ -2032,9 +2071,13 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
                        read_from = path;
                }
                mode = canon_mode(st.st_mode);
+
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
-                       if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
+                       if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+                           textconv_object(read_from, null_sha1, &buf.buf, &buf_len))
+                               buf.len = buf_len;
+                       else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
                        break;
                case S_IFLNK:
@@ -2250,6 +2293,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        git_config(git_blame_config, NULL);
        init_revisions(&revs, NULL);
        revs.date_mode = blame_date_mode;
+       DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
 
        save_commit_buffer = 0;
        dashdash_pos = 0;
@@ -2324,11 +2368,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
         *
         * The remaining are:
         *
-        * (1) if dashdash_pos != 0, its either
+        * (1) if dashdash_pos != 0, it is either
         *     "blame [revisions] -- <path>" or
         *     "blame -- <path> <rev>"
         *
-        * (2) otherwise, its one of the two:
+        * (2) otherwise, it is one of the two:
         *     "blame [revisions] <path>"
         *     "blame <path> <rev>"
         *
@@ -2386,7 +2430,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                 * or "--contents".
                 */
                setup_work_tree();
-               sb.final = fake_working_tree_commit(path, contents_from);
+               sb.final = fake_working_tree_commit(&sb.revs->diffopt,
+                                                   path, contents_from);
                add_pending_object(&revs, &(sb.final->object), ":");
        }
        else if (contents_from)
@@ -2413,8 +2458,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                if (fill_blob_sha1(o))
                        die("no such path %s in %s", path, final_commit_name);
 
-               sb.final_buf = read_sha1_file(o->blob_sha1, &type,
-                                             &sb.final_buf_size);
+               if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
+                   textconv_object(path, o->blob_sha1, (char **) &sb.final_buf,
+                                   &sb.final_buf_size))
+                       ;
+               else
+                       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),