Skip to content

Commit 3b8a12e

Browse files
Axel Bonnetgitster
authored andcommitted
textconv: support for blame
This patches enables to perform textconv with blame if a textconv driver is available fos the file. The main task is performed by the textconv_object function which prepares diff_filespec and if possible converts the file using diff textconv API. Only regular files are converted, so the mode of diff_filespec is faked. Textconv conversion is enabled by default (equivalent to the option --textconv), since blaming binary files is useless in most cases. The option --no-textconv is used to disable textconv conversion. The declarations of several functions are modified to give access to a diff_options, in order to know whether the textconv option is activated or not. Signed-off-by: Axel Bonnet <[email protected]> Signed-off-by: Clément Poulain <[email protected]> Signed-off-by: Diane Gasselin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a788d7d commit 3b8a12e

File tree

1 file changed

+72
-13
lines changed

1 file changed

+72
-13
lines changed

builtin/blame.c

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "mailmap.h"
2121
#include "parse-options.h"
2222
#include "utf8.h"
23+
#include "userdiff.h"
2324

2425
static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
2526

@@ -85,17 +86,51 @@ struct origin {
8586
char path[FLEX_ARRAY];
8687
};
8788

89+
/*
90+
* Prepare diff_filespec and convert it using diff textconv API
91+
* if the textconv driver exists.
92+
* Return 1 if the conversion succeeds, 0 otherwise.
93+
*/
94+
static int textconv_object(const char *path,
95+
const unsigned char *sha1,
96+
char **buf,
97+
unsigned long *buf_size)
98+
{
99+
struct diff_filespec *df;
100+
struct userdiff_driver *textconv;
101+
102+
df = alloc_filespec(path);
103+
fill_filespec(df, sha1, S_IFREG | 0664);
104+
textconv = get_textconv(df);
105+
if (!textconv) {
106+
free_filespec(df);
107+
return 0;
108+
}
109+
110+
*buf_size = fill_textconv(textconv, df, buf);
111+
free_filespec(df);
112+
return 1;
113+
}
114+
88115
/*
89116
* Given an origin, prepare mmfile_t structure to be used by the
90117
* diff machinery
91118
*/
92-
static void fill_origin_blob(struct origin *o, mmfile_t *file)
119+
static void fill_origin_blob(struct diff_options *opt,
120+
struct origin *o, mmfile_t *file)
93121
{
94122
if (!o->file.ptr) {
95123
enum object_type type;
124+
unsigned long file_size;
125+
96126
num_read_blob++;
97-
file->ptr = read_sha1_file(o->blob_sha1, &type,
98-
(unsigned long *)(&(file->size)));
127+
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
128+
textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size))
129+
;
130+
else
131+
file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
132+
file->size = file_size;
133+
99134
if (!file->ptr)
100135
die("Cannot read blob %s for path %s",
101136
sha1_to_hex(o->blob_sha1),
@@ -282,7 +317,6 @@ static struct origin *get_origin(struct scoreboard *sb,
282317
static int fill_blob_sha1(struct origin *origin)
283318
{
284319
unsigned mode;
285-
286320
if (!is_null_sha1(origin->blob_sha1))
287321
return 0;
288322
if (get_tree_entry(origin->commit->object.sha1,
@@ -741,8 +775,8 @@ static int pass_blame_to_parent(struct scoreboard *sb,
741775
if (last_in_target < 0)
742776
return 1; /* nothing remains for this target */
743777

744-
fill_origin_blob(parent, &file_p);
745-
fill_origin_blob(target, &file_o);
778+
fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
779+
fill_origin_blob(&sb->revs->diffopt, target, &file_o);
746780
num_get_patch++;
747781

748782
memset(&xpp, 0, sizeof(xpp));
@@ -922,7 +956,7 @@ static int find_move_in_parent(struct scoreboard *sb,
922956
if (last_in_target < 0)
923957
return 1; /* nothing remains for this target */
924958

925-
fill_origin_blob(parent, &file_p);
959+
fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
926960
if (!file_p.ptr)
927961
return 0;
928962

@@ -1063,7 +1097,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
10631097

10641098
norigin = get_origin(sb, parent, p->one->path);
10651099
hashcpy(norigin->blob_sha1, p->one->sha1);
1066-
fill_origin_blob(norigin, &file_p);
1100+
fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
10671101
if (!file_p.ptr)
10681102
continue;
10691103

@@ -1983,14 +2017,26 @@ static int git_blame_config(const char *var, const char *value, void *cb)
19832017
blame_date_mode = parse_date_format(value);
19842018
return 0;
19852019
}
2020+
2021+
switch (userdiff_config(var, value)) {
2022+
case 0:
2023+
break;
2024+
case -1:
2025+
return -1;
2026+
default:
2027+
return 0;
2028+
}
2029+
19862030
return git_default_config(var, value, cb);
19872031
}
19882032

19892033
/*
19902034
* Prepare a dummy commit that represents the work tree (or staged) item.
19912035
* Note that annotating work tree item never works in the reverse.
19922036
*/
1993-
static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
2037+
static struct commit *fake_working_tree_commit(struct diff_options *opt,
2038+
const char *path,
2039+
const char *contents_from)
19942040
{
19952041
struct commit *commit;
19962042
struct origin *origin;
@@ -2018,6 +2064,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
20182064
if (!contents_from || strcmp("-", contents_from)) {
20192065
struct stat st;
20202066
const char *read_from;
2067+
unsigned long buf_len;
20212068

20222069
if (contents_from) {
20232070
if (stat(contents_from, &st) < 0)
@@ -2030,9 +2077,13 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
20302077
read_from = path;
20312078
}
20322079
mode = canon_mode(st.st_mode);
2080+
20332081
switch (st.st_mode & S_IFMT) {
20342082
case S_IFREG:
2035-
if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
2083+
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
2084+
textconv_object(read_from, null_sha1, &buf.buf, &buf_len))
2085+
buf.len = buf_len;
2086+
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
20362087
die_errno("cannot open or read '%s'", read_from);
20372088
break;
20382089
case S_IFLNK:
@@ -2248,6 +2299,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
22482299
git_config(git_blame_config, NULL);
22492300
init_revisions(&revs, NULL);
22502301
revs.date_mode = blame_date_mode;
2302+
DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
22512303

22522304
save_commit_buffer = 0;
22532305
dashdash_pos = 0;
@@ -2384,7 +2436,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
23842436
* or "--contents".
23852437
*/
23862438
setup_work_tree();
2387-
sb.final = fake_working_tree_commit(path, contents_from);
2439+
sb.final = fake_working_tree_commit(&sb.revs->diffopt,
2440+
path, contents_from);
23882441
add_pending_object(&revs, &(sb.final->object), ":");
23892442
}
23902443
else if (contents_from)
@@ -2411,8 +2464,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
24112464
if (fill_blob_sha1(o))
24122465
die("no such path %s in %s", path, final_commit_name);
24132466

2414-
sb.final_buf = read_sha1_file(o->blob_sha1, &type,
2415-
&sb.final_buf_size);
2467+
if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
2468+
textconv_object(path, o->blob_sha1, (char **) &sb.final_buf,
2469+
&sb.final_buf_size))
2470+
;
2471+
else
2472+
sb.final_buf = read_sha1_file(o->blob_sha1, &type,
2473+
&sb.final_buf_size);
2474+
24162475
if (!sb.final_buf)
24172476
die("Cannot read blob %s for path %s",
24182477
sha1_to_hex(o->blob_sha1),

0 commit comments

Comments
 (0)