#include "mailmap.h"
#include "parse-options.h"
#include "utf8.h"
+#include "userdiff.h"
static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
struct commit *commit;
mmfile_t file;
unsigned char blob_sha1[20];
+ unsigned mode;
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,
+ unsigned mode,
+ 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, mode);
+ 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->mode, 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),
* for an origin is also used to pass the blame for the entire file to
* the parent to detect the case where a child's blob is identical to
* that of its parent's.
+ *
+ * This also fills origin->mode for corresponding tree path.
*/
-static int fill_blob_sha1(struct origin *origin)
+static int fill_blob_sha1_and_mode(struct origin *origin)
{
- unsigned mode;
-
if (!is_null_sha1(origin->blob_sha1))
return 0;
if (get_tree_entry(origin->commit->object.sha1,
origin->path,
- origin->blob_sha1, &mode))
+ origin->blob_sha1, &origin->mode))
goto error_out;
if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)
goto error_out;
return 0;
error_out:
hashclr(origin->blob_sha1);
+ origin->mode = S_IFINVALID;
return -1;
}
/*
* If the origin was newly created (i.e. get_origin
* would call make_origin if none is found in the
- * scoreboard), it does not know the blob_sha1,
+ * scoreboard), it does not know the blob_sha1/mode,
* so copy it. Otherwise porigin was in the
- * scoreboard and already knows blob_sha1.
+ * scoreboard and already knows blob_sha1/mode.
*/
- if (porigin->refcnt == 1)
+ if (porigin->refcnt == 1) {
hashcpy(porigin->blob_sha1, cached->blob_sha1);
+ porigin->mode = cached->mode;
+ }
return porigin;
}
/* otherwise it was not very useful; free it */
/* The path is the same as parent */
porigin = get_origin(sb, parent, origin->path);
hashcpy(porigin->blob_sha1, origin->blob_sha1);
+ porigin->mode = origin->mode;
} else {
/*
* Since origin->path is a pathspec, if the parent
case 'M':
porigin = get_origin(sb, parent, origin->path);
hashcpy(porigin->blob_sha1, p->one->sha1);
+ porigin->mode = p->one->mode;
break;
case 'A':
case 'T':
cached = make_origin(porigin->commit, porigin->path);
hashcpy(cached->blob_sha1, porigin->blob_sha1);
+ cached->mode = porigin->mode;
parent->util = cached;
}
return porigin;
!strcmp(p->two->path, origin->path)) {
porigin = get_origin(sb, parent, p->one->path);
hashcpy(porigin->blob_sha1, p->one->sha1);
+ porigin->mode = p->one->mode;
break;
}
}
{
int last_in_target;
mmfile_t file_p, file_o;
- struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
+ struct blame_chunk_cb_data d;
xpparam_t xpp;
xdemitconf_t xecfg;
-
+ memset(&d, 0, sizeof(d));
+ d.sb = sb; d.target = target; d.parent = parent;
last_in_target = find_last_in_target(sb, target);
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));
const char *cp;
int cnt;
mmfile_t file_o;
- struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
+ struct handle_split_cb_data d;
xpparam_t xpp;
xdemitconf_t xecfg;
-
+ memset(&d, 0, sizeof(d));
+ d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
/*
* Prepare mmfile that contains only the lines in ent.
*/
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;
norigin = get_origin(sb, parent, p->one->path);
hashcpy(norigin->blob_sha1, p->one->sha1);
- fill_origin_blob(norigin, &file_p);
+ norigin->mode = p->one->mode;
+ fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
if (!file_p.ptr)
continue;
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];
&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);
}
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);
}
* 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;
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)
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, mode, 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:
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;
*
* 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>"
*
* 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)
}
else {
o = get_origin(&sb, sb.final, path);
- if (fill_blob_sha1(o))
+ if (fill_blob_sha1_and_mode(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->mode, 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),