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
This commit is contained in:
Junio C Hamano 2010-11-17 14:59:27 -08:00
Родитель 430fac9e5b 900647104e
Коммит dd9d290bc9
6 изменённых файлов: 114 добавлений и 25 удалений

Просмотреть файл

@ -36,7 +36,7 @@ void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
extern int check_pager_config(const char *cmd); extern int check_pager_config(const char *cmd);
extern int textconv_object(const char *path, const unsigned char *sha1, char **buf, unsigned long *buf_size); extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, char **buf, unsigned long *buf_size);
extern int cmd_add(int argc, const char **argv, const char *prefix); extern int cmd_add(int argc, const char **argv, const char *prefix);
extern int cmd_annotate(int argc, const char **argv, const char *prefix); extern int cmd_annotate(int argc, const char **argv, const char *prefix);

Просмотреть файл

@ -83,6 +83,7 @@ struct origin {
struct commit *commit; struct commit *commit;
mmfile_t file; mmfile_t file;
unsigned char blob_sha1[20]; unsigned char blob_sha1[20];
unsigned mode;
char path[FLEX_ARRAY]; char path[FLEX_ARRAY];
}; };
@ -92,6 +93,7 @@ struct origin {
* Return 1 if the conversion succeeds, 0 otherwise. * Return 1 if the conversion succeeds, 0 otherwise.
*/ */
int textconv_object(const char *path, int textconv_object(const char *path,
unsigned mode,
const unsigned char *sha1, const unsigned char *sha1,
char **buf, char **buf,
unsigned long *buf_size) unsigned long *buf_size)
@ -100,7 +102,7 @@ int textconv_object(const char *path,
struct userdiff_driver *textconv; struct userdiff_driver *textconv;
df = alloc_filespec(path); df = alloc_filespec(path);
fill_filespec(df, sha1, S_IFREG | 0664); fill_filespec(df, sha1, mode);
textconv = get_textconv(df); textconv = get_textconv(df);
if (!textconv) { if (!textconv) {
free_filespec(df); free_filespec(df);
@ -125,7 +127,7 @@ static void fill_origin_blob(struct diff_options *opt,
num_read_blob++; num_read_blob++;
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size)) textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
; ;
else else
file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size); file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
@ -313,21 +315,23 @@ static struct origin *get_origin(struct scoreboard *sb,
* for an origin is also used to pass the blame for the entire file to * 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 * the parent to detect the case where a child's blob is identical to
* that of its parent's. * 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)) if (!is_null_sha1(origin->blob_sha1))
return 0; return 0;
if (get_tree_entry(origin->commit->object.sha1, if (get_tree_entry(origin->commit->object.sha1,
origin->path, origin->path,
origin->blob_sha1, &mode)) origin->blob_sha1, &origin->mode))
goto error_out; goto error_out;
if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB) if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)
goto error_out; goto error_out;
return 0; return 0;
error_out: error_out:
hashclr(origin->blob_sha1); hashclr(origin->blob_sha1);
origin->mode = S_IFINVALID;
return -1; return -1;
} }
@ -360,12 +364,14 @@ static struct origin *find_origin(struct scoreboard *sb,
/* /*
* If the origin was newly created (i.e. get_origin * If the origin was newly created (i.e. get_origin
* would call make_origin if none is found in the * 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 * 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); hashcpy(porigin->blob_sha1, cached->blob_sha1);
porigin->mode = cached->mode;
}
return porigin; return porigin;
} }
/* otherwise it was not very useful; free it */ /* otherwise it was not very useful; free it */
@ -400,6 +406,7 @@ static struct origin *find_origin(struct scoreboard *sb,
/* The path is the same as parent */ /* The path is the same as parent */
porigin = get_origin(sb, parent, origin->path); porigin = get_origin(sb, parent, origin->path);
hashcpy(porigin->blob_sha1, origin->blob_sha1); hashcpy(porigin->blob_sha1, origin->blob_sha1);
porigin->mode = origin->mode;
} else { } else {
/* /*
* Since origin->path is a pathspec, if the parent * Since origin->path is a pathspec, if the parent
@ -425,6 +432,7 @@ static struct origin *find_origin(struct scoreboard *sb,
case 'M': case 'M':
porigin = get_origin(sb, parent, origin->path); porigin = get_origin(sb, parent, origin->path);
hashcpy(porigin->blob_sha1, p->one->sha1); hashcpy(porigin->blob_sha1, p->one->sha1);
porigin->mode = p->one->mode;
break; break;
case 'A': case 'A':
case 'T': case 'T':
@ -444,6 +452,7 @@ static struct origin *find_origin(struct scoreboard *sb,
cached = make_origin(porigin->commit, porigin->path); cached = make_origin(porigin->commit, porigin->path);
hashcpy(cached->blob_sha1, porigin->blob_sha1); hashcpy(cached->blob_sha1, porigin->blob_sha1);
cached->mode = porigin->mode;
parent->util = cached; parent->util = cached;
} }
return porigin; return porigin;
@ -486,6 +495,7 @@ static struct origin *find_rename(struct scoreboard *sb,
!strcmp(p->two->path, origin->path)) { !strcmp(p->two->path, origin->path)) {
porigin = get_origin(sb, parent, p->one->path); porigin = get_origin(sb, parent, p->one->path);
hashcpy(porigin->blob_sha1, p->one->sha1); hashcpy(porigin->blob_sha1, p->one->sha1);
porigin->mode = p->one->mode;
break; break;
} }
} }
@ -1099,6 +1109,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
norigin = get_origin(sb, parent, p->one->path); norigin = get_origin(sb, parent, p->one->path);
hashcpy(norigin->blob_sha1, p->one->sha1); hashcpy(norigin->blob_sha1, p->one->sha1);
norigin->mode = p->one->mode;
fill_origin_blob(&sb->revs->diffopt, norigin, &file_p); fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
if (!file_p.ptr) if (!file_p.ptr)
continue; continue;
@ -2075,7 +2086,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
switch (st.st_mode & S_IFMT) { switch (st.st_mode & S_IFMT) {
case S_IFREG: case S_IFREG:
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
textconv_object(read_from, null_sha1, &buf.buf, &buf_len)) textconv_object(read_from, mode, null_sha1, &buf.buf, &buf_len))
buf.len = buf_len; buf.len = buf_len;
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size) else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
die_errno("cannot open or read '%s'", read_from); die_errno("cannot open or read '%s'", read_from);
@ -2455,11 +2466,11 @@ parse_done:
} }
else { else {
o = get_origin(&sb, sb.final, path); 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); die("no such path %s in %s", path, final_commit_name);
if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) && if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
textconv_object(path, o->blob_sha1, (char **) &sb.final_buf, textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
&sb.final_buf_size)) &sb.final_buf_size))
; ;
else else

Просмотреть файл

@ -143,7 +143,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
die("git cat-file --textconv %s: <object> must be <sha1:path>", die("git cat-file --textconv %s: <object> must be <sha1:path>",
obj_name); obj_name);
if (!textconv_object(obj_context.path, sha1, &buf, &size)) if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
die("git cat-file --textconv: unable to run textconv on %s", die("git cat-file --textconv: unable to run textconv on %s",
obj_name); obj_name);
break; break;

Просмотреть файл

@ -1069,6 +1069,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
struct cache_entry *ce; struct cache_entry *ce;
int pos; int pos;
if (namelen > 2 && name[1] == '/') if (namelen > 2 && name[1] == '/')
/* don't need mode for commit */
return get_sha1_oneline(name + 2, sha1); return get_sha1_oneline(name + 2, sha1);
if (namelen < 3 || if (namelen < 3 ||
name[2] != ':' || name[2] != ':' ||
@ -1096,6 +1097,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
break; break;
if (ce_stage(ce) == stage) { if (ce_stage(ce) == stage) {
hashcpy(sha1, ce->sha1); hashcpy(sha1, ce->sha1);
oc->mode = ce->ce_mode;
return 0; return 0;
} }
pos++; pos++;

Просмотреть файл

@ -9,22 +9,29 @@ find_blame() {
cat >helper <<'EOF' cat >helper <<'EOF'
#!/bin/sh #!/bin/sh
sed 's/^/converted: /' "$@" grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; }
sed 's/^bin: /converted: /' "$1"
EOF EOF
chmod +x helper chmod +x helper
test_expect_success 'setup ' ' test_expect_success 'setup ' '
echo test 1 >one.bin && echo "bin: test 1" >one.bin &&
echo test number 2 >two.bin && echo "bin: test number 2" >two.bin &&
if test_have_prereq SYMLINKS; then
ln -s one.bin symlink.bin
fi &&
git add . && git add . &&
GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" && GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
echo test 1 version 2 >one.bin && echo "bin: test 1 version 2" >one.bin &&
echo test number 2 version 2 >>two.bin && echo "bin: test number 2 version 2" >>two.bin &&
if test_have_prereq SYMLINKS; then
ln -sf two.bin symlink.bin
fi &&
GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00" GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
' '
cat >expected <<EOF cat >expected <<EOF
(Number2 2010-01-01 20:00:00 +0000 1) test 1 version 2 (Number2 2010-01-01 20:00:00 +0000 1) bin: test 1 version 2
EOF EOF
test_expect_success 'no filter specified' ' test_expect_success 'no filter specified' '
@ -67,7 +74,7 @@ test_expect_success 'blame --textconv going through revisions' '
' '
test_expect_success 'make a new commit' ' test_expect_success 'make a new commit' '
echo "test number 2 version 3" >>two.bin && echo "bin: test number 2 version 3" >>two.bin &&
GIT_AUTHOR_NAME=Number3 git commit -a -m Third --date="2010-01-01 22:00:00" GIT_AUTHOR_NAME=Number3 git commit -a -m Third --date="2010-01-01 22:00:00"
' '
@ -77,4 +84,45 @@ test_expect_success 'blame from previous revision' '
test_cmp expected result test_cmp expected result
' '
cat >expected <<EOF
(Number2 2010-01-01 20:00:00 +0000 1) two.bin
EOF
test_expect_success SYMLINKS 'blame with --no-textconv (on symlink)' '
git blame --no-textconv symlink.bin >blame &&
find_blame <blame >result &&
test_cmp expected result
'
test_expect_success SYMLINKS 'blame --textconv (on symlink)' '
git blame --textconv symlink.bin >blame &&
find_blame <blame >result &&
test_cmp expected result
'
# cp two.bin three.bin and make small tweak
# (this will direct blame -C -C three.bin to consider two.bin and symlink.bin)
test_expect_success SYMLINKS 'make another new commit' '
cat >three.bin <<\EOF &&
bin: test number 2
bin: test number 2 version 2
bin: test number 2 version 3
bin: test number 3
EOF
git add three.bin &&
GIT_AUTHOR_NAME=Number4 git commit -a -m Fourth --date="2010-01-01 23:00:00"
'
test_expect_success SYMLINKS 'blame on last commit (-C -C, symlink)' '
git blame -C -C three.bin >blame &&
find_blame <blame >result &&
cat >expected <<\EOF &&
(Number1 2010-01-01 18:00:00 +0000 1) converted: test number 2
(Number2 2010-01-01 20:00:00 +0000 2) converted: test number 2 version 2
(Number3 2010-01-01 22:00:00 +0000 3) converted: test number 2 version 3
(Number4 2010-01-01 23:00:00 +0000 4) converted: test number 3
EOF
test_cmp expected result
'
test_done test_done

Просмотреть файл

@ -5,15 +5,19 @@ test_description='git cat-file textconv support'
cat >helper <<'EOF' cat >helper <<'EOF'
#!/bin/sh #!/bin/sh
sed 's/^/converted: /' "$@" grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; }
sed 's/^bin: /converted: /' "$1"
EOF EOF
chmod +x helper chmod +x helper
test_expect_success 'setup ' ' test_expect_success 'setup ' '
echo test >one.bin && echo "bin: test" >one.bin &&
if test_have_prereq SYMLINKS; then
ln -s one.bin symlink.bin
fi &&
git add . && git add . &&
GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" && GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
echo test version 2 >one.bin && echo "bin: test version 2" >one.bin &&
GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00" GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
' '
@ -33,7 +37,7 @@ test_expect_success 'setup textconv filters' '
' '
cat >expected <<EOF cat >expected <<EOF
test version 2 bin: test version 2
EOF EOF
test_expect_success 'cat-file without --textconv' ' test_expect_success 'cat-file without --textconv' '
@ -42,7 +46,7 @@ test_expect_success 'cat-file without --textconv' '
' '
cat >expected <<EOF cat >expected <<EOF
test bin: test
EOF EOF
test_expect_success 'cat-file without --textconv on previous commit' ' test_expect_success 'cat-file without --textconv on previous commit' '
@ -67,4 +71,28 @@ test_expect_success 'cat-file --textconv on previous commit' '
git cat-file --textconv HEAD^:one.bin >result && git cat-file --textconv HEAD^:one.bin >result &&
test_cmp expected result test_cmp expected result
' '
test_expect_success SYMLINKS 'cat-file without --textconv (symlink)' '
git cat-file blob :symlink.bin >result &&
printf "%s" "one.bin" >expected
test_cmp expected result
'
test_expect_success SYMLINKS 'cat-file --textconv on index (symlink)' '
! git cat-file --textconv :symlink.bin 2>result &&
cat >expected <<\EOF &&
fatal: git cat-file --textconv: unable to run textconv on :symlink.bin
EOF
test_cmp expected result
'
test_expect_success SYMLINKS 'cat-file --textconv on HEAD (symlink)' '
! git cat-file --textconv HEAD:symlink.bin 2>result &&
cat >expected <<EOF &&
fatal: git cat-file --textconv: unable to run textconv on HEAD:symlink.bin
EOF
test_cmp expected result
'
test_done test_done