{"thread":{"id":"47525","subject":"[PATCH 0/5] pickaxe refactorings and a new mode to find blobs (WAS: diffcore: add a pickaxe option to find a specific blob)","startedAt":"2018-01-03T00:46:35Z","lastAt":"2018-01-04T22:53:22Z","messageCount":19,"participants":["Stefan Beller","Jacob Keller","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"335675","messageId":"20180103004624.222528-1-sbeller@google.com","threadId":"47525","inReplyTo":null,"subject":"[PATCH 0/5] pickaxe refactorings and a new mode to find blobs (WAS: diffcore: add a pickaxe option to find a specific blob)","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-03T00:46:19Z","receivedAt":"2018-01-03T00:46:35Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"After some discussion [1], we're convinced that the original approach for\nadding in just another pickaxe mode to find blobs was too hacky.\n\nSo I went the less hacky way and did some refactoring first (patches 1-3),\nThen we'll have the new pickaxe mode to find blobs in patch 4. It grew\nslightly larger as it had issues with the setup (we neither have a regex\nnor a KWS to init) in this new world, so there are a few more lines in there.\n\nThe last patch is just the cherry on the cake, helping to keep users sane by\nwarning when they try to use different pickaxe modes at the same time.\n\nThanks,\nStefan\n\n\n[1] https://public-inbox.org/git/CAGZ79kaB0G9zetF6QtC45+ZGLM3gOsYWV7e+gkCe2yKOhb0Ssg@mail.gmail.com/\n\n\nStefan Beller (5):\n  diff.h: Make pickaxe_opts an unsigned bit field\n  diff: migrate diff_flags.pickaxe_ignore_case to a pickaxe_opts bit\n  diff: introduce DIFF_PICKAXE_KINDS_MASK\n  diffcore: add a pickaxe option to find a specific blob\n  diff: properly error out when combining multiple pickaxe options\n\n Documentation/diff-options.txt | 10 +++++++++\n builtin/log.c                  |  4 ++--\n combine-diff.c                 |  2 +-\n diff.c                         | 35 +++++++++++++++++++++++++++---\n diff.h                         | 13 ++++++++++--\n diffcore-pickaxe.c             | 48 ++++++++++++++++++++++++------------------\n revision.c                     |  7 ++++--\n 7 files changed, 89 insertions(+), 30 deletions(-)\n\n-- \n2.15.1.620.gb9897f4670-goog\n\n"},{"id":"335676","messageId":"20180103004624.222528-2-sbeller@google.com","threadId":"47525","inReplyTo":"20180103004624.222528-1-sbeller@google.com","subject":"[PATCH 1/5] diff.h: Make pickaxe_opts an unsigned bit field","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-03T00:46:20Z","receivedAt":"2018-01-03T00:46:37Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This variable is used as a bit field[1], and as we are about to add more\nfields, indicate its usage as a bit field by making it unsigned.\n\n[1] containing the bits\n\n    #define DIFF_PICKAXE_ALL\t1\n    #define DIFF_PICKAXE_REGEX\t2\n    #define DIFF_PICKAXE_KIND_S\t4\n    #define DIFF_PICKAXE_KIND_G\t8\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n diff.h | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/diff.h b/diff.h\nindex 0fb18dd735..ea310f76fd 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -146,7 +146,7 @@ struct diff_options {\n \tint skip_stat_unmatch;\n \tint line_termination;\n \tint output_format;\n-\tint pickaxe_opts;\n+\tunsigned pickaxe_opts;\n \tint rename_score;\n \tint rename_limit;\n \tint needed_rename_limit;\n-- \n2.15.1.620.gb9897f4670-goog\n\n"},{"id":"335677","messageId":"20180103004624.222528-3-sbeller@google.com","threadId":"47525","inReplyTo":"20180103004624.222528-1-sbeller@google.com","subject":"[PATCH 2/5] diff: migrate diff_flags.pickaxe_ignore_case to a pickaxe_opts bit","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-03T00:46:21Z","receivedAt":"2018-01-03T00:46:39Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Currently flags for pickaxing are found in different places. Unify the\nflags into the `pickaxe_opts` field, which will contain any pickaxe related\nflags.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n diff.h             | 3 ++-\n diffcore-pickaxe.c | 6 +++---\n revision.c         | 2 +-\n 3 files changed, 6 insertions(+), 5 deletions(-)\n\ndiff --git a/diff.h b/diff.h\nindex ea310f76fd..8af1213684 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -91,7 +91,6 @@ struct diff_flags {\n \tunsigned override_submodule_config:1;\n \tunsigned dirstat_by_line:1;\n \tunsigned funccontext:1;\n-\tunsigned pickaxe_ignore_case:1;\n \tunsigned default_follow_renames:1;\n };\n \n@@ -327,6 +326,8 @@ extern void diff_setup_done(struct diff_options *);\n #define DIFF_PICKAXE_KIND_S\t4 /* traditional plumbing counter */\n #define DIFF_PICKAXE_KIND_G\t8 /* grep in the patch */\n \n+#define DIFF_PICKAXE_IGNORE_CASE\t32\n+\n extern void diffcore_std(struct diff_options *);\n extern void diffcore_fix_diff_index(struct diff_options *);\n \ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\nindex 9476bd2108..4b5d88ea46 100644\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -222,11 +222,11 @@ void diffcore_pickaxe(struct diff_options *o)\n \n \tif (opts & (DIFF_PICKAXE_REGEX | DIFF_PICKAXE_KIND_G)) {\n \t\tint cflags = REG_EXTENDED | REG_NEWLINE;\n-\t\tif (o->flags.pickaxe_ignore_case)\n+\t\tif (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE)\n \t\t\tcflags |= REG_ICASE;\n \t\tregcomp_or_die(&regex, needle, cflags);\n \t\tregexp = &regex;\n-\t} else if (o->flags.pickaxe_ignore_case &&\n+\t} else if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE &&\n \t\t   has_non_ascii(needle)) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint cflags = REG_NEWLINE | REG_ICASE;\n@@ -236,7 +236,7 @@ void diffcore_pickaxe(struct diff_options *o)\n \t\tstrbuf_release(&sb);\n \t\tregexp = &regex;\n \t} else {\n-\t\tkws = kwsalloc(o->flags.pickaxe_ignore_case\n+\t\tkws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE\n \t\t\t       ? tolower_trans_tbl : NULL);\n \t\tkwsincr(kws, needle, strlen(needle));\n \t\tkwsprep(kws);\ndiff --git a/revision.c b/revision.c\nindex e2e691dd5a..ccf1d212ce 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2076,7 +2076,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\trevs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;\n \t} else if (!strcmp(arg, \"--regexp-ignore-case\") || !strcmp(arg, \"-i\")) {\n \t\trevs->grep_filter.ignore_case = 1;\n-\t\trevs->diffopt.flags.pickaxe_ignore_case = 1;\n+\t\trevs->diffopt.pickaxe_opts |= DIFF_PICKAXE_IGNORE_CASE;\n \t} else if (!strcmp(arg, \"--fixed-strings\") || !strcmp(arg, \"-F\")) {\n \t\trevs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED;\n \t} else if (!strcmp(arg, \"--perl-regexp\") || !strcmp(arg, \"-P\")) {\n-- \n2.15.1.620.gb9897f4670-goog\n\n"},{"id":"335678","messageId":"20180103004624.222528-5-sbeller@google.com","threadId":"47525","inReplyTo":"20180103004624.222528-1-sbeller@google.com","subject":"[PATCH 4/5] diffcore: add a pickaxe option to find a specific blob","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-03T00:46:23Z","receivedAt":"2018-01-03T00:46:41Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Sometimes users are given a hash of an object and they want to\nidentify it further (ex.: Use verify-pack to find the largest blobs,\nbut what are these? or [1])\n\nOne might be tempted to extend git-describe to also work with blobs,\nsuch that `git describe <blob-id>` gives a description as\n'<commit-ish>:<path>'.  This was implemented at [2]; as seen by the sheer\nnumber of responses (>110), it turns out this is tricky to get right.\nThe hard part to get right is picking the correct 'commit-ish' as that\ncould be the commit that (re-)introduced the blob or the blob that\nremoved the blob; the blob could exist in different branches.\n\nJunio hinted at a different approach of solving this problem, which this\npatch implements. Teach the diff machinery another flag for restricting\nthe information to what is shown. For example:\n\n    $ ./git log --oneline --find-object=v2.0.0:Makefile\n    b2feb64309 Revert the whole \"ask curl-config\" topic for now\n    47fbfded53 i18n: only extract comments marked with \"TRANSLATORS:\"\n\nwe observe that the Makefile as shipped with 2.0 was appeared in\nv1.9.2-471-g47fbfded53 and in v2.0.0-rc1-5-gb2feb6430b.  The\nreason why these commits both occur prior to v2.0.0 are evil\nmerges that are not found using this new mechanism.\n\n[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob\n[2] https://public-inbox.org/git/20171028004419.10139-1-sbeller@google.com/\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n Documentation/diff-options.txt | 10 ++++++++++\n diff.c                         | 21 +++++++++++++++++++-\n diff.h                         |  8 +++++++-\n diffcore-pickaxe.c             | 45 +++++++++++++++++++++++++-----------------\n revision.c                     |  3 +++\n 5 files changed, 67 insertions(+), 20 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex dd0dba5b1d..f9cf85db05 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -492,6 +492,15 @@ occurrences of that string did not change).\n See the 'pickaxe' entry in linkgit:gitdiffcore[7] for more\n information.\n \n+--find-object=<object-id>::\n+\tLook for differences that change the number of occurrences of\n+\tthe specified object. Similar to `-S`, just the argument is different\n+\tin that it doesn't search for a specific string but for a specific\n+\tobject id.\n++\n+The object can be a blob or a submodule commit. It implies the `-t` option in\n+`git-log` to also find trees.\n+\n --pickaxe-all::\n \tWhen `-S` or `-G` finds a change, show all the changes in that\n \tchangeset, not just the files that contain the change\n@@ -500,6 +509,7 @@ information.\n --pickaxe-regex::\n \tTreat the <string> given to `-S` as an extended POSIX regular\n \texpression to match.\n+\n endif::git-format-patch[]\n \n -O<orderfile>::\ndiff --git a/diff.c b/diff.c\nindex 5508745dc8..a872bdcac1 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -4082,6 +4082,7 @@ void diff_setup(struct diff_options *options)\n \toptions->interhunkcontext = diff_interhunk_context_default;\n \toptions->ws_error_highlight = ws_error_highlight_default;\n \toptions->flags.rename_empty = 1;\n+\toptions->objfind = NULL;\n \n \t/* pathchange left =NULL by default */\n \toptions->change = diff_change;\n@@ -4487,6 +4488,23 @@ static int parse_ws_error_highlight_opt(struct diff_options *opt, const char *ar\n \treturn 1;\n }\n \n+static int parse_objfind_opt(struct diff_options *opt, const char *arg)\n+{\n+\tstruct object_id oid;\n+\n+\tif (get_oid(arg, &oid))\n+\t\treturn error(\"unable to resolve '%s'\", arg);\n+\n+\tif (!opt->objfind)\n+\t\topt->objfind = xcalloc(1, sizeof(*opt->objfind));\n+\n+\topt->pickaxe_opts |= DIFF_PICKAXE_KIND_OBJFIND;\n+\topt->flags.recursive = 1;\n+\topt->flags.tree_in_recursive = 1;\n+\toidset_insert(opt->objfind, &oid);\n+\treturn 1;\n+}\n+\n int diff_opt_parse(struct diff_options *options,\n \t\t   const char **av, int ac, const char *prefix)\n {\n@@ -4736,7 +4754,8 @@ int diff_opt_parse(struct diff_options *options,\n \telse if ((argcount = short_opt('O', av, &optarg))) {\n \t\toptions->orderfile = prefix_filename(prefix, optarg);\n \t\treturn argcount;\n-\t}\n+\t} else if (skip_prefix(arg, \"--find-object=\", &arg))\n+\t\treturn parse_objfind_opt(options, arg);\n \telse if ((argcount = parse_long_opt(\"diff-filter\", av, &optarg))) {\n \t\tint offending = parse_diff_filter_opt(optarg, options);\n \t\tif (offending)\ndiff --git a/diff.h b/diff.h\nindex 9ec4f824fe..8a56cac2ad 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -7,6 +7,7 @@\n #include \"tree-walk.h\"\n #include \"pathspec.h\"\n #include \"object.h\"\n+#include \"oidset.h\"\n \n struct rev_info;\n struct diff_options;\n@@ -173,6 +174,8 @@ struct diff_options {\n \tenum diff_words_type word_diff;\n \tenum diff_submodule_format submodule_format;\n \n+\tstruct oidset *objfind;\n+\n \t/* this is set by diffcore for DIFF_FORMAT_PATCH */\n \tint found_changes;\n \n@@ -325,8 +328,11 @@ extern void diff_setup_done(struct diff_options *);\n \n #define DIFF_PICKAXE_KIND_S\t4 /* traditional plumbing counter */\n #define DIFF_PICKAXE_KIND_G\t8 /* grep in the patch */\n+#define DIFF_PICKAXE_KIND_OBJFIND\t16 /* specific object IDs */\n \n-#define DIFF_PICKAXE_KINDS_MASK (DIFF_PICKAXE_KIND_S | DIFF_PICKAXE_KIND_G)\n+#define DIFF_PICKAXE_KINDS_MASK (DIFF_PICKAXE_KIND_S | \\\n+\t\t\t\t DIFF_PICKAXE_KIND_G | \\\n+\t\t\t\t DIFF_PICKAXE_KIND_OBJFIND)\n \n #define DIFF_PICKAXE_IGNORE_CASE\t32\n \ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\nindex 4b5d88ea46..72bb5a9514 100644\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -124,13 +124,20 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,\n \tmmfile_t mf1, mf2;\n \tint ret;\n \n-\tif (!o->pickaxe[0])\n-\t\treturn 0;\n-\n \t/* ignore unmerged */\n \tif (!DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two))\n \t\treturn 0;\n \n+\tif (o->objfind) {\n+\t\treturn  (DIFF_FILE_VALID(p->one) &&\n+\t\t\t oidset_contains(o->objfind, &p->one->oid)) ||\n+\t\t\t(DIFF_FILE_VALID(p->two) &&\n+\t\t\t oidset_contains(o->objfind, &p->two->oid));\n+\t}\n+\n+\tif (!o->pickaxe[0])\n+\t\treturn 0;\n+\n \tif (o->flags.allow_textconv) {\n \t\ttextconv_one = get_textconv(p->one);\n \t\ttextconv_two = get_textconv(p->two);\n@@ -226,20 +233,22 @@ void diffcore_pickaxe(struct diff_options *o)\n \t\t\tcflags |= REG_ICASE;\n \t\tregcomp_or_die(&regex, needle, cflags);\n \t\tregexp = &regex;\n-\t} else if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE &&\n-\t\t   has_non_ascii(needle)) {\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\tint cflags = REG_NEWLINE | REG_ICASE;\n-\n-\t\tbasic_regex_quote_buf(&sb, needle);\n-\t\tregcomp_or_die(&regex, sb.buf, cflags);\n-\t\tstrbuf_release(&sb);\n-\t\tregexp = &regex;\n-\t} else {\n-\t\tkws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE\n-\t\t\t       ? tolower_trans_tbl : NULL);\n-\t\tkwsincr(kws, needle, strlen(needle));\n-\t\tkwsprep(kws);\n+\t} else if (opts & DIFF_PICKAXE_KIND_S) {\n+\t\tif (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE &&\n+\t\t    has_non_ascii(needle)) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tint cflags = REG_NEWLINE | REG_ICASE;\n+\n+\t\t\tbasic_regex_quote_buf(&sb, needle);\n+\t\t\tregcomp_or_die(&regex, sb.buf, cflags);\n+\t\t\tstrbuf_release(&sb);\n+\t\t\tregexp = &regex;\n+\t\t} else {\n+\t\t\tkws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE\n+\t\t\t\t       ? tolower_trans_tbl : NULL);\n+\t\t\tkwsincr(kws, needle, strlen(needle));\n+\t\t\tkwsprep(kws);\n+\t\t}\n \t}\n \n \t/* Might want to warn when both S and G are on; I don't care... */\n@@ -248,7 +257,7 @@ void diffcore_pickaxe(struct diff_options *o)\n \n \tif (regexp)\n \t\tregfree(regexp);\n-\telse\n+\tif (kws)\n \t\tkwsfree(kws);\n \treturn;\n }\ndiff --git a/revision.c b/revision.c\nindex 5d11ecaf27..30f65b3bbd 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2412,6 +2412,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \t    revs->diffopt.flags.follow_renames)\n \t\trevs->diff = 1;\n \n+\tif (revs->diffopt.objfind)\n+\t\trevs->simplify_history = 0;\n+\n \tif (revs->topo_order)\n \t\trevs->limited = 1;\n \n-- \n2.15.1.620.gb9897f4670-goog\n\n"},{"id":"335679","messageId":"20180103004624.222528-6-sbeller@google.com","threadId":"47525","inReplyTo":"20180103004624.222528-1-sbeller@google.com","subject":"[PATCH 5/5] diff: properly error out when combining multiple pickaxe options","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-03T00:46:24Z","receivedAt":"2018-01-03T00:46:45Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"In f506b8e8b5 (git log/diff: add -G<regexp> that greps in the patch text,\n2010-08-23) we were hesitant to check if the user requests both -S and\n-G at the same time. Now that the pickaxe family also offers --find-object,\nwhich looks slightly more different than the former two, let's add a check\nthat those are not used at the same time.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n diff.c             | 10 ++++++++++\n diffcore-pickaxe.c |  1 -\n 2 files changed, 10 insertions(+), 1 deletion(-)\n\ndiff --git a/diff.c b/diff.c\nindex a872bdcac1..29973e78d6 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -4122,6 +4122,16 @@ void diff_setup_done(struct diff_options *options)\n \t\tcount++;\n \tif (count > 1)\n \t\tdie(_(\"--name-only, --name-status, --check and -s are mutually exclusive\"));\n+\tcount = 0;\n+\n+\tif (options->pickaxe_opts & DIFF_PICKAXE_KIND_S)\n+\t\tcount++;\n+\tif (options->pickaxe_opts & DIFF_PICKAXE_KIND_G)\n+\t\tcount++;\n+\tif (options->pickaxe_opts & DIFF_PICKAXE_KIND_OBJFIND)\n+\t\tcount++;\n+\tif (count > 1)\n+\t\tdie(_(\"-G, -S, --find-object are mutually exclusive\"));\n \n \t/*\n \t * Most of the time we can say \"there are changes\"\ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\nindex 72bb5a9514..239ce5122b 100644\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -251,7 +251,6 @@ void diffcore_pickaxe(struct diff_options *o)\n \t\t}\n \t}\n \n-\t/* Might want to warn when both S and G are on; I don't care... */\n \tpickaxe(&diff_queued_diff, o, regexp, kws,\n \t\t(opts & DIFF_PICKAXE_KIND_G) ? diff_grep : has_changes);\n \n-- \n2.15.1.620.gb9897f4670-goog\n\n"},{"id":"335680","messageId":"20180103004624.222528-4-sbeller@google.com","threadId":"47525","inReplyTo":"20180103004624.222528-1-sbeller@google.com","subject":"[PATCH 3/5] diff: introduce DIFF_PICKAXE_KINDS_MASK","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-03T00:46:22Z","receivedAt":"2018-01-03T00:46:47Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Currently the check whether to perform pickaxing is done via checking\n`diffopt->pickaxe`, which contains the command line argument that we\nwant to pickaxe for. Soon we'll introduce a new type of pickaxing, that\nwill not store anything in the `.pickaxe` field, so let's migrate the\ncheck to be dependent on pickaxe_opts.\n\nIt is not enough to just replace the check for pickaxe by pickaxe_opts,\nbecause flags might be set, but pickaxing was not requested ('-i').\nTo cope with that, introduce a mask to check only for the bits indicating\nthe modes of operation.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n builtin/log.c  | 4 ++--\n combine-diff.c | 2 +-\n diff.c         | 4 ++--\n diff.h         | 2 ++\n revision.c     | 2 +-\n 5 files changed, 8 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 6c1fa896ad..bd6f2d1efb 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -180,8 +180,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,\n \tif (rev->show_notes)\n \t\tinit_display_notes(&rev->notes_opt);\n \n-\tif (rev->diffopt.pickaxe || rev->diffopt.filter ||\n-\t    rev->diffopt.flags.follow_renames)\n+\tif ((rev->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||\n+\t    rev->diffopt.filter || rev->diffopt.flags.follow_renames)\n \t\trev->always_show_header = 0;\n \n \tif (source)\ndiff --git a/combine-diff.c b/combine-diff.c\nindex 2505de119a..bc08c4c5b1 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -1438,7 +1438,7 @@ void diff_tree_combined(const struct object_id *oid,\n \t\t\topt->flags.follow_renames\t||\n \t\t\topt->break_opt != -1\t||\n \t\t\topt->detect_rename\t||\n-\t\t\topt->pickaxe\t\t||\n+\t\t\t(opt->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)\t||\n \t\t\topt->filter;\n \n \ndiff --git a/diff.c b/diff.c\nindex 0763e89263..5508745dc8 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -4173,7 +4173,7 @@ void diff_setup_done(struct diff_options *options)\n \t/*\n \t * Also pickaxe would not work very well if you do not say recursive\n \t */\n-\tif (options->pickaxe)\n+\tif (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)\n \t\toptions->flags.recursive = 1;\n \t/*\n \t * When patches are generated, submodules diffed against the work tree\n@@ -5777,7 +5777,7 @@ void diffcore_std(struct diff_options *options)\n \t\tif (options->break_opt != -1)\n \t\t\tdiffcore_merge_broken();\n \t}\n-\tif (options->pickaxe)\n+\tif (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)\n \t\tdiffcore_pickaxe(options);\n \tif (options->orderfile)\n \t\tdiffcore_order(options->orderfile);\ndiff --git a/diff.h b/diff.h\nindex 8af1213684..9ec4f824fe 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -326,6 +326,8 @@ extern void diff_setup_done(struct diff_options *);\n #define DIFF_PICKAXE_KIND_S\t4 /* traditional plumbing counter */\n #define DIFF_PICKAXE_KIND_G\t8 /* grep in the patch */\n \n+#define DIFF_PICKAXE_KINDS_MASK (DIFF_PICKAXE_KIND_S | DIFF_PICKAXE_KIND_G)\n+\n #define DIFF_PICKAXE_IGNORE_CASE\t32\n \n extern void diffcore_std(struct diff_options *);\ndiff --git a/revision.c b/revision.c\nindex ccf1d212ce..5d11ecaf27 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2407,7 +2407,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \t\trevs->diff = 1;\n \n \t/* Pickaxe, diff-filter and rename following need diffs */\n-\tif (revs->diffopt.pickaxe ||\n+\tif ((revs->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||\n \t    revs->diffopt.filter ||\n \t    revs->diffopt.flags.follow_renames)\n \t\trevs->diff = 1;\n-- \n2.15.1.620.gb9897f4670-goog\n\n"},{"id":"335681","messageId":"20180103005620.202940-1-sbeller@google.com","threadId":"47525","inReplyTo":"20180103004624.222528-5-sbeller@google.com","subject":"[PATCHv2 4/5] diffcore: add a pickaxe option to find a specific blob","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-03T00:56:20Z","receivedAt":"2018-01-03T00:56:31Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Sometimes users are given a hash of an object and they want to\nidentify it further (ex.: Use verify-pack to find the largest blobs,\nbut what are these? or [1])\n\nOne might be tempted to extend git-describe to also work with blobs,\nsuch that `git describe <blob-id>` gives a description as\n'<commit-ish>:<path>'.  This was implemented at [2]; as seen by the sheer\nnumber of responses (>110), it turns out this is tricky to get right.\nThe hard part to get right is picking the correct 'commit-ish' as that\ncould be the commit that (re-)introduced the blob or the blob that\nremoved the blob; the blob could exist in different branches.\n\nJunio hinted at a different approach of solving this problem, which this\npatch implements. Teach the diff machinery another flag for restricting\nthe information to what is shown. For example:\n\n    $ ./git log --oneline --find-object=v2.0.0:Makefile\n    b2feb64309 Revert the whole \"ask curl-config\" topic for now\n    47fbfded53 i18n: only extract comments marked with \"TRANSLATORS:\"\n\nwe observe that the Makefile as shipped with 2.0 was appeared in\nv1.9.2-471-g47fbfded53 and in v2.0.0-rc1-5-gb2feb6430b.  The\nreason why these commits both occur prior to v2.0.0 are evil\nmerges that are not found using this new mechanism.\n\n[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob\n[2] https://public-inbox.org/git/20171028004419.10139-1-sbeller@google.com/\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n\nWe lost the tests in the first version, this includes the new file containing\nthe tests.\n\nStefan\n\n Documentation/diff-options.txt | 10 +++++++\n diff.c                         | 21 ++++++++++++-\n diff.h                         |  8 ++++-\n diffcore-pickaxe.c             | 45 +++++++++++++++++-----------\n revision.c                     |  3 ++\n t/t4064-diff-oidfind.sh        | 68 ++++++++++++++++++++++++++++++++++++++++++\n 6 files changed, 135 insertions(+), 20 deletions(-)\n create mode 100755 t/t4064-diff-oidfind.sh\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex dd0dba5b1d..f9cf85db05 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -492,6 +492,15 @@ occurrences of that string did not change).\n See the 'pickaxe' entry in linkgit:gitdiffcore[7] for more\n information.\n \n+--find-object=<object-id>::\n+\tLook for differences that change the number of occurrences of\n+\tthe specified object. Similar to `-S`, just the argument is different\n+\tin that it doesn't search for a specific string but for a specific\n+\tobject id.\n++\n+The object can be a blob or a submodule commit. It implies the `-t` option in\n+`git-log` to also find trees.\n+\n --pickaxe-all::\n \tWhen `-S` or `-G` finds a change, show all the changes in that\n \tchangeset, not just the files that contain the change\n@@ -500,6 +509,7 @@ information.\n --pickaxe-regex::\n \tTreat the <string> given to `-S` as an extended POSIX regular\n \texpression to match.\n+\n endif::git-format-patch[]\n \n -O<orderfile>::\ndiff --git a/diff.c b/diff.c\nindex 5508745dc8..a872bdcac1 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -4082,6 +4082,7 @@ void diff_setup(struct diff_options *options)\n \toptions->interhunkcontext = diff_interhunk_context_default;\n \toptions->ws_error_highlight = ws_error_highlight_default;\n \toptions->flags.rename_empty = 1;\n+\toptions->objfind = NULL;\n \n \t/* pathchange left =NULL by default */\n \toptions->change = diff_change;\n@@ -4487,6 +4488,23 @@ static int parse_ws_error_highlight_opt(struct diff_options *opt, const char *ar\n \treturn 1;\n }\n \n+static int parse_objfind_opt(struct diff_options *opt, const char *arg)\n+{\n+\tstruct object_id oid;\n+\n+\tif (get_oid(arg, &oid))\n+\t\treturn error(\"unable to resolve '%s'\", arg);\n+\n+\tif (!opt->objfind)\n+\t\topt->objfind = xcalloc(1, sizeof(*opt->objfind));\n+\n+\topt->pickaxe_opts |= DIFF_PICKAXE_KIND_OBJFIND;\n+\topt->flags.recursive = 1;\n+\topt->flags.tree_in_recursive = 1;\n+\toidset_insert(opt->objfind, &oid);\n+\treturn 1;\n+}\n+\n int diff_opt_parse(struct diff_options *options,\n \t\t   const char **av, int ac, const char *prefix)\n {\n@@ -4736,7 +4754,8 @@ int diff_opt_parse(struct diff_options *options,\n \telse if ((argcount = short_opt('O', av, &optarg))) {\n \t\toptions->orderfile = prefix_filename(prefix, optarg);\n \t\treturn argcount;\n-\t}\n+\t} else if (skip_prefix(arg, \"--find-object=\", &arg))\n+\t\treturn parse_objfind_opt(options, arg);\n \telse if ((argcount = parse_long_opt(\"diff-filter\", av, &optarg))) {\n \t\tint offending = parse_diff_filter_opt(optarg, options);\n \t\tif (offending)\ndiff --git a/diff.h b/diff.h\nindex 9ec4f824fe..8a56cac2ad 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -7,6 +7,7 @@\n #include \"tree-walk.h\"\n #include \"pathspec.h\"\n #include \"object.h\"\n+#include \"oidset.h\"\n \n struct rev_info;\n struct diff_options;\n@@ -173,6 +174,8 @@ struct diff_options {\n \tenum diff_words_type word_diff;\n \tenum diff_submodule_format submodule_format;\n \n+\tstruct oidset *objfind;\n+\n \t/* this is set by diffcore for DIFF_FORMAT_PATCH */\n \tint found_changes;\n \n@@ -325,8 +328,11 @@ extern void diff_setup_done(struct diff_options *);\n \n #define DIFF_PICKAXE_KIND_S\t4 /* traditional plumbing counter */\n #define DIFF_PICKAXE_KIND_G\t8 /* grep in the patch */\n+#define DIFF_PICKAXE_KIND_OBJFIND\t16 /* specific object IDs */\n \n-#define DIFF_PICKAXE_KINDS_MASK (DIFF_PICKAXE_KIND_S | DIFF_PICKAXE_KIND_G)\n+#define DIFF_PICKAXE_KINDS_MASK (DIFF_PICKAXE_KIND_S | \\\n+\t\t\t\t DIFF_PICKAXE_KIND_G | \\\n+\t\t\t\t DIFF_PICKAXE_KIND_OBJFIND)\n \n #define DIFF_PICKAXE_IGNORE_CASE\t32\n \ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\nindex 4b5d88ea46..72bb5a9514 100644\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -124,13 +124,20 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,\n \tmmfile_t mf1, mf2;\n \tint ret;\n \n-\tif (!o->pickaxe[0])\n-\t\treturn 0;\n-\n \t/* ignore unmerged */\n \tif (!DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two))\n \t\treturn 0;\n \n+\tif (o->objfind) {\n+\t\treturn  (DIFF_FILE_VALID(p->one) &&\n+\t\t\t oidset_contains(o->objfind, &p->one->oid)) ||\n+\t\t\t(DIFF_FILE_VALID(p->two) &&\n+\t\t\t oidset_contains(o->objfind, &p->two->oid));\n+\t}\n+\n+\tif (!o->pickaxe[0])\n+\t\treturn 0;\n+\n \tif (o->flags.allow_textconv) {\n \t\ttextconv_one = get_textconv(p->one);\n \t\ttextconv_two = get_textconv(p->two);\n@@ -226,20 +233,22 @@ void diffcore_pickaxe(struct diff_options *o)\n \t\t\tcflags |= REG_ICASE;\n \t\tregcomp_or_die(&regex, needle, cflags);\n \t\tregexp = &regex;\n-\t} else if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE &&\n-\t\t   has_non_ascii(needle)) {\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\tint cflags = REG_NEWLINE | REG_ICASE;\n-\n-\t\tbasic_regex_quote_buf(&sb, needle);\n-\t\tregcomp_or_die(&regex, sb.buf, cflags);\n-\t\tstrbuf_release(&sb);\n-\t\tregexp = &regex;\n-\t} else {\n-\t\tkws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE\n-\t\t\t       ? tolower_trans_tbl : NULL);\n-\t\tkwsincr(kws, needle, strlen(needle));\n-\t\tkwsprep(kws);\n+\t} else if (opts & DIFF_PICKAXE_KIND_S) {\n+\t\tif (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE &&\n+\t\t    has_non_ascii(needle)) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tint cflags = REG_NEWLINE | REG_ICASE;\n+\n+\t\t\tbasic_regex_quote_buf(&sb, needle);\n+\t\t\tregcomp_or_die(&regex, sb.buf, cflags);\n+\t\t\tstrbuf_release(&sb);\n+\t\t\tregexp = &regex;\n+\t\t} else {\n+\t\t\tkws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE\n+\t\t\t\t       ? tolower_trans_tbl : NULL);\n+\t\t\tkwsincr(kws, needle, strlen(needle));\n+\t\t\tkwsprep(kws);\n+\t\t}\n \t}\n \n \t/* Might want to warn when both S and G are on; I don't care... */\n@@ -248,7 +257,7 @@ void diffcore_pickaxe(struct diff_options *o)\n \n \tif (regexp)\n \t\tregfree(regexp);\n-\telse\n+\tif (kws)\n \t\tkwsfree(kws);\n \treturn;\n }\ndiff --git a/revision.c b/revision.c\nindex 5d11ecaf27..30f65b3bbd 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2412,6 +2412,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \t    revs->diffopt.flags.follow_renames)\n \t\trevs->diff = 1;\n \n+\tif (revs->diffopt.objfind)\n+\t\trevs->simplify_history = 0;\n+\n \tif (revs->topo_order)\n \t\trevs->limited = 1;\n \ndiff --git a/t/t4064-diff-oidfind.sh b/t/t4064-diff-oidfind.sh\nnew file mode 100755\nindex 0000000000..3bdf317af8\n--- /dev/null\n+++ b/t/t4064-diff-oidfind.sh\n@@ -0,0 +1,68 @@\n+#!/bin/sh\n+\n+test_description='test finding specific blobs in the revision walking'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup ' '\n+\tgit commit --allow-empty -m \"empty initial commit\" &&\n+\n+\techo \"Hello, world!\" >greeting &&\n+\tgit add greeting &&\n+\tgit commit -m \"add the greeting blob\" && # borrowed from Git from the Bottom Up\n+\tgit tag -m \"the blob\" greeting $(git rev-parse HEAD:greeting) &&\n+\n+\techo asdf >unrelated &&\n+\tgit add unrelated &&\n+\tgit commit -m \"unrelated history\" &&\n+\n+\tgit revert HEAD^ &&\n+\n+\tgit commit --allow-empty -m \"another unrelated commit\"\n+'\n+\n+test_expect_success 'find the greeting blob' '\n+\tcat >expect <<-EOF &&\n+\tRevert \"add the greeting blob\"\n+\tadd the greeting blob\n+\tEOF\n+\n+\tgit log --format=%s --find-object=greeting^{blob} >actual &&\n+\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'setup a tree' '\n+\tmkdir a &&\n+\techo asdf >a/file &&\n+\tgit add a/file &&\n+\tgit commit -m \"add a file in a subdirectory\"\n+'\n+\n+test_expect_success 'find a tree' '\n+\tcat >expect <<-EOF &&\n+\tadd a file in a subdirectory\n+\tEOF\n+\n+\tgit log --format=%s -t --find-object=HEAD:a >actual &&\n+\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'setup a submodule' '\n+\ttest_create_repo sub &&\n+\ttest_commit -C sub sub &&\n+\tgit submodule add ./sub sub &&\n+\tgit commit -a -m \"add sub\"\n+'\n+\n+test_expect_success 'find a submodule' '\n+\tcat >expect <<-EOF &&\n+\tadd sub\n+\tEOF\n+\n+\tgit log --format=%s --find-object=HEAD:sub >actual &&\n+\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n2.15.1.620.gb9897f4670-goog\n\n"},{"id":"335682","messageId":"CA+P7+xr7W2sTtUBAhQDZXOTVypuiCU0fL+-W4tbVPd++QbUJcA@mail.gmail.com","threadId":"47525","inReplyTo":"20180103004624.222528-6-sbeller@google.com","subject":"Re: [PATCH 5/5] diff: properly error out when combining multiple pickaxe options","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2018-01-03T00:58:33Z","receivedAt":"2018-01-03T00:58:59Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Jan 2, 2018 at 4:46 PM, Stefan Beller <sbeller@google.com> wrote:\n>                 die(_(\"--name-only, --name-status, --check and -s are mutually exclusive\"));\n> +       count = 0;\n> +\n> +       if (options->pickaxe_opts & DIFF_PICKAXE_KIND_S)\n> +               count++;\n> +       if (options->pickaxe_opts & DIFF_PICKAXE_KIND_G)\n> +               count++;\n> +       if (options->pickaxe_opts & DIFF_PICKAXE_KIND_OBJFIND)\n> +               count++;\n> +       if (count > 1)\n> +               die(_(\"-G, -S, --find-object are mutually exclusive\"));\n\nNit: I'd put an \"and\" in the sentence.\n"},{"id":"335794","messageId":"xmqqfu7mhata.fsf@gitster.mtv.corp.google.com","threadId":"47525","inReplyTo":"20180103004624.222528-2-sbeller@google.com","subject":"Re: [PATCH 1/5] diff.h: Make pickaxe_opts an unsigned bit field","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-03T21:49:53Z","receivedAt":"2018-01-03T21:50:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> This variable is used as a bit field[1], and as we are about to add more\n> fields, indicate its usage as a bit field by making it unsigned.\n>\n> [1] containing the bits\n>\n>     #define DIFF_PICKAXE_ALL\t1\n>     #define DIFF_PICKAXE_REGEX\t2\n>     #define DIFF_PICKAXE_KIND_S\t4\n>     #define DIFF_PICKAXE_KIND_G\t8\n>\n> Signed-off-by: Stefan Beller <sbeller@google.com>\n> ---\n>  diff.h | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n\nMakes perfect sense.\n\n>\n> diff --git a/diff.h b/diff.h\n> index 0fb18dd735..ea310f76fd 100644\n> --- a/diff.h\n> +++ b/diff.h\n> @@ -146,7 +146,7 @@ struct diff_options {\n>  \tint skip_stat_unmatch;\n>  \tint line_termination;\n>  \tint output_format;\n> -\tint pickaxe_opts;\n> +\tunsigned pickaxe_opts;\n>  \tint rename_score;\n>  \tint rename_limit;\n>  \tint needed_rename_limit;\n"},{"id":"335795","messageId":"xmqqbmiaha9b.fsf@gitster.mtv.corp.google.com","threadId":"47525","inReplyTo":"20180103004624.222528-4-sbeller@google.com","subject":"Re: [PATCH 3/5] diff: introduce DIFF_PICKAXE_KINDS_MASK","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-03T22:01:52Z","receivedAt":"2018-01-03T22:01:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> Currently the check whether to perform pickaxing is done via checking\n> `diffopt->pickaxe`, which contains the command line argument that we\n> want to pickaxe for. Soon we'll introduce a new type of pickaxing, that\n> will not store anything in the `.pickaxe` field, so let's migrate the\n> check to be dependent on pickaxe_opts.\n>\n> It is not enough to just replace the check for pickaxe by pickaxe_opts,\n> because flags might be set, but pickaxing was not requested ('-i').\n> To cope with that, introduce a mask to check only for the bits indicating\n> the modes of operation.\n\nThe resulting code after this series would leave a few \"huh?\" if it\nwere new code, but the series is not making anything worse, so take\nthis as just something noticed, not as something needs further work.\n\nBecause we do not allow \"log -S<something> -G<something>\", there is\nno legitimate reason why they have to be a bit in the pickaxe_opts\nflag word.  A single enum that says \"We are doing pickaxe search and\n_this_ is the kind of pickaxe search we are doing\" would suffice,\ni.e. the NULL-ness check of rev->diffopt.pickaxe string can be\nreplaced with a check of that enum field against PICKAXE_NONE or\nsomething that signals us that no pickaxe is in effect.\n\nOn the other hand, if somebody comes up with a sensible way to\ncombine more than one pickaxe queries in a single traversal\n(e.g. \"log -S<something> -S<somethingelse>\" might mean \"find a\nchange that loses or gains <something> and <somethingelse> in the\nsame commit\", or it may mean the same with \"...<something> or\n<somethingelse>\"), then a more sensible data structure to represent\nthe pickaxe request may have been a list of struct, each of which\nrecords the kind and the parameter (e.g. \"-S\" and \"<something>\"\nwould be in a single struct, and \"-S\" and \"<somethingelse>\" would be\nin another, and these two are in the list that is diffopt->pickaxe).\nThe NULL-ness check of rev->diffopt.pickaxe string would be replaced\nwith a check of the length of that list.\n\nIn any case, this step looks sensible.\n\n>\n> Signed-off-by: Stefan Beller <sbeller@google.com>\n> ---\n>  builtin/log.c  | 4 ++--\n>  combine-diff.c | 2 +-\n>  diff.c         | 4 ++--\n>  diff.h         | 2 ++\n>  revision.c     | 2 +-\n>  5 files changed, 8 insertions(+), 6 deletions(-)\n>\n> diff --git a/builtin/log.c b/builtin/log.c\n> index 6c1fa896ad..bd6f2d1efb 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -180,8 +180,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,\n>  \tif (rev->show_notes)\n>  \t\tinit_display_notes(&rev->notes_opt);\n>  \n> -\tif (rev->diffopt.pickaxe || rev->diffopt.filter ||\n> -\t    rev->diffopt.flags.follow_renames)\n> +\tif ((rev->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||\n> +\t    rev->diffopt.filter || rev->diffopt.flags.follow_renames)\n>  \t\trev->always_show_header = 0;\n>  \n>  \tif (source)\n> diff --git a/combine-diff.c b/combine-diff.c\n> index 2505de119a..bc08c4c5b1 100644\n> --- a/combine-diff.c\n> +++ b/combine-diff.c\n> @@ -1438,7 +1438,7 @@ void diff_tree_combined(const struct object_id *oid,\n>  \t\t\topt->flags.follow_renames\t||\n>  \t\t\topt->break_opt != -1\t||\n>  \t\t\topt->detect_rename\t||\n> -\t\t\topt->pickaxe\t\t||\n> +\t\t\t(opt->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)\t||\n>  \t\t\topt->filter;\n>  \n>  \n> diff --git a/diff.c b/diff.c\n> index 0763e89263..5508745dc8 100644\n> --- a/diff.c\n> +++ b/diff.c\n> @@ -4173,7 +4173,7 @@ void diff_setup_done(struct diff_options *options)\n>  \t/*\n>  \t * Also pickaxe would not work very well if you do not say recursive\n>  \t */\n> -\tif (options->pickaxe)\n> +\tif (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)\n>  \t\toptions->flags.recursive = 1;\n>  \t/*\n>  \t * When patches are generated, submodules diffed against the work tree\n> @@ -5777,7 +5777,7 @@ void diffcore_std(struct diff_options *options)\n>  \t\tif (options->break_opt != -1)\n>  \t\t\tdiffcore_merge_broken();\n>  \t}\n> -\tif (options->pickaxe)\n> +\tif (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)\n>  \t\tdiffcore_pickaxe(options);\n>  \tif (options->orderfile)\n>  \t\tdiffcore_order(options->orderfile);\n> diff --git a/diff.h b/diff.h\n> index 8af1213684..9ec4f824fe 100644\n> --- a/diff.h\n> +++ b/diff.h\n> @@ -326,6 +326,8 @@ extern void diff_setup_done(struct diff_options *);\n>  #define DIFF_PICKAXE_KIND_S\t4 /* traditional plumbing counter */\n>  #define DIFF_PICKAXE_KIND_G\t8 /* grep in the patch */\n>  \n> +#define DIFF_PICKAXE_KINDS_MASK (DIFF_PICKAXE_KIND_S | DIFF_PICKAXE_KIND_G)\n> +\n>  #define DIFF_PICKAXE_IGNORE_CASE\t32\n>  \n>  extern void diffcore_std(struct diff_options *);\n> diff --git a/revision.c b/revision.c\n> index ccf1d212ce..5d11ecaf27 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2407,7 +2407,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>  \t\trevs->diff = 1;\n>  \n>  \t/* Pickaxe, diff-filter and rename following need diffs */\n> -\tif (revs->diffopt.pickaxe ||\n> +\tif ((revs->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||\n>  \t    revs->diffopt.filter ||\n>  \t    revs->diffopt.flags.follow_renames)\n>  \t\trevs->diff = 1;\n"},{"id":"335796","messageId":"xmqq7esyh9ym.fsf@gitster.mtv.corp.google.com","threadId":"47525","inReplyTo":"20180103004624.222528-6-sbeller@google.com","subject":"Re: [PATCH 5/5] diff: properly error out when combining multiple pickaxe options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-03T22:08:17Z","receivedAt":"2018-01-03T22:08:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n;\n> +\tcount = 0;\n> +\n> +\tif (options->pickaxe_opts & DIFF_PICKAXE_KIND_S)\n> +\t\tcount++;\n> +\tif (options->pickaxe_opts & DIFF_PICKAXE_KIND_G)\n> +\t\tcount++;\n> +\tif (options->pickaxe_opts & DIFF_PICKAXE_KIND_OBJFIND)\n> +\t\tcount++;\n> +\tif (count > 1)\n> +\t\tdie(_(\"-G, -S, --find-object are mutually exclusive\"));\n\nI thought the reason you defined pickaxe-kind bitmask was so that\nyou can mask this field to grab these (and only these) bits.\nOnce you have that mask, you should be able to use HAS_MULTI_BITS()\non the masked result without counting, no?\n"},{"id":"335798","messageId":"CAGZ79kaef93BVM+Xaihad7cij-tc6DvWAgwo+Ps1zt_3Jn84Tg@mail.gmail.com","threadId":"47525","inReplyTo":"xmqq7esyh9ym.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 5/5] diff: properly error out when combining multiple pickaxe options","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-03T22:18:16Z","receivedAt":"2018-01-03T22:18:22Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Wed, Jan 3, 2018 at 2:08 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Stefan Beller <sbeller@google.com> writes:\n>\n> ;\n>> +     count = 0;\n>> +\n>> +     if (options->pickaxe_opts & DIFF_PICKAXE_KIND_S)\n>> +             count++;\n>> +     if (options->pickaxe_opts & DIFF_PICKAXE_KIND_G)\n>> +             count++;\n>> +     if (options->pickaxe_opts & DIFF_PICKAXE_KIND_OBJFIND)\n>> +             count++;\n>> +     if (count > 1)\n>> +             die(_(\"-G, -S, --find-object are mutually exclusive\"));\n>\n> I thought the reason you defined pickaxe-kind bitmask was so that\n> you can mask this field to grab these (and only these) bits.\n\nOriginally I only wanted to mask out the 'case independency'\nbit and keep it future proof for any similar bits.\n\n> Once you have that mask, you should be able to use HAS_MULTI_BITS()\n> on the masked result without counting, no?\n\nOh, what a nice macro! Thanks for pointing at it.\n\nAs soon as I figured out the right place where to put this check,\nI saw the lines above, whose style I imitated.\n(I guess there is just no mask defined for \"--name-only, --name-status,\n--check and -s\", nor would it make sense to do so; though that given\nmacro should work just fine even for non-continuous masks)\n"},{"id":"335884","messageId":"20180104225044.191220-1-sbeller@google.com","threadId":"47525","inReplyTo":"20180103004624.222528-1-sbeller@google.com","subject":"[PATCHv2 0/6] pickaxe refactorings and a new mode to find blobs (WAS: diffcore: add a pickaxe option to find a specific blob)","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-04T22:50:38Z","receivedAt":"2018-01-04T22:52:54Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"v2:\nThanks Junio and Jacob for review!\n* fixed up the last patch to rephrase the error message to contain an 'and'\n* use HAS_MULTI_BITS as well\n* a bonus patch that uses HAS_MULTI_BITS for the existing code, too.\n\nv1\nAfter some discussion [1], we're convinced that the original approach for\nadding in just another pickaxe mode to find blobs was too hacky.\n\nSo I went the less hacky way and did some refactoring first (patches 1-3),\nThen we'll have the new pickaxe mode to find blobs in patch 4. It grew\nslightly larger as it had issues with the setup (we neither have a regex\nnor a KWS to init) in this new world, so there are a few more lines in there.\n\nThe last patch is just the cherry on the cake, helping to keep users sane by\nwarning when they try to use different pickaxe modes at the same time.\n\nThanks,\nStefan\n\n\n[1] https://public-inbox.org/git/CAGZ79kaB0G9zetF6QtC45+ZGLM3gOsYWV7e+gkCe2yKOhb0Ssg@mail.gmail.com/\n\n\nStefan Beller (6):\n  diff.h: Make pickaxe_opts an unsigned bit field\n  diff: migrate diff_flags.pickaxe_ignore_case to a pickaxe_opts bit\n  diff: introduce DIFF_PICKAXE_KINDS_MASK\n  diffcore: add a pickaxe option to find a specific blob\n  diff: properly error out when combining multiple pickaxe options\n  diff: use HAS_MULTI_BITS instead of counting bits manually\n\n Documentation/diff-options.txt | 10 +++++++\n builtin/log.c                  |  4 +--\n combine-diff.c                 |  2 +-\n diff.c                         | 43 ++++++++++++++++++--------\n diff.h                         | 13 ++++++--\n diffcore-pickaxe.c             | 48 ++++++++++++++++-------------\n revision.c                     |  7 +++--\n t/t4064-diff-oidfind.sh        | 68 ++++++++++++++++++++++++++++++++++++++++++\n 8 files changed, 155 insertions(+), 40 deletions(-)\n create mode 100755 t/t4064-diff-oidfind.sh\n\n-- \n2.16.0.rc0.223.g4a4ac83678-goog\n\n"},{"id":"335885","messageId":"20180104225044.191220-2-sbeller@google.com","threadId":"47525","inReplyTo":"20180104225044.191220-1-sbeller@google.com","subject":"[PATCHv2 1/6] diff.h: Make pickaxe_opts an unsigned bit field","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-04T22:50:39Z","receivedAt":"2018-01-04T22:52:57Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This variable is used as a bit field[1], and as we are about to add more\nfields, indicate its usage as a bit field by making it unsigned.\n\n[1] containing the bits\n\n    #define DIFF_PICKAXE_ALL\t1\n    #define DIFF_PICKAXE_REGEX\t2\n    #define DIFF_PICKAXE_KIND_S\t4\n    #define DIFF_PICKAXE_KIND_G\t8\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n diff.h | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/diff.h b/diff.h\nindex 0fb18dd735..ea310f76fd 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -146,7 +146,7 @@ struct diff_options {\n \tint skip_stat_unmatch;\n \tint line_termination;\n \tint output_format;\n-\tint pickaxe_opts;\n+\tunsigned pickaxe_opts;\n \tint rename_score;\n \tint rename_limit;\n \tint needed_rename_limit;\n-- \n2.16.0.rc0.223.g4a4ac83678-goog\n\n"},{"id":"335886","messageId":"20180104225044.191220-4-sbeller@google.com","threadId":"47525","inReplyTo":"20180104225044.191220-1-sbeller@google.com","subject":"[PATCHv2 3/6] diff: introduce DIFF_PICKAXE_KINDS_MASK","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-04T22:50:41Z","receivedAt":"2018-01-04T22:53:00Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Currently the check whether to perform pickaxing is done via checking\n`diffopt->pickaxe`, which contains the command line argument that we\nwant to pickaxe for. Soon we'll introduce a new type of pickaxing, that\nwill not store anything in the `.pickaxe` field, so let's migrate the\ncheck to be dependent on pickaxe_opts.\n\nIt is not enough to just replace the check for pickaxe by pickaxe_opts,\nbecause flags might be set, but pickaxing was not requested ('-i').\nTo cope with that, introduce a mask to check only for the bits indicating\nthe modes of operation.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n builtin/log.c  | 4 ++--\n combine-diff.c | 2 +-\n diff.c         | 4 ++--\n diff.h         | 2 ++\n revision.c     | 2 +-\n 5 files changed, 8 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 6c1fa896ad..bd6f2d1efb 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -180,8 +180,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,\n \tif (rev->show_notes)\n \t\tinit_display_notes(&rev->notes_opt);\n \n-\tif (rev->diffopt.pickaxe || rev->diffopt.filter ||\n-\t    rev->diffopt.flags.follow_renames)\n+\tif ((rev->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||\n+\t    rev->diffopt.filter || rev->diffopt.flags.follow_renames)\n \t\trev->always_show_header = 0;\n \n \tif (source)\ndiff --git a/combine-diff.c b/combine-diff.c\nindex 2505de119a..bc08c4c5b1 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -1438,7 +1438,7 @@ void diff_tree_combined(const struct object_id *oid,\n \t\t\topt->flags.follow_renames\t||\n \t\t\topt->break_opt != -1\t||\n \t\t\topt->detect_rename\t||\n-\t\t\topt->pickaxe\t\t||\n+\t\t\t(opt->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)\t||\n \t\t\topt->filter;\n \n \ndiff --git a/diff.c b/diff.c\nindex 0763e89263..5508745dc8 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -4173,7 +4173,7 @@ void diff_setup_done(struct diff_options *options)\n \t/*\n \t * Also pickaxe would not work very well if you do not say recursive\n \t */\n-\tif (options->pickaxe)\n+\tif (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)\n \t\toptions->flags.recursive = 1;\n \t/*\n \t * When patches are generated, submodules diffed against the work tree\n@@ -5777,7 +5777,7 @@ void diffcore_std(struct diff_options *options)\n \t\tif (options->break_opt != -1)\n \t\t\tdiffcore_merge_broken();\n \t}\n-\tif (options->pickaxe)\n+\tif (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)\n \t\tdiffcore_pickaxe(options);\n \tif (options->orderfile)\n \t\tdiffcore_order(options->orderfile);\ndiff --git a/diff.h b/diff.h\nindex 8af1213684..9ec4f824fe 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -326,6 +326,8 @@ extern void diff_setup_done(struct diff_options *);\n #define DIFF_PICKAXE_KIND_S\t4 /* traditional plumbing counter */\n #define DIFF_PICKAXE_KIND_G\t8 /* grep in the patch */\n \n+#define DIFF_PICKAXE_KINDS_MASK (DIFF_PICKAXE_KIND_S | DIFF_PICKAXE_KIND_G)\n+\n #define DIFF_PICKAXE_IGNORE_CASE\t32\n \n extern void diffcore_std(struct diff_options *);\ndiff --git a/revision.c b/revision.c\nindex ccf1d212ce..5d11ecaf27 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2407,7 +2407,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \t\trevs->diff = 1;\n \n \t/* Pickaxe, diff-filter and rename following need diffs */\n-\tif (revs->diffopt.pickaxe ||\n+\tif ((revs->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||\n \t    revs->diffopt.filter ||\n \t    revs->diffopt.flags.follow_renames)\n \t\trevs->diff = 1;\n-- \n2.16.0.rc0.223.g4a4ac83678-goog\n\n"},{"id":"335887","messageId":"20180104225044.191220-5-sbeller@google.com","threadId":"47525","inReplyTo":"20180104225044.191220-1-sbeller@google.com","subject":"[PATCHv2 4/6] diffcore: add a pickaxe option to find a specific blob","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-04T22:50:42Z","receivedAt":"2018-01-04T22:53:06Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Sometimes users are given a hash of an object and they want to\nidentify it further (ex.: Use verify-pack to find the largest blobs,\nbut what are these? or [1])\n\nOne might be tempted to extend git-describe to also work with blobs,\nsuch that `git describe <blob-id>` gives a description as\n'<commit-ish>:<path>'.  This was implemented at [2]; as seen by the sheer\nnumber of responses (>110), it turns out this is tricky to get right.\nThe hard part to get right is picking the correct 'commit-ish' as that\ncould be the commit that (re-)introduced the blob or the blob that\nremoved the blob; the blob could exist in different branches.\n\nJunio hinted at a different approach of solving this problem, which this\npatch implements. Teach the diff machinery another flag for restricting\nthe information to what is shown. For example:\n\n    $ ./git log --oneline --find-object=v2.0.0:Makefile\n    b2feb64309 Revert the whole \"ask curl-config\" topic for now\n    47fbfded53 i18n: only extract comments marked with \"TRANSLATORS:\"\n\nwe observe that the Makefile as shipped with 2.0 was appeared in\nv1.9.2-471-g47fbfded53 and in v2.0.0-rc1-5-gb2feb6430b.  The\nreason why these commits both occur prior to v2.0.0 are evil\nmerges that are not found using this new mechanism.\n\n[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob\n[2] https://public-inbox.org/git/20171028004419.10139-1-sbeller@google.com/\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n Documentation/diff-options.txt | 10 +++++++\n diff.c                         | 21 ++++++++++++-\n diff.h                         |  8 ++++-\n diffcore-pickaxe.c             | 45 +++++++++++++++++-----------\n revision.c                     |  3 ++\n t/t4064-diff-oidfind.sh        | 68 ++++++++++++++++++++++++++++++++++++++++++\n 6 files changed, 135 insertions(+), 20 deletions(-)\n create mode 100755 t/t4064-diff-oidfind.sh\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex dd0dba5b1d..f9cf85db05 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -492,6 +492,15 @@ occurrences of that string did not change).\n See the 'pickaxe' entry in linkgit:gitdiffcore[7] for more\n information.\n \n+--find-object=<object-id>::\n+\tLook for differences that change the number of occurrences of\n+\tthe specified object. Similar to `-S`, just the argument is different\n+\tin that it doesn't search for a specific string but for a specific\n+\tobject id.\n++\n+The object can be a blob or a submodule commit. It implies the `-t` option in\n+`git-log` to also find trees.\n+\n --pickaxe-all::\n \tWhen `-S` or `-G` finds a change, show all the changes in that\n \tchangeset, not just the files that contain the change\n@@ -500,6 +509,7 @@ information.\n --pickaxe-regex::\n \tTreat the <string> given to `-S` as an extended POSIX regular\n \texpression to match.\n+\n endif::git-format-patch[]\n \n -O<orderfile>::\ndiff --git a/diff.c b/diff.c\nindex 5508745dc8..a872bdcac1 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -4082,6 +4082,7 @@ void diff_setup(struct diff_options *options)\n \toptions->interhunkcontext = diff_interhunk_context_default;\n \toptions->ws_error_highlight = ws_error_highlight_default;\n \toptions->flags.rename_empty = 1;\n+\toptions->objfind = NULL;\n \n \t/* pathchange left =NULL by default */\n \toptions->change = diff_change;\n@@ -4487,6 +4488,23 @@ static int parse_ws_error_highlight_opt(struct diff_options *opt, const char *ar\n \treturn 1;\n }\n \n+static int parse_objfind_opt(struct diff_options *opt, const char *arg)\n+{\n+\tstruct object_id oid;\n+\n+\tif (get_oid(arg, &oid))\n+\t\treturn error(\"unable to resolve '%s'\", arg);\n+\n+\tif (!opt->objfind)\n+\t\topt->objfind = xcalloc(1, sizeof(*opt->objfind));\n+\n+\topt->pickaxe_opts |= DIFF_PICKAXE_KIND_OBJFIND;\n+\topt->flags.recursive = 1;\n+\topt->flags.tree_in_recursive = 1;\n+\toidset_insert(opt->objfind, &oid);\n+\treturn 1;\n+}\n+\n int diff_opt_parse(struct diff_options *options,\n \t\t   const char **av, int ac, const char *prefix)\n {\n@@ -4736,7 +4754,8 @@ int diff_opt_parse(struct diff_options *options,\n \telse if ((argcount = short_opt('O', av, &optarg))) {\n \t\toptions->orderfile = prefix_filename(prefix, optarg);\n \t\treturn argcount;\n-\t}\n+\t} else if (skip_prefix(arg, \"--find-object=\", &arg))\n+\t\treturn parse_objfind_opt(options, arg);\n \telse if ((argcount = parse_long_opt(\"diff-filter\", av, &optarg))) {\n \t\tint offending = parse_diff_filter_opt(optarg, options);\n \t\tif (offending)\ndiff --git a/diff.h b/diff.h\nindex 9ec4f824fe..8a56cac2ad 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -7,6 +7,7 @@\n #include \"tree-walk.h\"\n #include \"pathspec.h\"\n #include \"object.h\"\n+#include \"oidset.h\"\n \n struct rev_info;\n struct diff_options;\n@@ -173,6 +174,8 @@ struct diff_options {\n \tenum diff_words_type word_diff;\n \tenum diff_submodule_format submodule_format;\n \n+\tstruct oidset *objfind;\n+\n \t/* this is set by diffcore for DIFF_FORMAT_PATCH */\n \tint found_changes;\n \n@@ -325,8 +328,11 @@ extern void diff_setup_done(struct diff_options *);\n \n #define DIFF_PICKAXE_KIND_S\t4 /* traditional plumbing counter */\n #define DIFF_PICKAXE_KIND_G\t8 /* grep in the patch */\n+#define DIFF_PICKAXE_KIND_OBJFIND\t16 /* specific object IDs */\n \n-#define DIFF_PICKAXE_KINDS_MASK (DIFF_PICKAXE_KIND_S | DIFF_PICKAXE_KIND_G)\n+#define DIFF_PICKAXE_KINDS_MASK (DIFF_PICKAXE_KIND_S | \\\n+\t\t\t\t DIFF_PICKAXE_KIND_G | \\\n+\t\t\t\t DIFF_PICKAXE_KIND_OBJFIND)\n \n #define DIFF_PICKAXE_IGNORE_CASE\t32\n \ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\nindex 4b5d88ea46..72bb5a9514 100644\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -124,13 +124,20 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,\n \tmmfile_t mf1, mf2;\n \tint ret;\n \n-\tif (!o->pickaxe[0])\n-\t\treturn 0;\n-\n \t/* ignore unmerged */\n \tif (!DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two))\n \t\treturn 0;\n \n+\tif (o->objfind) {\n+\t\treturn  (DIFF_FILE_VALID(p->one) &&\n+\t\t\t oidset_contains(o->objfind, &p->one->oid)) ||\n+\t\t\t(DIFF_FILE_VALID(p->two) &&\n+\t\t\t oidset_contains(o->objfind, &p->two->oid));\n+\t}\n+\n+\tif (!o->pickaxe[0])\n+\t\treturn 0;\n+\n \tif (o->flags.allow_textconv) {\n \t\ttextconv_one = get_textconv(p->one);\n \t\ttextconv_two = get_textconv(p->two);\n@@ -226,20 +233,22 @@ void diffcore_pickaxe(struct diff_options *o)\n \t\t\tcflags |= REG_ICASE;\n \t\tregcomp_or_die(&regex, needle, cflags);\n \t\tregexp = &regex;\n-\t} else if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE &&\n-\t\t   has_non_ascii(needle)) {\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\tint cflags = REG_NEWLINE | REG_ICASE;\n-\n-\t\tbasic_regex_quote_buf(&sb, needle);\n-\t\tregcomp_or_die(&regex, sb.buf, cflags);\n-\t\tstrbuf_release(&sb);\n-\t\tregexp = &regex;\n-\t} else {\n-\t\tkws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE\n-\t\t\t       ? tolower_trans_tbl : NULL);\n-\t\tkwsincr(kws, needle, strlen(needle));\n-\t\tkwsprep(kws);\n+\t} else if (opts & DIFF_PICKAXE_KIND_S) {\n+\t\tif (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE &&\n+\t\t    has_non_ascii(needle)) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tint cflags = REG_NEWLINE | REG_ICASE;\n+\n+\t\t\tbasic_regex_quote_buf(&sb, needle);\n+\t\t\tregcomp_or_die(&regex, sb.buf, cflags);\n+\t\t\tstrbuf_release(&sb);\n+\t\t\tregexp = &regex;\n+\t\t} else {\n+\t\t\tkws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE\n+\t\t\t\t       ? tolower_trans_tbl : NULL);\n+\t\t\tkwsincr(kws, needle, strlen(needle));\n+\t\t\tkwsprep(kws);\n+\t\t}\n \t}\n \n \t/* Might want to warn when both S and G are on; I don't care... */\n@@ -248,7 +257,7 @@ void diffcore_pickaxe(struct diff_options *o)\n \n \tif (regexp)\n \t\tregfree(regexp);\n-\telse\n+\tif (kws)\n \t\tkwsfree(kws);\n \treturn;\n }\ndiff --git a/revision.c b/revision.c\nindex 5d11ecaf27..30f65b3bbd 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2412,6 +2412,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \t    revs->diffopt.flags.follow_renames)\n \t\trevs->diff = 1;\n \n+\tif (revs->diffopt.objfind)\n+\t\trevs->simplify_history = 0;\n+\n \tif (revs->topo_order)\n \t\trevs->limited = 1;\n \ndiff --git a/t/t4064-diff-oidfind.sh b/t/t4064-diff-oidfind.sh\nnew file mode 100755\nindex 0000000000..3bdf317af8\n--- /dev/null\n+++ b/t/t4064-diff-oidfind.sh\n@@ -0,0 +1,68 @@\n+#!/bin/sh\n+\n+test_description='test finding specific blobs in the revision walking'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup ' '\n+\tgit commit --allow-empty -m \"empty initial commit\" &&\n+\n+\techo \"Hello, world!\" >greeting &&\n+\tgit add greeting &&\n+\tgit commit -m \"add the greeting blob\" && # borrowed from Git from the Bottom Up\n+\tgit tag -m \"the blob\" greeting $(git rev-parse HEAD:greeting) &&\n+\n+\techo asdf >unrelated &&\n+\tgit add unrelated &&\n+\tgit commit -m \"unrelated history\" &&\n+\n+\tgit revert HEAD^ &&\n+\n+\tgit commit --allow-empty -m \"another unrelated commit\"\n+'\n+\n+test_expect_success 'find the greeting blob' '\n+\tcat >expect <<-EOF &&\n+\tRevert \"add the greeting blob\"\n+\tadd the greeting blob\n+\tEOF\n+\n+\tgit log --format=%s --find-object=greeting^{blob} >actual &&\n+\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'setup a tree' '\n+\tmkdir a &&\n+\techo asdf >a/file &&\n+\tgit add a/file &&\n+\tgit commit -m \"add a file in a subdirectory\"\n+'\n+\n+test_expect_success 'find a tree' '\n+\tcat >expect <<-EOF &&\n+\tadd a file in a subdirectory\n+\tEOF\n+\n+\tgit log --format=%s -t --find-object=HEAD:a >actual &&\n+\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'setup a submodule' '\n+\ttest_create_repo sub &&\n+\ttest_commit -C sub sub &&\n+\tgit submodule add ./sub sub &&\n+\tgit commit -a -m \"add sub\"\n+'\n+\n+test_expect_success 'find a submodule' '\n+\tcat >expect <<-EOF &&\n+\tadd sub\n+\tEOF\n+\n+\tgit log --format=%s --find-object=HEAD:sub >actual &&\n+\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n2.16.0.rc0.223.g4a4ac83678-goog\n\n"},{"id":"335888","messageId":"20180104225044.191220-6-sbeller@google.com","threadId":"47525","inReplyTo":"20180104225044.191220-1-sbeller@google.com","subject":"[PATCHv2 5/6] diff: properly error out when combining multiple pickaxe options","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-04T22:50:43Z","receivedAt":"2018-01-04T22:53:17Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"In f506b8e8b5 (git log/diff: add -G<regexp> that greps in the patch text,\n2010-08-23) we were hesitant to check if the user requests both -S and\n-G at the same time. Now that the pickaxe family also offers --find-object,\nwhich looks slightly more different than the former two, let's add a check\nthat those are not used at the same time.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n diff.c             | 3 +++\n diffcore-pickaxe.c | 1 -\n 2 files changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/diff.c b/diff.c\nindex a872bdcac1..42858d4c7d 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -4123,6 +4123,9 @@ void diff_setup_done(struct diff_options *options)\n \tif (count > 1)\n \t\tdie(_(\"--name-only, --name-status, --check and -s are mutually exclusive\"));\n \n+\tif (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK))\n+\t\tdie(_(\"-G, -S and --find-object are mutually exclusive\"));\n+\n \t/*\n \t * Most of the time we can say \"there are changes\"\n \t * only by checking if there are changed paths, but\ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\nindex 72bb5a9514..239ce5122b 100644\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -251,7 +251,6 @@ void diffcore_pickaxe(struct diff_options *o)\n \t\t}\n \t}\n \n-\t/* Might want to warn when both S and G are on; I don't care... */\n \tpickaxe(&diff_queued_diff, o, regexp, kws,\n \t\t(opts & DIFF_PICKAXE_KIND_G) ? diff_grep : has_changes);\n \n-- \n2.16.0.rc0.223.g4a4ac83678-goog\n\n"},{"id":"335889","messageId":"20180104225044.191220-3-sbeller@google.com","threadId":"47525","inReplyTo":"20180104225044.191220-1-sbeller@google.com","subject":"[PATCHv2 2/6] diff: migrate diff_flags.pickaxe_ignore_case to a pickaxe_opts bit","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-04T22:50:40Z","receivedAt":"2018-01-04T22:53:20Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Currently flags for pickaxing are found in different places. Unify the\nflags into the `pickaxe_opts` field, which will contain any pickaxe related\nflags.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n diff.h             | 3 ++-\n diffcore-pickaxe.c | 6 +++---\n revision.c         | 2 +-\n 3 files changed, 6 insertions(+), 5 deletions(-)\n\ndiff --git a/diff.h b/diff.h\nindex ea310f76fd..8af1213684 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -91,7 +91,6 @@ struct diff_flags {\n \tunsigned override_submodule_config:1;\n \tunsigned dirstat_by_line:1;\n \tunsigned funccontext:1;\n-\tunsigned pickaxe_ignore_case:1;\n \tunsigned default_follow_renames:1;\n };\n \n@@ -327,6 +326,8 @@ extern void diff_setup_done(struct diff_options *);\n #define DIFF_PICKAXE_KIND_S\t4 /* traditional plumbing counter */\n #define DIFF_PICKAXE_KIND_G\t8 /* grep in the patch */\n \n+#define DIFF_PICKAXE_IGNORE_CASE\t32\n+\n extern void diffcore_std(struct diff_options *);\n extern void diffcore_fix_diff_index(struct diff_options *);\n \ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\nindex 9476bd2108..4b5d88ea46 100644\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -222,11 +222,11 @@ void diffcore_pickaxe(struct diff_options *o)\n \n \tif (opts & (DIFF_PICKAXE_REGEX | DIFF_PICKAXE_KIND_G)) {\n \t\tint cflags = REG_EXTENDED | REG_NEWLINE;\n-\t\tif (o->flags.pickaxe_ignore_case)\n+\t\tif (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE)\n \t\t\tcflags |= REG_ICASE;\n \t\tregcomp_or_die(&regex, needle, cflags);\n \t\tregexp = &regex;\n-\t} else if (o->flags.pickaxe_ignore_case &&\n+\t} else if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE &&\n \t\t   has_non_ascii(needle)) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint cflags = REG_NEWLINE | REG_ICASE;\n@@ -236,7 +236,7 @@ void diffcore_pickaxe(struct diff_options *o)\n \t\tstrbuf_release(&sb);\n \t\tregexp = &regex;\n \t} else {\n-\t\tkws = kwsalloc(o->flags.pickaxe_ignore_case\n+\t\tkws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE\n \t\t\t       ? tolower_trans_tbl : NULL);\n \t\tkwsincr(kws, needle, strlen(needle));\n \t\tkwsprep(kws);\ndiff --git a/revision.c b/revision.c\nindex e2e691dd5a..ccf1d212ce 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2076,7 +2076,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\trevs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;\n \t} else if (!strcmp(arg, \"--regexp-ignore-case\") || !strcmp(arg, \"-i\")) {\n \t\trevs->grep_filter.ignore_case = 1;\n-\t\trevs->diffopt.flags.pickaxe_ignore_case = 1;\n+\t\trevs->diffopt.pickaxe_opts |= DIFF_PICKAXE_IGNORE_CASE;\n \t} else if (!strcmp(arg, \"--fixed-strings\") || !strcmp(arg, \"-F\")) {\n \t\trevs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED;\n \t} else if (!strcmp(arg, \"--perl-regexp\") || !strcmp(arg, \"-P\")) {\n-- \n2.16.0.rc0.223.g4a4ac83678-goog\n\n"},{"id":"335890","messageId":"20180104225044.191220-7-sbeller@google.com","threadId":"47525","inReplyTo":"20180104225044.191220-1-sbeller@google.com","subject":"[PATCHv2 6/6] diff: use HAS_MULTI_BITS instead of counting bits manually","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-01-04T22:50:44Z","receivedAt":"2018-01-04T22:53:22Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This aligns the style to the previous patch.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n diff.c | 15 +++++----------\n 1 file changed, 5 insertions(+), 10 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 42858d4c7d..c2ee81a1fa 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -4107,20 +4107,15 @@ void diff_setup(struct diff_options *options)\n \n void diff_setup_done(struct diff_options *options)\n {\n-\tint count = 0;\n+\tunsigned check_mask = DIFF_FORMAT_NAME |\n+\t\t\t      DIFF_FORMAT_NAME_STATUS |\n+\t\t\t      DIFF_FORMAT_CHECKDIFF |\n+\t\t\t      DIFF_FORMAT_NO_OUTPUT;\n \n \tif (options->set_default)\n \t\toptions->set_default(options);\n \n-\tif (options->output_format & DIFF_FORMAT_NAME)\n-\t\tcount++;\n-\tif (options->output_format & DIFF_FORMAT_NAME_STATUS)\n-\t\tcount++;\n-\tif (options->output_format & DIFF_FORMAT_CHECKDIFF)\n-\t\tcount++;\n-\tif (options->output_format & DIFF_FORMAT_NO_OUTPUT)\n-\t\tcount++;\n-\tif (count > 1)\n+\tif (HAS_MULTI_BITS(options->output_format & check_mask))\n \t\tdie(_(\"--name-only, --name-status, --check and -s are mutually exclusive\"));\n \n \tif (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK))\n-- \n2.16.0.rc0.223.g4a4ac83678-goog\n\n"}]}