{"thread":{"id":"39801","subject":"[PATCH v8 00/11] add options to for-each-ref","startedAt":"2015-07-07T16:05:21Z","lastAt":"2015-07-09T06:03:21Z","messageCount":18,"participants":["Karthik Nayak","Torsten Bögershausen","Matthieu Moy"],"isPatch":true,"patchVersion":8,"patchTotal":11},"messages":[{"id":"265736","messageId":"CAOLa=ZTP+=aQL_JW4+O7jUh5jTD1bWpk7xbguYUsW1DkotAafA@mail.gmail.com","threadId":"39801","inReplyTo":null,"subject":"[PATCH v8 00/11] add options to for-each-ref","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:05:21Z","receivedAt":"2015-07-07T16:05:21Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"v7 of this patch series can be found here :\nhttp://article.gmane.org/gmane.comp.version-control.git/273233\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 v8:\n[04/11]: Change \"pertain to\" to \"points at\", Grammatical change.\n[05/11], [04/11]: Random spaces left in macro definition\n[10/11]: Mention the movement of code from tag.c\n\nThanks to Matthieu Moy for suggestions on the previous versions.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"265737","messageId":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"CAOLa=ZTP+=aQL_JW4+O7jUh5jTD1bWpk7xbguYUsW1DkotAafA@mail.gmail.com","subject":"[PATCH v8 01/11] t6302: for-each-ref tests for ref-filter APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:06:07Z","receivedAt":"2015-07-07T16:06:07Z","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/t6302-for-each-ref-filter.sh | 20 ++++++++++++++++++++\n 1 file changed, 20 insertions(+)\n create mode 100755 t/t6302-for-each-ref-filter.sh\n\ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nnew file mode 100755\nindex 0000000..44d2f24\n--- /dev/null\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -0,0 +1,20 @@\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 tag -s -m \"Annonated doubly\" double-tag signed-tag &&\n+\tgit checkout master &&\n+\tgit update-ref refs/odd/spot master\n+'\n+\n+test_done\n-- \n2.4.5\n"},{"id":"265739","messageId":"1436285177-12279-2-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v8 02/11] tag: libify parse_opt_points_at()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:06:08Z","receivedAt":"2015-07-07T16:06:08Z","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 ca865f6..1478818 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -223,6 +223,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.5\n"},{"id":"265740","messageId":"1436285177-12279-3-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v8 03/11] ref-filter: implement '--points-at' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:06:09Z","receivedAt":"2015-07-07T16:06:09Z","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 of a given object. Implement this option in\n'ref-filter.{c,h}' so that other commands can benefit 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  | 35 +++++++++++++++++++++++++++++++++++\n ref-filter.h  |  1 +\n 3 files changed, 40 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 c4004db..58e532c 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -842,6 +842,38 @@ static int match_name_as_path(const char **pattern, const char *refname)\n \treturn 0;\n }\n \n+/*\n+ * Given a ref (sha1, refname), check if the ref belongs to the array\n+ * of sha1s. If the given ref is a tag, check if the given tag points\n+ * at one of the sha1s in the given sha1 array.\n+ * the given sha1_array.\n+ * NEEDSWORK:\n+ * 1. Only a single level of inderection is obtained, we might want to\n+ * change this to account for multiple levels (e.g. annotated tags\n+ * pointing to annotated tags pointing to a commit.)\n+ * 2. As the refs are cached we might know what refname peels to without\n+ * the need to parse the object via parse_object(). peel_ref() might be a\n+ * more efficient alternative to obtain the pointee.\n+ */\n+static const unsigned char *match_points_at(struct sha1_array *points_at,\n+\t\t\t\t\t    const unsigned char *sha1,\n+\t\t\t\t\t    const char *refname)\n+{\n+\tconst unsigned char *tagged_sha1 = NULL;\n+\tstruct object *obj;\n+\n+\tif (sha1_array_lookup(points_at, sha1) >= 0)\n+\t\treturn sha1;\n+\tobj = parse_object(sha1);\n+\tif (!obj)\n+\t\tdie(_(\"malformed object at '%s'\"), refname);\n+\tif (obj->type == OBJ_TAG)\n+\t\ttagged_sha1 = ((struct tag *)obj)->tagged->sha1;\n+\tif (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)\n+\t\treturn tagged_sha1;\n+\treturn NULL;\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@@ -880,6 +912,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 (filter->points_at.nr && !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.5\n"},{"id":"265741","messageId":"1436285177-12279-4-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v8 04/11] for-each-ref: add '--points-at' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:06:10Z","receivedAt":"2015-07-07T16:06:10Z","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 list only refs which points at the\ngiven object.\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/t6302-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..ff0283b 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 which points at the given object.\n \n FIELD NAMES\n -----------\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 7919206..ae5419e 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 which points at 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/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex 44d2f24..457991f 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -17,4 +17,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+\tsed -e \"s/Z$//\" >expect <<-\\EOF &&\n+\trefs/heads/side Z\n+\trefs/tags/four Z\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.5\n"},{"id":"265742","messageId":"1436285177-12279-5-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v8 05/11] ref-filter: add parse_opt_merge_filter()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:06:11Z","receivedAt":"2015-07-07T16:06:11Z","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 58e532c..b4753ab 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1139,3 +1139,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..443cfa7 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) \\\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.5\n"},{"id":"265748","messageId":"1436285177-12279-6-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v8 06/11] ref-filter: implement '--merged' and '--no-merged' options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:06:12Z","receivedAt":"2015-07-07T16:06:12Z","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 builtin/branch.c |  4 ++++\n ref-filter.c     | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++----\n ref-filter.h     |  8 +++++++\n 3 files changed, 81 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex ddd90e6..e63102e 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -635,6 +635,10 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru\n \tcb.pattern = pattern;\n \tcb.ret = 0;\n \tfor_each_rawref(append_ref, &cb);\n+\t/*\n+\t * The following implementation is currently duplicated in ref-filter. It\n+\t * will eventually be removed when we port branch.c to use ref-filter APIs.\n+\t */\n \tif (merge_filter != NO_FILTER) {\n \t\tstruct commit *filter;\n \t\tfilter = lookup_commit_reference_gently(merge_filter_ref, 0);\ndiff --git a/ref-filter.c b/ref-filter.c\nindex b4753ab..148b7cd 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@@ -898,6 +899,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@@ -916,11 +918,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@@ -946,6 +960,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@@ -955,17 +1013,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 443cfa7..f115174 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.5\n"},{"id":"265745","messageId":"1436285177-12279-7-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v8 07/11] for-each-ref: add '--merged' and '--no-merged' options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:06:13Z","receivedAt":"2015-07-07T16:06:13Z","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/t6302-for-each-ref-filter.sh     | 23 +++++++++++++++++++++++\n 3 files changed, 35 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex ff0283b..2842195 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 which points at 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 ae5419e..7521850 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 which points at 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/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex 457991f..73dbf53 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -37,4 +37,27 @@ 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/double-tag\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.5\n"},{"id":"265744","messageId":"1436285177-12279-8-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v8 08/11] parse-option: rename parse_opt_with_commit()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:06:14Z","receivedAt":"2015-07-07T16:06:14Z","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 e63102e..ae9a0eb 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -832,13 +832,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 1478818..583690c 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -224,7 +224,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.5\n"},{"id":"265749","messageId":"1436285177-12279-9-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v8 09/11] parse-options.h: add macros for '--contains' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:06:15Z","receivedAt":"2015-07-07T16:06:15Z","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 ae9a0eb..c443cd8 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -828,18 +828,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 583690c..7ea22b2 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -246,5 +246,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) \\\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.5\n"},{"id":"265747","messageId":"1436285177-12279-10-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v8 10/11] ref-filter: implement '--contains' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:06:16Z","receivedAt":"2015-07-07T16:06:16Z","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 builtin/tag.c |   5 +++\n ref-filter.c  | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n ref-filter.h  |   3 ++\n 3 files changed, 121 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 767162e..071d001 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -86,6 +86,11 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)\n \treturn 0;\n }\n \n+/*\n+ * The entire code segment for supporting the --contains option has been\n+ * copied over to ref-filter.{c,h}. This will be deleted evetually when\n+ * we port tag.c to use ref-filter APIs.\n+ */\n enum contains_result {\n \tCONTAINS_UNKNOWN = -1,\n \tCONTAINS_NO = 0,\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 148b7cd..dd0709d 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@@ -922,10 +1030,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 f115174..6bf27d8 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.5\n"},{"id":"265746","messageId":"1436285177-12279-11-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v8 11/11] for-each-ref: add '--contains' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-07T16:06:17Z","receivedAt":"2015-07-07T16:06:17Z","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/t6302-for-each-ref-filter.sh     | 15 +++++++++++++++\n 3 files changed, 22 insertions(+)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 2842195..e49d578 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 7521850..40f343b 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/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex 73dbf53..9969a08 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -60,4 +60,19 @@ 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/double-tag\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.5\n"},{"id":"265819","messageId":"559CDAC1.8030600@web.de","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"Re: [PATCH v8 01/11] t6302: for-each-ref tests for ref-filter APIs","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2015-07-08T08:09:37Z","receivedAt":"2015-07-08T08:09:37Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On 2015-07-07 18.06, Karthik Nayak wrote:\n> Add a test suite for testing the ref-filter APIs used\n> by for-each-ref. We just intialize the test suite for now.\n> More tests will be added in the following patches as more\n> options are added to for-each-ref.\n> \n> Based-on-patch-by: Jeff King <peff@peff.net>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  t/t6302-for-each-ref-filter.sh | 20 ++++++++++++++++++++\n>  1 file changed, 20 insertions(+)\n>  create mode 100755 t/t6302-for-each-ref-filter.sh\n> \n> diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\n> new file mode 100755\n> index 0000000..44d2f24\n> --- /dev/null\n> +++ b/t/t6302-for-each-ref-filter.sh\n> @@ -0,0 +1,20 @@\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 tag -s -m \"Annonated doubly\" double-tag signed-tag &&\n> +\tgit checkout master &&\n> +\tgit update-ref refs/odd/spot master\n> +'\n> +\n> +test_done\n> \nCould we have a tweak for people without gpg?\n\nerror: cannot run gpg: No such file or directory\nerror: could not run gpg.\nerror: unable to sign the tag\nnot ok 1 - setup some history and refs\n"},{"id":"265821","messageId":"vpqwpybf25f.fsf@anie.imag.fr","threadId":"39801","inReplyTo":"559CDAC1.8030600@web.de","subject":"Re: [PATCH v8 01/11] t6302: for-each-ref tests for ref-filter APIs","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-07-08T09:07:40Z","receivedAt":"2015-07-08T09:07:40Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Torsten Bögershausen <tboegi@web.de> writes:\n\n> Could we have a tweak for people without gpg?\n\nI guess we need stg like\n\nif ! test_have_prereq GPG; then\n\tskip_all='skipping for-each-ref tests, gpg not available'\n\ttest_done\nfi\n\nsince we need GPG in the setup test and almost all other tests will rely\non it.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"265854","messageId":"CAOLa=ZQ=Mjx-L8Xn9kA86b7nv_bF24Mje1rU-Rmcq4kMqp8qMg@mail.gmail.com","threadId":"39801","inReplyTo":"vpqwpybf25f.fsf@anie.imag.fr","subject":"Re: [PATCH v8 01/11] t6302: for-each-ref tests for ref-filter APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-08T13:59:58Z","receivedAt":"2015-07-08T13:59:58Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Jul 8, 2015 at 2:37 PM, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> wrote:\n> Torsten Bögershausen <tboegi@web.de> writes:\n>\n>> Could we have a tweak for people without gpg?\n>\n> I guess we need stg like\n>\n> if ! test_have_prereq GPG; then\n>         skip_all='skipping for-each-ref tests, gpg not available'\n>         test_done\n> fi\n>\n> since we need GPG in the setup test and almost all other tests will rely\n> on it.\n>\n\nDoes this need to be there? I see a lot of test files which need GPG, and since\nthis test file contains tests which rely on it, without it, it would\nbe kinda useless\nanyways?\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"265857","messageId":"vpq615ud53i.fsf@anie.imag.fr","threadId":"39801","inReplyTo":"CAOLa=ZQ=Mjx-L8Xn9kA86b7nv_bF24Mje1rU-Rmcq4kMqp8qMg@mail.gmail.com","subject":"Re: [PATCH v8 01/11] t6302: for-each-ref tests for ref-filter APIs","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-07-08T15:46:57Z","receivedAt":"2015-07-08T15:46:57Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> On Wed, Jul 8, 2015 at 2:37 PM, Matthieu Moy\n> <Matthieu.Moy@grenoble-inp.fr> wrote:\n>> Torsten Bögershausen <tboegi@web.de> writes:\n>>\n>>> Could we have a tweak for people without gpg?\n>>\n>> I guess we need stg like\n>>\n>> if ! test_have_prereq GPG; then\n>>         skip_all='skipping for-each-ref tests, gpg not available'\n>>         test_done\n>> fi\n>>\n>> since we need GPG in the setup test and almost all other tests will rely\n>> on it.\n>>\n>\n> Does this need to be there? I see a lot of test files which need GPG, and since\n> this test file contains tests which rely on it, without it, it would\n> be kinda useless\n> anyways?\n\nIn many cases, the tests look like\n\n. lib-gpg.sh\n\ntest_expect_success 'test that does not use GPG' '\n        ...\n'\n\ntest_expect_success GPG 'test that does use GPG' '\n        ...\n'\n\nso you can't decide in lib-gpg.sh that you'll skip all tests, because\nyou would skip some tests that could be ran without gpg.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"265891","messageId":"CAOLa=ZS3Sb=OjGFZWykLKctdvoZNy0WCoOfQct--+5W4ck6FYQ@mail.gmail.com","threadId":"39801","inReplyTo":"vpq615ud53i.fsf@anie.imag.fr","subject":"Re: [PATCH v8 01/11] t6302: for-each-ref tests for ref-filter APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-09T05:52:52Z","receivedAt":"2015-07-09T05:52:52Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Jul 8, 2015 at 9:16 PM, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> On Wed, Jul 8, 2015 at 2:37 PM, Matthieu Moy\n>> <Matthieu.Moy@grenoble-inp.fr> wrote:\n>>> Torsten Bögershausen <tboegi@web.de> writes:\n>>>\n>>>> Could we have a tweak for people without gpg?\n>>>\n>>> I guess we need stg like\n>>>\n>>> if ! test_have_prereq GPG; then\n>>>         skip_all='skipping for-each-ref tests, gpg not available'\n>>>         test_done\n>>> fi\n>>>\n>>> since we need GPG in the setup test and almost all other tests will rely\n>>> on it.\n>>>\n>>\n>> Does this need to be there? I see a lot of test files which need GPG, and since\n>> this test file contains tests which rely on it, without it, it would\n>> be kinda useless\n>> anyways?\n>\n> In many cases, the tests look like\n>\n> . lib-gpg.sh\n>\n> test_expect_success 'test that does not use GPG' '\n>         ...\n> '\n>\n> test_expect_success GPG 'test that does use GPG' '\n>         ...\n> '\n>\n> so you can't decide in lib-gpg.sh that you'll skip all tests, because\n> you would skip some tests that could be ran without gpg.\n>\n\nAh! Thanks. Will reply here with your fix included :)\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"265894","messageId":"1436421801-7781-1-git-send-email-karthik.188@gmail.com","threadId":"39801","inReplyTo":"1436285177-12279-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v8 01/11] t6302: for-each-ref tests for ref-filter APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-09T06:03:21Z","receivedAt":"2015-07-09T06:03:21Z","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/t6302-for-each-ref-filter.sh | 26 ++++++++++++++++++++++++++\n 1 file changed, 26 insertions(+)\n create mode 100755 t/t6302-for-each-ref-filter.sh\n\ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nnew file mode 100755\nindex 0000000..ae75116\n--- /dev/null\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -0,0 +1,26 @@\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+if ! test_have_prereq GPG\n+then\n+\tskip_all=\"skipping for-each-ref tests, GPG not available\"\n+\ttest_done\n+fi\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 tag -s -m \"Annonated doubly\" double-tag signed-tag &&\n+\tgit checkout master &&\n+\tgit update-ref refs/odd/spot master\n+'\n+\n+test_done\n-- \n2.4.5\n"}]}