From 346abf01a2b8f108386ed541368c25dfd4c7e352 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 9 Aug 2016 09:47:51 +0200 Subject: [PATCH 1/5] 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 d96fc1da97fe3f2ef966a3e479fdc7be8313cd86 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 8 Aug 2016 17:48:57 +0200 Subject: [PATCH 2/5] 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. 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 --- Documentation/git-cat-file.txt | 12 +++++++--- builtin/cat-file.c | 41 +++++++++++++++++++++++++++++++++- t/t8010-cat-file-filters.sh | 34 ++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) create mode 100755 t/t8010-cat-file-filters.sh diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index 071029b4ef..537d02c87c 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 | --filters ) '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 +`--filters` 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,12 @@ OPTIONS order to apply the filter to the content recorded in the index at . +--filters:: + Show the content as converted by the filters configured in + the current working tree for the given (i.e. smudge filters, + end-of-line conversion, etc). 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..0b74afaaf2 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -20,6 +20,33 @@ struct batch_options { 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) { + 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 +88,16 @@ 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 --filters %s: must be " + "", obj_name); + + if (filter_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 +477,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|--filters) "), N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"), NULL }; @@ -486,6 +523,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, "filters", &opt, + N_("for blob objects, run 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 100755 index 0000000000..e466634732 --- /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 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 From 6f44437016af4dd61070e63f84616909b24735ac Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 8 Aug 2016 17:48:57 +0200 Subject: [PATCH 3/5] cat-file --textconv/--filters: 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 name of the containing tree. 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 --filters. 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 --path=main.c 0f1937fd Signed-off-by: Johannes Schindelin --- Documentation/git-cat-file.txt | 7 ++++++- builtin/cat-file.c | 26 +++++++++++++++++++++----- t/t8010-cat-file-filters.sh | 20 ++++++++++++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index 537d02c87c..4fa9041490 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 | --filters ) +'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | | --textconv | --filters ) [--path=] 'git cat-file' (--batch | --batch-check) [--follow-symlinks] DESCRIPTION @@ -64,6 +64,11 @@ OPTIONS end-of-line conversion, etc). In this case, has to be of the form :, or :. +--path=:: + For use with --textconv or --filters, 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 0b74afaaf2..2c799acb29 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 filter_object(const char *path, unsigned mode, const unsigned char *sha1, char **buf, unsigned long *size) @@ -58,6 +60,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, struct object_info oi = {NULL}; struct strbuf sb = STRBUF_INIT; unsigned flags = LOOKUP_REPLACE_OBJECT; + const char *path = force_path; if (unknown_type) flags |= LOOKUP_UNKNOWN_OBJECT; @@ -65,6 +68,11 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, if (get_sha1_with_context(obj_name, 0, sha1, &obj_context)) die("Not a valid object name %s", obj_name); + if (!path) + path = obj_context.path; + else if (obj_context.mode == S_IFINVALID) + obj_context.mode = 0100644; + buf = NULL; switch (opt) { case 't': @@ -89,21 +97,22 @@ 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 (!path[0]) die("git cat-file --filters %s: must be " "", obj_name); - if (filter_object(obj_context.path, obj_context.mode, + if (filter_object(path, obj_context.mode, sha1, &buf, &size)) return -1; break; case 'c': - if (!obj_context.path[0]) + if (!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(path, obj_context.mode, + sha1, 1, &buf, &size)) break; case 'p': @@ -477,7 +486,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|--filters) "), + N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p||--textconv|--filters) [--path=] "), N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"), NULL }; @@ -525,6 +534,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, "filters", &opt, N_("for blob objects, run filters on object's content"), 'w'), + OPT_STRING(0, "path", &force_path, N_("blob"), + N_("use a specific path for --textconv/--filters")), 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")), @@ -567,6 +578,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("--path= needs --textconv or --filters"); + 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 e466634732..0d5c33e29c 100755 --- a/t/t8010-cat-file-filters.sh +++ b/t/t8010-cat-file-filters.sh @@ -31,4 +31,24 @@ test_expect_success 'cat-file --filters converts to worktree version' ' has_cr actual ' +test_expect_success 'cat-file --filters --path= works' ' + sha1=$(git rev-parse -q --verify HEAD:world.txt) && + git cat-file --filters --path=world.txt $sha1 >actual && + has_cr actual +' + +test_expect_success 'cat-file --textconv --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 --path=hello.txt $sha1 >rot13 && + test uryyb = "$(cat rot13 | remove_cr)" +' + +test_expect_success '----path= complains without --textconv/--filters' ' + sha1=$(git rev-parse -q --verify HEAD:world.txt) && + test_must_fail git cat-file --path=hello.txt blob $sha1 >actual 2>err && + test ! -s actual && + grep "path.*needs.*filters" err +' + test_done From 6f9f15356e056a34653847ddac962a7b2218464c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 17 Aug 2016 18:04:57 +0200 Subject: [PATCH 4/5] cat-file: support --textconv/--filters in batch mode With this patch, --batch can be combined with --textconv or --filters. For this to work, the input needs to have the form so that the filters can be chosen appropriately. Signed-off-by: Johannes Schindelin --- Documentation/git-cat-file.txt | 18 +++++++++---- builtin/cat-file.c | 49 ++++++++++++++++++++++++++++++---- t/t8010-cat-file-filters.sh | 10 +++++++ 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index 4fa9041490..204541c690 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | | --textconv | --filters ) [--path=] -'git cat-file' (--batch | --batch-check) [--follow-symlinks] +'git cat-file' (--batch | --batch-check) [ --textconv | --filters ] [--follow-symlinks] DESCRIPTION ----------- @@ -20,7 +20,11 @@ object type, or `-s` is used to find the object size, or `--textconv` or `--filters` 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. +stdin, and the SHA-1, type, and size of each object is printed on stdout. The +output format can be overridden using the optional `` argument. If +either `--textconv` or `--filters` was specified, the input is expected to +list the object names followed by the path name, separated by a single white +space, so that the appropriate drivers can be determined. OPTIONS ------- @@ -72,13 +76,17 @@ OPTIONS --batch:: --batch=:: Print object information and contents for each object provided - on stdin. May not be combined with any other options or arguments. - See the section `BATCH OUTPUT` below for details. + on stdin. May not be combined with any other options or arguments + except `--textconv` or `--filters`, in which case the input lines + also need to specify the path, separated by white space. See the + section `BATCH OUTPUT` below for details. --batch-check:: --batch-check=:: Print object information for each object provided on stdin. May - not be combined with any other options or arguments. See the + not be combined with any other options or arguments except + `--textconv` or `--filters`, in which case the input lines also + need to specify the path, separated by white space. See the section `BATCH OUTPUT` below for details. --batch-all-objects:: diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 2c799acb29..f8a3a081ee 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -17,6 +17,7 @@ struct batch_options { int print_contents; int buffer_output; int all_objects; + int cmdmode; /* may be 'w' or 'c' for --filters or --textconv */ const char *format; }; @@ -285,7 +286,32 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d if (data->type == OBJ_BLOB) { if (opt->buffer_output) fflush(stdout); - if (stream_blob_to_fd(1, sha1, NULL, 0) < 0) + if (opt->cmdmode) { + char *contents; + unsigned long size; + + if (!data->rest) + die("missing path for '%s'", sha1_to_hex(sha1)); + + if (opt->cmdmode == 'w') { + if (filter_object(data->rest, 0100644, sha1, + &contents, &size)) + die("could not convert '%s' %s", + sha1_to_hex(sha1), data->rest); + } else if (opt->cmdmode == 'c') { + enum object_type type; + if (!textconv_object(data->rest, 0100644, sha1, + 1, &contents, &size)) + contents = read_sha1_file(sha1, &type, + &size); + if (!contents) + die("could not convert '%s' %s", + sha1_to_hex(sha1), data->rest); + } else + die("BUG: invalid cmdmode: %c", opt->cmdmode); + batch_write(opt, contents, size); + free(contents); + } else if (stream_blob_to_fd(1, sha1, NULL, 0) < 0) die("unable to stream %s to stdout", sha1_to_hex(sha1)); } else { @@ -422,6 +448,8 @@ static int batch_objects(struct batch_options *opt) data.mark_query = 1; strbuf_expand(&buf, opt->format, expand_format, &data); data.mark_query = 0; + if (opt->cmdmode) + data.split_on_whitespace = 1; if (opt->all_objects) { struct object_info empty; @@ -487,7 +515,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|--filters) [--path=] "), - N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"), + N_("git cat-file (--batch | --batch-check) [--follow-symlinks] [--textconv|--filters]"), NULL }; @@ -558,7 +586,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0); if (opt) { - if (argc == 1) + if (batch.enabled && (opt == 'c' || opt == 'w')) + batch.cmdmode = opt; + else if (argc == 1) obj_name = argv[0]; else usage_with_options(cat_file_usage, options); @@ -570,8 +600,12 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) } else usage_with_options(cat_file_usage, options); } - if (batch.enabled && (opt || argc)) { - usage_with_options(cat_file_usage, options); + if (batch.enabled) { + if (batch.cmdmode != opt || argc) + usage_with_options(cat_file_usage, options); + if (batch.cmdmode && batch.all_objects) + die("--batch-all-objects cannot be combined with " + "--textconv nor with --filters"); } if ((batch.follow_symlinks || batch.all_objects) && !batch.enabled) { @@ -583,6 +617,11 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) usage_with_options(cat_file_usage, options); } + if (force_path && batch.enabled) { + error("--path= incompatible with --batch"); + 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 0d5c33e29c..acdfa09ffb 100755 --- a/t/t8010-cat-file-filters.sh +++ b/t/t8010-cat-file-filters.sh @@ -51,4 +51,14 @@ test_expect_success '----path= complains without --textconv/--filters' ' grep "path.*needs.*filters" err ' +test_expect_success 'cat-file --textconv --batch works' ' + sha1=$(git rev-parse -q --verify HEAD:world.txt) && + test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" && + printf "%s hello.txt\n%s hello\n" $sha1 $sha1 | + git cat-file --textconv --batch >actual && + printf "%s blob 6\nuryyb\r\n\n%s blob 6\nhello\n\n" \ + $sha1 $sha1 >expect && + test_cmp expect actual +' + test_done From dd9a1c8027d7a0ba7e98986c95b7bb93dd31b3c6 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Aug 2016 14:03:50 +0200 Subject: [PATCH 5/5] cat-file: reintroduce deprecated --smudge and --use-path options For backwards compatibility with Git for Windows v2.9.3, we need to support the --smudge and --use-path options (the options are now called --filters and --path). Let's do that, but print out a honking warning that these options are deprecated. The plan is to drop them in v2.10.0 anyway. Signed-off-by: Johannes Schindelin --- builtin/cat-file.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index f8a3a081ee..a4cc57e2a3 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -547,7 +547,7 @@ static int batch_option_callback(const struct option *opt, int cmd_cat_file(int argc, const char **argv, const char *prefix) { int opt = 0; - const char *exp_type = NULL, *obj_name = NULL; + const char *exp_type = NULL, *obj_name = NULL, *force_use_path = NULL; struct batch_options batch = {0}; int unknown_type = 0; @@ -564,6 +564,10 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) N_("for blob objects, run filters on object's content"), 'w'), OPT_STRING(0, "path", &force_path, N_("blob"), N_("use a specific path for --textconv/--filters")), + OPT_CMDMODE(0, "smudge", &opt, + N_("deprecated, use --filters instead"), 'W'), + OPT_STRING(0, "use-path", &force_use_path, N_("blob"), + N_("deprecated, use --path= instead")), 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")), @@ -585,6 +589,16 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) batch.buffer_output = -1; argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0); + if (opt == 'W') { + warning("--smudge is deprecated, please use --filters instead"); + opt = 'w'; + } + if (force_use_path) { + warning("--use-path= is deprecated, please use " + "--path= instead"); + force_path = force_use_path; + } + if (opt) { if (batch.enabled && (opt == 'c' || opt == 'w')) batch.cmdmode = opt;