Skip to content

Commit 4af574d

Browse files
committed
Merge branch 'ab/blame-textconv'
* ab/blame-textconv: t/t8006: test textconv support for blame textconv: support for blame textconv: make the API public Conflicts: diff.h
2 parents a81f1a8 + 37d29e1 commit 4af574d

File tree

4 files changed

+165
-21
lines changed

4 files changed

+165
-21
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,
@@ -742,8 +776,8 @@ static int pass_blame_to_parent(struct scoreboard *sb,
742776
if (last_in_target < 0)
743777
return 1; /* nothing remains for this target */
744778

745-
fill_origin_blob(parent, &file_p);
746-
fill_origin_blob(target, &file_o);
779+
fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
780+
fill_origin_blob(&sb->revs->diffopt, target, &file_o);
747781
num_get_patch++;
748782

749783
memset(&xpp, 0, sizeof(xpp));
@@ -924,7 +958,7 @@ static int find_move_in_parent(struct scoreboard *sb,
924958
if (last_in_target < 0)
925959
return 1; /* nothing remains for this target */
926960

927-
fill_origin_blob(parent, &file_p);
961+
fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
928962
if (!file_p.ptr)
929963
return 0;
930964

@@ -1065,7 +1099,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
10651099

10661100
norigin = get_origin(sb, parent, p->one->path);
10671101
hashcpy(norigin->blob_sha1, p->one->sha1);
1068-
fill_origin_blob(norigin, &file_p);
1102+
fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
10691103
if (!file_p.ptr)
10701104
continue;
10711105

@@ -1985,14 +2019,26 @@ static int git_blame_config(const char *var, const char *value, void *cb)
19852019
blame_date_mode = parse_date_format(value);
19862020
return 0;
19872021
}
2022+
2023+
switch (userdiff_config(var, value)) {
2024+
case 0:
2025+
break;
2026+
case -1:
2027+
return -1;
2028+
default:
2029+
return 0;
2030+
}
2031+
19882032
return git_default_config(var, value, cb);
19892033
}
19902034

19912035
/*
19922036
* Prepare a dummy commit that represents the work tree (or staged) item.
19932037
* Note that annotating work tree item never works in the reverse.
19942038
*/
1995-
static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
2039+
static struct commit *fake_working_tree_commit(struct diff_options *opt,
2040+
const char *path,
2041+
const char *contents_from)
19962042
{
19972043
struct commit *commit;
19982044
struct origin *origin;
@@ -2020,6 +2066,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
20202066
if (!contents_from || strcmp("-", contents_from)) {
20212067
struct stat st;
20222068
const char *read_from;
2069+
unsigned long buf_len;
20232070

20242071
if (contents_from) {
20252072
if (stat(contents_from, &st) < 0)
@@ -2032,9 +2079,13 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
20322079
read_from = path;
20332080
}
20342081
mode = canon_mode(st.st_mode);
2082+
20352083
switch (st.st_mode & S_IFMT) {
20362084
case S_IFREG:
2037-
if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
2085+
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
2086+
textconv_object(read_from, null_sha1, &buf.buf, &buf_len))
2087+
buf.len = buf_len;
2088+
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
20382089
die_errno("cannot open or read '%s'", read_from);
20392090
break;
20402091
case S_IFLNK:
@@ -2250,6 +2301,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
22502301
git_config(git_blame_config, NULL);
22512302
init_revisions(&revs, NULL);
22522303
revs.date_mode = blame_date_mode;
2304+
DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
22532305

22542306
save_commit_buffer = 0;
22552307
dashdash_pos = 0;
@@ -2386,7 +2438,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
23862438
* or "--contents".
23872439
*/
23882440
setup_work_tree();
2389-
sb.final = fake_working_tree_commit(path, contents_from);
2441+
sb.final = fake_working_tree_commit(&sb.revs->diffopt,
2442+
path, contents_from);
23902443
add_pending_object(&revs, &(sb.final->object), ":");
23912444
}
23922445
else if (contents_from)
@@ -2413,8 +2466,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
24132466
if (fill_blob_sha1(o))
24142467
die("no such path %s in %s", path, final_commit_name);
24152468

2416-
sb.final_buf = read_sha1_file(o->blob_sha1, &type,
2417-
&sb.final_buf_size);
2469+
if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
2470+
textconv_object(path, o->blob_sha1, (char **) &sb.final_buf,
2471+
&sb.final_buf_size))
2472+
;
2473+
else
2474+
sb.final_buf = read_sha1_file(o->blob_sha1, &type,
2475+
&sb.final_buf_size);
2476+
24182477
if (!sb.final_buf)
24192478
die("Cannot read blob %s for path %s",
24202479
sha1_to_hex(o->blob_sha1),

diff.c

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@ static char diff_colors[][COLOR_MAXLEN] = {
4444
GIT_COLOR_NORMAL, /* FUNCINFO */
4545
};
4646

47-
static void diff_filespec_load_driver(struct diff_filespec *one);
48-
static size_t fill_textconv(struct userdiff_driver *driver,
49-
struct diff_filespec *df, char **outbuf);
50-
5147
static int parse_diff_color_slot(const char *var, int ofs)
5248
{
5349
if (!strcasecmp(var+ofs, "plain"))
@@ -1810,7 +1806,7 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
18101806
options->b_prefix = b;
18111807
}
18121808

1813-
static struct userdiff_driver *get_textconv(struct diff_filespec *one)
1809+
struct userdiff_driver *get_textconv(struct diff_filespec *one)
18141810
{
18151811
if (!DIFF_FILE_VALID(one))
18161812
return NULL;
@@ -4243,9 +4239,9 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
42434239
return strbuf_detach(&buf, outsize);
42444240
}
42454241

4246-
static size_t fill_textconv(struct userdiff_driver *driver,
4247-
struct diff_filespec *df,
4248-
char **outbuf)
4242+
size_t fill_textconv(struct userdiff_driver *driver,
4243+
struct diff_filespec *df,
4244+
char **outbuf)
42494245
{
42504246
size_t size;
42514247

diff.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ struct rev_info;
1010
struct diff_options;
1111
struct diff_queue_struct;
1212
struct strbuf;
13+
struct diff_filespec;
14+
struct userdiff_driver;
1315

1416
typedef void (*change_fn_t)(struct diff_options *options,
1517
unsigned old_mode, unsigned new_mode,
@@ -74,6 +76,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
7476
#define DIFF_OPT_SUBMODULE_LOG (1 << 23)
7577
#define DIFF_OPT_DIRTY_SUBMODULES (1 << 24)
7678
#define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
79+
#define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
7780

7881
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
7982
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
@@ -292,4 +295,10 @@ extern void diff_no_index(struct rev_info *, int, const char **, int, const char
292295

293296
extern int index_differs_from(const char *def, int diff_flags);
294297

298+
extern size_t fill_textconv(struct userdiff_driver *driver,
299+
struct diff_filespec *df,
300+
char **outbuf);
301+
302+
extern struct userdiff_driver *get_textconv(struct diff_filespec *one);
303+
295304
#endif /* DIFF_H */

t/t8006-blame-textconv.sh

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/bin/sh
2+
3+
test_description='git blame textconv support'
4+
. ./test-lib.sh
5+
6+
find_blame() {
7+
sed -e 's/^[^(]*//'
8+
}
9+
10+
cat >helper <<'EOF'
11+
#!/bin/sh
12+
sed 's/^/converted: /' "$@"
13+
EOF
14+
chmod +x helper
15+
16+
test_expect_success 'setup ' '
17+
echo test 1 >one.bin &&
18+
echo test number 2 >two.bin &&
19+
git add . &&
20+
GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
21+
echo test 1 version 2 >one.bin &&
22+
echo test number 2 version 2 >>two.bin &&
23+
GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
24+
'
25+
26+
cat >expected <<EOF
27+
(Number2 2010-01-01 20:00:00 +0000 1) test 1 version 2
28+
EOF
29+
30+
test_expect_success 'no filter specified' '
31+
git blame one.bin >blame &&
32+
find_blame Number2 <blame >result &&
33+
test_cmp expected result
34+
'
35+
36+
test_expect_success 'setup textconv filters' '
37+
echo "*.bin diff=test" >.gitattributes &&
38+
git config diff.test.textconv ./helper &&
39+
git config diff.test.cachetextconv false
40+
'
41+
42+
test_expect_success 'blame with --no-textconv' '
43+
git blame --no-textconv one.bin >blame &&
44+
find_blame <blame> result &&
45+
test_cmp expected result
46+
'
47+
48+
cat >expected <<EOF
49+
(Number2 2010-01-01 20:00:00 +0000 1) converted: test 1 version 2
50+
EOF
51+
52+
test_expect_success 'basic blame on last commit' '
53+
git blame one.bin >blame &&
54+
find_blame <blame >result &&
55+
test_cmp expected result
56+
'
57+
58+
cat >expected <<EOF
59+
(Number1 2010-01-01 18:00:00 +0000 1) converted: test number 2
60+
(Number2 2010-01-01 20:00:00 +0000 2) converted: test number 2 version 2
61+
EOF
62+
63+
test_expect_success 'blame --textconv going through revisions' '
64+
git blame --textconv two.bin >blame &&
65+
find_blame <blame >result &&
66+
test_cmp expected result
67+
'
68+
69+
test_expect_success 'make a new commit' '
70+
echo "test number 2 version 3" >>two.bin &&
71+
GIT_AUTHOR_NAME=Number3 git commit -a -m Third --date="2010-01-01 22:00:00"
72+
'
73+
74+
test_expect_success 'blame from previous revision' '
75+
git blame HEAD^ two.bin >blame &&
76+
find_blame <blame >result &&
77+
test_cmp expected result
78+
'
79+
80+
test_done

0 commit comments

Comments
 (0)