Skip to content

Commit dd9d290

Browse files
committed
Merge branch 'ks/no-textconv-symlink'
* ks/no-textconv-symlink: blame,cat-file --textconv: Don't assume mode is ``S_IFREF | 0664'' blame,cat-file: Demonstrate --textconv is wrongly running converter on symlinks blame,cat-file: Prepare --textconv tests for correctly-failing conversion program
2 parents 430fac9 + 9006471 commit dd9d290

File tree

6 files changed

+114
-25
lines changed

6 files changed

+114
-25
lines changed

builtin.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
3636

3737
extern int check_pager_config(const char *cmd);
3838

39-
extern int textconv_object(const char *path, const unsigned char *sha1, char **buf, unsigned long *buf_size);
39+
extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, char **buf, unsigned long *buf_size);
4040

4141
extern int cmd_add(int argc, const char **argv, const char *prefix);
4242
extern int cmd_annotate(int argc, const char **argv, const char *prefix);

builtin/blame.c

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ struct origin {
8383
struct commit *commit;
8484
mmfile_t file;
8585
unsigned char blob_sha1[20];
86+
unsigned mode;
8687
char path[FLEX_ARRAY];
8788
};
8889

@@ -92,6 +93,7 @@ struct origin {
9293
* Return 1 if the conversion succeeds, 0 otherwise.
9394
*/
9495
int textconv_object(const char *path,
96+
unsigned mode,
9597
const unsigned char *sha1,
9698
char **buf,
9799
unsigned long *buf_size)
@@ -100,7 +102,7 @@ int textconv_object(const char *path,
100102
struct userdiff_driver *textconv;
101103

102104
df = alloc_filespec(path);
103-
fill_filespec(df, sha1, S_IFREG | 0664);
105+
fill_filespec(df, sha1, mode);
104106
textconv = get_textconv(df);
105107
if (!textconv) {
106108
free_filespec(df);
@@ -125,7 +127,7 @@ static void fill_origin_blob(struct diff_options *opt,
125127

126128
num_read_blob++;
127129
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
128-
textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size))
130+
textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
129131
;
130132
else
131133
file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
@@ -313,21 +315,23 @@ static struct origin *get_origin(struct scoreboard *sb,
313315
* for an origin is also used to pass the blame for the entire file to
314316
* the parent to detect the case where a child's blob is identical to
315317
* that of its parent's.
318+
*
319+
* This also fills origin->mode for corresponding tree path.
316320
*/
317-
static int fill_blob_sha1(struct origin *origin)
321+
static int fill_blob_sha1_and_mode(struct origin *origin)
318322
{
319-
unsigned mode;
320323
if (!is_null_sha1(origin->blob_sha1))
321324
return 0;
322325
if (get_tree_entry(origin->commit->object.sha1,
323326
origin->path,
324-
origin->blob_sha1, &mode))
327+
origin->blob_sha1, &origin->mode))
325328
goto error_out;
326329
if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)
327330
goto error_out;
328331
return 0;
329332
error_out:
330333
hashclr(origin->blob_sha1);
334+
origin->mode = S_IFINVALID;
331335
return -1;
332336
}
333337

@@ -360,12 +364,14 @@ static struct origin *find_origin(struct scoreboard *sb,
360364
/*
361365
* If the origin was newly created (i.e. get_origin
362366
* would call make_origin if none is found in the
363-
* scoreboard), it does not know the blob_sha1,
367+
* scoreboard), it does not know the blob_sha1/mode,
364368
* so copy it. Otherwise porigin was in the
365-
* scoreboard and already knows blob_sha1.
369+
* scoreboard and already knows blob_sha1/mode.
366370
*/
367-
if (porigin->refcnt == 1)
371+
if (porigin->refcnt == 1) {
368372
hashcpy(porigin->blob_sha1, cached->blob_sha1);
373+
porigin->mode = cached->mode;
374+
}
369375
return porigin;
370376
}
371377
/* otherwise it was not very useful; free it */
@@ -400,6 +406,7 @@ static struct origin *find_origin(struct scoreboard *sb,
400406
/* The path is the same as parent */
401407
porigin = get_origin(sb, parent, origin->path);
402408
hashcpy(porigin->blob_sha1, origin->blob_sha1);
409+
porigin->mode = origin->mode;
403410
} else {
404411
/*
405412
* Since origin->path is a pathspec, if the parent
@@ -425,6 +432,7 @@ static struct origin *find_origin(struct scoreboard *sb,
425432
case 'M':
426433
porigin = get_origin(sb, parent, origin->path);
427434
hashcpy(porigin->blob_sha1, p->one->sha1);
435+
porigin->mode = p->one->mode;
428436
break;
429437
case 'A':
430438
case 'T':
@@ -444,6 +452,7 @@ static struct origin *find_origin(struct scoreboard *sb,
444452

445453
cached = make_origin(porigin->commit, porigin->path);
446454
hashcpy(cached->blob_sha1, porigin->blob_sha1);
455+
cached->mode = porigin->mode;
447456
parent->util = cached;
448457
}
449458
return porigin;
@@ -486,6 +495,7 @@ static struct origin *find_rename(struct scoreboard *sb,
486495
!strcmp(p->two->path, origin->path)) {
487496
porigin = get_origin(sb, parent, p->one->path);
488497
hashcpy(porigin->blob_sha1, p->one->sha1);
498+
porigin->mode = p->one->mode;
489499
break;
490500
}
491501
}
@@ -1099,6 +1109,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
10991109

11001110
norigin = get_origin(sb, parent, p->one->path);
11011111
hashcpy(norigin->blob_sha1, p->one->sha1);
1112+
norigin->mode = p->one->mode;
11021113
fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
11031114
if (!file_p.ptr)
11041115
continue;
@@ -2075,7 +2086,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
20752086
switch (st.st_mode & S_IFMT) {
20762087
case S_IFREG:
20772088
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
2078-
textconv_object(read_from, null_sha1, &buf.buf, &buf_len))
2089+
textconv_object(read_from, mode, null_sha1, &buf.buf, &buf_len))
20792090
buf.len = buf_len;
20802091
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
20812092
die_errno("cannot open or read '%s'", read_from);
@@ -2455,11 +2466,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
24552466
}
24562467
else {
24572468
o = get_origin(&sb, sb.final, path);
2458-
if (fill_blob_sha1(o))
2469+
if (fill_blob_sha1_and_mode(o))
24592470
die("no such path %s in %s", path, final_commit_name);
24602471

24612472
if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
2462-
textconv_object(path, o->blob_sha1, (char **) &sb.final_buf,
2473+
textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
24632474
&sb.final_buf_size))
24642475
;
24652476
else

builtin/cat-file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
143143
die("git cat-file --textconv %s: <object> must be <sha1:path>",
144144
obj_name);
145145

146-
if (!textconv_object(obj_context.path, sha1, &buf, &size))
146+
if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
147147
die("git cat-file --textconv: unable to run textconv on %s",
148148
obj_name);
149149
break;

sha1_name.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
10691069
struct cache_entry *ce;
10701070
int pos;
10711071
if (namelen > 2 && name[1] == '/')
1072+
/* don't need mode for commit */
10721073
return get_sha1_oneline(name + 2, sha1);
10731074
if (namelen < 3 ||
10741075
name[2] != ':' ||
@@ -1096,6 +1097,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
10961097
break;
10971098
if (ce_stage(ce) == stage) {
10981099
hashcpy(sha1, ce->sha1);
1100+
oc->mode = ce->ce_mode;
10991101
return 0;
11001102
}
11011103
pos++;

t/t8006-blame-textconv.sh

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,29 @@ find_blame() {
99

1010
cat >helper <<'EOF'
1111
#!/bin/sh
12-
sed 's/^/converted: /' "$@"
12+
grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; }
13+
sed 's/^bin: /converted: /' "$1"
1314
EOF
1415
chmod +x helper
1516

1617
test_expect_success 'setup ' '
17-
echo test 1 >one.bin &&
18-
echo test number 2 >two.bin &&
18+
echo "bin: test 1" >one.bin &&
19+
echo "bin: test number 2" >two.bin &&
20+
if test_have_prereq SYMLINKS; then
21+
ln -s one.bin symlink.bin
22+
fi &&
1923
git add . &&
2024
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 &&
25+
echo "bin: test 1 version 2" >one.bin &&
26+
echo "bin: test number 2 version 2" >>two.bin &&
27+
if test_have_prereq SYMLINKS; then
28+
ln -sf two.bin symlink.bin
29+
fi &&
2330
GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
2431
'
2532

2633
cat >expected <<EOF
27-
(Number2 2010-01-01 20:00:00 +0000 1) test 1 version 2
34+
(Number2 2010-01-01 20:00:00 +0000 1) bin: test 1 version 2
2835
EOF
2936

3037
test_expect_success 'no filter specified' '
@@ -67,7 +74,7 @@ test_expect_success 'blame --textconv going through revisions' '
6774
'
6875

6976
test_expect_success 'make a new commit' '
70-
echo "test number 2 version 3" >>two.bin &&
77+
echo "bin: test number 2 version 3" >>two.bin &&
7178
GIT_AUTHOR_NAME=Number3 git commit -a -m Third --date="2010-01-01 22:00:00"
7279
'
7380

@@ -77,4 +84,45 @@ test_expect_success 'blame from previous revision' '
7784
test_cmp expected result
7885
'
7986

87+
cat >expected <<EOF
88+
(Number2 2010-01-01 20:00:00 +0000 1) two.bin
89+
EOF
90+
91+
test_expect_success SYMLINKS 'blame with --no-textconv (on symlink)' '
92+
git blame --no-textconv symlink.bin >blame &&
93+
find_blame <blame >result &&
94+
test_cmp expected result
95+
'
96+
97+
test_expect_success SYMLINKS 'blame --textconv (on symlink)' '
98+
git blame --textconv symlink.bin >blame &&
99+
find_blame <blame >result &&
100+
test_cmp expected result
101+
'
102+
103+
# cp two.bin three.bin and make small tweak
104+
# (this will direct blame -C -C three.bin to consider two.bin and symlink.bin)
105+
test_expect_success SYMLINKS 'make another new commit' '
106+
cat >three.bin <<\EOF &&
107+
bin: test number 2
108+
bin: test number 2 version 2
109+
bin: test number 2 version 3
110+
bin: test number 3
111+
EOF
112+
git add three.bin &&
113+
GIT_AUTHOR_NAME=Number4 git commit -a -m Fourth --date="2010-01-01 23:00:00"
114+
'
115+
116+
test_expect_success SYMLINKS 'blame on last commit (-C -C, symlink)' '
117+
git blame -C -C three.bin >blame &&
118+
find_blame <blame >result &&
119+
cat >expected <<\EOF &&
120+
(Number1 2010-01-01 18:00:00 +0000 1) converted: test number 2
121+
(Number2 2010-01-01 20:00:00 +0000 2) converted: test number 2 version 2
122+
(Number3 2010-01-01 22:00:00 +0000 3) converted: test number 2 version 3
123+
(Number4 2010-01-01 23:00:00 +0000 4) converted: test number 3
124+
EOF
125+
test_cmp expected result
126+
'
127+
80128
test_done

t/t8007-cat-file-textconv.sh

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,19 @@ test_description='git cat-file textconv support'
55

66
cat >helper <<'EOF'
77
#!/bin/sh
8-
sed 's/^/converted: /' "$@"
8+
grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; }
9+
sed 's/^bin: /converted: /' "$1"
910
EOF
1011
chmod +x helper
1112

1213
test_expect_success 'setup ' '
13-
echo test >one.bin &&
14+
echo "bin: test" >one.bin &&
15+
if test_have_prereq SYMLINKS; then
16+
ln -s one.bin symlink.bin
17+
fi &&
1418
git add . &&
1519
GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
16-
echo test version 2 >one.bin &&
20+
echo "bin: test version 2" >one.bin &&
1721
GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
1822
'
1923

@@ -33,7 +37,7 @@ test_expect_success 'setup textconv filters' '
3337
'
3438

3539
cat >expected <<EOF
36-
test version 2
40+
bin: test version 2
3741
EOF
3842

3943
test_expect_success 'cat-file without --textconv' '
@@ -42,7 +46,7 @@ test_expect_success 'cat-file without --textconv' '
4246
'
4347

4448
cat >expected <<EOF
45-
test
49+
bin: test
4650
EOF
4751

4852
test_expect_success 'cat-file without --textconv on previous commit' '
@@ -67,4 +71,28 @@ test_expect_success 'cat-file --textconv on previous commit' '
6771
git cat-file --textconv HEAD^:one.bin >result &&
6872
test_cmp expected result
6973
'
74+
75+
test_expect_success SYMLINKS 'cat-file without --textconv (symlink)' '
76+
git cat-file blob :symlink.bin >result &&
77+
printf "%s" "one.bin" >expected
78+
test_cmp expected result
79+
'
80+
81+
82+
test_expect_success SYMLINKS 'cat-file --textconv on index (symlink)' '
83+
! git cat-file --textconv :symlink.bin 2>result &&
84+
cat >expected <<\EOF &&
85+
fatal: git cat-file --textconv: unable to run textconv on :symlink.bin
86+
EOF
87+
test_cmp expected result
88+
'
89+
90+
test_expect_success SYMLINKS 'cat-file --textconv on HEAD (symlink)' '
91+
! git cat-file --textconv HEAD:symlink.bin 2>result &&
92+
cat >expected <<EOF &&
93+
fatal: git cat-file --textconv: unable to run textconv on HEAD:symlink.bin
94+
EOF
95+
test_cmp expected result
96+
'
97+
7098
test_done

0 commit comments

Comments
 (0)