cat-file: introduce the --filters option

The --filters option applies the convert_to_working_tree() filter for
the path when showing the contents of a regular file blob object;
the contents are written out as-is for other types of objects.

This feature comes in handy when a 3rd-party tool wants to work with
the contents of files from past revisions as if they had been checked
out, but without detouring via temporary files.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Johannes Schindelin 2016-08-24 14:23:39 +02:00 committed by Junio C Hamano
parent 16dcc2992b
commit b9e62f6011
3 changed files with 78 additions and 4 deletions

View File

@ -9,15 +9,15 @@ git-cat-file - Provide content or type and size information for repository objec
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv ) <object> 'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv | --filters ) <object>
'git cat-file' (--batch | --batch-check) [--follow-symlinks] 'git cat-file' (--batch | --batch-check) [--follow-symlinks]
DESCRIPTION DESCRIPTION
----------- -----------
In its first form, the command provides the content or the type of an object in In its first form, the command provides the content or the type of an object in
the repository. The type is required unless `-t` or `-p` is used to find the the repository. The type is required unless `-t` or `-p` is used to find the
object type, or `-s` is used to find the object size, or `--textconv` is used object type, or `-s` is used to find the object size, or `--textconv` or
(which implies type "blob"). `--filters` is used (which imply type "blob").
In the second form, a list of objects (separated by linefeeds) is provided on In the second form, a list of objects (separated by linefeeds) is provided on
stdin, and the SHA-1, type, and size of each object is printed on stdout. stdin, and the SHA-1, type, and size of each object is printed on stdout.
@ -58,6 +58,12 @@ OPTIONS
order to apply the filter to the content recorded in the index at order to apply the filter to the content recorded in the index at
<path>. <path>.
--filters::
Show the content as converted by the filters configured in
the current working tree for the given <path> (i.e. smudge filters,
end-of-line conversion, etc). In this case, <object> has to be of
the form <tree-ish>:<path>, or :<path>.
--batch:: --batch::
--batch=<format>:: --batch=<format>::
Print object information and contents for each object provided Print object information and contents for each object provided

View File

@ -20,6 +20,28 @@ struct batch_options {
const char *format; const char *format;
}; };
static int filter_object(const char *path, unsigned mode,
const unsigned char *sha1,
char **buf, unsigned long *size)
{
enum object_type type;
*buf = read_sha1_file(sha1, &type, size);
if (!*buf)
return error(_("cannot read object %s '%s'"),
sha1_to_hex(sha1), path);
if ((type == OBJ_BLOB) && S_ISREG(mode)) {
struct strbuf strbuf = STRBUF_INIT;
if (convert_to_working_tree(path, *buf, *size, &strbuf)) {
free(*buf);
*size = strbuf.len;
*buf = strbuf_detach(&strbuf, NULL);
}
}
return 0;
}
static int cat_one_file(int opt, const char *exp_type, const char *obj_name, static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
int unknown_type) int unknown_type)
{ {
@ -61,6 +83,16 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
case 'e': case 'e':
return !has_sha1_file(sha1); return !has_sha1_file(sha1);
case 'w':
if (!obj_context.path[0])
die("git cat-file --filters %s: <object> must be "
"<sha1:path>", obj_name);
if (filter_object(obj_context.path, obj_context.mode,
sha1, &buf, &size))
return -1;
break;
case 'c': case 'c':
if (!obj_context.path[0]) if (!obj_context.path[0])
die("git cat-file --textconv %s: <object> must be <sha1:path>", die("git cat-file --textconv %s: <object> must be <sha1:path>",
@ -440,7 +472,7 @@ static int batch_objects(struct batch_options *opt)
} }
static const char * const cat_file_usage[] = { static const char * const cat_file_usage[] = {
N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|<type>|--textconv) <object>"), N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|<type>|--textconv|--filters) <object>"),
N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"), N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"),
NULL NULL
}; };
@ -486,6 +518,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
OPT_CMDMODE('p', NULL, &opt, N_("pretty-print object's content"), 'p'), OPT_CMDMODE('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
OPT_CMDMODE(0, "textconv", &opt, OPT_CMDMODE(0, "textconv", &opt,
N_("for blob objects, run textconv on object's content"), 'c'), N_("for blob objects, run textconv on object's content"), 'c'),
OPT_CMDMODE(0, "filters", &opt,
N_("for blob objects, run filters on object's content"), 'w'),
OPT_BOOL(0, "allow-unknown-type", &unknown_type, OPT_BOOL(0, "allow-unknown-type", &unknown_type,
N_("allow -s and -t to work with broken/corrupt objects")), N_("allow -s and -t to work with broken/corrupt objects")),
OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")), OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),

34
t/t8010-cat-file-filters.sh Executable file
View File

@ -0,0 +1,34 @@
#!/bin/sh
test_description='git cat-file filters support'
. ./test-lib.sh
test_expect_success 'setup ' '
echo "*.txt eol=crlf diff=txt" >.gitattributes &&
echo "hello" | append_cr >world.txt &&
git add .gitattributes world.txt &&
test_tick &&
git commit -m "Initial commit"
'
has_cr () {
tr '\015' Q <"$1" | grep Q >/dev/null
}
test_expect_success 'no filters with `git show`' '
git show HEAD:world.txt >actual &&
! has_cr actual
'
test_expect_success 'no filters with cat-file' '
git cat-file blob HEAD:world.txt >actual &&
! has_cr actual
'
test_expect_success 'cat-file --filters converts to worktree version' '
git cat-file --filters HEAD:world.txt >actual &&
has_cr actual
'
test_done