From 4bd52d0956f5ba2a922968057cabd77a98f6da5c Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Fri, 10 May 2013 17:10:10 +0200 Subject: [PATCH 1/7] t4030: demonstrate behavior of show with textconv "git show " honors the --textconv option while "git show " does not. Demonstrate this in the test. Since the current behavior is supposed to stay as is, we expect the default for "git show " to remain --no-textconv. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- t/t4030-diff-textconv.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh index 53ec330ce8..3950fc9858 100755 --- a/t/t4030-diff-textconv.sh +++ b/t/t4030-diff-textconv.sh @@ -58,6 +58,12 @@ test_expect_success 'diff produces text' ' test_cmp expect.text actual ' +test_expect_success 'show commit produces text' ' + git show HEAD >diff && + find_diff actual && + test_cmp expect.text actual +' + test_expect_success 'diff-tree produces binary' ' git diff-tree -p HEAD^ HEAD >diff && find_diff actual && @@ -84,6 +90,24 @@ test_expect_success 'status -v produces text' ' git reset --soft HEAD@{1} ' +test_expect_success 'show blob produces binary' ' + git show HEAD:file >actual && + printf "\\0\\n\\01\\n" >expect && + test_cmp expect actual +' + +test_expect_failure 'show --textconv blob produces text' ' + git show --textconv HEAD:file >actual && + printf "0\\n1\\n" >expect && + test_cmp expect actual +' + +test_success 'show --no-textconv blob produces binary' ' + git show --textconv HEAD:file >actual && + printf "\\0\\n\\01\\n" >expect && + test_cmp expect actual +' + test_expect_success 'grep-diff (-G) operates on textconv data (add)' ' echo one >expect && git log --root --format=%s -G0 >actual && From 6c374008b1a4e3b66469168aace47003e9771e2d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 10 May 2013 17:10:11 +0200 Subject: [PATCH 2/7] diff_opt: track whether flags have been set explicitly The diff_opt infrastructure sets flags based on defaults and command line options. It is impossible to tell whether a flag has been set as a default or on explicit request. Update the structure so that this detection is possible: * Add an extra "opt->touched_flags" that keeps track of all the fields that have been touched by DIFF_OPT_SET and DIFF_OPT_CLR. * You may continue setting the default values to the flags, like commands in the "log" family do in cmd_log_init_defaults(), but after you finished setting the defaults, you clear the touched_flags field; * And then you let the usual callchain call diff_opt_parse(), allowing the opt->flags be set or unset, while keeping track of which bits the user touched; * There is an optional callback "opt->set_default" that is called at the very beginning to let you inspect touched_flags and update opt->flags appropriately, before the remainder of the diffcore machinery is set up, taking the opt->flags value into account. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- Documentation/technical/api-diff.txt | 10 +++++++++- builtin/log.c | 1 + diff.c | 3 +++ diff.h | 8 ++++++-- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Documentation/technical/api-diff.txt b/Documentation/technical/api-diff.txt index 2d2ebc04b7..8b001de0db 100644 --- a/Documentation/technical/api-diff.txt +++ b/Documentation/technical/api-diff.txt @@ -28,7 +28,8 @@ Calling sequence * Call `diff_setup_done()`; this inspects the options set up so far for internal consistency and make necessary tweaking to it (e.g. if - textual patch output was asked, recursive behaviour is turned on). + textual patch output was asked, recursive behaviour is turned on); + the callback set_default in diff_options can be used to tweak this more. * As you find different pairs of files, call `diff_change()` to feed modified files, `diff_addremove()` to feed created or deleted files, @@ -115,6 +116,13 @@ Notable members are: operation, but some do not have anything to do with the diffcore library. +`touched_flags`:: + Records whether a flag has been changed due to user request + (rather than just set/unset by default). + +`set_default`:: + Callback which allows tweaking the options in diff_setup_done(). + BINARY, TEXT;; Affects the way how a file that is seemingly binary is treated. diff --git a/builtin/log.c b/builtin/log.c index ad46f72950..3abbc668a7 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -91,6 +91,7 @@ static void cmd_log_init_defaults(struct rev_info *rev) if (default_date_mode) rev->date_mode = parse_date_format(default_date_mode); + rev->diffopt.touched_flags = 0; } static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, diff --git a/diff.c b/diff.c index f0b3e7cfe3..7c248726ad 100644 --- a/diff.c +++ b/diff.c @@ -3213,6 +3213,9 @@ void diff_setup_done(struct diff_options *options) { int count = 0; + if (options->set_default) + options->set_default(options); + if (options->output_format & DIFF_FORMAT_NAME) count++; if (options->output_format & DIFF_FORMAT_NAME_STATUS) diff --git a/diff.h b/diff.h index 78b4091dd5..e995ae12fe 100644 --- a/diff.h +++ b/diff.h @@ -87,8 +87,9 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data) #define DIFF_OPT_PICKAXE_IGNORE_CASE (1 << 30) #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) -#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) -#define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag) +#define DIFF_OPT_TOUCHED(opts, flag) ((opts)->touched_flags & DIFF_OPT_##flag) +#define DIFF_OPT_SET(opts, flag) (((opts)->flags |= DIFF_OPT_##flag),((opts)->touched_flags |= DIFF_OPT_##flag)) +#define DIFF_OPT_CLR(opts, flag) (((opts)->flags &= ~DIFF_OPT_##flag),((opts)->touched_flags |= DIFF_OPT_##flag)) #define DIFF_XDL_TST(opts, flag) ((opts)->xdl_opts & XDF_##flag) #define DIFF_XDL_SET(opts, flag) ((opts)->xdl_opts |= XDF_##flag) #define DIFF_XDL_CLR(opts, flag) ((opts)->xdl_opts &= ~XDF_##flag) @@ -109,6 +110,7 @@ struct diff_options { const char *single_follow; const char *a_prefix, *b_prefix; unsigned flags; + unsigned touched_flags; int use_color; int context; int interhunkcontext; @@ -145,6 +147,8 @@ struct diff_options { /* to support internal diff recursion by --follow hack*/ int found_follow; + void (*set_default)(struct diff_options *); + FILE *file; int close_file; From 083b99310978c6768d8e954c9bab1d3692a515a1 Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Fri, 10 May 2013 17:10:12 +0200 Subject: [PATCH 3/7] show: honor --textconv for blobs Currently, "diff" and "cat-file" for blobs honor "--textconv" options (with the former defaulting to "--textconv" and the latter to "--no-textconv") whereas "show" does not honor this option, even though it takes diff options. Make "show" on blobs honor "--textconv" when it is asked. The default is not to apply textconv, which is in line with what "cat-file" does. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- builtin/log.c | 25 ++++++++++++++++++++++--- t/t4030-diff-textconv.sh | 6 +++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index 3abbc668a7..815c5b84ed 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -406,10 +406,29 @@ static void show_tagger(char *buf, int len, struct rev_info *rev) strbuf_release(&out); } -static int show_blob_object(const unsigned char *sha1, struct rev_info *rev) +static int show_blob_object(const unsigned char *sha1, struct rev_info *rev, const char *obj_name) { + unsigned char sha1c[20]; + struct object_context obj_context; + char *buf; + unsigned long size; + fflush(stdout); - return stream_blob_to_fd(1, sha1, NULL, 0); + if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) || + !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV)) + return stream_blob_to_fd(1, sha1, NULL, 0); + + if (get_sha1_with_context(obj_name, 0, sha1c, &obj_context)) + die("Not a valid object name %s", obj_name); + if (!obj_context.path[0] || + !textconv_object(obj_context.path, obj_context.mode, sha1c, 1, &buf, &size)) + return stream_blob_to_fd(1, sha1, NULL, 0); + + if (!buf) + die("git show %s: bad file", obj_name); + + write_or_die(1, buf, size); + return 0; } static int show_tag_object(const unsigned char *sha1, struct rev_info *rev) @@ -495,7 +514,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) const char *name = objects[i].name; switch (o->type) { case OBJ_BLOB: - ret = show_blob_object(o->sha1, NULL); + ret = show_blob_object(o->sha1, &rev, name); break; case OBJ_TAG: { struct tag *t = (struct tag *)o; diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh index 3950fc9858..0ebb028ccc 100755 --- a/t/t4030-diff-textconv.sh +++ b/t/t4030-diff-textconv.sh @@ -96,14 +96,14 @@ test_expect_success 'show blob produces binary' ' test_cmp expect actual ' -test_expect_failure 'show --textconv blob produces text' ' +test_expect_success 'show --textconv blob produces text' ' git show --textconv HEAD:file >actual && printf "0\\n1\\n" >expect && test_cmp expect actual ' -test_success 'show --no-textconv blob produces binary' ' - git show --textconv HEAD:file >actual && +test_expect_success 'show --no-textconv blob produces binary' ' + git show --no-textconv HEAD:file >actual && printf "\\0\\n\\01\\n" >expect && test_cmp expect actual ' From 3ac21617b0f56dd76be3bbaa663bf0fff7f3095d Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Fri, 10 May 2013 17:10:13 +0200 Subject: [PATCH 4/7] cat-file: do not die on --textconv without textconv filters When a command is supposed to use textconv filters (by default or with "--textconv") and none are configured then the blob is output without conversion; the only exception to this rule is "cat-file --textconv". Make it behave like the rest of textconv aware commands. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 18 ++++++++---------- t/t8007-cat-file-textconv.sh | 20 +++++--------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 40f87b4649..fbaa1213f1 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -115,6 +115,14 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) case 'e': return !has_sha1_file(sha1); + case 'c': + if (!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)) + break; + case 'p': type = sha1_object_info(sha1, NULL); if (type < 0) @@ -141,16 +149,6 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) /* otherwise just spit out the data */ break; - case 'c': - if (!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)) - die("git cat-file --textconv: unable to run textconv on %s", - obj_name); - break; - case 0: if (type_from_string(exp_type) == OBJ_BLOB) { unsigned char blob_sha1[20]; diff --git a/t/t8007-cat-file-textconv.sh b/t/t8007-cat-file-textconv.sh index 78a0085e64..83c663678f 100755 --- a/t/t8007-cat-file-textconv.sh +++ b/t/t8007-cat-file-textconv.sh @@ -22,11 +22,11 @@ test_expect_success 'setup ' ' ' cat >expected <result + git cat-file --textconv :one.bin >result && test_cmp expected result ' @@ -36,10 +36,6 @@ test_expect_success 'setup textconv filters' ' git config diff.test.cachetextconv false ' -cat >expected <result && test_cmp expected result @@ -73,25 +69,19 @@ test_expect_success 'cat-file --textconv on previous commit' ' ' test_expect_success SYMLINKS 'cat-file without --textconv (symlink)' ' + printf "%s" "one.bin" >expected && git cat-file blob :symlink.bin >result && - printf "%s" "one.bin" >expected test_cmp expected result ' test_expect_success SYMLINKS 'cat-file --textconv on index (symlink)' ' - ! git cat-file --textconv :symlink.bin 2>result && - cat >expected <<\EOF && -fatal: git cat-file --textconv: unable to run textconv on :symlink.bin -EOF + git cat-file --textconv :symlink.bin >result && test_cmp expected result ' test_expect_success SYMLINKS 'cat-file --textconv on HEAD (symlink)' ' - ! git cat-file --textconv HEAD:symlink.bin 2>result && - cat >expected <result && test_cmp expected result ' From 97f6a9c975bb14688b60a5134069159b4cae1a07 Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Fri, 10 May 2013 17:10:14 +0200 Subject: [PATCH 5/7] t7008: demonstrate behavior of grep with textconv Currently, "git grep" does not honor any textconv filters, with nor without --textconv. Demonstrate this in the tests. The default is expected to remain unchanged. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- t/t7008-grep-binary.sh | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh index 26f831984d..1c0946f844 100755 --- a/t/t7008-grep-binary.sh +++ b/t/t7008-grep-binary.sh @@ -145,4 +145,35 @@ test_expect_success 'grep respects not-binary diff attribute' ' test_cmp expect actual ' +cat >nul_to_q_textconv <<'EOF' +#!/bin/sh +"$PERL_PATH" -pe 'y/\000/Q/' < "$1" +EOF +chmod +x nul_to_q_textconv + +test_expect_success 'setup textconv filters' ' + echo a diff=foo >.gitattributes && + git config diff.foo.textconv "\"$(pwd)\""/nul_to_q_textconv +' + +test_expect_success 'grep does not honor textconv' ' + test_must_fail git grep Qfile +' + +test_expect_failure 'grep --textconv honors textconv' ' + echo "a:binaryQfile" >expect && + git grep --textconv Qfile >actual && + test_cmp expect actual +' + +test_expect_success 'grep --no-textconv does not honor textconv' ' + test_must_fail git grep --no-textconv Qfile +' + +test_expect_failure 'grep --textconv blob honors textconv' ' + echo "HEAD:a:binaryQfile" >expect && + git grep --textconv Qfile HEAD:a >actual && + test_cmp expect actual +' + test_done From 335ec3bf411ae6606b58885deb281192c86651c6 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 10 May 2013 17:10:15 +0200 Subject: [PATCH 6/7] grep: allow to use textconv filters Recently and not so recently, we made sure that log/grep type operations use textconv filters when a userfacing diff would do the same: ef90ab6 (pickaxe: use textconv for -S counting, 2012-10-28) b1c2f57 (diff_grep: use textconv buffers for add/deleted files, 2012-10-28) 0508fe5 (combine-diff: respect textconv attributes, 2011-05-23) "git grep" currently does not use textconv filters at all, that is neither for displaying the match and context nor for the actual grepping, even when requested by --textconv. Introduce an option "--textconv" which makes git grep use any configured textconv filters for grepping and output purposes. It is off by default. Signed-off-by: Jeff King Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- Documentation/git-grep.txt | 9 +++- builtin/grep.c | 2 + grep.c | 100 +++++++++++++++++++++++++++++++------ grep.h | 1 + t/t7008-grep-binary.sh | 6 ++- 5 files changed, 102 insertions(+), 16 deletions(-) diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index 50d46e1a7b..a5c5a27da4 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -9,7 +9,7 @@ git-grep - Print lines matching a pattern SYNOPSIS -------- [verse] -'git grep' [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp] +'git grep' [-a | --text] [-I] [--textconv] [-i | --ignore-case] [-w | --word-regexp] [-v | --invert-match] [-h|-H] [--full-name] [-E | --extended-regexp] [-G | --basic-regexp] [-P | --perl-regexp] @@ -80,6 +80,13 @@ OPTIONS --text:: Process binary files as if they were text. +--textconv:: + Honor textconv filter settings. + +--no-textconv:: + Do not honor textconv filter settings. + This is the default. + -i:: --ignore-case:: Ignore case differences between the patterns and the diff --git a/builtin/grep.c b/builtin/grep.c index 159e65d47a..00ee57d47a 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -659,6 +659,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_SET_INT('I', NULL, &opt.binary, N_("don't match patterns in binary files"), GREP_BINARY_NOMATCH), + OPT_BOOL(0, "textconv", &opt.allow_textconv, + N_("process binary files with textconv filters")), { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"), N_("descend at most levels"), PARSE_OPT_NONEG, NULL, 1 }, diff --git a/grep.c b/grep.c index bb548cae69..c668034739 100644 --- a/grep.c +++ b/grep.c @@ -2,6 +2,8 @@ #include "grep.h" #include "userdiff.h" #include "xdiff-interface.h" +#include "diff.h" +#include "diffcore.h" static int grep_source_load(struct grep_source *gs); static int grep_source_is_binary(struct grep_source *gs); @@ -1322,6 +1324,58 @@ static void std_output(struct grep_opt *opt, const void *buf, size_t size) fwrite(buf, size, 1, stdout); } +static int fill_textconv_grep(struct userdiff_driver *driver, + struct grep_source *gs) +{ + struct diff_filespec *df; + char *buf; + size_t size; + + if (!driver || !driver->textconv) + return grep_source_load(gs); + + /* + * The textconv interface is intimately tied to diff_filespecs, so we + * have to pretend to be one. If we could unify the grep_source + * and diff_filespec structs, this mess could just go away. + */ + df = alloc_filespec(gs->path); + switch (gs->type) { + case GREP_SOURCE_SHA1: + fill_filespec(df, gs->identifier, 1, 0100644); + break; + case GREP_SOURCE_FILE: + fill_filespec(df, null_sha1, 0, 0100644); + break; + default: + die("BUG: attempt to textconv something without a path?"); + } + + /* + * fill_textconv is not remotely thread-safe; it may load objects + * behind the scenes, and it modifies the global diff tempfile + * structure. + */ + grep_read_lock(); + size = fill_textconv(driver, df, &buf); + grep_read_unlock(); + free_filespec(df); + + /* + * The normal fill_textconv usage by the diff machinery would just keep + * the textconv'd buf separate from the diff_filespec. But much of the + * grep code passes around a grep_source and assumes that its "buf" + * pointer is the beginning of the thing we are searching. So let's + * install our textconv'd version into the grep_source, taking care not + * to leak any existing buffer. + */ + grep_source_clear_data(gs); + gs->buf = buf; + gs->size = size; + + return 0; +} + static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits) { char *bol; @@ -1332,6 +1386,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle unsigned count = 0; int try_lookahead = 0; int show_function = 0; + struct userdiff_driver *textconv = NULL; enum grep_context ctx = GREP_CONTEXT_HEAD; xdemitconf_t xecfg; @@ -1353,19 +1408,36 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle } opt->last_shown = 0; - switch (opt->binary) { - case GREP_BINARY_DEFAULT: - if (grep_source_is_binary(gs)) - binary_match_only = 1; - break; - case GREP_BINARY_NOMATCH: - if (grep_source_is_binary(gs)) - return 0; /* Assume unmatch */ - break; - case GREP_BINARY_TEXT: - break; - default: - die("bug: unknown binary handling mode"); + if (opt->allow_textconv) { + grep_source_load_driver(gs); + /* + * We might set up the shared textconv cache data here, which + * is not thread-safe. + */ + grep_attr_lock(); + textconv = userdiff_get_textconv(gs->driver); + grep_attr_unlock(); + } + + /* + * We know the result of a textconv is text, so we only have to care + * about binary handling if we are not using it. + */ + if (!textconv) { + switch (opt->binary) { + case GREP_BINARY_DEFAULT: + if (grep_source_is_binary(gs)) + binary_match_only = 1; + break; + case GREP_BINARY_NOMATCH: + if (grep_source_is_binary(gs)) + return 0; /* Assume unmatch */ + break; + case GREP_BINARY_TEXT: + break; + default: + die("bug: unknown binary handling mode"); + } } memset(&xecfg, 0, sizeof(xecfg)); @@ -1373,7 +1445,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle try_lookahead = should_lookahead(opt); - if (grep_source_load(gs) < 0) + if (fill_textconv_grep(textconv, gs) < 0) return 0; bol = gs->buf; diff --git a/grep.h b/grep.h index e4a1df56a4..eaaced1973 100644 --- a/grep.h +++ b/grep.h @@ -107,6 +107,7 @@ struct grep_opt { #define GREP_BINARY_NOMATCH 1 #define GREP_BINARY_TEXT 2 int binary; + int allow_textconv; int extended; int use_reflog_filter; int pcre; diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh index 1c0946f844..a91260a02a 100755 --- a/t/t7008-grep-binary.sh +++ b/t/t7008-grep-binary.sh @@ -160,7 +160,7 @@ test_expect_success 'grep does not honor textconv' ' test_must_fail git grep Qfile ' -test_expect_failure 'grep --textconv honors textconv' ' +test_expect_success 'grep --textconv honors textconv' ' echo "a:binaryQfile" >expect && git grep --textconv Qfile >actual && test_cmp expect actual @@ -176,4 +176,8 @@ test_expect_failure 'grep --textconv blob honors textconv' ' test_cmp expect actual ' +test_expect_success 'grep --no-textconv blob does not honor textconv' ' + test_must_fail git grep --no-textconv Qfile HEAD:a +' + test_done From afa15f3cd8f4cbf9572138329be374ff8566b10a Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Fri, 10 May 2013 17:10:16 +0200 Subject: [PATCH 7/7] grep: honor --textconv for the case rev:path Make "grep" honor the "--textconv" option also for the object case, i.e. when used with an argument "rev:path". Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- builtin/grep.c | 11 ++++++----- object.c | 26 ++++++++++++++++++++------ object.h | 2 ++ t/t7008-grep-binary.sh | 6 +----- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/builtin/grep.c b/builtin/grep.c index 00ee57d47a..bb7f970ca0 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -458,10 +458,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, } static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, - struct object *obj, const char *name) + struct object *obj, const char *name, struct object_context *oc) { if (obj->type == OBJ_BLOB) - return grep_sha1(opt, obj->sha1, name, 0, NULL); + return grep_sha1(opt, obj->sha1, name, 0, oc ? oc->path : NULL); if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) { struct tree_desc tree; void *data; @@ -503,7 +503,7 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, for (i = 0; i < nr; i++) { struct object *real_obj; real_obj = deref_tag(list->objects[i].item, NULL, 0); - if (grep_object(opt, pathspec, real_obj, list->objects[i].name)) { + if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].context)) { hit = 1; if (opt->status_only) break; @@ -820,12 +820,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix) for (i = 0; i < argc; i++) { const char *arg = argv[i]; unsigned char sha1[20]; + struct object_context oc; /* Is it a rev? */ - if (!get_sha1(arg, sha1)) { + if (!get_sha1_with_context(arg, 0, sha1, &oc)) { struct object *object = parse_object_or_die(sha1, arg); if (!seen_dashdash) verify_non_filename(prefix, arg); - add_object_array(object, arg, &list); + add_object_array_with_context(object, arg, &list, xmemdupz(&oc, sizeof(struct object_context))); continue; } if (!strcmp(arg, "--")) { diff --git a/object.c b/object.c index 20703f52ed..c8ffc9e006 100644 --- a/object.c +++ b/object.c @@ -255,12 +255,7 @@ int object_list_contains(struct object_list *list, struct object *obj) return 0; } -void add_object_array(struct object *obj, const char *name, struct object_array *array) -{ - add_object_array_with_mode(obj, name, array, S_IFINVALID); -} - -void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode) +static void add_object_array_with_mode_context(struct object *obj, const char *name, struct object_array *array, unsigned mode, struct object_context *context) { unsigned nr = array->nr; unsigned alloc = array->alloc; @@ -275,9 +270,28 @@ void add_object_array_with_mode(struct object *obj, const char *name, struct obj objects[nr].item = obj; objects[nr].name = name; objects[nr].mode = mode; + objects[nr].context = context; array->nr = ++nr; } +void add_object_array(struct object *obj, const char *name, struct object_array *array) +{ + add_object_array_with_mode(obj, name, array, S_IFINVALID); +} + +void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode) +{ + add_object_array_with_mode_context(obj, name, array, mode, NULL); +} + +void add_object_array_with_context(struct object *obj, const char *name, struct object_array *array, struct object_context *context) +{ + if (context) + add_object_array_with_mode_context(obj, name, array, context->mode, context); + else + add_object_array_with_mode_context(obj, name, array, S_IFINVALID, context); +} + void object_array_remove_duplicates(struct object_array *array) { unsigned int ref, src, dst; diff --git a/object.h b/object.h index 97d384b80a..695847dfd4 100644 --- a/object.h +++ b/object.h @@ -13,6 +13,7 @@ struct object_array { struct object *item; const char *name; unsigned mode; + struct object_context *context; } *objects; }; @@ -85,6 +86,7 @@ int object_list_contains(struct object_list *list, struct object *obj); /* Object array handling .. */ void add_object_array(struct object *obj, const char *name, struct object_array *array); void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode); +void add_object_array_with_context(struct object *obj, const char *name, struct object_array *array, struct object_context *context); void object_array_remove_duplicates(struct object_array *); void clear_object_flags(unsigned flags); diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh index a91260a02a..b146406e9c 100755 --- a/t/t7008-grep-binary.sh +++ b/t/t7008-grep-binary.sh @@ -170,14 +170,10 @@ test_expect_success 'grep --no-textconv does not honor textconv' ' test_must_fail git grep --no-textconv Qfile ' -test_expect_failure 'grep --textconv blob honors textconv' ' +test_expect_success 'grep --textconv blob honors textconv' ' echo "HEAD:a:binaryQfile" >expect && git grep --textconv Qfile HEAD:a >actual && test_cmp expect actual ' -test_expect_success 'grep --no-textconv blob does not honor textconv' ' - test_must_fail git grep --no-textconv Qfile HEAD:a -' - test_done