From b9e62f60115c75c5be5de593862925c8b8d7e683 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Aug 2016 14:23:39 +0200 Subject: 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; the contents are written out as-is for other types of objects. 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 Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'builtin') diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 2dfe6265f7..96007cb1a0 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -20,6 +20,28 @@ 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) && 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 +83,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 +472,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 +518,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")), -- cgit v1.2.3 From 7bcf341453572a227036afec76bdab451b798870 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 9 Sep 2016 12:10:50 +0200 Subject: 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 Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'builtin') diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 96007cb1a0..51f4c546ee 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) @@ -53,6 +55,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; @@ -60,6 +63,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; + if (obj_context.mode == S_IFINVALID) + obj_context.mode = 0100644; + buf = NULL; switch (opt) { case 't': @@ -84,21 +92,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': @@ -472,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|--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 }; @@ -520,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, "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")), @@ -562,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("--path= needs --textconv or --filters"); + usage_with_options(cat_file_usage, options); + } + if (batch.buffer_output < 0) batch.buffer_output = batch.all_objects; -- cgit v1.2.3 From 321459439e19517c412cab1cfbb64a2749f272c9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 9 Sep 2016 12:10:54 +0200 Subject: 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 Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) (limited to 'builtin') diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 51f4c546ee..59dea48dae 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; }; @@ -280,7 +281,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 { @@ -417,6 +443,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; @@ -482,7 +510,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 }; @@ -553,7 +581,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); @@ -565,8 +595,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) { @@ -578,6 +612,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; -- cgit v1.2.3