Skip to content

Commit 21c4537

Browse files
committed
Merge 'cat-file-filters'
This topic branch adds the support for --filters (TAFKA --smudge) and --path (TAFKA --use-path). While at it, we also add support for --filters/--textconv in --batch mode (the input lines now need to contain the path in addition to the object name, separated by a single white space character). Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 129eeb5 + aa55647 commit 21c4537

File tree

3 files changed

+211
-19
lines changed

3 files changed

+211
-19
lines changed

Documentation/git-cat-file.txt

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,22 @@ git-cat-file - Provide content or type and size information for repository objec
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv ) <object>
13-
'git cat-file' (--batch | --batch-check) [--follow-symlinks]
12+
'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv | --filters ) [--path=<path>] <object>
13+
'git cat-file' (--batch | --batch-check) [ --textconv | --filters ] [--follow-symlinks]
1414

1515
DESCRIPTION
1616
-----------
1717
In its first form, the command provides the content or the type of an object in
1818
the repository. The type is required unless `-t` or `-p` is used to find the
19-
object type, or `-s` is used to find the object size, or `--textconv` is used
20-
(which implies type "blob").
19+
object type, or `-s` is used to find the object size, or `--textconv` or
20+
`--filters` is used (which imply type "blob").
2121

2222
In the second form, a list of objects (separated by linefeeds) is provided on
23-
stdin, and the SHA-1, type, and size of each object is printed on stdout.
23+
stdin, and the SHA-1, type, and size of each object is printed on stdout. The
24+
output format can be overridden using the optional `<format>` argument. If
25+
either `--textconv` or `--filters` was specified, the input is expected to
26+
list the object names followed by the path name, separated by a single white
27+
space, so that the appropriate drivers can be determined.
2428

2529
OPTIONS
2630
-------
@@ -54,19 +58,35 @@ OPTIONS
5458

5559
--textconv::
5660
Show the content as transformed by a textconv filter. In this case,
57-
<object> has be of the form <tree-ish>:<path>, or :<path> in order
58-
to apply the filter to the content recorded in the index at <path>.
61+
<object> has to be of the form <tree-ish>:<path>, or :<path> in
62+
order to apply the filter to the content recorded in the index at
63+
<path>.
64+
65+
--filters::
66+
Show the content as converted by the filters configured in
67+
the current working tree for the given <path> (i.e. smudge filters,
68+
end-of-line conversion, etc). In this case, <object> has to be of
69+
the form <tree-ish>:<path>, or :<path>.
70+
71+
--path=<path>::
72+
For use with --textconv or --filters, to allow specifying an object
73+
name and a path separately, e.g. when it is difficult to figure out
74+
the revision from which the blob came.
5975

6076
--batch::
6177
--batch=<format>::
6278
Print object information and contents for each object provided
63-
on stdin. May not be combined with any other options or arguments.
64-
See the section `BATCH OUTPUT` below for details.
79+
on stdin. May not be combined with any other options or arguments
80+
except `--textconv` or `--filters`, in which case the input lines
81+
also need to specify the path, separated by white space. See the
82+
section `BATCH OUTPUT` below for details.
6583

6684
--batch-check::
6785
--batch-check=<format>::
6886
Print object information for each object provided on stdin. May
69-
not be combined with any other options or arguments. See the
87+
not be combined with any other options or arguments except
88+
`--textconv` or `--filters`, in which case the input lines also
89+
need to specify the path, separated by white space. See the
7090
section `BATCH OUTPUT` below for details.
7191

7292
--batch-all-objects::

builtin/cat-file.c

Lines changed: 117 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,39 @@ struct batch_options {
1717
int print_contents;
1818
int buffer_output;
1919
int all_objects;
20+
int cmdmode; /* may be 'w' or 'c' for --filters or --textconv */
2021
const char *format;
2122
};
2223

24+
static const char *force_path;
25+
26+
static int filter_object(const char *path, unsigned mode,
27+
const unsigned char *sha1,
28+
char **buf, unsigned long *size)
29+
{
30+
enum object_type type;
31+
32+
*buf = read_sha1_file(sha1, &type, size);
33+
if (!*buf)
34+
return error(_("cannot read object %s '%s'"),
35+
sha1_to_hex(sha1), path);
36+
if (type != OBJ_BLOB) {
37+
free(*buf);
38+
return error(_("blob expected for %s '%s'"),
39+
sha1_to_hex(sha1), path);
40+
}
41+
if (S_ISREG(mode)) {
42+
struct strbuf strbuf = STRBUF_INIT;
43+
if (convert_to_working_tree(path, *buf, *size, &strbuf)) {
44+
free(*buf);
45+
*size = strbuf.len;
46+
*buf = strbuf_detach(&strbuf, NULL);
47+
}
48+
}
49+
50+
return 0;
51+
}
52+
2353
static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
2454
int unknown_type)
2555
{
@@ -31,13 +61,19 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
3161
struct object_info oi = {NULL};
3262
struct strbuf sb = STRBUF_INIT;
3363
unsigned flags = LOOKUP_REPLACE_OBJECT;
64+
const char *path = force_path;
3465

3566
if (unknown_type)
3667
flags |= LOOKUP_UNKNOWN_OBJECT;
3768

3869
if (get_sha1_with_context(obj_name, 0, sha1, &obj_context))
3970
die("Not a valid object name %s", obj_name);
4071

72+
if (!path)
73+
path = obj_context.path;
74+
else if (obj_context.mode == S_IFINVALID)
75+
obj_context.mode = 0100644;
76+
4177
buf = NULL;
4278
switch (opt) {
4379
case 't':
@@ -61,12 +97,23 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
6197
case 'e':
6298
return !has_sha1_file(sha1);
6399

100+
case 'w':
101+
if (!path[0])
102+
die("git cat-file --filters %s: <object> must be "
103+
"<sha1:path>", obj_name);
104+
105+
if (filter_object(path, obj_context.mode,
106+
sha1, &buf, &size))
107+
return -1;
108+
break;
109+
64110
case 'c':
65-
if (!obj_context.path[0])
111+
if (!path[0])
66112
die("git cat-file --textconv %s: <object> must be <sha1:path>",
67113
obj_name);
68114

69-
if (textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size))
115+
if (textconv_object(path, obj_context.mode,
116+
sha1, 1, &buf, &size))
70117
break;
71118

72119
case 'p':
@@ -239,7 +286,32 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
239286
if (data->type == OBJ_BLOB) {
240287
if (opt->buffer_output)
241288
fflush(stdout);
242-
if (stream_blob_to_fd(1, sha1, NULL, 0) < 0)
289+
if (opt->cmdmode) {
290+
char *contents;
291+
unsigned long size;
292+
293+
if (!data->rest)
294+
die("missing path for '%s'", sha1_to_hex(sha1));
295+
296+
if (opt->cmdmode == 'w') {
297+
if (filter_object(data->rest, 0100644, sha1,
298+
&contents, &size))
299+
die("could not convert '%s' %s",
300+
sha1_to_hex(sha1), data->rest);
301+
} else if (opt->cmdmode == 'c') {
302+
enum object_type type;
303+
if (!textconv_object(data->rest, 0100644, sha1,
304+
1, &contents, &size))
305+
contents = read_sha1_file(sha1, &type,
306+
&size);
307+
if (!contents)
308+
die("could not convert '%s' %s",
309+
sha1_to_hex(sha1), data->rest);
310+
} else
311+
die("BUG: invalid cmdmode: %c", opt->cmdmode);
312+
batch_write(opt, contents, size);
313+
free(contents);
314+
} else if (stream_blob_to_fd(1, sha1, NULL, 0) < 0)
243315
die("unable to stream %s to stdout", sha1_to_hex(sha1));
244316
}
245317
else {
@@ -376,6 +448,8 @@ static int batch_objects(struct batch_options *opt)
376448
data.mark_query = 1;
377449
strbuf_expand(&buf, opt->format, expand_format, &data);
378450
data.mark_query = 0;
451+
if (opt->cmdmode)
452+
data.split_on_whitespace = 1;
379453

380454
if (opt->all_objects) {
381455
struct object_info empty;
@@ -440,8 +514,8 @@ static int batch_objects(struct batch_options *opt)
440514
}
441515

442516
static const char * const cat_file_usage[] = {
443-
N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|<type>|--textconv) <object>"),
444-
N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"),
517+
N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|<type>|--textconv|--filters) [--path=<path>] <object>"),
518+
N_("git cat-file (--batch | --batch-check) [--follow-symlinks] [--textconv|--filters]"),
445519
NULL
446520
};
447521

@@ -473,7 +547,7 @@ static int batch_option_callback(const struct option *opt,
473547
int cmd_cat_file(int argc, const char **argv, const char *prefix)
474548
{
475549
int opt = 0;
476-
const char *exp_type = NULL, *obj_name = NULL;
550+
const char *exp_type = NULL, *obj_name = NULL, *force_use_path = NULL;
477551
struct batch_options batch = {0};
478552
int unknown_type = 0;
479553

@@ -486,6 +560,14 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
486560
OPT_CMDMODE('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
487561
OPT_CMDMODE(0, "textconv", &opt,
488562
N_("for blob objects, run textconv on object's content"), 'c'),
563+
OPT_CMDMODE(0, "filters", &opt,
564+
N_("for blob objects, run filters on object's content"), 'w'),
565+
OPT_STRING(0, "path", &force_path, N_("blob"),
566+
N_("use a specific path for --textconv/--filters")),
567+
OPT_CMDMODE(0, "smudge", &opt,
568+
N_("deprecated, use --filters instead"), 'W'),
569+
OPT_STRING(0, "use-path", &force_use_path, N_("blob"),
570+
N_("deprecated, use --path=<path> instead")),
489571
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
490572
N_("allow -s and -t to work with broken/corrupt objects")),
491573
OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),
@@ -507,8 +589,20 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
507589
batch.buffer_output = -1;
508590
argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
509591

592+
if (opt == 'W') {
593+
warning("--smudge is deprecated, please use --filters instead");
594+
opt = 'w';
595+
}
596+
if (force_use_path) {
597+
warning("--use-path=<path> is deprecated, please use "
598+
"--path=<path> instead");
599+
force_path = force_use_path;
600+
}
601+
510602
if (opt) {
511-
if (argc == 1)
603+
if (batch.enabled && (opt == 'c' || opt == 'w'))
604+
batch.cmdmode = opt;
605+
else if (argc == 1)
512606
obj_name = argv[0];
513607
else
514608
usage_with_options(cat_file_usage, options);
@@ -520,14 +614,28 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
520614
} else
521615
usage_with_options(cat_file_usage, options);
522616
}
523-
if (batch.enabled && (opt || argc)) {
524-
usage_with_options(cat_file_usage, options);
617+
if (batch.enabled) {
618+
if (batch.cmdmode != opt || argc)
619+
usage_with_options(cat_file_usage, options);
620+
if (batch.cmdmode && batch.all_objects)
621+
die("--batch-all-objects cannot be combined with "
622+
"--textconv nor with --filters");
525623
}
526624

527625
if ((batch.follow_symlinks || batch.all_objects) && !batch.enabled) {
528626
usage_with_options(cat_file_usage, options);
529627
}
530628

629+
if (force_path && opt != 'c' && opt != 'w') {
630+
error("--path=<path> needs --textconv or --filters");
631+
usage_with_options(cat_file_usage, options);
632+
}
633+
634+
if (force_path && batch.enabled) {
635+
error("--path=<path> incompatible with --batch");
636+
usage_with_options(cat_file_usage, options);
637+
}
638+
531639
if (batch.buffer_output < 0)
532640
batch.buffer_output = batch.all_objects;
533641

t/t8010-cat-file-filters.sh

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/bin/sh
2+
3+
test_description='git cat-file filters support'
4+
. ./test-lib.sh
5+
6+
test_expect_success 'setup ' '
7+
echo "*.txt eol=crlf diff=txt" >.gitattributes &&
8+
echo "hello" | append_cr >world.txt &&
9+
git add .gitattributes world.txt &&
10+
test_tick &&
11+
git commit -m "Initial commit"
12+
'
13+
14+
has_cr () {
15+
tr '\015' Q <"$1" | grep Q >/dev/null
16+
}
17+
18+
test_expect_success 'no filters with `git show`' '
19+
git show HEAD:world.txt >actual &&
20+
! has_cr actual
21+
22+
'
23+
24+
test_expect_success 'no filters with cat-file' '
25+
git cat-file blob HEAD:world.txt >actual &&
26+
! has_cr actual
27+
'
28+
29+
test_expect_success 'cat-file --filters converts to worktree version' '
30+
git cat-file --filters HEAD:world.txt >actual &&
31+
has_cr actual
32+
'
33+
34+
test_expect_success 'cat-file --filters --path=<path> works' '
35+
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
36+
git cat-file --filters --path=world.txt $sha1 >actual &&
37+
has_cr actual
38+
'
39+
40+
test_expect_success 'cat-file --textconv --path=<path> works' '
41+
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
42+
test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
43+
git cat-file --textconv --path=hello.txt $sha1 >rot13 &&
44+
test uryyb = "$(cat rot13 | remove_cr)"
45+
'
46+
47+
test_expect_success '----path=<path> complains without --textconv/--filters' '
48+
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
49+
test_must_fail git cat-file --path=hello.txt blob $sha1 >actual 2>err &&
50+
test ! -s actual &&
51+
grep "path.*needs.*filters" err
52+
'
53+
54+
test_expect_success 'cat-file --textconv --batch works' '
55+
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
56+
test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
57+
printf "%s hello.txt\n%s hello\n" $sha1 $sha1 |
58+
git cat-file --textconv --batch >actual &&
59+
printf "%s blob 6\nuryyb\r\n\n%s blob 6\nhello\n\n" \
60+
$sha1 $sha1 >expect &&
61+
test_cmp expect actual
62+
'
63+
64+
test_done

0 commit comments

Comments
 (0)