{"thread":{"id":"17532","subject":"[RFC PATCH 1/1] git-tag: Add --regex option","startedAt":"2009-02-03T16:11:52Z","lastAt":"2009-02-04T20:23:36Z","messageCount":6,"participants":["Jake Goulding","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":1},"messages":[{"id":"102978","messageId":"1233677512-1846-1-git-send-email-goulding@vivisimo.com","threadId":"17532","inReplyTo":null,"subject":"[RFC PATCH 1/1] git-tag: Add --regex option","fromName":"Jake Goulding","fromEmail":"goulding@vivisimo.com","sentAt":"2009-02-03T16:11:52Z","receivedAt":"2009-02-03T16:11:52Z","isPatch":true,"sender":{"key":"goulding@vivisimo.com","avatar":"https://gravatar.com/avatar/d8938ff226c618b80857e27f7e0a2d4335215bfb9733c321b8dd30c660b7f1e9?d=mp&s=160"},"body":"Allows the tag pattern to be expressed as a regular expression.\n\nSigned-off-by: Jake Goulding <goulding@vivisimo.com>\n---\n Documentation/git-tag.txt |    5 ++++-\n builtin-tag.c             |   34 ++++++++++++++++++++++++++++++----\n t/t7004-tag.sh            |   26 ++++++++++++++++++++++++++\n 3 files changed, 60 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 533d18b..5c08a45 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -12,7 +12,7 @@ SYNOPSIS\n 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n \t<name> [<commit> | <object>]\n 'git tag' -d <name>...\n-'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]\n+'git tag' [-n[<num>]] -l [--contains <commit>] [--regex] [<pattern>]\n 'git tag' -v <name>...\n \n DESCRIPTION\n@@ -71,6 +71,9 @@ OPTIONS\n --contains <commit>::\n \tOnly list tags which contain the specified commit.\n \n+--regex::\n+\tTreat the pattern as a regular expression, instead of a shell wildcard pattern.\n+\n -m <msg>::\n \tUse the given tag message (instead of prompting).\n \tIf multiple `-m` options are given, their values are\ndiff --git a/builtin-tag.c b/builtin-tag.c\nindex 01e7374..dad4e53 100644\n--- a/builtin-tag.c\n+++ b/builtin-tag.c\n@@ -27,6 +27,8 @@ struct tag_filter {\n \tconst char *pattern;\n \tint lines;\n \tstruct commit_list *with_commit;\n+\tint use_regex;\n+\tregex_t tag_regex;\n };\n \n #define PGP_SIGNATURE \"-----BEGIN PGP SIGNATURE-----\"\n@@ -35,8 +37,14 @@ static int show_reference(const char *refname, const unsigned char *sha1,\n \t\t\t  int flag, void *cb_data)\n {\n \tstruct tag_filter *filter = cb_data;\n+\tint matched = 0;\n \n-\tif (!fnmatch(filter->pattern, refname, 0)) {\n+\tif (filter->use_regex)\n+\t\tmatched = !regexec(&filter->tag_regex, refname, 0, NULL, 0);\n+\telse\n+\t\tmatched = !fnmatch(filter->pattern, refname, 0);\n+\n+\tif (matched) {\n \t\tint i;\n \t\tunsigned long size;\n \t\tenum object_type type;\n@@ -91,7 +99,7 @@ static int show_reference(const char *refname, const unsigned char *sha1,\n }\n \n static int list_tags(const char *pattern, int lines,\n-\t\t\tstruct commit_list *with_commit)\n+\t\t\tstruct commit_list *with_commit, int use_regex)\n {\n \tstruct tag_filter filter;\n \n@@ -101,9 +109,24 @@ static int list_tags(const char *pattern, int lines,\n \tfilter.pattern = pattern;\n \tfilter.lines = lines;\n \tfilter.with_commit = with_commit;\n+\tfilter.use_regex = use_regex;\n+\tif (use_regex) {\n+\t\tint regex_errcode;\n+\n+\t\tregex_errcode = regcomp(&filter.tag_regex, pattern, 0);\n+\t\tif (regex_errcode) {\n+\t\t\tchar regex_error[1024];\n+\n+\t\t\tregerror(regex_errcode, &filter.tag_regex, regex_error, 1024);\n+\t\t\treturn error(\"Invalid tag pattern \\\"%s\\\": %s\\n\", pattern, regex_error);\n+\t\t}\n+\t}\n \n \tfor_each_tag_ref(show_reference, (void *) &filter);\n \n+\tif (use_regex)\n+\t\tregfree(&filter.tag_regex);\n+\n \treturn 0;\n }\n \n@@ -370,7 +393,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tstruct ref_lock *lock;\n \n \tint annotate = 0, sign = 0, force = 0, lines = -1,\n-\t\tlist = 0, delete = 0, verify = 0;\n+\t\tlist = 0, delete = 0, verify = 0, use_regex = 0;\n \tconst char *msgfile = NULL, *keyid = NULL;\n \tstruct msg_arg msg = { 0, STRBUF_INIT };\n \tstruct commit_list *with_commit = NULL;\n@@ -400,6 +423,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\t\tPARSE_OPT_LASTARG_DEFAULT,\n \t\t\tparse_opt_with_commit, (intptr_t)\"HEAD\",\n \t\t},\n+\t\tOPT_BOOLEAN(0, \"regex\", &use_regex, \"use regex matching\"),\n \t\tOPT_END()\n \t};\n \n@@ -425,11 +449,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_tag_usage, options);\n \tif (list)\n \t\treturn list_tags(argv[0], lines == -1 ? 0 : lines,\n-\t\t\t\t with_commit);\n+\t\t\t\t with_commit, use_regex);\n \tif (lines != -1)\n \t\tdie(\"-n option is only allowed with -l.\");\n \tif (with_commit)\n \t\tdie(\"--contains option is only allowed with -l.\");\n+\tif (use_regex)\n+\t\tdie(\"--regex option is only allowed with -l.\");\n \tif (delete)\n \t\treturn for_each_tag_name(argv, delete_tag);\n \tif (verify)\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex 69501e2..a93de89 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -256,6 +256,32 @@ test_expect_success \\\n \ttest_cmp expect actual\n '\n \n+# regex pattern matching:\n+\n+test_expect_success 'listing a tag using a matching regex pattern should succeed' \\\n+\t'git tag -l --regex \"^aa1$\"'\n+\n+test_expect_success 'listing a tag using a nonmatching regex pattern should succeed' \\\n+\t'git tag -l --regex \"^xxxxxxxxxxxxx$\"'\n+\n+test_expect_success \\\n+\t'listing a tag using a matching regex pattern should output that tag' \\\n+\t'test `git tag -l --regex \"^aa1$\"` = aa1'\n+\n+test_expect_success \\\n+\t'listing tags using a matching regex pattern should output those tags' \\\n+\t'test `git tag -l --regex \"^v1.[[:digit:]]$\"` = \"v1.0\"'\n+\n+cat > expected <<EOF\n+v1.0.1\n+v1.1.3\n+EOF\n+\n+test_expect_success \\\n+\t'listing a tag using a matching regex pattern should output that tag' \\\n+\t'git tag -l --regex \"^v1.[[:digit:]].[[:digit:]]$\" > actual &&\n+         test_cmp expected actual'\n+\n # creating and verifying lightweight tags:\n \n test_expect_success \\\n-- \n1.6.0.6\n"},{"id":"103079","messageId":"7vljsm3aow.fsf@gitster.siamese.dyndns.org","threadId":"17532","inReplyTo":"1233677512-1846-1-git-send-email-goulding@vivisimo.com","subject":"Re: [RFC PATCH 1/1] git-tag: Add --regex option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-02-04T07:47:43Z","receivedAt":"2009-02-04T07:47:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jake Goulding <goulding@vivisimo.com> writes:\n\n> Allows the tag pattern to be expressed as a regular expression.\n\nWe use shell globs for refname throughout the system (not just tags).  Why\nis this a good thing, other than \"because we can\"?\n"},{"id":"103134","messageId":"4989A34C.4080104@vivisimo.com","threadId":"17532","inReplyTo":"7vljsm3aow.fsf@gitster.siamese.dyndns.org","subject":"Re: [RFC PATCH 1/1] git-tag: Add --regex option","fromName":"Jake Goulding","fromEmail":"goulding@vivisimo.com","sentAt":"2009-02-04T14:16:44Z","receivedAt":"2009-02-04T14:16:44Z","isPatch":true,"sender":{"key":"goulding@vivisimo.com","avatar":"https://gravatar.com/avatar/d8938ff226c618b80857e27f7e0a2d4335215bfb9733c321b8dd30c660b7f1e9?d=mp&s=160"},"body":"\nJunio C Hamano wrote:\n> Jake Goulding <goulding@vivisimo.com> writes:\n> \n>> Allows the tag pattern to be expressed as a regular expression.\n> \n> We use shell globs for refname throughout the system (not just tags).  Why\n> is this a good thing, other than \"because we can\"?\n> \n\nI'll give the particular use-case that we are using it for:\n\nIn preparation for a release, we have a nightly tagging/building\nprocess. We start by tagging something as 1.0.0-build1. We then do that\nseries for a while, then decide it is time to shift to a more thorough\nQA cycle. We branch a QA branch, then start tagging at 1.0-0-rc1.\nEventually, a rc passes all QA tests and we tag that rc again as 1.0-0.\n\nThus, our tags look like something of the form:\n\n1.0.0-build1\n1.0.0-rc1\n1.0.0\n\nAs we fix bugs, a hook automatically adds the commit hash is as a\ncomment to the appropriate bugzilla bug.\n\nWe whipped up a dinky little web application that takes a hash and a\nbranch, and shows which tags contain that particular hash (which is the\nreason for my previous commit for --contains support in git tag). We\nhacked bugzilla to match on git hashes, and provide a link to this webapp.\n\nI wanted to be able to limit the search space to (builds, rcs,\nreleases), but globs don't allow that amount of flexibility.\n\nIs that a complete enough description for a rational use-case?\n\n-Jake\n"},{"id":"103172","messageId":"7v4ozaum08.fsf@gitster.siamese.dyndns.org","threadId":"17532","inReplyTo":"4989A34C.4080104@vivisimo.com","subject":"Re: [RFC PATCH 1/1] git-tag: Add --regex option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-02-04T17:53:27Z","receivedAt":"2009-02-04T17:53:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jake Goulding <goulding@vivisimo.com> writes:\n\n> Junio C Hamano wrote:\n>> Jake Goulding <goulding@vivisimo.com> writes:\n>> \n>>> Allows the tag pattern to be expressed as a regular expression.\n>> \n>> We use shell globs for refname throughout the system (not just tags).  Why\n>> is this a good thing, other than \"because we can\"?\n>\n> I'll give the particular use-case that we are using it for:\n>\n> In preparation for a release, we have a nightly tagging/building\n> process. We start by tagging something as 1.0.0-build1. We then do that\n> series for a while, then decide it is time to shift to a more thorough\n> QA cycle. We branch a QA branch, then start tagging at 1.0-0-rc1.\n> Eventually, a rc passes all QA tests and we tag that rc again as 1.0-0.\n>\n> Thus, our tags look like something of the form:\n>\n> 1.0.0-build1\n> 1.0.0-rc1\n> 1.0.0\n>\n> As we fix bugs, a hook automatically adds the commit hash is as a\n> comment to the appropriate bugzilla bug.\n>\n> We whipped up a dinky little web application that takes a hash and a\n> branch, and shows which tags contain that particular hash (which is the\n> reason for my previous commit for --contains support in git tag). We\n> hacked bugzilla to match on git hashes, and provide a link to this webapp.\n>\n> I wanted to be able to limit the search space to (builds, rcs,\n> releases), but globs don't allow that amount of flexibility.\n>\n> Is that a complete enough description for a rational use-case?\n\nIt certainly describes what you are trying to use it for much better than\na patch that does not say anything other than \"because we can\".  A patch\nmarked as RFC could have been written with such an explanation from the\nbeginning to save everybody's time.\n\nI still do not like the patch, and it is not entirely your fault.\n\nImagine we are not discussing git nor tags, but doing something similar on\nthe filesystem (e.g. you have files that store release-notes for key\nversions) and would want to have a way to limit the filename arguments you\ngive to the commands, e.g. \"wc -l 1.0.0*\".  You most likely would not\npatch your shell to accept \"set regexpglob; wc -l 1\\.0\\.0.*\".  Instead you\nwould arrange your naming convention to make it easy to limit to what you\nare interested in by globbing.\n\nIn that sense, the above explanation does not change the fact at all that\nyour patch is \"because we can\".\n\nBut that is minor.\n\nI liked the \"git-tag --contains\" patch, because the ref filters git-branch\nhas are really ref filters, not branch filters, and should not belong only\nto git-branch.  But now that you revealed that you are using the Porcelain\nfor scripting, it made me realize that these features should be accessible\nfrom the plumbing, perhaps for-each-ref, and your little web application\nshould be using that, not \"git-branch\" nor \"git-tag\".\n\nCurrently it cannot, because these useful ref filters are implemented\nfirst at the Porcelain level.  Because the plumbing _is_ meant for people\nwriting scripts, that is the issue we should be fixing first.\n\nI would have liked the patch if it were a series to refactor the code to\nfilter refs based on their relationships from \"git branch\" (one of which\nyou did with the --contains patch, but --merged, --no-merged should be\naddressed, too, I think), and make them available to for-each-ref, branch\nand tag.  If done right, adding regexp support or other fancier filtering\ncan be done by enhancing the shared ref filtering logic once and both\nPorcelain and plumbing will benefit.\n\nAdding --regexp only to \"git-tag\" is going backwards, as you would have to\nthen sideport that to \"git-branch\", and the new feature is still not\navailable to the plumbing.  Seeing such a patch from somebody who improved\nthe world by freeing --contains from the grip of \"git-branch\" makes me\nmoderately unhappy.  It shows that you did not understand why your earlier\npatch was a good thing to begin with.\n"},{"id":"103186","messageId":"4989EE20.1010307@vivisimo.com","threadId":"17532","inReplyTo":"7v4ozaum08.fsf@gitster.siamese.dyndns.org","subject":"Re: [RFC PATCH 1/1] git-tag: Add --regex option","fromName":"Jake Goulding","fromEmail":"goulding@vivisimo.com","sentAt":"2009-02-04T19:36:00Z","receivedAt":"2009-02-04T19:36:00Z","isPatch":true,"sender":{"key":"goulding@vivisimo.com","avatar":"https://gravatar.com/avatar/d8938ff226c618b80857e27f7e0a2d4335215bfb9733c321b8dd30c660b7f1e9?d=mp&s=160"},"body":"Junio C Hamano wrote:\n> Jake Goulding <goulding@vivisimo.com> writes:\n> \n>> Is that a complete enough description for a rational use-case?\n> \n> It certainly describes what you are trying to use it for much better than\n> a patch that does not say anything other than \"because we can\".  A patch\n> marked as RFC could have been written with such an explanation from the\n> beginning to save everybody's time.\n\nMy apologies - my previous patch, as you pointed out, was more\nself-evident, and I felt this one was too.\n\n> Imagine we are not discussing git nor tags, but doing something similar on\n> the filesystem (e.g. you have files that store release-notes for key\n> versions) and would want to have a way to limit the filename arguments you\n> give to the commands, e.g. \"wc -l 1.0.0*\".  You most likely would not\n> patch your shell to accept \"set regexpglob; wc -l 1\\.0\\.0.*\".  Instead you\n> would arrange your naming convention to make it easy to limit to what you\n> are interested in by globbing.\n\nThis is true. In this case, I would instead post-process the globbed\nlist with my regex, then pass the munged list to wc. I thought about\ndoing something similar here, where I would do the list, munge it, then\nperform the contains test. Since I had just messed with git tag,\nhowever, I felt like messing some more. :-) I\n\n> I liked the \"git-tag --contains\" patch, because the ref filters git-branch\n> has are really ref filters, not branch filters, and should not belong only\n> to git-branch.  But now that you revealed that you are using the Porcelain\n> for scripting, it made me realize that these features should be accessible\n> from the plumbing, perhaps for-each-ref, and your little web application\n> should be using that, not \"git-branch\" nor \"git-tag\".\n\nThis is a good point. I originally started by using for-each-ref, then\nrealized that it did not have contains. Someone on IRC suggested I look\nat the functionality in git branch, and copy over to git tag. That is\nhow I arrived at the previous patch series.\n\n> Currently it cannot, because these useful ref filters are implemented\n> first at the Porcelain level.  Because the plumbing _is_ meant for people\n> writing scripts, that is the issue we should be fixing first.\n\nThis is something that I constantly forget, and it happened again in\nthis case. I get so used to using the porcelain in day-to-day use, it is\nthe most prevalent thing in my head when I go to write simple scripts.\n\n> I would have liked the patch if it were a series to refactor the code to\n> filter refs based on their relationships from \"git branch\" (one of which\n> you did with the --contains patch, but --merged, --no-merged should be\n> addressed, too, I think), and make them available to for-each-ref, branch\n> and tag.  If done right, adding regexp support or other fancier filtering\n> can be done by enhancing the shared ref filtering logic once and both\n> Porcelain and plumbing will benefit.\n\n> Adding --regexp only to \"git-tag\" is going backwards, as you would have to\n> then sideport that to \"git-branch\", and the new feature is still not\n> available to the plumbing.  Seeing such a patch from somebody who improved\n> the world by freeing --contains from the grip of \"git-branch\" makes me\n> moderately unhappy.  It shows that you did not understand why your earlier\n> patch was a good thing to begin with.\n\nWell, in my defense, it still is a good thing, just not as good as it\ncould have been. I will admit that I do not have a total grasp of the\ngit code, but I figured (correctly) that someone would be kind enough to\npoint it out when I show my ignorance ;-).\n\nI agree that it feels dumb to add this type of functionality to git tag\nand not git branch (and not to the plumbing).\n\nI'm sure that adding the regex support to for-each-ref would be nearly\nas trivial as it was for git tag, and I will happily do that instead of\nthis patch.\n\nWhat I am not as clear about is how I can then use that functionality in\ngit tag/branch. The main code I messed with in git tag calls\nfor_each_tag_ref (and for_each_ref in branch). Would it be appropriate\nto add a struct reference_filter to these functions?\n\nReally, that is what I am trying to accomplish - to have finer-grained\nfiltering on the references that are being enumerated.\n\nIf that seems fine, then I will work on refactoring that code. If not,\nlet me know. :-)\n\n-Jake\n"},{"id":"103195","messageId":"7vk586oss7.fsf@gitster.siamese.dyndns.org","threadId":"17532","inReplyTo":"4989EE20.1010307@vivisimo.com","subject":"Re: [RFC PATCH 1/1] git-tag: Add --regex option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-02-04T20:23:36Z","receivedAt":"2009-02-04T20:23:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jake Goulding <goulding@vivisimo.com> writes:\n\n> Junio C Hamano wrote:\n>> Jake Goulding <goulding@vivisimo.com> writes:\n>> \n>>> Is that a complete enough description for a rational use-case?\n>> \n>> It certainly describes what you are trying to use it for much better than\n>> a patch that does not say anything other than \"because we can\".  A patch\n>> marked as RFC could have been written with such an explanation from the\n>> beginning to save everybody's time.\n>\n> My apologies - my previous patch, as you pointed out, was more\n> self-evident, and I felt this one was too.\n\nThere is no need to apologize.  If a patch without RFC does not have\nsufficient justification, I'd just reject it and that would be that, but\nan RFC patch is for people to comment on, and it would be a way for you to\nget more feedback to give sufficient background material.  It was just a\nsuggestion to help _you_, not request to help _me_.\n\n>> Currently it cannot, because these useful ref filters are implemented\n>> first at the Porcelain level.  Because the plumbing _is_ meant for people\n>> writing scripts, that is the issue we should be fixing first.\n> ...\n> Well, in my defense, it still is a good thing, just not as good as it\n> could have been.\n\nPlease don't take my \"Sad\" too seriously.  As I said upfront, it is not\nyour fault.  I was sad mostly because the existing code was not structured\nthat way before you started touching it.\n\n> What I am not as clear about is how I can then use that functionality in\n> git tag/branch. The main code I messed with in git tag calls\n> for_each_tag_ref (and for_each_ref in branch). Would it be appropriate\n> to add a struct reference_filter to these functions?\n\nYeah, if we realize that for_each_*_ref() are all based on for_each_ref()\nand about implementing trivial filtering on the result from the latter,\nit might be a reasonable approach to do something like this.\n\n\t/*\n         * generic filter description.  base is typically \"refs/heads/\"\n         * and things like that to cheaply filter the ref.\n         *\n         * filter_one callback can return 0 to skip, or positive to call\n         * the user function supplied to for_each_ref_filtered(), one by\n         * one.\n         *\n         * when filtering many refs at once is more efficient,\n         * filter_group can be specified. for_each_ref_filtered() function\n         * first collects all the eligible refs into a ref-list, and calls\n         * the filter_group callback.  The callback is expected to modify\n         * the given ref_list in-place to omit the ones it does not want\n         * the user callback to be called. for_each_ref_filtered() will\n         * then call the user callback for each of the refs left in the\n         * ref_list.\n         *\n         * when using filter_group, callback data can be placed in\n         * cb_data.  when using filter_one callback, the field is used to\n         * hold the callback data for the user callback function.\n         */\n\tstruct ref_filter {\n\t\tchar *base;\n                each_ref_fn filter_one;\n                int (*filter_group)(struct ref_list **, struct ref_filter *);\n\t\tvoid *cb_data;\n\t\teach_ref_fn user_fn;\n        };\n\n\tstatic int collect_refs(const char *name,\n        \t\t\tconst unsigned char *sha1,\n                                int flags, void *cb_data)\n        {\n\t\tstruct ref_list **tail = cb_data;\n                struct ref_list *entry;\n                entry = xmalloc(sizeof(*entry) + strlen(name) + 1);\n                strcpy(entry->name, name);\n                entry->flag = flag;\n                memcpy(entry->sha1, sha1, 20);\n                hashclr(entry->peeled);\n\t\t*tail = entry;\n                entry->next = NULL;\n                **tail = &entry->next;\n                return 0;\n\t}\n\n\tstatic int filter_ref(const char *name,\n        \t\t      const unsigned char *sha1,\n                              int flags, void *cb_data)\n        {\n\t\tstruct ref_filter *filter = cb_data;\n\t\tint ret = filter->filter_one(name, sha1, flags, cb_data);\n\t\tif (ret <= 0)\n                \treturn ret;\n\t\treturn filter->user_fn(name, sha1, flags, filter->cb_data);\n        }\n\n        int for_each_ref_filtered(each_ref_fn fn,\n        \t\t\t  struct ref_filter *filter,\n                                  void *cb_data)\n\t{\n\t\tchar *base = filter->base;\n\n\t\tif (!filter->filter_one && !filter->filter_group)\n\t\t\treturn for_each_ref(fn, base, cb_data);\n                if (filter->filter_group) {\n\t\t\tstruct ref_list *collect = NULL;\n                        struct ref_list **tail = &collect;\n\t\t\tfor_each_ref(collect_refs, base, tail);\n\t\t\tfilter->filter_group(&collect_refs, filter->cb_data);\n                        while (collect) {\n\t\t\t\tstruct ref_list *entry = collect;\n                                retval = do_one_ref(base, fn, 0, entry);\n                                if (retval) {\n                                \tfree_ref_list(collect);\n                                        return retval;\n\t\t\t\t}\n\t\t\t\tcollect = entry->next;\n                                free(entry);\n                        }\n                        return 0;\n                }\n\t\tfilter->user_fn = fn;\n                return for_each_ref(filter_ref, base, filter);\n        }\n"}]}