From: Junio C Hamano Date: Wed, 21 Sep 2016 22:15:18 +0000 (-0700) Subject: Merge branch 'js/cat-file-filters' X-Git-Tag: v2.11.0-rc0~138 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/7889ed25ac709624446b0b4d887dd3633e81f44a?ds=inline;hp=-c Merge branch 'js/cat-file-filters' Even though "git hash-objects", which is a tool to take an on-filesystem data stream and put it into the Git object store, allowed to perform the "outside-world-to-Git" conversions (e.g. end-of-line conversions and application of the clean-filter), and it had the feature on by default from very early days, its reverse operation "git cat-file", which takes an object from the Git object store and externalize for the consumption by the outside world, lacked an equivalent mechanism to run the "Git-to-outside-world" conversion. The command learned the "--filters" option to do so. * js/cat-file-filters: cat-file: support --textconv/--filters in batch mode cat-file --textconv/--filters: allow specifying the path separately cat-file: introduce the --filters option cat-file: fix a grammo in the man page --- 7889ed25ac709624446b0b4d887dd3633e81f44a diff --combined builtin/cat-file.c index 3ee9bc9ad1,59dea48dae..94e67ebb7e --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@@ -17,13 -17,38 +17,38 @@@ 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; }; + static const char *force_path; + + static int filter_object(const char *path, unsigned mode, - const unsigned char *sha1, ++ const struct object_id *oid, + char **buf, unsigned long *size) + { + enum object_type type; + - *buf = read_sha1_file(sha1, &type, size); ++ *buf = read_sha1_file(oid->hash, &type, size); + if (!*buf) + return error(_("cannot read object %s '%s'"), - sha1_to_hex(sha1), path); ++ oid_to_hex(oid), 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) { - unsigned char sha1[20]; + struct object_id oid; enum object_type type; char *buf; unsigned long size; @@@ -31,18 -56,24 +56,24 @@@ 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; - if (get_sha1_with_context(obj_name, 0, sha1, &obj_context)) + if (get_sha1_with_context(obj_name, 0, oid.hash, &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': oi.typename = &sb; - if (sha1_object_info_extended(sha1, &oi, flags) < 0) + if (sha1_object_info_extended(oid.hash, &oi, flags) < 0) die("git cat-file: could not get object info"); if (sb.len) { printf("%s\n", sb.buf); @@@ -53,24 -84,35 +84,34 @@@ case 's': oi.sizep = &size; - if (sha1_object_info_extended(sha1, &oi, flags) < 0) + if (sha1_object_info_extended(oid.hash, &oi, flags) < 0) die("git cat-file: could not get object info"); printf("%lu\n", size); return 0; case 'e': - return !has_sha1_file(sha1); + return !has_object_file(&oid); + case 'w': + if (!path[0]) + die("git cat-file --filters %s: must be " + "", obj_name); + + if (filter_object(path, obj_context.mode, - sha1, &buf, &size)) ++ &oid, &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, &oid, 1, &buf, &size)) - if (textconv_object(path, obj_context.mode, - sha1, 1, &buf, &size)) ++ if (textconv_object(path, obj_context.mode, &oid, 1, &buf, &size)) break; case 'p': - type = sha1_object_info(sha1, NULL); + type = sha1_object_info(oid.hash, NULL); if (type < 0) die("Not a valid object name %s", obj_name); @@@ -83,8 -125,8 +124,8 @@@ } if (type == OBJ_BLOB) - return stream_blob_to_fd(1, sha1, NULL, 0); - buf = read_sha1_file(sha1, &type, &size); + return stream_blob_to_fd(1, &oid, NULL, 0); + buf = read_sha1_file(oid.hash, &type, &size); if (!buf) die("Cannot read object %s", obj_name); @@@ -93,19 -135,19 +134,19 @@@ case 0: if (type_from_string(exp_type) == OBJ_BLOB) { - unsigned char blob_sha1[20]; - if (sha1_object_info(sha1, NULL) == OBJ_TAG) { - char *buffer = read_sha1_file(sha1, &type, &size); + struct object_id blob_oid; + if (sha1_object_info(oid.hash, NULL) == OBJ_TAG) { + char *buffer = read_sha1_file(oid.hash, &type, &size); const char *target; if (!skip_prefix(buffer, "object ", &target) || - get_sha1_hex(target, blob_sha1)) - die("%s not a valid tag", sha1_to_hex(sha1)); + get_oid_hex(target, &blob_oid)) + die("%s not a valid tag", oid_to_hex(&oid)); free(buffer); } else - hashcpy(blob_sha1, sha1); + oidcpy(&blob_oid, &oid); - if (sha1_object_info(blob_sha1, NULL) == OBJ_BLOB) - return stream_blob_to_fd(1, blob_sha1, NULL, 0); + if (sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB) + return stream_blob_to_fd(1, &blob_oid, NULL, 0); /* * we attempted to dereference a tag to a blob * and failed; there may be new dereference @@@ -113,7 -155,7 +154,7 @@@ * fall-back to the usual case. */ } - buf = read_object_with_reference(sha1, exp_type, &size, NULL); + buf = read_object_with_reference(oid.hash, exp_type, &size, NULL); break; default: @@@ -128,12 -170,12 +169,12 @@@ } struct expand_data { - unsigned char sha1[20]; + struct object_id oid; enum object_type type; unsigned long size; off_t disk_size; const char *rest; - unsigned char delta_base_sha1[20]; + struct object_id delta_base_oid; /* * If mark_query is true, we do not expand anything, but rather @@@ -176,7 -218,7 +217,7 @@@ static void expand_atom(struct strbuf * if (is_atom("objectname", atom, len)) { if (!data->mark_query) - strbuf_addstr(sb, sha1_to_hex(data->sha1)); + strbuf_addstr(sb, oid_to_hex(&data->oid)); } else if (is_atom("objecttype", atom, len)) { if (data->mark_query) data->info.typep = &data->type; @@@ -199,10 -241,9 +240,10 @@@ strbuf_addstr(sb, data->rest); } else if (is_atom("deltabase", atom, len)) { if (data->mark_query) - data->info.delta_base_sha1 = data->delta_base_sha1; + data->info.delta_base_sha1 = data->delta_base_oid.hash; else - strbuf_addstr(sb, sha1_to_hex(data->delta_base_sha1)); + strbuf_addstr(sb, + oid_to_hex(&data->delta_base_oid)); } else die("unknown format element: %.*s", len, atom); } @@@ -233,28 -274,53 +274,53 @@@ static void batch_write(struct batch_op static void print_object_or_die(struct batch_options *opt, struct expand_data *data) { - const unsigned char *sha1 = data->sha1; + const struct object_id *oid = &data->oid; assert(data->info.typep); if (data->type == OBJ_BLOB) { if (opt->buffer_output) fflush(stdout); - if (stream_blob_to_fd(1, oid, NULL, 0) < 0) + if (opt->cmdmode) { + char *contents; + unsigned long size; + + if (!data->rest) - die("missing path for '%s'", sha1_to_hex(sha1)); ++ die("missing path for '%s'", oid_to_hex(oid)); + + if (opt->cmdmode == 'w') { - if (filter_object(data->rest, 0100644, sha1, ++ if (filter_object(data->rest, 0100644, oid, + &contents, &size)) + die("could not convert '%s' %s", - sha1_to_hex(sha1), data->rest); ++ oid_to_hex(oid), data->rest); + } else if (opt->cmdmode == 'c') { + enum object_type type; - if (!textconv_object(data->rest, 0100644, sha1, ++ if (!textconv_object(data->rest, 0100644, oid, + 1, &contents, &size)) - contents = read_sha1_file(sha1, &type, ++ contents = read_sha1_file(oid->hash, &type, + &size); + if (!contents) + die("could not convert '%s' %s", - sha1_to_hex(sha1), data->rest); ++ oid_to_hex(oid), 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 if (stream_blob_to_fd(1, oid, NULL, 0) < 0) + die("unable to stream %s to stdout", oid_to_hex(oid)); } else { enum object_type type; unsigned long size; void *contents; - contents = read_sha1_file(sha1, &type, &size); + contents = read_sha1_file(oid->hash, &type, &size); if (!contents) - die("object %s disappeared", sha1_to_hex(sha1)); + die("object %s disappeared", oid_to_hex(oid)); if (type != data->type) - die("object %s changed type!?", sha1_to_hex(sha1)); + die("object %s changed type!?", oid_to_hex(oid)); if (data->info.sizep && size != data->size) - die("object %s changed size!?", sha1_to_hex(sha1)); + die("object %s changed size!?", oid_to_hex(oid)); batch_write(opt, contents, size); free(contents); @@@ -267,9 -333,8 +333,9 @@@ static void batch_object_write(const ch struct strbuf buf = STRBUF_INIT; if (!data->skip_object_info && - sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) { - printf("%s missing\n", obj_name ? obj_name : sha1_to_hex(data->sha1)); + sha1_object_info_extended(data->oid.hash, &data->info, LOOKUP_REPLACE_OBJECT) < 0) { + printf("%s missing\n", + obj_name ? obj_name : oid_to_hex(&data->oid)); fflush(stdout); return; } @@@ -292,7 -357,7 +358,7 @@@ static void batch_one_object(const cha int flags = opt->follow_symlinks ? GET_SHA1_FOLLOW_SYMLINKS : 0; enum follow_symlinks_result result; - result = get_sha1_with_context(obj_name, flags, data->sha1, &ctx); + result = get_sha1_with_context(obj_name, flags, data->oid.hash, &ctx); if (result != FOUND) { switch (result) { case MISSING_OBJECT: @@@ -338,7 -403,7 +404,7 @@@ struct object_cb_data static void batch_object_cb(const unsigned char sha1[20], void *vdata) { struct object_cb_data *data = vdata; - hashcpy(data->expand->sha1, sha1); + hashcpy(data->expand->oid.hash, sha1); batch_object_write(NULL, data->opt, data->expand); } @@@ -378,6 -443,8 +444,8 @@@ static int batch_objects(struct batch_o 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; @@@ -442,8 -509,8 +510,8 @@@ } 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 (--batch | --batch-check) [--follow-symlinks]"), - 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] [--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] [--textconv | --filters]"), NULL }; @@@ -488,6 -555,10 +556,10 @@@ int cmd_cat_file(int argc, const char * 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_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")), @@@ -510,7 -581,9 +582,9 @@@ 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); @@@ -522,14 -595,28 +596,28 @@@ } 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) { 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 (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;