{"thread":{"id":"39713","subject":"[PATCH v5 00/11] add options to for-each-ref","startedAt":"2015-06-24T19:51:52Z","lastAt":"2015-06-25T08:40:06Z","messageCount":14,"participants":["Karthik Nayak","Junio C Hamano"],"isPatch":true,"patchVersion":5,"patchTotal":11},"messages":[{"id":"264762","messageId":"CAOLa=ZTbXCRFx6xEv+cB7DQhu92=ePb_MJ5zeEjsr_-=HPAU6g@mail.gmail.com","threadId":"39713","inReplyTo":null,"subject":"[PATCH v5 00/11] add options to for-each-ref","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:51:52Z","receivedAt":"2015-06-24T19:51:52Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"v4 of this patch series can be found here :\nhttp://article.gmane.org/gmane.comp.version-control.git/272285\n\nThis is a continuation of my GSoC project to unify git tag -l, git\nbranch -l and for-each-ref. Continued from this patch series :\nhttp://thread.gmane.org/gmane.comp.version-control.git/271563\n\nChanges in v5:\n03/11: grammatical changes.\n04/11: grammatical changes.\n05/11: remove unnecessary if statement and remove caps used in commit message.\n06/11: use xcalloc instead of using a commit_list as we know the size\nof the array.\n07/11: s/<object>/[<object>]/ as the object is optional.\n08/11: rename parse_opt_commit_object_name() to parse_opt_commits().\n09/11: make branch.c also to use the macros.\n11/11: s/<object>/[<object>]/ as the object is optional.\n\nThanks to Matthieu, Christian, Junio and Eric for the input on the\nlast iteration.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"264764","messageId":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","threadId":"39713","inReplyTo":"CAOLa=ZTbXCRFx6xEv+cB7DQhu92=ePb_MJ5zeEjsr_-=HPAU6g@mail.gmail.com","subject":"[PATCH v5 01/11] t6301: for-each-ref tests for ref-filter APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:53:42Z","receivedAt":"2015-06-24T19:53:42Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add a test suite for testing the ref-filter APIs used\nby for-each-ref. We just intialize the test suite for now.\nMore tests will be added in the following patches as more\noptions are added to for-each-ref.\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n t/t6301-for-each-ref-filter.sh | 19 +++++++++++++++++++\n 1 file changed, 19 insertions(+)\n create mode 100755 t/t6301-for-each-ref-filter.sh\n\ndiff --git a/t/t6301-for-each-ref-filter.sh b/t/t6301-for-each-ref-filter.sh\nnew file mode 100755\nindex 0000000..b1fa8d4\n--- /dev/null\n+++ b/t/t6301-for-each-ref-filter.sh\n@@ -0,0 +1,19 @@\n+#!/bin/sh\n+\n+test_description='test for-each-refs usage of ref-filter APIs'\n+\n+. ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-gpg.sh\n+\n+test_expect_success 'setup some history and refs' '\n+\ttest_commit one &&\n+\ttest_commit two &&\n+\ttest_commit three &&\n+\tgit checkout -b side &&\n+\ttest_commit four &&\n+\tgit tag -s -m \"A signed tag message\" signed-tag &&\n+\tgit checkout master &&\n+\tgit update-ref refs/odd/spot master\n+'\n+\n+test_done\n-- \n2.4.4\n"},{"id":"264768","messageId":"1435175632-27803-2-git-send-email-karthik.188@gmail.com","threadId":"39713","inReplyTo":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v5 02/11] tag: libify parse_opt_points_at()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:53:43Z","receivedAt":"2015-06-24T19:53:43Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Rename 'parse_opt_points_at()' to 'parse_opt_object_name()' and\nmove it from 'tag.c' to 'parse-options'. This now acts as a common\nparse_opt function which accepts an objectname and stores it into\na sha1_array.\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/tag.c      | 21 ++-------------------\n parse-options-cb.c | 17 +++++++++++++++++\n parse-options.h    |  1 +\n 3 files changed, 20 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 5f6cdc5..e36c43e 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -546,23 +546,6 @@ static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)\n \treturn check_refname_format(sb->buf, 0);\n }\n \n-static int parse_opt_points_at(const struct option *opt __attribute__((unused)),\n-\t\t\tconst char *arg, int unset)\n-{\n-\tunsigned char sha1[20];\n-\n-\tif (unset) {\n-\t\tsha1_array_clear(&points_at);\n-\t\treturn 0;\n-\t}\n-\tif (!arg)\n-\t\treturn error(_(\"switch 'points-at' requires an object\"));\n-\tif (get_sha1(arg, sha1))\n-\t\treturn error(_(\"malformed object name '%s'\"), arg);\n-\tsha1_array_append(&points_at, sha1);\n-\treturn 0;\n-}\n-\n static int parse_opt_sort(const struct option *opt, const char *arg, int unset)\n {\n \tint *sort = opt->value;\n@@ -625,8 +608,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\t\tparse_opt_with_commit, (intptr_t)\"HEAD\",\n \t\t},\n \t\t{\n-\t\t\tOPTION_CALLBACK, 0, \"points-at\", NULL, N_(\"object\"),\n-\t\t\tN_(\"print only tags of the object\"), 0, parse_opt_points_at\n+\t\t\tOPTION_CALLBACK, 0, \"points-at\", &points_at, N_(\"object\"),\n+\t\t\tN_(\"print only tags of the object\"), 0, parse_opt_object_name\n \t\t},\n \t\tOPT_END()\n \t};\ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex be8c413..de75411 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -4,6 +4,7 @@\n #include \"commit.h\"\n #include \"color.h\"\n #include \"string-list.h\"\n+#include \"sha1-array.h\"\n \n /*----- some often used options -----*/\n \n@@ -92,6 +93,22 @@ int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+int parse_opt_object_name(const struct option *opt, const char *arg, int unset)\n+{\n+\tunsigned char sha1[20];\n+\n+\tif (unset) {\n+\t\tsha1_array_clear(opt->value);\n+\t\treturn 0;\n+\t}\n+\tif (!arg)\n+\t\treturn -1;\n+\tif (get_sha1(arg, sha1))\n+\t\treturn error(_(\"malformed object name '%s'\"), arg);\n+\tsha1_array_append(opt->value, sha1);\n+\treturn 0;\n+}\n+\n int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)\n {\n \tint *target = opt->value;\ndiff --git a/parse-options.h b/parse-options.h\nindex c71e9da..36c71fe 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -220,6 +220,7 @@ extern int parse_opt_approxidate_cb(const struct option *, const char *, int);\n extern int parse_opt_expiry_date_cb(const struct option *, const char *, int);\n extern int parse_opt_color_flag_cb(const struct option *, const char *, int);\n extern int parse_opt_verbosity_cb(const struct option *, const char *, int);\n+extern int parse_opt_object_name(const struct option *, const char *, int);\n extern int parse_opt_with_commit(const struct option *, const char *, int);\n extern int parse_opt_tertiary(const struct option *, const char *, int);\n extern int parse_opt_string_list(const struct option *, const char *, int);\n-- \n2.4.4\n"},{"id":"264765","messageId":"1435175632-27803-3-git-send-email-karthik.188@gmail.com","threadId":"39713","inReplyTo":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v5 03/11] ref-filter: implement '--points-at' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:53:44Z","receivedAt":"2015-06-24T19:53:44Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"In 'tag -l' we have '--points-at' option which lets users\nlist only tags which point to a particular commit. Implement\nthis option in 'ref-filter.{c,h}' so that other commands can\nbenefit from this.\n\nThis is duplicated from tag.c, we will eventually remove that\nwhen we port tag.c to use ref-filter APIs.\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/tag.c |  4 ++++\n ref-filter.c  | 26 ++++++++++++++++++++++++++\n ref-filter.h  |  1 +\n 3 files changed, 31 insertions(+)\n\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex e36c43e..280981f 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -56,6 +56,10 @@ static int match_pattern(const char **patterns, const char *ref)\n \treturn 0;\n }\n \n+/*\n+ * This is currently duplicated in ref-filter.c, and will eventually be\n+ * removed as we port tag.c to use the ref-filter APIs.\n+ */\n static const unsigned char *match_points_at(const char *refname,\n \t\t\t\t\t    const unsigned char *sha1)\n {\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 43502a4..f40f06e 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -842,6 +842,29 @@ static int match_name_as_path(const char **pattern, const char *refname)\n \treturn 0;\n }\n \n+/*\n+ * Given a ref (sha1, refname) see if it points to one of the sha1s\n+ * in a sha1_array.\n+ */\n+static int match_points_at(struct sha1_array *points_at, const unsigned char *sha1,\n+\t\t\t   const char *refname)\n+{\n+\tstruct object *obj;\n+\n+\tif (!points_at || !points_at->nr)\n+\t\treturn 1;\n+\n+\tif (sha1_array_lookup(points_at, sha1) >= 0)\n+\t\treturn 1;\n+\n+\tobj = parse_object_or_die(sha1, refname);\n+\tif (obj->type == OBJ_TAG &&\n+\t    sha1_array_lookup(points_at, ((struct tag *)obj)->tagged->sha1) >= 0)\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n /* Allocate space for a new ref_array_item and copy the objectname and flag to it */\n static struct ref_array_item *new_ref_array_item(const char *refname,\n \t\t\t\t\t\t const unsigned char *objectname,\n@@ -875,6 +898,9 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,\n \tif (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))\n \t\treturn 0;\n \n+\tif (!match_points_at(&filter->points_at, oid->hash, refname))\n+\t\treturn 0;\n+\n \t/*\n \t * We do not open the object yet; sort may only need refname\n \t * to do its job and the resulting list may yet to be pruned\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 6997984..c2856b8 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -42,6 +42,7 @@ struct ref_array {\n \n struct ref_filter {\n \tconst char **name_patterns;\n+\tstruct sha1_array points_at;\n };\n \n struct ref_filter_cbdata {\n-- \n2.4.4\n"},{"id":"264766","messageId":"1435175632-27803-4-git-send-email-karthik.188@gmail.com","threadId":"39713","inReplyTo":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v5 04/11] for-each-ref: add '--points-at' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:53:45Z","receivedAt":"2015-06-24T19:53:45Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add the '--points-at' option provided by 'ref-filter'. The\noption lets the user to pick only refs which point to a particular\ncommit.\n\nAdd documentation and tests for the same.\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  3 +++\n builtin/for-each-ref.c             |  9 +++++++--\n t/t6301-for-each-ref-filter.sh     | 20 ++++++++++++++++++++\n 3 files changed, 30 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 7f8d9a5..0ede41d 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -10,6 +10,7 @@ SYNOPSIS\n [verse]\n 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]\n \t\t   [(--sort=<key>)...] [--format=<format>] [<pattern>...]\n+\t\t   [--points-at <object>]\n \n DESCRIPTION\n -----------\n@@ -62,6 +63,8 @@ OPTIONS\n \tthe specified host language.  This is meant to produce\n \ta scriptlet that can directly be `eval`ed.\n \n+--points-at <object>::\n+\tOnly list refs pointing to the given object.\n \n FIELD NAMES\n -----------\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 7919206..46f9b05 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -7,6 +7,7 @@\n \n static char const * const for_each_ref_usage[] = {\n \tN_(\"git for-each-ref [<options>] [<pattern>]\"),\n+\tN_(\"git for-each-ref [--points-at <object>]\"),\n \tNULL\n };\n \n@@ -34,9 +35,15 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(  0 , \"format\", &format, N_(\"format\"), N_(\"format to use for the output\")),\n \t\tOPT_CALLBACK(0 , \"sort\", sorting_tail, N_(\"key\"),\n \t\t\t    N_(\"field name to sort on\"), &parse_opt_ref_sorting),\n+\t\tOPT_CALLBACK(0, \"points-at\", &filter.points_at,\n+\t\t\t     N_(\"object\"), N_(\"print only refs pointing to the given object\"),\n+\t\t\t     parse_opt_object_name),\n \t\tOPT_END(),\n \t};\n \n+\tmemset(&array, 0, sizeof(array));\n+\tmemset(&filter, 0, sizeof(filter));\n+\n \tparse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);\n \tif (maxcount < 0) {\n \t\terror(\"invalid --count argument: `%d'\", maxcount);\n@@ -55,8 +62,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t/* for warn_ambiguous_refs */\n \tgit_config(git_default_config, NULL);\n \n-\tmemset(&array, 0, sizeof(array));\n-\tmemset(&filter, 0, sizeof(filter));\n \tfilter.name_patterns = argv;\n \tfilter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);\n \tref_array_sort(sorting, &array);\ndiff --git a/t/t6301-for-each-ref-filter.sh b/t/t6301-for-each-ref-filter.sh\nindex b1fa8d4..7269a66 100755\n--- a/t/t6301-for-each-ref-filter.sh\n+++ b/t/t6301-for-each-ref-filter.sh\n@@ -16,4 +16,24 @@ test_expect_success 'setup some history and refs' '\n \tgit update-ref refs/odd/spot master\n '\n \n+test_expect_success 'filtering with --points-at' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/master\n+\trefs/odd/spot\n+\trefs/tags/three\n+\tEOF\n+\tgit for-each-ref --format=\"%(refname)\" --points-at=master >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'check signed tags with --points-at' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/side\n+\trefs/tags/four\n+\trefs/tags/signed-tag four\n+\tEOF\n+\tgit for-each-ref --format=\"%(refname) %(*subject)\" --points-at=side >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.4\n"},{"id":"264769","messageId":"1435175632-27803-5-git-send-email-karthik.188@gmail.com","threadId":"39713","inReplyTo":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v5 05/11] ref-filter: add parse_opt_merge_filter()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:53:46Z","receivedAt":"2015-06-24T19:53:46Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add 'parse_opt_merge_filter()' to parse '--merged' and '--no-merged'\noptions and write macros for the same.\n\nThis is copied from 'builtin/branch.c' which will eventually be removed\nwhen we port 'branch.c' to use ref-filter APIs.\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/branch.c |  4 ++++\n ref-filter.c     | 19 +++++++++++++++++++\n ref-filter.h     | 11 +++++++++++\n 3 files changed, 34 insertions(+)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex b42e5b6..ddd90e6 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -745,6 +745,10 @@ static void rename_branch(const char *oldname, const char *newname, int force)\n \tstrbuf_release(&newsection);\n }\n \n+/*\n+ * This function is duplicated in ref-filter. It will eventually be removed\n+ * when we port branch.c to use ref-filter APIs.\n+ */\n static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)\n {\n \tmerge_filter = ((opt->long_name[0] == 'n')\ndiff --git a/ref-filter.c b/ref-filter.c\nindex f40f06e..0c2d67c 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1125,3 +1125,22 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)\n \ts->atom = parse_ref_filter_atom(arg, arg+len);\n \treturn 0;\n }\n+\n+int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct ref_filter *rf = opt->value;\n+\tunsigned char sha1[20];\n+\n+\trf->merge = starts_with(opt->long_name, \"no\")\n+\t\t? REF_FILTER_MERGED_OMIT\n+\t\t: REF_FILTER_MERGED_INCLUDE;\n+\n+\tif (get_sha1(arg, sha1))\n+\t\tdie(_(\"malformed object name %s\"), arg);\n+\n+\trf->merge_commit = lookup_commit_reference_gently(sha1, 0);\n+\tif (!rf->merge_commit)\n+\t\treturn opterror(opt, \"must point to a commit\", 0);\n+\n+\treturn 0;\n+}\ndiff --git a/ref-filter.h b/ref-filter.h\nindex c2856b8..ad2902b 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -50,6 +50,15 @@ struct ref_filter_cbdata {\n \tstruct ref_filter *filter;\n };\n \n+/*  Macros for checking --merged and --no-merged options */\n+#define _OPT_MERGED_NO_MERGED(option, filter, h)\t\t\t\t\\\n+\t{ OPTION_CALLBACK, 0, option, (filter), N_(\"commit\"), (h), \\\n+\t  PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, \\\n+\t  parse_opt_merge_filter, (intptr_t) \"HEAD\" \\\n+\t}\n+#define OPT_MERGED(f, h) _OPT_MERGED_NO_MERGED(\"merged\", f, h)\n+#define OPT_NO_MERGED(f, h) _OPT_MERGED_NO_MERGED(\"no-merged\", f, h)\n+\n /*\n  * API for filtering a set of refs. Based on the type of refs the user\n  * has requested, we iterate through those refs and apply filters\n@@ -71,5 +80,7 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);\n /*  Default sort option based on refname */\n struct ref_sorting *ref_default_sorting(void);\n+/*  Function to parse --merged and --no-merged options */\n+int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);\n \n #endif /*  REF_FILTER_H  */\n-- \n2.4.4\n"},{"id":"264767","messageId":"1435175632-27803-6-git-send-email-karthik.188@gmail.com","threadId":"39713","inReplyTo":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v5 06/11] ref-filter: implement '--merged' and '--no-merged' options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:53:47Z","receivedAt":"2015-06-24T19:53:47Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"In 'branch -l' we have '--merged' option which only lists refs (branches)\nmerged into the named commit and '--no-merged' option which only lists\nrefs (branches) not merged into the named commit. Implement these two\noptions in ref-filter.{c,h} so that other commands can benefit from this.\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n ref-filter.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----\n ref-filter.h |  8 +++++++\n 2 files changed, 77 insertions(+), 4 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 0c2d67c..2f20cb3 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -9,6 +9,7 @@\n #include \"tag.h\"\n #include \"quote.h\"\n #include \"ref-filter.h\"\n+#include \"revision.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n \n@@ -889,6 +890,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_filter *filter = ref_cbdata->filter;\n \tstruct ref_array_item *ref;\n+\tstruct commit *commit = NULL;\n \n \tif (flag & REF_BAD_NAME) {\n \t\t  warning(\"ignoring ref with broken name %s\", refname);\n@@ -902,11 +904,23 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,\n \t\treturn 0;\n \n \t/*\n+\t * A merge filter is applied on refs pointing to commits. Hence\n+\t * obtain the commit using the 'oid' available and discard all\n+\t * non-commits early. The actual filtering is done later.\n+\t */\n+\tif (filter->merge_commit) {\n+\t\tcommit = lookup_commit_reference_gently(oid->hash, 1);\n+\t\tif (!commit)\n+\t\t\treturn 0;\n+\t}\n+\n+\t/*\n \t * We do not open the object yet; sort may only need refname\n \t * to do its job and the resulting list may yet to be pruned\n \t * by maxcount logic.\n \t */\n \tref = new_ref_array_item(refname, oid->hash, flag);\n+\tref->commit = commit;\n \n \tREALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1);\n \tref_cbdata->array->items[ref_cbdata->array->nr++] = ref;\n@@ -932,6 +946,50 @@ void ref_array_clear(struct ref_array *array)\n \tarray->nr = array->alloc = 0;\n }\n \n+static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)\n+{\n+\tstruct rev_info revs;\n+\tint i, old_nr;\n+\tstruct ref_filter *filter = ref_cbdata->filter;\n+\tstruct ref_array *array = ref_cbdata->array;\n+\tstruct commit **to_clear = xcalloc(sizeof(struct commit *), array->nr);\n+\n+\tinit_revisions(&revs, NULL);\n+\n+\tfor (i = 0; i < array->nr; i++) {\n+\t\tstruct ref_array_item *item = array->items[i];\n+\t\tadd_pending_object(&revs, &item->commit->object, item->refname);\n+\t\tto_clear[i] = item->commit;\n+\t}\n+\n+\tfilter->merge_commit->object.flags |= UNINTERESTING;\n+\tadd_pending_object(&revs, &filter->merge_commit->object, \"\");\n+\n+\trevs.limited = 1;\n+\tif (prepare_revision_walk(&revs))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\told_nr = array->nr;\n+\tarray->nr = 0;\n+\n+\tfor (i = 0; i < old_nr; i++) {\n+\t\tstruct ref_array_item *item = array->items[i];\n+\t\tstruct commit *commit = item->commit;\n+\n+\t\tint is_merged = !!(commit->object.flags & UNINTERESTING);\n+\n+\t\tif (is_merged == (filter->merge == REF_FILTER_MERGED_INCLUDE))\n+\t\t\tarray->items[array->nr++] = array->items[i];\n+\t\telse\n+\t\t\tfree_array_item(item);\n+\t}\n+\n+\tfor (i = 0; i < old_nr; i++)\n+\t\tclear_commit_marks(to_clear[i], ALL_REV_FLAGS);\n+\tclear_commit_marks(filter->merge_commit, ALL_REV_FLAGS);\n+\tfree(to_clear);\n+}\n+\n /*\n  * API for filtering a set of refs. Based on the type of refs the user\n  * has requested, we iterate through those refs and apply filters\n@@ -941,17 +999,24 @@ void ref_array_clear(struct ref_array *array)\n int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type)\n {\n \tstruct ref_filter_cbdata ref_cbdata;\n+\tint ret = 0;\n \n \tref_cbdata.array = array;\n \tref_cbdata.filter = filter;\n \n+\t/*  Simple per-ref filtering */\n \tif (type & (FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN))\n-\t\treturn for_each_rawref(ref_filter_handler, &ref_cbdata);\n+\t\tret = for_each_rawref(ref_filter_handler, &ref_cbdata);\n \telse if (type & FILTER_REFS_ALL)\n-\t\treturn for_each_ref(ref_filter_handler, &ref_cbdata);\n-\telse\n+\t\tret = for_each_ref(ref_filter_handler, &ref_cbdata);\n+\telse if (type)\n \t\tdie(\"filter_refs: invalid type\");\n-\treturn 0;\n+\n+\t/*  Filters that need revision walking */\n+\tif (filter->merge_commit)\n+\t\tdo_merge_filter(&ref_cbdata);\n+\n+\treturn ret;\n }\n \n static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)\ndiff --git a/ref-filter.h b/ref-filter.h\nindex ad2902b..7241a1d 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -31,6 +31,7 @@ struct ref_array_item {\n \tunsigned char objectname[20];\n \tint flag;\n \tconst char *symref;\n+\tstruct commit *commit;\n \tstruct atom_value *value;\n \tchar refname[FLEX_ARRAY];\n };\n@@ -43,6 +44,13 @@ struct ref_array {\n struct ref_filter {\n \tconst char **name_patterns;\n \tstruct sha1_array points_at;\n+\n+\tenum {\n+\t\tREF_FILTER_MERGED_NONE = 0,\n+\t\tREF_FILTER_MERGED_INCLUDE,\n+\t\tREF_FILTER_MERGED_OMIT\n+\t} merge;\n+\tstruct commit *merge_commit;\n };\n \n struct ref_filter_cbdata {\n-- \n2.4.4\n"},{"id":"264770","messageId":"1435175632-27803-7-git-send-email-karthik.188@gmail.com","threadId":"39713","inReplyTo":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v5 07/11] for-each-ref: add '--merged' and '--no-merged' options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:53:48Z","receivedAt":"2015-06-24T19:53:48Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add the '--merged' and '--no-merged' options provided by 'ref-filter'.\nThe '--merged' option lets the user to only list refs merged into the\nnamed commit. The '--no-merged' option lets the user to only list refs\nnot merged into the named commit.\n\nAdd documentation and tests for the same.\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 10 +++++++++-\n builtin/for-each-ref.c             |  3 +++\n t/t6301-for-each-ref-filter.sh     | 22 ++++++++++++++++++++++\n 3 files changed, 34 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 0ede41d..290a5c1 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n [verse]\n 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]\n \t\t   [(--sort=<key>)...] [--format=<format>] [<pattern>...]\n-\t\t   [--points-at <object>]\n+\t\t   [--points-at <object>] [(--merged | --no-merged) [<object>]]\n \n DESCRIPTION\n -----------\n@@ -66,6 +66,14 @@ OPTIONS\n --points-at <object>::\n \tOnly list refs pointing to the given object.\n \n+--merged [<object>]::\n+\tOnly list refs whose tips are reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--no-merged [<object>]::\n+\tOnly list refs whose tips are not reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n FIELD NAMES\n -----------\n \ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 46f9b05..ba70798 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -8,6 +8,7 @@\n static char const * const for_each_ref_usage[] = {\n \tN_(\"git for-each-ref [<options>] [<pattern>]\"),\n \tN_(\"git for-each-ref [--points-at <object>]\"),\n+\tN_(\"git for-each-ref [(--merged | --no-merged) [<object>]]\"),\n \tNULL\n };\n \n@@ -38,6 +39,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\tOPT_CALLBACK(0, \"points-at\", &filter.points_at,\n \t\t\t     N_(\"object\"), N_(\"print only refs pointing to the given object\"),\n \t\t\t     parse_opt_object_name),\n+\t\tOPT_MERGED(&filter, N_(\"print only refs that are merged\")),\n+\t\tOPT_NO_MERGED(&filter, N_(\"print only refs that are not merged\")),\n \t\tOPT_END(),\n \t};\n \ndiff --git a/t/t6301-for-each-ref-filter.sh b/t/t6301-for-each-ref-filter.sh\nindex 7269a66..f5ccfb9 100755\n--- a/t/t6301-for-each-ref-filter.sh\n+++ b/t/t6301-for-each-ref-filter.sh\n@@ -36,4 +36,26 @@ test_expect_success 'check signed tags with --points-at' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'filtering with --merged' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/master\n+\trefs/odd/spot\n+\trefs/tags/one\n+\trefs/tags/three\n+\trefs/tags/two\n+\tEOF\n+\tgit for-each-ref --format=\"%(refname)\" --merged=master >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'filtering with --no-merged' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/side\n+\trefs/tags/four\n+\trefs/tags/signed-tag\n+\tEOF\n+\tgit for-each-ref --format=\"%(refname)\" --no-merged=master >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.4\n"},{"id":"264771","messageId":"1435175632-27803-8-git-send-email-karthik.188@gmail.com","threadId":"39713","inReplyTo":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v5 08/11] parse-option: rename parse_opt_with_commit()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:53:49Z","receivedAt":"2015-06-24T19:53:49Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Rename parse_opt_with_commit() to parse_opt_commits() to show\nthat it can be used to obtain a list of commits and is not\nconstricted to usage of '--contains' option.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/branch.c   | 4 ++--\n builtin/tag.c      | 4 ++--\n parse-options-cb.c | 2 +-\n parse-options.h    | 2 +-\n 4 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex ddd90e6..a008b0d 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -828,13 +828,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tOPTION_CALLBACK, 0, \"contains\", &with_commit, N_(\"commit\"),\n \t\t\tN_(\"print only branches that contain the commit\"),\n \t\t\tPARSE_OPT_LASTARG_DEFAULT,\n-\t\t\tparse_opt_with_commit, (intptr_t)\"HEAD\",\n+\t\t\tparse_opt_commits, (intptr_t)\"HEAD\",\n \t\t},\n \t\t{\n \t\t\tOPTION_CALLBACK, 0, \"with\", &with_commit, N_(\"commit\"),\n \t\t\tN_(\"print only branches that contain the commit\"),\n \t\t\tPARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,\n-\t\t\tparse_opt_with_commit, (intptr_t) \"HEAD\",\n+\t\t\tparse_opt_commits, (intptr_t) \"HEAD\",\n \t\t},\n \t\tOPT__ABBREV(&abbrev),\n \ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 280981f..7af45a0 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -603,13 +603,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\t\tOPTION_CALLBACK, 0, \"contains\", &with_commit, N_(\"commit\"),\n \t\t\tN_(\"print only tags that contain the commit\"),\n \t\t\tPARSE_OPT_LASTARG_DEFAULT,\n-\t\t\tparse_opt_with_commit, (intptr_t)\"HEAD\",\n+\t\t\tparse_opt_commits, (intptr_t)\"HEAD\",\n \t\t},\n \t\t{\n \t\t\tOPTION_CALLBACK, 0, \"with\", &with_commit, N_(\"commit\"),\n \t\t\tN_(\"print only tags that contain the commit\"),\n \t\t\tPARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,\n-\t\t\tparse_opt_with_commit, (intptr_t)\"HEAD\",\n+\t\t\tparse_opt_commits, (intptr_t)\"HEAD\",\n \t\t},\n \t\t{\n \t\t\tOPTION_CALLBACK, 0, \"points-at\", &points_at, N_(\"object\"),\ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex de75411..632f10f 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -77,7 +77,7 @@ int parse_opt_verbosity_cb(const struct option *opt, const char *arg,\n \treturn 0;\n }\n \n-int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)\n+int parse_opt_commits(const struct option *opt, const char *arg, int unset)\n {\n \tunsigned char sha1[20];\n \tstruct commit *commit;\ndiff --git a/parse-options.h b/parse-options.h\nindex 36c71fe..2ffd857 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -221,7 +221,7 @@ extern int parse_opt_expiry_date_cb(const struct option *, const char *, int);\n extern int parse_opt_color_flag_cb(const struct option *, const char *, int);\n extern int parse_opt_verbosity_cb(const struct option *, const char *, int);\n extern int parse_opt_object_name(const struct option *, const char *, int);\n-extern int parse_opt_with_commit(const struct option *, const char *, int);\n+extern int parse_opt_commits(const struct option *, const char *, int);\n extern int parse_opt_tertiary(const struct option *, const char *, int);\n extern int parse_opt_string_list(const struct option *, const char *, int);\n extern int parse_opt_noop_cb(const struct option *, const char *, int);\n-- \n2.4.4\n"},{"id":"264772","messageId":"1435175632-27803-9-git-send-email-karthik.188@gmail.com","threadId":"39713","inReplyTo":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v5 09/11] parse-options.h: add macros for '--contains' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:53:50Z","receivedAt":"2015-06-24T19:53:50Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add a macro for using the '--contains' option in parse-options.h\nalso include an optional '--with' option macro which performs the\nsame action as '--contains'.\n\nMake tag.c and branch.c use this new macro.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/branch.c | 14 ++------------\n builtin/tag.c    | 14 ++------------\n parse-options.h  |  7 +++++++\n 3 files changed, 11 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex a008b0d..b04e341 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -824,18 +824,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\tOPT__COLOR(&branch_use_color, N_(\"use colored output\")),\n \t\tOPT_SET_INT('r', \"remotes\",     &kinds, N_(\"act on remote-tracking branches\"),\n \t\t\tREF_REMOTE_BRANCH),\n-\t\t{\n-\t\t\tOPTION_CALLBACK, 0, \"contains\", &with_commit, N_(\"commit\"),\n-\t\t\tN_(\"print only branches that contain the commit\"),\n-\t\t\tPARSE_OPT_LASTARG_DEFAULT,\n-\t\t\tparse_opt_commits, (intptr_t)\"HEAD\",\n-\t\t},\n-\t\t{\n-\t\t\tOPTION_CALLBACK, 0, \"with\", &with_commit, N_(\"commit\"),\n-\t\t\tN_(\"print only branches that contain the commit\"),\n-\t\t\tPARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,\n-\t\t\tparse_opt_commits, (intptr_t) \"HEAD\",\n-\t\t},\n+\t\tOPT_CONTAINS(&with_commit, N_(\"print only branches that contain the commit\")),\n+\t\tOPT_WITH(&with_commit, N_(\"print only branches that contain the commit\")),\n \t\tOPT__ABBREV(&abbrev),\n \n \t\tOPT_GROUP(N_(\"Specific git-branch actions:\")),\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 7af45a0..767162e 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -595,23 +595,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \n \t\tOPT_GROUP(N_(\"Tag listing options\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show tag list in columns\")),\n+\t\tOPT_CONTAINS(&with_commit, N_(\"print only tags that contain the commit\")),\n+\t\tOPT_WITH(&with_commit, N_(\"print only tags that contain the commit\")),\n \t\t{\n \t\t\tOPTION_CALLBACK, 0, \"sort\", &tag_sort, N_(\"type\"), N_(\"sort tags\"),\n \t\t\tPARSE_OPT_NONEG, parse_opt_sort\n \t\t},\n \t\t{\n-\t\t\tOPTION_CALLBACK, 0, \"contains\", &with_commit, N_(\"commit\"),\n-\t\t\tN_(\"print only tags that contain the commit\"),\n-\t\t\tPARSE_OPT_LASTARG_DEFAULT,\n-\t\t\tparse_opt_commits, (intptr_t)\"HEAD\",\n-\t\t},\n-\t\t{\n-\t\t\tOPTION_CALLBACK, 0, \"with\", &with_commit, N_(\"commit\"),\n-\t\t\tN_(\"print only tags that contain the commit\"),\n-\t\t\tPARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,\n-\t\t\tparse_opt_commits, (intptr_t)\"HEAD\",\n-\t\t},\n-\t\t{\n \t\t\tOPTION_CALLBACK, 0, \"points-at\", &points_at, N_(\"object\"),\n \t\t\tN_(\"print only tags of the object\"), 0, parse_opt_object_name\n \t\t},\ndiff --git a/parse-options.h b/parse-options.h\nindex 2ffd857..627423f 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -243,5 +243,12 @@ extern int parse_opt_noop_cb(const struct option *, const char *, int);\n \tOPT_COLOR_FLAG(0, \"color\", (var), (h))\n #define OPT_COLUMN(s, l, v, h) \\\n \t{ OPTION_CALLBACK, (s), (l), (v), N_(\"style\"), (h), PARSE_OPT_OPTARG, parseopt_column_callback }\n+#define _OPT_CONTAINS_OR_WITH(name, variable, help, flag)\t\t      \\\n+\t{ OPTION_CALLBACK, 0, name, (variable), N_(\"commit\"), (help), \\\n+\t  PARSE_OPT_LASTARG_DEFAULT | flag, \\\n+\t  parse_opt_commits, (intptr_t) \"HEAD\" \\\n+\t}\n+#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH(\"contains\", v, h, 0)\n+#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH(\"with\", v, h, PARSE_OPT_HIDDEN)\n \n #endif\n-- \n2.4.4\n"},{"id":"264773","messageId":"1435175632-27803-10-git-send-email-karthik.188@gmail.com","threadId":"39713","inReplyTo":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v5 10/11] ref-filter: implement '--contains' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:53:51Z","receivedAt":"2015-06-24T19:53:51Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"'tag -l' and 'branch -l' have two different ways of finding\nout if a certain ref contains a commit. Implement both these\nmethods in ref-filter and give the caller of ref-filter API\nthe option to pick which implementation to be used.\n\n'branch -l' uses 'is_descendant_of()' from commit.c which is\nleft as the default implementation to be used.\n\n'tag -l' uses a more specific algorithm since ffc4b80. This\nimplementation is used whenever the 'with_commit_tag_algo' bit\nis set in 'struct ref_filter'.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n ref-filter.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n ref-filter.h |   3 ++\n 2 files changed, 116 insertions(+), 1 deletion(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 2f20cb3..f793386 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -818,6 +818,114 @@ static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom\n \t*v = &ref->value[atom];\n }\n \n+enum contains_result {\n+\tCONTAINS_UNKNOWN = -1,\n+\tCONTAINS_NO = 0,\n+\tCONTAINS_YES = 1\n+};\n+\n+/*\n+ * Mimicking the real stack, this stack lives on the heap, avoiding stack\n+ * overflows.\n+ *\n+ * At each recursion step, the stack items points to the commits whose\n+ * ancestors are to be inspected.\n+ */\n+struct contains_stack {\n+\tint nr, alloc;\n+\tstruct contains_stack_entry {\n+\t\tstruct commit *commit;\n+\t\tstruct commit_list *parents;\n+\t} *contains_stack;\n+};\n+\n+static int in_commit_list(const struct commit_list *want, struct commit *c)\n+{\n+\tfor (; want; want = want->next)\n+\t\tif (!hashcmp(want->item->object.sha1, c->object.sha1))\n+\t\t\treturn 1;\n+\treturn 0;\n+}\n+\n+/*\n+ * Test whether the candidate or one of its parents is contained in the list.\n+ * Do not recurse to find out, though, but return -1 if inconclusive.\n+ */\n+static enum contains_result contains_test(struct commit *candidate,\n+\t\t\t    const struct commit_list *want)\n+{\n+\t/* was it previously marked as containing a want commit? */\n+\tif (candidate->object.flags & TMP_MARK)\n+\t\treturn 1;\n+\t/* or marked as not possibly containing a want commit? */\n+\tif (candidate->object.flags & UNINTERESTING)\n+\t\treturn 0;\n+\t/* or are we it? */\n+\tif (in_commit_list(want, candidate)) {\n+\t\tcandidate->object.flags |= TMP_MARK;\n+\t\treturn 1;\n+\t}\n+\n+\tif (parse_commit(candidate) < 0)\n+\t\treturn 0;\n+\n+\treturn -1;\n+}\n+\n+static void push_to_contains_stack(struct commit *candidate, struct contains_stack *contains_stack)\n+{\n+\tALLOC_GROW(contains_stack->contains_stack, contains_stack->nr + 1, contains_stack->alloc);\n+\tcontains_stack->contains_stack[contains_stack->nr].commit = candidate;\n+\tcontains_stack->contains_stack[contains_stack->nr++].parents = candidate->parents;\n+}\n+\n+static enum contains_result contains_tag_algo(struct commit *candidate,\n+\t\tconst struct commit_list *want)\n+{\n+\tstruct contains_stack contains_stack = { 0, 0, NULL };\n+\tint result = contains_test(candidate, want);\n+\n+\tif (result != CONTAINS_UNKNOWN)\n+\t\treturn result;\n+\n+\tpush_to_contains_stack(candidate, &contains_stack);\n+\twhile (contains_stack.nr) {\n+\t\tstruct contains_stack_entry *entry = &contains_stack.contains_stack[contains_stack.nr - 1];\n+\t\tstruct commit *commit = entry->commit;\n+\t\tstruct commit_list *parents = entry->parents;\n+\n+\t\tif (!parents) {\n+\t\t\tcommit->object.flags |= UNINTERESTING;\n+\t\t\tcontains_stack.nr--;\n+\t\t}\n+\t\t/*\n+\t\t * If we just popped the stack, parents->item has been marked,\n+\t\t * therefore contains_test will return a meaningful 0 or 1.\n+\t\t */\n+\t\telse switch (contains_test(parents->item, want)) {\n+\t\tcase CONTAINS_YES:\n+\t\t\tcommit->object.flags |= TMP_MARK;\n+\t\t\tcontains_stack.nr--;\n+\t\t\tbreak;\n+\t\tcase CONTAINS_NO:\n+\t\t\tentry->parents = parents->next;\n+\t\t\tbreak;\n+\t\tcase CONTAINS_UNKNOWN:\n+\t\t\tpush_to_contains_stack(parents->item, &contains_stack);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tfree(contains_stack.contains_stack);\n+\treturn contains_test(candidate, want);\n+}\n+\n+static int commit_contains(struct ref_filter *filter, struct commit *commit)\n+{\n+\tif (filter->with_commit_tag_algo)\n+\t\treturn contains_tag_algo(commit, filter->with_commit);\n+\treturn is_descendant_of(commit, filter->with_commit);\n+}\n+\n /*\n  * Return 1 if the refname matches one of the patterns, otherwise 0.\n  * A pattern can be path prefix (e.g. a refname \"refs/heads/master\"\n@@ -908,10 +1016,14 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,\n \t * obtain the commit using the 'oid' available and discard all\n \t * non-commits early. The actual filtering is done later.\n \t */\n-\tif (filter->merge_commit) {\n+\tif (filter->merge_commit || filter->with_commit) {\n \t\tcommit = lookup_commit_reference_gently(oid->hash, 1);\n \t\tif (!commit)\n \t\t\treturn 0;\n+\t\t/* We perform the filtering for the '--contains' option */\n+\t\tif (filter->with_commit &&\n+\t\t    !commit_contains(filter, commit))\n+\t\t\treturn 0;\n \t}\n \n \t/*\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 7241a1d..3c59431 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -44,6 +44,7 @@ struct ref_array {\n struct ref_filter {\n \tconst char **name_patterns;\n \tstruct sha1_array points_at;\n+\tstruct commit_list *with_commit;\n \n \tenum {\n \t\tREF_FILTER_MERGED_NONE = 0,\n@@ -51,6 +52,8 @@ struct ref_filter {\n \t\tREF_FILTER_MERGED_OMIT\n \t} merge;\n \tstruct commit *merge_commit;\n+\n+\tunsigned int with_commit_tag_algo: 1;\n };\n \n struct ref_filter_cbdata {\n-- \n2.4.4\n"},{"id":"264774","messageId":"1435175632-27803-11-git-send-email-karthik.188@gmail.com","threadId":"39713","inReplyTo":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v5 11/11] for-each-ref: add '--contains' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-24T19:53:52Z","receivedAt":"2015-06-24T19:53:52Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add the '--contains' option provided by 'ref-filter'. The '--contains'\noption lists only refs which contain the mentioned commit (HEAD if no\ncommit is explicitly given).\n\nAdd documentation and tests for the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  5 +++++\n builtin/for-each-ref.c             |  2 ++\n t/t6301-for-each-ref-filter.sh     | 14 ++++++++++++++\n 3 files changed, 21 insertions(+)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 290a5c1..94bb5bd 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -11,6 +11,7 @@ SYNOPSIS\n 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]\n \t\t   [(--sort=<key>)...] [--format=<format>] [<pattern>...]\n \t\t   [--points-at <object>] [(--merged | --no-merged) [<object>]]\n+\t\t   [--contains [<object>]]\n \n DESCRIPTION\n -----------\n@@ -74,6 +75,10 @@ OPTIONS\n \tOnly list refs whose tips are not reachable from the\n \tspecified commit (HEAD if not specified).\n \n+--contains [<object>]::\n+\tOnly list tags which contain the specified commit (HEAD if not\n+\tspecified).\n+\n FIELD NAMES\n -----------\n \ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex ba70798..5868c48 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -9,6 +9,7 @@ static char const * const for_each_ref_usage[] = {\n \tN_(\"git for-each-ref [<options>] [<pattern>]\"),\n \tN_(\"git for-each-ref [--points-at <object>]\"),\n \tN_(\"git for-each-ref [(--merged | --no-merged) [<object>]]\"),\n+\tN_(\"git for-each-ref [--contains [<object>]]\"),\n \tNULL\n };\n \n@@ -41,6 +42,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\t\t     parse_opt_object_name),\n \t\tOPT_MERGED(&filter, N_(\"print only refs that are merged\")),\n \t\tOPT_NO_MERGED(&filter, N_(\"print only refs that are not merged\")),\n+\t\tOPT_CONTAINS(&filter.with_commit, N_(\"print only refs which contain the commit\")),\n \t\tOPT_END(),\n \t};\n \ndiff --git a/t/t6301-for-each-ref-filter.sh b/t/t6301-for-each-ref-filter.sh\nindex f5ccfb9..50ee115 100755\n--- a/t/t6301-for-each-ref-filter.sh\n+++ b/t/t6301-for-each-ref-filter.sh\n@@ -58,4 +58,18 @@ test_expect_success 'filtering with --no-merged' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'filtering with --contains' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/master\n+\trefs/heads/side\n+\trefs/odd/spot\n+\trefs/tags/four\n+\trefs/tags/signed-tag\n+\trefs/tags/three\n+\trefs/tags/two\n+\tEOF\n+\tgit for-each-ref --format=\"%(refname)\" --contains=two >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.4\n"},{"id":"264802","messageId":"xmqqoak48wwc.fsf@gitster.dls.corp.google.com","threadId":"39713","inReplyTo":"1435175632-27803-1-git-send-email-karthik.188@gmail.com","subject":"Re: [PATCH v5 01/11] t6301: for-each-ref tests for ref-filter APIs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-25T00:16:51Z","receivedAt":"2015-06-25T00:16:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n>  t/t6301-for-each-ref-filter.sh | 19 +++++++++++++++++++\n\nt6301 is already used in mh/reporting-broken-refs-from-for-each-ref\ntopic.  Please renumber.\n"},{"id":"264811","messageId":"CAOLa=ZQpLJWPVQK3X+UdShf2bVFxQ2riVVqcAHgevp5KqKPPLQ@mail.gmail.com","threadId":"39713","inReplyTo":"xmqqoak48wwc.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v5 01/11] t6301: for-each-ref tests for ref-filter APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-25T08:40:06Z","receivedAt":"2015-06-25T08:40:06Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Thu, Jun 25, 2015 at 5:46 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>>  t/t6301-for-each-ref-filter.sh | 19 +++++++++++++++++++\n>\n> t6301 is already used in mh/reporting-broken-refs-from-for-each-ref\n> topic.  Please renumber.\n>\n\nWill do :) Thanks\n\n-- \nRegards,\nKarthik Nayak\n"}]}