From 383705a9e8797aecb0fcdf8373c03cc08ca05e17 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 9 Aug 2016 09:47:51 +0200 Subject: [PATCH 1/3] cat-file: fix a grammo in the man page "... has be ..." -> "... has to be ..." Signed-off-by: Johannes Schindelin --- Documentation/git-cat-file.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index 18d03d8e8b..071029b4ef 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -54,8 +54,9 @@ OPTIONS --textconv:: Show the content as transformed by a textconv filter. In this case, - has be of the form :, or : in order - to apply the filter to the content recorded in the index at . + has to be of the form :, or : in + order to apply the filter to the content recorded in the index at + . --batch:: --batch=:: From c6b63f7572396130e072f3c3c73e265e6b34757a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 8 Aug 2016 17:48:57 +0200 Subject: [PATCH 2/3] 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 --- Documentation/git-cat-file.txt | 11 +++++++--- builtin/cat-file.c | 39 +++++++++++++++++++++++++++++++++- t/t8010-cat-file-filters.sh | 34 +++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 t/t8010-cat-file-filters.sh diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index 071029b4ef..b7e3623183 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -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 | | --textconv ) +'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | | --textconv | --smudge ) '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 . +--smudge:: + Show the content as transformed by the smudge filter configured in + the current working tree for the given . In this case, + has to be of the form :, or :. + --batch:: --batch=:: Print object information and contents for each object provided diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 2dfe6265f7..bb32f2dc5b 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -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: must be ", + 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: must be ", @@ -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||--textconv) "), + N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p||--textconv|--smudge) "), 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")), diff --git a/t/t8010-cat-file-filters.sh b/t/t8010-cat-file-filters.sh new file mode 100644 index 0000000000..e37e5f5589 --- /dev/null +++ b/t/t8010-cat-file-filters.sh @@ -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 From 213349abe76511264ffc89a5bbb0de4fe4250261 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 12 Aug 2016 15:22:55 +0200 Subject: [PATCH 3/3] cat-file --textconv/--smudge: allow specifying the path separately There are circumstances when it is relatively easy to figure out the object name for a given path, but not the revision. For example, when looking at a diff generated by Git, the object names are recorded, but not the revision. As a matter of fact, the revisions from which the diff was generated may not even exist locally. In such a case, the user would have to generate a fake revision just to be able to use --textconv or --smudge. Let's simplify this dramatically, because we do not really need that revision at all: all we care about is that we know the path. In the scenario described above, we do know the path, and we just want to specify it separately from the object name. Example usage: git cat-file --textconv --use-path=main.c 0f1937fd Signed-off-by: Johannes Schindelin --- Documentation/git-cat-file.txt | 7 ++++++- builtin/cat-file.c | 23 ++++++++++++++++++----- t/t8010-cat-file-filters.sh | 15 ++++++++++++++- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index b7e3623183..2d3703f786 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -9,7 +9,7 @@ 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 | | --textconv | --smudge ) +'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | | --textconv | --smudge ) [--use-path=] 'git cat-file' (--batch | --batch-check) [--follow-symlinks] DESCRIPTION @@ -63,6 +63,11 @@ OPTIONS the current working tree for the given . In this case, has to be of the form :, or :. +--use-path=:: + For use with --textconv or --smudge, to allow specifying an object + name and a path separately, e.g. when it is difficult to figure out + the revision from which the blob came. + --batch:: --batch=:: Print object information and contents for each object provided diff --git a/builtin/cat-file.c b/builtin/cat-file.c index bb32f2dc5b..62f4e09559 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -20,6 +20,8 @@ struct batch_options { const char *format; }; +static const char *force_path; + static int smudge_object(const char *path, unsigned mode, unsigned char *sha1, char **buf, unsigned long *size) { @@ -88,20 +90,24 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, return !has_sha1_file(sha1); case 'w': - if (!obj_context.path[0]) + if (!force_path && !obj_context.path[0]) die("git cat-file --smudge %s: must be ", obj_name); - if (smudge_object(obj_context.path, obj_context.mode, sha1, &buf, &size)) + if (smudge_object(force_path ? force_path : obj_context.path, + force_path ? 0100644 : obj_context.mode, + sha1, &buf, &size)) return -1; break; case 'c': - if (!obj_context.path[0]) + if (!force_path && !obj_context.path[0]) die("git cat-file --textconv %s: must be ", obj_name); - if (textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size)) + if (textconv_object(force_path ? force_path : obj_context.path, + force_path ? 0100644 : obj_context.mode, + sha1, 1, &buf, &size)) break; case 'p': @@ -475,7 +481,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||--textconv|--smudge) "), + N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p||--textconv|--smudge) [--use-path=] "), N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"), NULL }; @@ -523,6 +529,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) 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_STRING(0, "use-path", &force_path, N_("blob"), + N_("use a specific path for --textconv/--smudge")), 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")), @@ -565,6 +573,11 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) usage_with_options(cat_file_usage, options); } + if (force_path && opt != 'c' && opt != 'w') { + error("--use-path= needs --textconv or --smudge"); + usage_with_options(cat_file_usage, options); + } + if (batch.buffer_output < 0) batch.buffer_output = batch.all_objects; diff --git a/t/t8010-cat-file-filters.sh b/t/t8010-cat-file-filters.sh index e37e5f5589..ac1cc37652 100644 --- a/t/t8010-cat-file-filters.sh +++ b/t/t8010-cat-file-filters.sh @@ -4,7 +4,7 @@ test_description='git cat-file filters support' . ./test-lib.sh test_expect_success 'setup ' ' - echo "*.txt eol=crlf" >.gitattributes && + echo "*.txt eol=crlf diff=txt" >.gitattributes && echo "hello" | append_cr >world.txt && git add .gitattributes world.txt && test_tick && @@ -31,4 +31,17 @@ test_expect_success 'cat-file --smudge converts to worktree version' ' has_cr actual ' +test_expect_success 'cat-file --smudge --use-path= works' ' + sha1=$(git rev-parse -q --verify HEAD:world.txt) && + git cat-file --smudge --use-path=world.txt $sha1 >actual && + has_cr actual +' + +test_expect_success 'cat-file --textconv --use-path= works' ' + sha1=$(git rev-parse -q --verify HEAD:world.txt) && + test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" && + git cat-file --textconv --use-path=hello.txt $sha1 >rot13 && + test uryyb = "$(cat rot13)" +' + test_done