Skip to content

Commit 87bf61d

Browse files
committed
cat-file: introduce the --smudge option
As suggested by its name, the --smudge option applies the smudge filter that is currently configured for the specified path. 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. Note that we ensure that symbolic links are unaffected (we know from looking at the mode). Signed-off-by: Johannes Schindelin <[email protected]>
1 parent beb6b98 commit 87bf61d

File tree

3 files changed

+80
-4
lines changed

3 files changed

+80
-4
lines changed

Documentation/git-cat-file.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ 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>
12+
'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv | --smudge ) <object>
1313
'git cat-file' (--batch | --batch-check) [--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+
`--smudge` is used (which imply type "blob").
2121

2222
In the second form, a list of objects (separated by linefeeds) is provided on
2323
stdin, and the SHA-1, type, and size of each object is printed on stdout.
@@ -58,6 +58,11 @@ OPTIONS
5858
order to apply the filter to the content recorded in the index at
5959
<path>.
6060

61+
--smudge::
62+
Show the content as transformed by the smudge filter configured in
63+
the current working tree for the given <path>. In this case,
64+
<object> has to be of the form <tree-ish>:<path>, or :<path>.
65+
6166
--batch::
6267
--batch=<format>::
6368
Print object information and contents for each object provided

builtin/cat-file.c

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,32 @@ struct batch_options {
2020
const char *format;
2121
};
2222

23+
static int smudge_object(const char *path, unsigned mode, unsigned char *sha1,
24+
char **buf, unsigned long *size)
25+
{
26+
enum object_type type;
27+
28+
*buf = read_sha1_file(sha1, &type, size);
29+
if (!*buf)
30+
return error(_("cannot read object %s '%s'"),
31+
sha1_to_hex(sha1), path);
32+
if (type != OBJ_BLOB) {
33+
free(*buf);
34+
return error(_("blob expected for %s '%s'"),
35+
sha1_to_hex(sha1), path);
36+
}
37+
if (S_ISREG(mode)) {
38+
struct strbuf strbuf = STRBUF_INIT;
39+
if (convert_to_working_tree(path, *buf, *size, &strbuf)) {
40+
free(*buf);
41+
*size = strbuf.len;
42+
*buf = strbuf_detach(&strbuf, NULL);
43+
}
44+
}
45+
46+
return 0;
47+
}
48+
2349
static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
2450
int unknown_type)
2551
{
@@ -61,6 +87,15 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
6187
case 'e':
6288
return !has_sha1_file(sha1);
6389

90+
case 'w':
91+
if (!obj_context.path[0])
92+
die("git cat-file --smudge %s: <object> must be <sha1:path>",
93+
obj_name);
94+
95+
if (smudge_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
96+
return -1;
97+
break;
98+
6499
case 'c':
65100
if (!obj_context.path[0])
66101
die("git cat-file --textconv %s: <object> must be <sha1:path>",
@@ -440,7 +475,7 @@ static int batch_objects(struct batch_options *opt)
440475
}
441476

442477
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>"),
478+
N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|<type>|--textconv|--smudge) <object>"),
444479
N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"),
445480
NULL
446481
};
@@ -486,6 +521,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
486521
OPT_CMDMODE('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
487522
OPT_CMDMODE(0, "textconv", &opt,
488523
N_("for blob objects, run textconv on object's content"), 'c'),
524+
OPT_CMDMODE(0, "smudge", &opt,
525+
N_("for blob objects, run smudge filters on object's content"), 'w'),
489526
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
490527
N_("allow -s and -t to work with broken/corrupt objects")),
491528
OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),

t/t8010-cat-file-filters.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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" >.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 --smudge converts to worktree version' '
30+
git cat-file --smudge HEAD:world.txt >actual &&
31+
has_cr actual
32+
'
33+
34+
test_done

0 commit comments

Comments
 (0)