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 <johannes.schindelin@gmx.de>
This commit is contained in:
Johannes Schindelin
2016-08-08 17:48:57 +02:00
parent beb6b985eb
commit 87bf61d41c
3 changed files with 80 additions and 4 deletions

View File

@@ -9,15 +9,15 @@ git-cat-file - Provide content or type and size information for repository objec
SYNOPSIS
--------
[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 | --smudge ) <object>
'git cat-file' (--batch | --batch-check) [--follow-symlinks]
DESCRIPTION
-----------
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
object type, or `-s` is used to find the object size, or `--textconv` is used
(which implies type "blob").
object type, or `-s` is used to find the object size, or `--textconv` or
`--smudge` is used (which imply type "blob").
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.
@@ -58,6 +58,11 @@ OPTIONS
order to apply the filter to the content recorded in the index at
<path>.
--smudge::
Show the content as transformed by the smudge filter configured in
the current working tree for the given <path>. In this case,
<object> has to be of the form <tree-ish>:<path>, or :<path>.
--batch::
--batch=<format>::
Print object information and contents for each object provided

View File

@@ -20,6 +20,32 @@ struct batch_options {
const char *format;
};
static int smudge_object(const char *path, unsigned mode, 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) {
free(*buf);
return error(_("blob expected for %s '%s'"),
sha1_to_hex(sha1), path);
}
if (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,
int unknown_type)
{
@@ -61,6 +87,15 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
case 'e':
return !has_sha1_file(sha1);
case 'w':
if (!obj_context.path[0])
die("git cat-file --smudge %s: <object> must be <sha1:path>",
obj_name);
if (smudge_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
return -1;
break;
case 'c':
if (!obj_context.path[0])
die("git cat-file --textconv %s: <object> must be <sha1:path>",
@@ -440,7 +475,7 @@ static int batch_objects(struct batch_options *opt)
}
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|--smudge) <object>"),
N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"),
NULL
};
@@ -486,6 +521,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(0, "textconv", &opt,
N_("for blob objects, run textconv on object's content"), 'c'),
OPT_CMDMODE(0, "smudge", &opt,
N_("for blob objects, run smudge filters on object's content"), 'w'),
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
N_("allow -s and -t to work with broken/corrupt objects")),
OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),

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" >.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 --smudge converts to worktree version' '
git cat-file --smudge HEAD:world.txt >actual &&
has_cr actual
'
test_done