{"thread":{"id":"39543","subject":"[PATCH v6 0/11] create ref-filter from for-each-ref","startedAt":"2015-06-06T13:43:32Z","lastAt":"2015-06-08T18:37:31Z","messageCount":24,"participants":["Karthik Nayak","Matthieu Moy","Junio C Hamano"],"isPatch":true,"patchVersion":6,"patchTotal":11},"messages":[{"id":"263102","messageId":"5572F904.1080608@gmail.com","threadId":"39543","inReplyTo":null,"subject":"[PATCH v6 0/11] create ref-filter from for-each-ref","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:43:32Z","receivedAt":"2015-06-06T13:43:32Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Version four of this patch can be found here :\nhttp://www.mail-archive.com/git@vger.kernel.org/msg70280.html\n\nv5 : http://www.mail-archive.com/git@vger.kernel.org/msg70888.html\n\nChanges in v5:\n*    Rename functions to better suit the code.\n*    implement filter_refs()\n*    use FLEX_ARRAY for refname\n*    other small changes\n\nChanges in v6:\n*\tbased on latest master branch (github.com/git/git)\n*\trename variables named sort to sorting.\n-- \nRegards,\nKarthik\n"},{"id":"263103","messageId":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"5572F904.1080608@gmail.com","subject":"[PATCH v6 01/11] for-each-ref: extract helper functions out of grab_single_ref()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:48:06Z","receivedAt":"2015-06-06T13:48:06Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Extract two helper functions out of grab_single_ref(). Firstly,\nnew_refinfo() which is used to allocate memory for a new refinfo\nstructure and copy the objectname, refname and flag to it.\nSecondly, match_name_as_path() which when given an array of patterns\nand the refname checks if the refname matches any of the patterns\ngiven while the pattern is a pathname, also while supporting wild\ncharacters.\n\nThis is a preperatory patch for restructuring 'for-each-ref' and\neventually moving most of it to 'ref-filter' to provide the\nfunctionality to similar commands via public API's.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\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/for-each-ref.c | 63 ++++++++++++++++++++++++++++++++------------------\n 1 file changed, 40 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex f7e51a7..f939fd3 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -851,6 +851,43 @@ struct grab_ref_cbdata {\n };\n \n /*\n+ * Given a refname, return 1 if the refname matches with one of the patterns\n+ * while the pattern is a pathname like 'refs/tags' or 'refs/heads/master'\n+ * and so on, else return 0. Supports use of wild characters.\n+ */\n+static int match_name_as_path(const char **pattern, const char *refname)\n+{\n+\tint namelen = strlen(refname);\n+\tfor (; *pattern; pattern++) {\n+\t\tconst char *p = *pattern;\n+\t\tint plen = strlen(p);\n+\n+\t\tif ((plen <= namelen) &&\n+\t\t    !strncmp(refname, p, plen) &&\n+\t\t    (refname[plen] == '\\0' ||\n+\t\t     refname[plen] == '/' ||\n+\t\t     p[plen-1] == '/'))\n+\t\t\treturn 1;\n+\t\tif (!wildmatch(p, refname, WM_PATHNAME, NULL))\n+\t\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+/* Allocate space for a new refinfo and copy the objectname and flag to it */\n+static struct refinfo *new_refinfo(const char *refname,\n+\t\t\t\t   const unsigned char *objectname,\n+\t\t\t\t   int flag)\n+{\n+\tstruct refinfo *ref = xcalloc(1, sizeof(struct refinfo));\n+\tref->refname = xstrdup(refname);\n+\thashcpy(ref->objectname, objectname);\n+\tref->flag = flag;\n+\n+\treturn ref;\n+}\n+\n+/*\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n@@ -866,35 +903,15 @@ static int grab_single_ref(const char *refname, const struct object_id *oid,\n \t\t  return 0;\n \t}\n \n-\tif (*cb->grab_pattern) {\n-\t\tconst char **pattern;\n-\t\tint namelen = strlen(refname);\n-\t\tfor (pattern = cb->grab_pattern; *pattern; pattern++) {\n-\t\t\tconst char *p = *pattern;\n-\t\t\tint plen = strlen(p);\n-\n-\t\t\tif ((plen <= namelen) &&\n-\t\t\t    !strncmp(refname, p, plen) &&\n-\t\t\t    (refname[plen] == '\\0' ||\n-\t\t\t     refname[plen] == '/' ||\n-\t\t\t     p[plen-1] == '/'))\n-\t\t\t\tbreak;\n-\t\t\tif (!wildmatch(p, refname, WM_PATHNAME, NULL))\n-\t\t\t\tbreak;\n-\t\t}\n-\t\tif (!*pattern)\n-\t\t\treturn 0;\n-\t}\n+\tif (*cb->grab_pattern && !match_name_as_path(cb->grab_pattern, 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\n \t * by maxcount logic.\n \t */\n-\tref = xcalloc(1, sizeof(*ref));\n-\tref->refname = xstrdup(refname);\n-\thashcpy(ref->objectname, oid->hash);\n-\tref->flag = flag;\n+\tref = new_refinfo(refname, oid->hash, flag);\n \n \tcnt = cb->grab_cnt;\n \tREALLOC_ARRAY(cb->grab_array, cnt + 1);\n-- \n2.4.2\n"},{"id":"263104","messageId":"1433598496-31287-2-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 02/11] for-each-ref: clean up code","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:48:07Z","receivedAt":"2015-06-06T13:48:07Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"In 'grab_single_ref()' remove the extra count variable 'cnt' and\nuse the variable 'grab_cnt' of structure 'grab_ref_cbdata' directly\ninstead.\n\nChange comment in 'struct ref_sort' to reflect changes in code.\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/for-each-ref.c | 9 +++------\n 1 file changed, 3 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex f939fd3..406de88 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -27,7 +27,7 @@ struct atom_value {\n \n struct ref_sort {\n \tstruct ref_sort *next;\n-\tint atom; /* index into used_atom array */\n+\tint atom; /* index into 'struct atom_value *' array */\n \tunsigned reverse : 1;\n };\n \n@@ -896,7 +896,6 @@ static int grab_single_ref(const char *refname, const struct object_id *oid,\n {\n \tstruct grab_ref_cbdata *cb = cb_data;\n \tstruct refinfo *ref;\n-\tint cnt;\n \n \tif (flag & REF_BAD_NAME) {\n \t\t  warning(\"ignoring ref with broken name %s\", refname);\n@@ -913,10 +912,8 @@ static int grab_single_ref(const char *refname, const struct object_id *oid,\n \t */\n \tref = new_refinfo(refname, oid->hash, flag);\n \n-\tcnt = cb->grab_cnt;\n-\tREALLOC_ARRAY(cb->grab_array, cnt + 1);\n-\tcb->grab_array[cnt++] = ref;\n-\tcb->grab_cnt = cnt;\n+\tREALLOC_ARRAY(cb->grab_array, cb->grab_cnt + 1);\n+\tcb->grab_array[cb->grab_cnt++] = ref;\n \treturn 0;\n }\n \n-- \n2.4.2\n"},{"id":"263105","messageId":"1433598496-31287-3-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 03/11] for-each-ref: rename 'refinfo' to 'ref_array_item'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:48:08Z","receivedAt":"2015-06-06T13:48:08Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Rename 'refinfo' to 'ref_array_item' as a preparatory step for\nintroduction of new structures in the forthcoming patch.\n\nRe-order the fields in 'ref_array_item' so that refname can be\neventually converted to a FLEX_ARRAY.\n\nMake 'symref' a non const char pointer, so that the compiler doesn't\nthrow an error when we try to free the memory allocated to it.\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/for-each-ref.c | 42 +++++++++++++++++++++---------------------\n 1 file changed, 21 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 406de88..763224c 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -31,12 +31,12 @@ struct ref_sort {\n \tunsigned reverse : 1;\n };\n \n-struct refinfo {\n-\tchar *refname;\n+struct ref_array_item {\n \tunsigned char objectname[20];\n \tint flag;\n-\tconst char *symref;\n+\tchar *symref;\n \tstruct atom_value *value;\n+\tchar *refname;\n };\n \n static struct {\n@@ -86,7 +86,7 @@ static struct {\n  * a \"*\" to denote deref_tag().\n  *\n  * We parse given format string and sort specifiers, and make a list\n- * of properties that we need to extract out of objects.  refinfo\n+ * of properties that we need to extract out of objects.  ref_array_item\n  * structure will hold an array of values extracted that can be\n  * indexed with the \"atom number\", which is an index into this\n  * array.\n@@ -623,7 +623,7 @@ static inline char *copy_advance(char *dst, const char *src)\n /*\n  * Parse the object referred by ref, and grab needed value.\n  */\n-static void populate_value(struct refinfo *ref)\n+static void populate_value(struct ref_array_item *ref)\n {\n \tvoid *buf;\n \tstruct object *obj;\n@@ -835,7 +835,7 @@ static void populate_value(struct refinfo *ref)\n  * Given a ref, return the value for the atom.  This lazily gets value\n  * out of the object by calling populate value.\n  */\n-static void get_value(struct refinfo *ref, int atom, struct atom_value **v)\n+static void get_value(struct ref_array_item *ref, int atom, struct atom_value **v)\n {\n \tif (!ref->value) {\n \t\tpopulate_value(ref);\n@@ -845,7 +845,7 @@ static void get_value(struct refinfo *ref, int atom, struct atom_value **v)\n }\n \n struct grab_ref_cbdata {\n-\tstruct refinfo **grab_array;\n+\tstruct ref_array_item **grab_array;\n \tconst char **grab_pattern;\n \tint grab_cnt;\n };\n@@ -874,12 +874,12 @@ static int match_name_as_path(const char **pattern, const char *refname)\n \treturn 0;\n }\n \n-/* Allocate space for a new refinfo and copy the objectname and flag to it */\n-static struct refinfo *new_refinfo(const char *refname,\n-\t\t\t\t   const unsigned char *objectname,\n-\t\t\t\t   int flag)\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+\t\t\t\t\t\t int flag)\n {\n-\tstruct refinfo *ref = xcalloc(1, sizeof(struct refinfo));\n+\tstruct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item));\n \tref->refname = xstrdup(refname);\n \thashcpy(ref->objectname, objectname);\n \tref->flag = flag;\n@@ -895,7 +895,7 @@ static int grab_single_ref(const char *refname, const struct object_id *oid,\n \t\t\t   int flag, void *cb_data)\n {\n \tstruct grab_ref_cbdata *cb = cb_data;\n-\tstruct refinfo *ref;\n+\tstruct ref_array_item *ref;\n \n \tif (flag & REF_BAD_NAME) {\n \t\t  warning(\"ignoring ref with broken name %s\", refname);\n@@ -910,14 +910,14 @@ static int grab_single_ref(const char *refname, const struct object_id *oid,\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_refinfo(refname, oid->hash, flag);\n+\tref = new_ref_array_item(refname, oid->hash, flag);\n \n \tREALLOC_ARRAY(cb->grab_array, cb->grab_cnt + 1);\n \tcb->grab_array[cb->grab_cnt++] = ref;\n \treturn 0;\n }\n \n-static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)\n+static int cmp_ref_sort(struct ref_sort *s, struct ref_array_item *a, struct ref_array_item *b)\n {\n \tstruct atom_value *va, *vb;\n \tint cmp;\n@@ -944,8 +944,8 @@ static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b\n static struct ref_sort *ref_sort;\n static int compare_refs(const void *a_, const void *b_)\n {\n-\tstruct refinfo *a = *((struct refinfo **)a_);\n-\tstruct refinfo *b = *((struct refinfo **)b_);\n+\tstruct ref_array_item *a = *((struct ref_array_item **)a_);\n+\tstruct ref_array_item *b = *((struct ref_array_item **)b_);\n \tstruct ref_sort *s;\n \n \tfor (s = ref_sort; s; s = s->next) {\n@@ -956,10 +956,10 @@ static int compare_refs(const void *a_, const void *b_)\n \treturn 0;\n }\n \n-static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)\n+static void sort_refs(struct ref_sort *sort, struct ref_array_item **refs, int num_refs)\n {\n \tref_sort = sort;\n-\tqsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);\n+\tqsort(refs, num_refs, sizeof(struct ref_array_item *), compare_refs);\n }\n \n static void print_value(struct atom_value *v, int quote_style)\n@@ -1026,7 +1026,7 @@ static void emit(const char *cp, const char *ep)\n \t}\n }\n \n-static void show_ref(struct refinfo *info, const char *format, int quote_style)\n+static void show_ref(struct ref_array_item *info, const char *format, int quote_style)\n {\n \tconst char *cp, *sp, *ep;\n \n@@ -1099,7 +1099,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \tconst char *format = \"%(objectname) %(objecttype)\\t%(refname)\";\n \tstruct ref_sort *sort = NULL, **sort_tail = &sort;\n \tint maxcount = 0, quote_style = 0;\n-\tstruct refinfo **refs;\n+\tstruct ref_array_item **refs;\n \tstruct grab_ref_cbdata cbdata;\n \n \tstruct option opts[] = {\n-- \n2.4.2\n"},{"id":"263106","messageId":"1433598496-31287-4-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 04/11] for-each-ref: introduce new structures for better organisation","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:48:09Z","receivedAt":"2015-06-06T13:48:09Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Introduce 'ref_filter_cbdata' which will hold 'ref_filter'\n(conditions to filter the refs on) and 'ref_array' (the array\nof ref_array_items). Modify the code to use these new structures.\n\nThis is a preparatory patch to eventually move code from 'for-each-ref'\nto 'ref-filter' and make it publicly available.\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/for-each-ref.c | 54 ++++++++++++++++++++++++++++----------------------\n 1 file changed, 30 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 763224c..42a89cd 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -39,6 +39,20 @@ struct ref_array_item {\n \tchar *refname;\n };\n \n+struct ref_array {\n+\tint nr, alloc;\n+\tstruct ref_array_item **items;\n+};\n+\n+struct ref_filter {\n+\tconst char **name_patterns;\n+};\n+\n+struct ref_filter_cbdata {\n+\tstruct ref_array array;\n+\tstruct ref_filter filter;\n+};\n+\n static struct {\n \tconst char *name;\n \tcmp_type cmp_type;\n@@ -844,12 +858,6 @@ static void get_value(struct ref_array_item *ref, int atom, struct atom_value **\n \t*v = &ref->value[atom];\n }\n \n-struct grab_ref_cbdata {\n-\tstruct ref_array_item **grab_array;\n-\tconst char **grab_pattern;\n-\tint grab_cnt;\n-};\n-\n /*\n  * Given a refname, return 1 if the refname matches with one of the patterns\n  * while the pattern is a pathname like 'refs/tags' or 'refs/heads/master'\n@@ -894,7 +902,8 @@ static struct ref_array_item *new_ref_array_item(const char *refname,\n static int grab_single_ref(const char *refname, const struct object_id *oid,\n \t\t\t   int flag, void *cb_data)\n {\n-\tstruct grab_ref_cbdata *cb = cb_data;\n+\tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n+\tstruct ref_filter *filter = &ref_cbdata->filter;\n \tstruct ref_array_item *ref;\n \n \tif (flag & REF_BAD_NAME) {\n@@ -902,7 +911,7 @@ static int grab_single_ref(const char *refname, const struct object_id *oid,\n \t\t  return 0;\n \t}\n \n-\tif (*cb->grab_pattern && !match_name_as_path(cb->grab_pattern, refname))\n+\tif (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))\n \t\treturn 0;\n \n \t/*\n@@ -912,8 +921,8 @@ static int grab_single_ref(const char *refname, const struct object_id *oid,\n \t */\n \tref = new_ref_array_item(refname, oid->hash, flag);\n \n-\tREALLOC_ARRAY(cb->grab_array, cb->grab_cnt + 1);\n-\tcb->grab_array[cb->grab_cnt++] = ref;\n+\tREALLOC_ARRAY(ref_cbdata->array.items, ref_cbdata->array.nr + 1);\n+\tref_cbdata->array.items[ref_cbdata->array.nr++] = ref;\n \treturn 0;\n }\n \n@@ -956,10 +965,10 @@ static int compare_refs(const void *a_, const void *b_)\n \treturn 0;\n }\n \n-static void sort_refs(struct ref_sort *sort, struct ref_array_item **refs, int num_refs)\n+static void sort_refs(struct ref_sort *sort, struct ref_array *array)\n {\n \tref_sort = sort;\n-\tqsort(refs, num_refs, sizeof(struct ref_array_item *), compare_refs);\n+\tqsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);\n }\n \n static void print_value(struct atom_value *v, int quote_style)\n@@ -1095,12 +1104,11 @@ static char const * const for_each_ref_usage[] = {\n \n int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n {\n-\tint i, num_refs;\n+\tint i;\n \tconst char *format = \"%(objectname) %(objecttype)\\t%(refname)\";\n \tstruct ref_sort *sort = NULL, **sort_tail = &sort;\n \tint maxcount = 0, quote_style = 0;\n-\tstruct ref_array_item **refs;\n-\tstruct grab_ref_cbdata cbdata;\n+\tstruct ref_filter_cbdata ref_cbdata;\n \n \tstruct option opts[] = {\n \t\tOPT_BIT('s', \"shell\", &quote_style,\n@@ -1138,17 +1146,15 @@ 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(&cbdata, 0, sizeof(cbdata));\n-\tcbdata.grab_pattern = argv;\n-\tfor_each_rawref(grab_single_ref, &cbdata);\n-\trefs = cbdata.grab_array;\n-\tnum_refs = cbdata.grab_cnt;\n+\tmemset(&ref_cbdata, 0, sizeof(ref_cbdata));\n+\tref_cbdata.filter.name_patterns = argv;\n+\tfor_each_rawref(grab_single_ref, &ref_cbdata);\n \n-\tsort_refs(sort, refs, num_refs);\n+\tsort_refs(sort, &ref_cbdata.array);\n \n-\tif (!maxcount || num_refs < maxcount)\n-\t\tmaxcount = num_refs;\n+\tif (!maxcount || ref_cbdata.array.nr < maxcount)\n+\t\tmaxcount = ref_cbdata.array.nr;\n \tfor (i = 0; i < maxcount; i++)\n-\t\tshow_ref(refs[i], format, quote_style);\n+\t\tshow_ref(ref_cbdata.array.items[i], format, quote_style);\n \treturn 0;\n }\n-- \n2.4.2\n"},{"id":"263107","messageId":"1433598496-31287-5-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 05/11] for-each-ref: introduce 'ref_array_clear()'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:48:10Z","receivedAt":"2015-06-06T13:48:10Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Introduce and implement 'ref_array_clear()' which will free\nall allocated memory for 'ref_array'.\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/for-each-ref.c | 21 +++++++++++++++++++++\n 1 file changed, 21 insertions(+)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 42a89cd..a65a4db 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -926,6 +926,26 @@ static int grab_single_ref(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n+/*  Free memory allocated for a ref_array_item */\n+static void free_array_item(struct ref_array_item *item)\n+{\n+\tfree(item->symref);\n+\tfree(item->refname);\n+\tfree(item);\n+}\n+\n+/* Free all memory allocated for ref_array */\n+void ref_array_clear(struct ref_array *array)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < array->nr; i++)\n+\t\tfree_array_item(array->items[i]);\n+\tfree(array->items);\n+\tarray->items = NULL;\n+\tarray->nr = array->alloc = 0;\n+}\n+\n static int cmp_ref_sort(struct ref_sort *s, struct ref_array_item *a, struct ref_array_item *b)\n {\n \tstruct atom_value *va, *vb;\n@@ -1156,5 +1176,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\tmaxcount = ref_cbdata.array.nr;\n \tfor (i = 0; i < maxcount; i++)\n \t\tshow_ref(ref_cbdata.array.items[i], format, quote_style);\n+\tref_array_clear(&ref_cbdata.array);\n \treturn 0;\n }\n-- \n2.4.2\n"},{"id":"263108","messageId":"1433598496-31287-6-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 06/11] for-each-ref: rename some functions and make them public","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:48:11Z","receivedAt":"2015-06-06T13:48:11Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Rename some of the functions and make them publicly available.\nThis is a preparatory step for moving code from 'for-each-ref'\nto 'ref-filter' to make meaningful, targeted services available to\nother commands via public APIs.\n\nFunctions renamed are:\nparse_atom()\t\t-> \tparse_ref_filter_atom()\nverify_format()\t\t-> \tverify_ref_format()\nget_value()\t\t-> \tget_ref_atom_value()\ngrab_single_ref()\t-> \tref_filter_handler()\nsort_refs()\t\t->\tref_array_sort()\nshow_ref()\t\t->\tshow_ref_array_item()\ndefault_sort()\t\t->\tref_default_sorting()\nopt_parse_sort()\t->\tparse_opt_ref_sorting()\ncmp_ref_sort()\t\t->\tcmp_ref_sorting()\n\nRename 'struct ref_sort' to 'struct ref_sorting' in this context.\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/for-each-ref.c | 68 +++++++++++++++++++++++++-------------------------\n 1 file changed, 34 insertions(+), 34 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex a65a4db..11552ad 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -25,8 +25,8 @@ struct atom_value {\n \tunsigned long ul; /* used for sorting when not FIELD_STR */\n };\n \n-struct ref_sort {\n-\tstruct ref_sort *next;\n+struct ref_sorting {\n+\tstruct ref_sorting *next;\n \tint atom; /* index into 'struct atom_value *' array */\n \tunsigned reverse : 1;\n };\n@@ -113,7 +113,7 @@ static int need_color_reset_at_eol;\n /*\n  * Used to parse format string and sort specifiers\n  */\n-static int parse_atom(const char *atom, const char *ep)\n+int parse_ref_filter_atom(const char *atom, const char *ep)\n {\n \tconst char *sp;\n \tint i, at;\n@@ -190,7 +190,7 @@ static const char *find_next(const char *cp)\n  * Make sure the format string is well formed, and parse out\n  * the used atoms.\n  */\n-static int verify_format(const char *format)\n+int verify_ref_format(const char *format)\n {\n \tconst char *cp, *sp;\n \n@@ -202,7 +202,7 @@ static int verify_format(const char *format)\n \t\tif (!ep)\n \t\t\treturn error(\"malformed format string %s\", sp);\n \t\t/* sp points at \"%(\" and ep points at the closing \")\" */\n-\t\tat = parse_atom(sp + 2, ep);\n+\t\tat = parse_ref_filter_atom(sp + 2, ep);\n \t\tcp = ep + 1;\n \n \t\tif (skip_prefix(used_atom[at], \"color:\", &color))\n@@ -409,7 +409,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam\n \t/*\n \t * We got here because atomname ends in \"date\" or \"date<something>\";\n \t * it's not possible that <something> is not \":<format>\" because\n-\t * parse_atom() wouldn't have allowed it, so we can assume that no\n+\t * parse_ref_filter_atom() wouldn't have allowed it, so we can assume that no\n \t * \":\" means no format is specified, and use the default.\n \t */\n \tformatp = strchr(atomname, ':');\n@@ -849,7 +849,7 @@ static void populate_value(struct ref_array_item *ref)\n  * Given a ref, return the value for the atom.  This lazily gets value\n  * out of the object by calling populate value.\n  */\n-static void get_value(struct ref_array_item *ref, int atom, struct atom_value **v)\n+static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom_value **v)\n {\n \tif (!ref->value) {\n \t\tpopulate_value(ref);\n@@ -899,8 +899,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname,\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n-static int grab_single_ref(const char *refname, const struct object_id *oid,\n-\t\t\t   int flag, void *cb_data)\n+int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_filter *filter = &ref_cbdata->filter;\n@@ -946,14 +945,14 @@ void ref_array_clear(struct ref_array *array)\n \tarray->nr = array->alloc = 0;\n }\n \n-static int cmp_ref_sort(struct ref_sort *s, struct ref_array_item *a, struct ref_array_item *b)\n+static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)\n {\n \tstruct atom_value *va, *vb;\n \tint cmp;\n \tcmp_type cmp_type = used_atom_type[s->atom];\n \n-\tget_value(a, s->atom, &va);\n-\tget_value(b, s->atom, &vb);\n+\tget_ref_atom_value(a, s->atom, &va);\n+\tget_ref_atom_value(b, s->atom, &vb);\n \tswitch (cmp_type) {\n \tcase FIELD_STR:\n \t\tcmp = strcmp(va->s, vb->s);\n@@ -970,24 +969,24 @@ static int cmp_ref_sort(struct ref_sort *s, struct ref_array_item *a, struct ref\n \treturn (s->reverse) ? -cmp : cmp;\n }\n \n-static struct ref_sort *ref_sort;\n+static struct ref_sorting *ref_sorting;\n static int compare_refs(const void *a_, const void *b_)\n {\n \tstruct ref_array_item *a = *((struct ref_array_item **)a_);\n \tstruct ref_array_item *b = *((struct ref_array_item **)b_);\n-\tstruct ref_sort *s;\n+\tstruct ref_sorting *s;\n \n-\tfor (s = ref_sort; s; s = s->next) {\n-\t\tint cmp = cmp_ref_sort(s, a, b);\n+\tfor (s = ref_sorting; s; s = s->next) {\n+\t\tint cmp = cmp_ref_sorting(s, a, b);\n \t\tif (cmp)\n \t\t\treturn cmp;\n \t}\n \treturn 0;\n }\n \n-static void sort_refs(struct ref_sort *sort, struct ref_array *array)\n+void ref_array_sort(struct ref_sorting *sort, struct ref_array *array)\n {\n-\tref_sort = sort;\n+\tref_sorting = sort;\n \tqsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);\n }\n \n@@ -1055,7 +1054,7 @@ static void emit(const char *cp, const char *ep)\n \t}\n }\n \n-static void show_ref(struct ref_array_item *info, const char *format, int quote_style)\n+void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n {\n \tconst char *cp, *sp, *ep;\n \n@@ -1065,7 +1064,7 @@ static void show_ref(struct ref_array_item *info, const char *format, int quote_\n \t\tep = strchr(sp, ')');\n \t\tif (cp < sp)\n \t\t\temit(cp, sp);\n-\t\tget_value(info, parse_atom(sp + 2, ep), &atomv);\n+\t\tget_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n \t\tprint_value(atomv, quote_style);\n \t}\n \tif (*cp) {\n@@ -1084,21 +1083,22 @@ static void show_ref(struct ref_array_item *info, const char *format, int quote_\n \tputchar('\\n');\n }\n \n-static struct ref_sort *default_sort(void)\n+/*  If no sorting option is given, use refname to sort as default */\n+struct ref_sorting *ref_default_sorting(void)\n {\n \tstatic const char cstr_name[] = \"refname\";\n \n-\tstruct ref_sort *sort = xcalloc(1, sizeof(*sort));\n+\tstruct ref_sorting *sort = xcalloc(1, sizeof(*sort));\n \n \tsort->next = NULL;\n-\tsort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));\n+\tsort->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name));\n \treturn sort;\n }\n \n-static int opt_parse_sort(const struct option *opt, const char *arg, int unset)\n+int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)\n {\n-\tstruct ref_sort **sort_tail = opt->value;\n-\tstruct ref_sort *s;\n+\tstruct ref_sorting **sort_tail = opt->value;\n+\tstruct ref_sorting *s;\n \tint len;\n \n \tif (!arg) /* should --no-sort void the list ? */\n@@ -1113,7 +1113,7 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset)\n \t\targ++;\n \t}\n \tlen = strlen(arg);\n-\ts->atom = parse_atom(arg, arg+len);\n+\ts->atom = parse_ref_filter_atom(arg, arg+len);\n \treturn 0;\n }\n \n@@ -1126,7 +1126,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n \tconst char *format = \"%(objectname) %(objecttype)\\t%(refname)\";\n-\tstruct ref_sort *sort = NULL, **sort_tail = &sort;\n+\tstruct ref_sorting *sort = NULL, **sort_tail = &sort;\n \tint maxcount = 0, quote_style = 0;\n \tstruct ref_filter_cbdata ref_cbdata;\n \n@@ -1144,7 +1144,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\tOPT_INTEGER( 0 , \"count\", &maxcount, N_(\"show only <n> matched refs\")),\n \t\tOPT_STRING(  0 , \"format\", &format, N_(\"format\"), N_(\"format to use for the output\")),\n \t\tOPT_CALLBACK(0 , \"sort\", sort_tail, N_(\"key\"),\n-\t\t\t    N_(\"field name to sort on\"), &opt_parse_sort),\n+\t\t\t    N_(\"field name to sort on\"), &parse_opt_ref_sorting),\n \t\tOPT_END(),\n \t};\n \n@@ -1157,25 +1157,25 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\terror(\"more than one quoting style?\");\n \t\tusage_with_options(for_each_ref_usage, opts);\n \t}\n-\tif (verify_format(format))\n+\tif (verify_ref_format(format))\n \t\tusage_with_options(for_each_ref_usage, opts);\n \n \tif (!sort)\n-\t\tsort = default_sort();\n+\t\tsort = ref_default_sorting();\n \n \t/* for warn_ambiguous_refs */\n \tgit_config(git_default_config, NULL);\n \n \tmemset(&ref_cbdata, 0, sizeof(ref_cbdata));\n \tref_cbdata.filter.name_patterns = argv;\n-\tfor_each_rawref(grab_single_ref, &ref_cbdata);\n+\tfor_each_rawref(ref_filter_handler, &ref_cbdata);\n \n-\tsort_refs(sort, &ref_cbdata.array);\n+\tref_array_sort(sort, &ref_cbdata.array);\n \n \tif (!maxcount || ref_cbdata.array.nr < maxcount)\n \t\tmaxcount = ref_cbdata.array.nr;\n \tfor (i = 0; i < maxcount; i++)\n-\t\tshow_ref(ref_cbdata.array.items[i], format, quote_style);\n+\t\tshow_ref_array_item(ref_cbdata.array.items[i], format, quote_style);\n \tref_array_clear(&ref_cbdata.array);\n \treturn 0;\n }\n-- \n2.4.2\n"},{"id":"263112","messageId":"1433598496-31287-7-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 07/11] for-each-ref: rename variables called sort to sorting","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:48:12Z","receivedAt":"2015-06-06T13:48:12Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Rename all the variables called sort to sorting to match the\nfunction name changes made in the previous patch.\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/for-each-ref.c | 28 ++++++++++++++--------------\n 1 file changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 11552ad..c0085be 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -984,9 +984,9 @@ static int compare_refs(const void *a_, const void *b_)\n \treturn 0;\n }\n \n-void ref_array_sort(struct ref_sorting *sort, struct ref_array *array)\n+void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n {\n-\tref_sorting = sort;\n+\tref_sorting = sorting;\n \tqsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);\n }\n \n@@ -1088,16 +1088,16 @@ struct ref_sorting *ref_default_sorting(void)\n {\n \tstatic const char cstr_name[] = \"refname\";\n \n-\tstruct ref_sorting *sort = xcalloc(1, sizeof(*sort));\n+\tstruct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));\n \n-\tsort->next = NULL;\n-\tsort->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name));\n-\treturn sort;\n+\tsorting->next = NULL;\n+\tsorting->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name));\n+\treturn sorting;\n }\n \n int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)\n {\n-\tstruct ref_sorting **sort_tail = opt->value;\n+\tstruct ref_sorting **sorting_tail = opt->value;\n \tstruct ref_sorting *s;\n \tint len;\n \n@@ -1105,8 +1105,8 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)\n \t\treturn -1;\n \n \ts = xcalloc(1, sizeof(*s));\n-\ts->next = *sort_tail;\n-\t*sort_tail = s;\n+\ts->next = *sorting_tail;\n+\t*sorting_tail = s;\n \n \tif (*arg == '-') {\n \t\ts->reverse = 1;\n@@ -1126,7 +1126,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n \tconst char *format = \"%(objectname) %(objecttype)\\t%(refname)\";\n-\tstruct ref_sorting *sort = NULL, **sort_tail = &sort;\n+\tstruct ref_sorting *sorting = NULL, **sorting_tail = &sorting;\n \tint maxcount = 0, quote_style = 0;\n \tstruct ref_filter_cbdata ref_cbdata;\n \n@@ -1143,7 +1143,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\tOPT_GROUP(\"\"),\n \t\tOPT_INTEGER( 0 , \"count\", &maxcount, N_(\"show only <n> matched refs\")),\n \t\tOPT_STRING(  0 , \"format\", &format, N_(\"format\"), N_(\"format to use for the output\")),\n-\t\tOPT_CALLBACK(0 , \"sort\", sort_tail, N_(\"key\"),\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_END(),\n \t};\n@@ -1160,8 +1160,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \tif (verify_ref_format(format))\n \t\tusage_with_options(for_each_ref_usage, opts);\n \n-\tif (!sort)\n-\t\tsort = ref_default_sorting();\n+\tif (!sorting)\n+\t\tsorting = ref_default_sorting();\n \n \t/* for warn_ambiguous_refs */\n \tgit_config(git_default_config, NULL);\n@@ -1170,7 +1170,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \tref_cbdata.filter.name_patterns = argv;\n \tfor_each_rawref(ref_filter_handler, &ref_cbdata);\n \n-\tref_array_sort(sort, &ref_cbdata.array);\n+\tref_array_sort(sorting, &ref_cbdata.array);\n \n \tif (!maxcount || ref_cbdata.array.nr < maxcount)\n \t\tmaxcount = ref_cbdata.array.nr;\n-- \n2.4.2\n"},{"id":"263110","messageId":"1433598496-31287-8-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 08/11] ref-filter: add 'ref-filter.h'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:48:13Z","receivedAt":"2015-06-06T13:48:13Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"This is step one of creating a common library for 'for-each-ref',\n'branch -l' and 'tag -l'. This creates a header file with the\nfunctions and data structures that ref-filter will provide.\nWe move the data structures created in for-each-ref to this header\nfile.\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/for-each-ref.c | 41 +------------------------------\n ref-filter.h           | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 67 insertions(+), 40 deletions(-)\n create mode 100644 ref-filter.h\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex c0085be..063de00 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -10,49 +10,10 @@\n #include \"parse-options.h\"\n #include \"remote.h\"\n #include \"color.h\"\n-\n-/* Quoting styles */\n-#define QUOTE_NONE 0\n-#define QUOTE_SHELL 1\n-#define QUOTE_PERL 2\n-#define QUOTE_PYTHON 4\n-#define QUOTE_TCL 8\n+#include \"ref-filter.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n \n-struct atom_value {\n-\tconst char *s;\n-\tunsigned long ul; /* used for sorting when not FIELD_STR */\n-};\n-\n-struct ref_sorting {\n-\tstruct ref_sorting *next;\n-\tint atom; /* index into 'struct atom_value *' array */\n-\tunsigned reverse : 1;\n-};\n-\n-struct ref_array_item {\n-\tunsigned char objectname[20];\n-\tint flag;\n-\tchar *symref;\n-\tstruct atom_value *value;\n-\tchar *refname;\n-};\n-\n-struct ref_array {\n-\tint nr, alloc;\n-\tstruct ref_array_item **items;\n-};\n-\n-struct ref_filter {\n-\tconst char **name_patterns;\n-};\n-\n-struct ref_filter_cbdata {\n-\tstruct ref_array array;\n-\tstruct ref_filter filter;\n-};\n-\n static struct {\n \tconst char *name;\n \tcmp_type cmp_type;\ndiff --git a/ref-filter.h b/ref-filter.h\nnew file mode 100644\nindex 0000000..c2c5d37\n--- /dev/null\n+++ b/ref-filter.h\n@@ -0,0 +1,66 @@\n+#ifndef REF_FILTER_H\n+#define REF_FILTER_H\n+\n+#include \"sha1-array.h\"\n+#include \"refs.h\"\n+#include \"commit.h\"\n+#include \"parse-options.h\"\n+\n+/* Quoting styles */\n+#define QUOTE_NONE 0\n+#define QUOTE_SHELL 1\n+#define QUOTE_PERL 2\n+#define QUOTE_PYTHON 4\n+#define QUOTE_TCL 8\n+\n+struct atom_value {\n+\tconst char *s;\n+\tunsigned long ul; /* used for sorting when not FIELD_STR */\n+};\n+\n+struct ref_sorting {\n+\tstruct ref_sorting *next;\n+\tint atom; /* index into 'struct atom_value *' array */\n+\tunsigned reverse : 1;\n+};\n+\n+struct ref_array_item {\n+\tunsigned char objectname[20];\n+\tint flag;\n+\tchar *symref;\n+\tstruct atom_value *value;\n+\tchar *refname;\n+};\n+\n+struct ref_array {\n+\tint nr, alloc;\n+\tstruct ref_array_item **items;\n+};\n+\n+struct ref_filter {\n+\tconst char **name_patterns;\n+};\n+\n+struct ref_filter_cbdata {\n+\tstruct ref_array array;\n+\tstruct ref_filter filter;\n+};\n+\n+/*  Callback function for for_each_*ref(). This filters the refs based on the filters set */\n+int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data);\n+/*  Clear all memory allocated to ref_array */\n+void ref_array_clear(struct ref_array *array);\n+/*  Parse format string and sort specifiers */\n+int parse_ref_filter_atom(const char *atom, const char *ep);\n+/*  Used to verify if the given format is correct and to parse out the used atoms */\n+int verify_ref_format(const char *format);\n+/*  Sort the given ref_array as per the ref_sorting provided */\n+void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);\n+/*  Print the ref using the given format and quote_style */\n+void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style);\n+/*  Callback function for parsing the sort option */\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+\n+#endif /*  REF_FILTER_H  */\n-- \n2.4.2\n"},{"id":"263109","messageId":"1433598496-31287-9-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 09/11] ref-filter: move code from 'for-each-ref'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:48:14Z","receivedAt":"2015-06-06T13:48:14Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Move most of the code from 'for-each-ref' to 'ref-filter' to make\nit publicly available to other commands, this is to unify the code\nof 'tag -l', 'branch -l' and 'for-each-ref' so that they can share\ntheir implementations with each other.\n\nAdd 'ref-filter' to the Makefile, this completes the movement of code\nfrom 'for-each-ref' to '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 Makefile               |    1 +\n builtin/for-each-ref.c | 1075 +----------------------------------------------\n ref-filter.c           | 1077 ++++++++++++++++++++++++++++++++++++++++++++++++\n ref-filter.h           |    8 +-\n 4 files changed, 1085 insertions(+), 1076 deletions(-)\n create mode 100644 ref-filter.c\n\ndiff --git a/Makefile b/Makefile\nindex 54ec511..d715b66 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -762,6 +762,7 @@ LIB_OBJS += reachable.o\n LIB_OBJS += read-cache.o\n LIB_OBJS += reflog-walk.o\n LIB_OBJS += refs.o\n+LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace_object.o\n LIB_OBJS += rerere.o\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 063de00..4d2d024 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -2,1082 +2,9 @@\n #include \"cache.h\"\n #include \"refs.h\"\n #include \"object.h\"\n-#include \"tag.h\"\n-#include \"commit.h\"\n-#include \"tree.h\"\n-#include \"blob.h\"\n-#include \"quote.h\"\n #include \"parse-options.h\"\n-#include \"remote.h\"\n-#include \"color.h\"\n #include \"ref-filter.h\"\n \n-typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n-\n-static struct {\n-\tconst char *name;\n-\tcmp_type cmp_type;\n-} valid_atom[] = {\n-\t{ \"refname\" },\n-\t{ \"objecttype\" },\n-\t{ \"objectsize\", FIELD_ULONG },\n-\t{ \"objectname\" },\n-\t{ \"tree\" },\n-\t{ \"parent\" },\n-\t{ \"numparent\", FIELD_ULONG },\n-\t{ \"object\" },\n-\t{ \"type\" },\n-\t{ \"tag\" },\n-\t{ \"author\" },\n-\t{ \"authorname\" },\n-\t{ \"authoremail\" },\n-\t{ \"authordate\", FIELD_TIME },\n-\t{ \"committer\" },\n-\t{ \"committername\" },\n-\t{ \"committeremail\" },\n-\t{ \"committerdate\", FIELD_TIME },\n-\t{ \"tagger\" },\n-\t{ \"taggername\" },\n-\t{ \"taggeremail\" },\n-\t{ \"taggerdate\", FIELD_TIME },\n-\t{ \"creator\" },\n-\t{ \"creatordate\", FIELD_TIME },\n-\t{ \"subject\" },\n-\t{ \"body\" },\n-\t{ \"contents\" },\n-\t{ \"contents:subject\" },\n-\t{ \"contents:body\" },\n-\t{ \"contents:signature\" },\n-\t{ \"upstream\" },\n-\t{ \"push\" },\n-\t{ \"symref\" },\n-\t{ \"flag\" },\n-\t{ \"HEAD\" },\n-\t{ \"color\" },\n-};\n-\n-/*\n- * An atom is a valid field atom listed above, possibly prefixed with\n- * a \"*\" to denote deref_tag().\n- *\n- * We parse given format string and sort specifiers, and make a list\n- * of properties that we need to extract out of objects.  ref_array_item\n- * structure will hold an array of values extracted that can be\n- * indexed with the \"atom number\", which is an index into this\n- * array.\n- */\n-static const char **used_atom;\n-static cmp_type *used_atom_type;\n-static int used_atom_cnt, need_tagged, need_symref;\n-static int need_color_reset_at_eol;\n-\n-/*\n- * Used to parse format string and sort specifiers\n- */\n-int parse_ref_filter_atom(const char *atom, const char *ep)\n-{\n-\tconst char *sp;\n-\tint i, at;\n-\n-\tsp = atom;\n-\tif (*sp == '*' && sp < ep)\n-\t\tsp++; /* deref */\n-\tif (ep <= sp)\n-\t\tdie(\"malformed field name: %.*s\", (int)(ep-atom), atom);\n-\n-\t/* Do we have the atom already used elsewhere? */\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tint len = strlen(used_atom[i]);\n-\t\tif (len == ep - atom && !memcmp(used_atom[i], atom, len))\n-\t\t\treturn i;\n-\t}\n-\n-\t/* Is the atom a valid one? */\n-\tfor (i = 0; i < ARRAY_SIZE(valid_atom); i++) {\n-\t\tint len = strlen(valid_atom[i].name);\n-\t\t/*\n-\t\t * If the atom name has a colon, strip it and everything after\n-\t\t * it off - it specifies the format for this entry, and\n-\t\t * shouldn't be used for checking against the valid_atom\n-\t\t * table.\n-\t\t */\n-\t\tconst char *formatp = strchr(sp, ':');\n-\t\tif (!formatp || ep < formatp)\n-\t\t\tformatp = ep;\n-\t\tif (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))\n-\t\t\tbreak;\n-\t}\n-\n-\tif (ARRAY_SIZE(valid_atom) <= i)\n-\t\tdie(\"unknown field name: %.*s\", (int)(ep-atom), atom);\n-\n-\t/* Add it in, including the deref prefix */\n-\tat = used_atom_cnt;\n-\tused_atom_cnt++;\n-\tREALLOC_ARRAY(used_atom, used_atom_cnt);\n-\tREALLOC_ARRAY(used_atom_type, used_atom_cnt);\n-\tused_atom[at] = xmemdupz(atom, ep - atom);\n-\tused_atom_type[at] = valid_atom[i].cmp_type;\n-\tif (*atom == '*')\n-\t\tneed_tagged = 1;\n-\tif (!strcmp(used_atom[at], \"symref\"))\n-\t\tneed_symref = 1;\n-\treturn at;\n-}\n-\n-/*\n- * In a format string, find the next occurrence of %(atom).\n- */\n-static const char *find_next(const char *cp)\n-{\n-\twhile (*cp) {\n-\t\tif (*cp == '%') {\n-\t\t\t/*\n-\t\t\t * %( is the start of an atom;\n-\t\t\t * %% is a quoted per-cent.\n-\t\t\t */\n-\t\t\tif (cp[1] == '(')\n-\t\t\t\treturn cp;\n-\t\t\telse if (cp[1] == '%')\n-\t\t\t\tcp++; /* skip over two % */\n-\t\t\t/* otherwise this is a singleton, literal % */\n-\t\t}\n-\t\tcp++;\n-\t}\n-\treturn NULL;\n-}\n-\n-/*\n- * Make sure the format string is well formed, and parse out\n- * the used atoms.\n- */\n-int verify_ref_format(const char *format)\n-{\n-\tconst char *cp, *sp;\n-\n-\tneed_color_reset_at_eol = 0;\n-\tfor (cp = format; *cp && (sp = find_next(cp)); ) {\n-\t\tconst char *color, *ep = strchr(sp, ')');\n-\t\tint at;\n-\n-\t\tif (!ep)\n-\t\t\treturn error(\"malformed format string %s\", sp);\n-\t\t/* sp points at \"%(\" and ep points at the closing \")\" */\n-\t\tat = parse_ref_filter_atom(sp + 2, ep);\n-\t\tcp = ep + 1;\n-\n-\t\tif (skip_prefix(used_atom[at], \"color:\", &color))\n-\t\t\tneed_color_reset_at_eol = !!strcmp(color, \"reset\");\n-\t}\n-\treturn 0;\n-}\n-\n-/*\n- * Given an object name, read the object data and size, and return a\n- * \"struct object\".  If the object data we are returning is also borrowed\n- * by the \"struct object\" representation, set *eaten as well---it is a\n- * signal from parse_object_buffer to us not to free the buffer.\n- */\n-static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)\n-{\n-\tenum object_type type;\n-\tvoid *buf = read_sha1_file(sha1, &type, sz);\n-\n-\tif (buf)\n-\t\t*obj = parse_object_buffer(sha1, type, *sz, buf, eaten);\n-\telse\n-\t\t*obj = NULL;\n-\treturn buf;\n-}\n-\n-static int grab_objectname(const char *name, const unsigned char *sha1,\n-\t\t\t    struct atom_value *v)\n-{\n-\tif (!strcmp(name, \"objectname\")) {\n-\t\tchar *s = xmalloc(41);\n-\t\tstrcpy(s, sha1_to_hex(sha1));\n-\t\tv->s = s;\n-\t\treturn 1;\n-\t}\n-\tif (!strcmp(name, \"objectname:short\")) {\n-\t\tv->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));\n-\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n-/* See grab_values */\n-static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tint i;\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\t\tif (!strcmp(name, \"objecttype\"))\n-\t\t\tv->s = typename(obj->type);\n-\t\telse if (!strcmp(name, \"objectsize\")) {\n-\t\t\tchar *s = xmalloc(40);\n-\t\t\tsprintf(s, \"%lu\", sz);\n-\t\t\tv->ul = sz;\n-\t\t\tv->s = s;\n-\t\t}\n-\t\telse if (deref)\n-\t\t\tgrab_objectname(name, obj->sha1, v);\n-\t}\n-}\n-\n-/* See grab_values */\n-static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tint i;\n-\tstruct tag *tag = (struct tag *) obj;\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\t\tif (!strcmp(name, \"tag\"))\n-\t\t\tv->s = tag->tag;\n-\t\telse if (!strcmp(name, \"type\") && tag->tagged)\n-\t\t\tv->s = typename(tag->tagged->type);\n-\t\telse if (!strcmp(name, \"object\") && tag->tagged) {\n-\t\t\tchar *s = xmalloc(41);\n-\t\t\tstrcpy(s, sha1_to_hex(tag->tagged->sha1));\n-\t\t\tv->s = s;\n-\t\t}\n-\t}\n-}\n-\n-/* See grab_values */\n-static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tint i;\n-\tstruct commit *commit = (struct commit *) obj;\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\t\tif (!strcmp(name, \"tree\")) {\n-\t\t\tchar *s = xmalloc(41);\n-\t\t\tstrcpy(s, sha1_to_hex(commit->tree->object.sha1));\n-\t\t\tv->s = s;\n-\t\t}\n-\t\tif (!strcmp(name, \"numparent\")) {\n-\t\t\tchar *s = xmalloc(40);\n-\t\t\tv->ul = commit_list_count(commit->parents);\n-\t\t\tsprintf(s, \"%lu\", v->ul);\n-\t\t\tv->s = s;\n-\t\t}\n-\t\telse if (!strcmp(name, \"parent\")) {\n-\t\t\tint num = commit_list_count(commit->parents);\n-\t\t\tint i;\n-\t\t\tstruct commit_list *parents;\n-\t\t\tchar *s = xmalloc(41 * num + 1);\n-\t\t\tv->s = s;\n-\t\t\tfor (i = 0, parents = commit->parents;\n-\t\t\t     parents;\n-\t\t\t     parents = parents->next, i = i + 41) {\n-\t\t\t\tstruct commit *parent = parents->item;\n-\t\t\t\tstrcpy(s+i, sha1_to_hex(parent->object.sha1));\n-\t\t\t\tif (parents->next)\n-\t\t\t\t\ts[i+40] = ' ';\n-\t\t\t}\n-\t\t\tif (!i)\n-\t\t\t\t*s = '\\0';\n-\t\t}\n-\t}\n-}\n-\n-static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)\n-{\n-\tconst char *eol;\n-\twhile (*buf) {\n-\t\tif (!strncmp(buf, who, wholen) &&\n-\t\t    buf[wholen] == ' ')\n-\t\t\treturn buf + wholen + 1;\n-\t\teol = strchr(buf, '\\n');\n-\t\tif (!eol)\n-\t\t\treturn \"\";\n-\t\teol++;\n-\t\tif (*eol == '\\n')\n-\t\t\treturn \"\"; /* end of header */\n-\t\tbuf = eol;\n-\t}\n-\treturn \"\";\n-}\n-\n-static const char *copy_line(const char *buf)\n-{\n-\tconst char *eol = strchrnul(buf, '\\n');\n-\treturn xmemdupz(buf, eol - buf);\n-}\n-\n-static const char *copy_name(const char *buf)\n-{\n-\tconst char *cp;\n-\tfor (cp = buf; *cp && *cp != '\\n'; cp++) {\n-\t\tif (!strncmp(cp, \" <\", 2))\n-\t\t\treturn xmemdupz(buf, cp - buf);\n-\t}\n-\treturn \"\";\n-}\n-\n-static const char *copy_email(const char *buf)\n-{\n-\tconst char *email = strchr(buf, '<');\n-\tconst char *eoemail;\n-\tif (!email)\n-\t\treturn \"\";\n-\teoemail = strchr(email, '>');\n-\tif (!eoemail)\n-\t\treturn \"\";\n-\treturn xmemdupz(email, eoemail + 1 - email);\n-}\n-\n-static char *copy_subject(const char *buf, unsigned long len)\n-{\n-\tchar *r = xmemdupz(buf, len);\n-\tint i;\n-\n-\tfor (i = 0; i < len; i++)\n-\t\tif (r[i] == '\\n')\n-\t\t\tr[i] = ' ';\n-\n-\treturn r;\n-}\n-\n-static void grab_date(const char *buf, struct atom_value *v, const char *atomname)\n-{\n-\tconst char *eoemail = strstr(buf, \"> \");\n-\tchar *zone;\n-\tunsigned long timestamp;\n-\tlong tz;\n-\tenum date_mode date_mode = DATE_NORMAL;\n-\tconst char *formatp;\n-\n-\t/*\n-\t * We got here because atomname ends in \"date\" or \"date<something>\";\n-\t * it's not possible that <something> is not \":<format>\" because\n-\t * parse_ref_filter_atom() wouldn't have allowed it, so we can assume that no\n-\t * \":\" means no format is specified, and use the default.\n-\t */\n-\tformatp = strchr(atomname, ':');\n-\tif (formatp != NULL) {\n-\t\tformatp++;\n-\t\tdate_mode = parse_date_format(formatp);\n-\t}\n-\n-\tif (!eoemail)\n-\t\tgoto bad;\n-\ttimestamp = strtoul(eoemail + 2, &zone, 10);\n-\tif (timestamp == ULONG_MAX)\n-\t\tgoto bad;\n-\ttz = strtol(zone, NULL, 10);\n-\tif ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)\n-\t\tgoto bad;\n-\tv->s = xstrdup(show_date(timestamp, tz, date_mode));\n-\tv->ul = timestamp;\n-\treturn;\n- bad:\n-\tv->s = \"\";\n-\tv->ul = 0;\n-}\n-\n-/* See grab_values */\n-static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tint i;\n-\tint wholen = strlen(who);\n-\tconst char *wholine = NULL;\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\t\tif (strncmp(who, name, wholen))\n-\t\t\tcontinue;\n-\t\tif (name[wholen] != 0 &&\n-\t\t    strcmp(name + wholen, \"name\") &&\n-\t\t    strcmp(name + wholen, \"email\") &&\n-\t\t    !starts_with(name + wholen, \"date\"))\n-\t\t\tcontinue;\n-\t\tif (!wholine)\n-\t\t\twholine = find_wholine(who, wholen, buf, sz);\n-\t\tif (!wholine)\n-\t\t\treturn; /* no point looking for it */\n-\t\tif (name[wholen] == 0)\n-\t\t\tv->s = copy_line(wholine);\n-\t\telse if (!strcmp(name + wholen, \"name\"))\n-\t\t\tv->s = copy_name(wholine);\n-\t\telse if (!strcmp(name + wholen, \"email\"))\n-\t\t\tv->s = copy_email(wholine);\n-\t\telse if (starts_with(name + wholen, \"date\"))\n-\t\t\tgrab_date(wholine, v, name);\n-\t}\n-\n-\t/*\n-\t * For a tag or a commit object, if \"creator\" or \"creatordate\" is\n-\t * requested, do something special.\n-\t */\n-\tif (strcmp(who, \"tagger\") && strcmp(who, \"committer\"))\n-\t\treturn; /* \"author\" for commit object is not wanted */\n-\tif (!wholine)\n-\t\twholine = find_wholine(who, wholen, buf, sz);\n-\tif (!wholine)\n-\t\treturn;\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\n-\t\tif (starts_with(name, \"creatordate\"))\n-\t\t\tgrab_date(wholine, v, name);\n-\t\telse if (!strcmp(name, \"creator\"))\n-\t\t\tv->s = copy_line(wholine);\n-\t}\n-}\n-\n-static void find_subpos(const char *buf, unsigned long sz,\n-\t\t\tconst char **sub, unsigned long *sublen,\n-\t\t\tconst char **body, unsigned long *bodylen,\n-\t\t\tunsigned long *nonsiglen,\n-\t\t\tconst char **sig, unsigned long *siglen)\n-{\n-\tconst char *eol;\n-\t/* skip past header until we hit empty line */\n-\twhile (*buf && *buf != '\\n') {\n-\t\teol = strchrnul(buf, '\\n');\n-\t\tif (*eol)\n-\t\t\teol++;\n-\t\tbuf = eol;\n-\t}\n-\t/* skip any empty lines */\n-\twhile (*buf == '\\n')\n-\t\tbuf++;\n-\n-\t/* parse signature first; we might not even have a subject line */\n-\t*sig = buf + parse_signature(buf, strlen(buf));\n-\t*siglen = strlen(*sig);\n-\n-\t/* subject is first non-empty line */\n-\t*sub = buf;\n-\t/* subject goes to first empty line */\n-\twhile (buf < *sig && *buf && *buf != '\\n') {\n-\t\teol = strchrnul(buf, '\\n');\n-\t\tif (*eol)\n-\t\t\teol++;\n-\t\tbuf = eol;\n-\t}\n-\t*sublen = buf - *sub;\n-\t/* drop trailing newline, if present */\n-\tif (*sublen && (*sub)[*sublen - 1] == '\\n')\n-\t\t*sublen -= 1;\n-\n-\t/* skip any empty lines */\n-\twhile (*buf == '\\n')\n-\t\tbuf++;\n-\t*body = buf;\n-\t*bodylen = strlen(buf);\n-\t*nonsiglen = *sig - buf;\n-}\n-\n-/* See grab_values */\n-static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tint i;\n-\tconst char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;\n-\tunsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\t\tif (strcmp(name, \"subject\") &&\n-\t\t    strcmp(name, \"body\") &&\n-\t\t    strcmp(name, \"contents\") &&\n-\t\t    strcmp(name, \"contents:subject\") &&\n-\t\t    strcmp(name, \"contents:body\") &&\n-\t\t    strcmp(name, \"contents:signature\"))\n-\t\t\tcontinue;\n-\t\tif (!subpos)\n-\t\t\tfind_subpos(buf, sz,\n-\t\t\t\t    &subpos, &sublen,\n-\t\t\t\t    &bodypos, &bodylen, &nonsiglen,\n-\t\t\t\t    &sigpos, &siglen);\n-\n-\t\tif (!strcmp(name, \"subject\"))\n-\t\t\tv->s = copy_subject(subpos, sublen);\n-\t\telse if (!strcmp(name, \"contents:subject\"))\n-\t\t\tv->s = copy_subject(subpos, sublen);\n-\t\telse if (!strcmp(name, \"body\"))\n-\t\t\tv->s = xmemdupz(bodypos, bodylen);\n-\t\telse if (!strcmp(name, \"contents:body\"))\n-\t\t\tv->s = xmemdupz(bodypos, nonsiglen);\n-\t\telse if (!strcmp(name, \"contents:signature\"))\n-\t\t\tv->s = xmemdupz(sigpos, siglen);\n-\t\telse if (!strcmp(name, \"contents\"))\n-\t\t\tv->s = xstrdup(subpos);\n-\t}\n-}\n-\n-/*\n- * We want to have empty print-string for field requests\n- * that do not apply (e.g. \"authordate\" for a tag object)\n- */\n-static void fill_missing_values(struct atom_value *val)\n-{\n-\tint i;\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (v->s == NULL)\n-\t\t\tv->s = \"\";\n-\t}\n-}\n-\n-/*\n- * val is a list of atom_value to hold returned values.  Extract\n- * the values for atoms in used_atom array out of (obj, buf, sz).\n- * when deref is false, (obj, buf, sz) is the object that is\n- * pointed at by the ref itself; otherwise it is the object the\n- * ref (which is a tag) refers to.\n- */\n-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tgrab_common_values(val, deref, obj, buf, sz);\n-\tswitch (obj->type) {\n-\tcase OBJ_TAG:\n-\t\tgrab_tag_values(val, deref, obj, buf, sz);\n-\t\tgrab_sub_body_contents(val, deref, obj, buf, sz);\n-\t\tgrab_person(\"tagger\", val, deref, obj, buf, sz);\n-\t\tbreak;\n-\tcase OBJ_COMMIT:\n-\t\tgrab_commit_values(val, deref, obj, buf, sz);\n-\t\tgrab_sub_body_contents(val, deref, obj, buf, sz);\n-\t\tgrab_person(\"author\", val, deref, obj, buf, sz);\n-\t\tgrab_person(\"committer\", val, deref, obj, buf, sz);\n-\t\tbreak;\n-\tcase OBJ_TREE:\n-\t\t/* grab_tree_values(val, deref, obj, buf, sz); */\n-\t\tbreak;\n-\tcase OBJ_BLOB:\n-\t\t/* grab_blob_values(val, deref, obj, buf, sz); */\n-\t\tbreak;\n-\tdefault:\n-\t\tdie(\"Eh?  Object of type %d?\", obj->type);\n-\t}\n-}\n-\n-static inline char *copy_advance(char *dst, const char *src)\n-{\n-\twhile (*src)\n-\t\t*dst++ = *src++;\n-\treturn dst;\n-}\n-\n-/*\n- * Parse the object referred by ref, and grab needed value.\n- */\n-static void populate_value(struct ref_array_item *ref)\n-{\n-\tvoid *buf;\n-\tstruct object *obj;\n-\tint eaten, i;\n-\tunsigned long size;\n-\tconst unsigned char *tagged;\n-\n-\tref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));\n-\n-\tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n-\t\tunsigned char unused1[20];\n-\t\tref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,\n-\t\t\t\t\t     unused1, NULL);\n-\t\tif (!ref->symref)\n-\t\t\tref->symref = \"\";\n-\t}\n-\n-\t/* Fill in specials first */\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &ref->value[i];\n-\t\tint deref = 0;\n-\t\tconst char *refname;\n-\t\tconst char *formatp;\n-\t\tstruct branch *branch = NULL;\n-\n-\t\tif (*name == '*') {\n-\t\t\tderef = 1;\n-\t\t\tname++;\n-\t\t}\n-\n-\t\tif (starts_with(name, \"refname\"))\n-\t\t\trefname = ref->refname;\n-\t\telse if (starts_with(name, \"symref\"))\n-\t\t\trefname = ref->symref ? ref->symref : \"\";\n-\t\telse if (starts_with(name, \"upstream\")) {\n-\t\t\tconst char *branch_name;\n-\t\t\t/* only local branches may have an upstream */\n-\t\t\tif (!skip_prefix(ref->refname, \"refs/heads/\",\n-\t\t\t\t\t &branch_name))\n-\t\t\t\tcontinue;\n-\t\t\tbranch = branch_get(branch_name);\n-\n-\t\t\trefname = branch_get_upstream(branch, NULL);\n-\t\t\tif (!refname)\n-\t\t\t\tcontinue;\n-\t\t} else if (starts_with(name, \"push\")) {\n-\t\t\tconst char *branch_name;\n-\t\t\tif (!skip_prefix(ref->refname, \"refs/heads/\",\n-\t\t\t\t\t &branch_name))\n-\t\t\t\tcontinue;\n-\t\t\tbranch = branch_get(branch_name);\n-\n-\t\t\trefname = branch_get_push(branch, NULL);\n-\t\t\tif (!refname)\n-\t\t\t\tcontinue;\n-\t\t} else if (starts_with(name, \"color:\")) {\n-\t\t\tchar color[COLOR_MAXLEN] = \"\";\n-\n-\t\t\tif (color_parse(name + 6, color) < 0)\n-\t\t\t\tdie(_(\"unable to parse format\"));\n-\t\t\tv->s = xstrdup(color);\n-\t\t\tcontinue;\n-\t\t} else if (!strcmp(name, \"flag\")) {\n-\t\t\tchar buf[256], *cp = buf;\n-\t\t\tif (ref->flag & REF_ISSYMREF)\n-\t\t\t\tcp = copy_advance(cp, \",symref\");\n-\t\t\tif (ref->flag & REF_ISPACKED)\n-\t\t\t\tcp = copy_advance(cp, \",packed\");\n-\t\t\tif (cp == buf)\n-\t\t\t\tv->s = \"\";\n-\t\t\telse {\n-\t\t\t\t*cp = '\\0';\n-\t\t\t\tv->s = xstrdup(buf + 1);\n-\t\t\t}\n-\t\t\tcontinue;\n-\t\t} else if (!deref && grab_objectname(name, ref->objectname, v)) {\n-\t\t\tcontinue;\n-\t\t} else if (!strcmp(name, \"HEAD\")) {\n-\t\t\tconst char *head;\n-\t\t\tunsigned char sha1[20];\n-\n-\t\t\thead = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING,\n-\t\t\t\t\t\t  sha1, NULL);\n-\t\t\tif (!strcmp(ref->refname, head))\n-\t\t\t\tv->s = \"*\";\n-\t\t\telse\n-\t\t\t\tv->s = \" \";\n-\t\t\tcontinue;\n-\t\t} else\n-\t\t\tcontinue;\n-\n-\t\tformatp = strchr(name, ':');\n-\t\tif (formatp) {\n-\t\t\tint num_ours, num_theirs;\n-\n-\t\t\tformatp++;\n-\t\t\tif (!strcmp(formatp, \"short\"))\n-\t\t\t\trefname = shorten_unambiguous_ref(refname,\n-\t\t\t\t\t\t      warn_ambiguous_refs);\n-\t\t\telse if (!strcmp(formatp, \"track\") &&\n-\t\t\t\t (starts_with(name, \"upstream\") ||\n-\t\t\t\t  starts_with(name, \"push\"))) {\n-\t\t\t\tchar buf[40];\n-\n-\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n-\t\t\t\t\t\t       &num_theirs, NULL))\n-\t\t\t\t\tcontinue;\n-\n-\t\t\t\tif (!num_ours && !num_theirs)\n-\t\t\t\t\tv->s = \"\";\n-\t\t\t\telse if (!num_ours) {\n-\t\t\t\t\tsprintf(buf, \"[behind %d]\", num_theirs);\n-\t\t\t\t\tv->s = xstrdup(buf);\n-\t\t\t\t} else if (!num_theirs) {\n-\t\t\t\t\tsprintf(buf, \"[ahead %d]\", num_ours);\n-\t\t\t\t\tv->s = xstrdup(buf);\n-\t\t\t\t} else {\n-\t\t\t\t\tsprintf(buf, \"[ahead %d, behind %d]\",\n-\t\t\t\t\t\tnum_ours, num_theirs);\n-\t\t\t\t\tv->s = xstrdup(buf);\n-\t\t\t\t}\n-\t\t\t\tcontinue;\n-\t\t\t} else if (!strcmp(formatp, \"trackshort\") &&\n-\t\t\t\t   (starts_with(name, \"upstream\") ||\n-\t\t\t\t    starts_with(name, \"push\"))) {\n-\t\t\t\tassert(branch);\n-\n-\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n-\t\t\t\t\t\t\t&num_theirs, NULL))\n-\t\t\t\t\tcontinue;\n-\n-\t\t\t\tif (!num_ours && !num_theirs)\n-\t\t\t\t\tv->s = \"=\";\n-\t\t\t\telse if (!num_ours)\n-\t\t\t\t\tv->s = \"<\";\n-\t\t\t\telse if (!num_theirs)\n-\t\t\t\t\tv->s = \">\";\n-\t\t\t\telse\n-\t\t\t\t\tv->s = \"<>\";\n-\t\t\t\tcontinue;\n-\t\t\t} else\n-\t\t\t\tdie(\"unknown %.*s format %s\",\n-\t\t\t\t    (int)(formatp - name), name, formatp);\n-\t\t}\n-\n-\t\tif (!deref)\n-\t\t\tv->s = refname;\n-\t\telse {\n-\t\t\tint len = strlen(refname);\n-\t\t\tchar *s = xmalloc(len + 4);\n-\t\t\tsprintf(s, \"%s^{}\", refname);\n-\t\t\tv->s = s;\n-\t\t}\n-\t}\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tstruct atom_value *v = &ref->value[i];\n-\t\tif (v->s == NULL)\n-\t\t\tgoto need_obj;\n-\t}\n-\treturn;\n-\n- need_obj:\n-\tbuf = get_obj(ref->objectname, &obj, &size, &eaten);\n-\tif (!buf)\n-\t\tdie(\"missing object %s for %s\",\n-\t\t    sha1_to_hex(ref->objectname), ref->refname);\n-\tif (!obj)\n-\t\tdie(\"parse_object_buffer failed on %s for %s\",\n-\t\t    sha1_to_hex(ref->objectname), ref->refname);\n-\n-\tgrab_values(ref->value, 0, obj, buf, size);\n-\tif (!eaten)\n-\t\tfree(buf);\n-\n-\t/*\n-\t * If there is no atom that wants to know about tagged\n-\t * object, we are done.\n-\t */\n-\tif (!need_tagged || (obj->type != OBJ_TAG))\n-\t\treturn;\n-\n-\t/*\n-\t * If it is a tag object, see if we use a value that derefs\n-\t * the object, and if we do grab the object it refers to.\n-\t */\n-\ttagged = ((struct tag *)obj)->tagged->sha1;\n-\n-\t/*\n-\t * NEEDSWORK: This derefs tag only once, which\n-\t * is good to deal with chains of trust, but\n-\t * is not consistent with what deref_tag() does\n-\t * which peels the onion to the core.\n-\t */\n-\tbuf = get_obj(tagged, &obj, &size, &eaten);\n-\tif (!buf)\n-\t\tdie(\"missing object %s for %s\",\n-\t\t    sha1_to_hex(tagged), ref->refname);\n-\tif (!obj)\n-\t\tdie(\"parse_object_buffer failed on %s for %s\",\n-\t\t    sha1_to_hex(tagged), ref->refname);\n-\tgrab_values(ref->value, 1, obj, buf, size);\n-\tif (!eaten)\n-\t\tfree(buf);\n-}\n-\n-/*\n- * Given a ref, return the value for the atom.  This lazily gets value\n- * out of the object by calling populate value.\n- */\n-static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom_value **v)\n-{\n-\tif (!ref->value) {\n-\t\tpopulate_value(ref);\n-\t\tfill_missing_values(ref->value);\n-\t}\n-\t*v = &ref->value[atom];\n-}\n-\n-/*\n- * Given a refname, return 1 if the refname matches with one of the patterns\n- * while the pattern is a pathname like 'refs/tags' or 'refs/heads/master'\n- * and so on, else return 0. Supports use of wild characters.\n- */\n-static int match_name_as_path(const char **pattern, const char *refname)\n-{\n-\tint namelen = strlen(refname);\n-\tfor (; *pattern; pattern++) {\n-\t\tconst char *p = *pattern;\n-\t\tint plen = strlen(p);\n-\n-\t\tif ((plen <= namelen) &&\n-\t\t    !strncmp(refname, p, plen) &&\n-\t\t    (refname[plen] == '\\0' ||\n-\t\t     refname[plen] == '/' ||\n-\t\t     p[plen-1] == '/'))\n-\t\t\treturn 1;\n-\t\tif (!wildmatch(p, refname, WM_PATHNAME, NULL))\n-\t\t\treturn 1;\n-\t}\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-\t\t\t\t\t\t int flag)\n-{\n-\tstruct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item));\n-\tref->refname = xstrdup(refname);\n-\thashcpy(ref->objectname, objectname);\n-\tref->flag = flag;\n-\n-\treturn ref;\n-}\n-\n-/*\n- * A call-back given to for_each_ref().  Filter refs and keep them for\n- * later object processing.\n- */\n-int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n-{\n-\tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n-\tstruct ref_filter *filter = &ref_cbdata->filter;\n-\tstruct ref_array_item *ref;\n-\n-\tif (flag & REF_BAD_NAME) {\n-\t\t  warning(\"ignoring ref with broken name %s\", refname);\n-\t\t  return 0;\n-\t}\n-\n-\tif (*filter->name_patterns && !match_name_as_path(filter->name_patterns, 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\n-\t * by maxcount logic.\n-\t */\n-\tref = new_ref_array_item(refname, oid->hash, flag);\n-\n-\tREALLOC_ARRAY(ref_cbdata->array.items, ref_cbdata->array.nr + 1);\n-\tref_cbdata->array.items[ref_cbdata->array.nr++] = ref;\n-\treturn 0;\n-}\n-\n-/*  Free memory allocated for a ref_array_item */\n-static void free_array_item(struct ref_array_item *item)\n-{\n-\tfree(item->symref);\n-\tfree(item->refname);\n-\tfree(item);\n-}\n-\n-/* Free all memory allocated for ref_array */\n-void ref_array_clear(struct ref_array *array)\n-{\n-\tint i;\n-\n-\tfor (i = 0; i < array->nr; i++)\n-\t\tfree_array_item(array->items[i]);\n-\tfree(array->items);\n-\tarray->items = NULL;\n-\tarray->nr = array->alloc = 0;\n-}\n-\n-static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)\n-{\n-\tstruct atom_value *va, *vb;\n-\tint cmp;\n-\tcmp_type cmp_type = used_atom_type[s->atom];\n-\n-\tget_ref_atom_value(a, s->atom, &va);\n-\tget_ref_atom_value(b, s->atom, &vb);\n-\tswitch (cmp_type) {\n-\tcase FIELD_STR:\n-\t\tcmp = strcmp(va->s, vb->s);\n-\t\tbreak;\n-\tdefault:\n-\t\tif (va->ul < vb->ul)\n-\t\t\tcmp = -1;\n-\t\telse if (va->ul == vb->ul)\n-\t\t\tcmp = 0;\n-\t\telse\n-\t\t\tcmp = 1;\n-\t\tbreak;\n-\t}\n-\treturn (s->reverse) ? -cmp : cmp;\n-}\n-\n-static struct ref_sorting *ref_sorting;\n-static int compare_refs(const void *a_, const void *b_)\n-{\n-\tstruct ref_array_item *a = *((struct ref_array_item **)a_);\n-\tstruct ref_array_item *b = *((struct ref_array_item **)b_);\n-\tstruct ref_sorting *s;\n-\n-\tfor (s = ref_sorting; s; s = s->next) {\n-\t\tint cmp = cmp_ref_sorting(s, a, b);\n-\t\tif (cmp)\n-\t\t\treturn cmp;\n-\t}\n-\treturn 0;\n-}\n-\n-void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n-{\n-\tref_sorting = sorting;\n-\tqsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);\n-}\n-\n-static void print_value(struct atom_value *v, int quote_style)\n-{\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tswitch (quote_style) {\n-\tcase QUOTE_NONE:\n-\t\tfputs(v->s, stdout);\n-\t\tbreak;\n-\tcase QUOTE_SHELL:\n-\t\tsq_quote_buf(&sb, v->s);\n-\t\tbreak;\n-\tcase QUOTE_PERL:\n-\t\tperl_quote_buf(&sb, v->s);\n-\t\tbreak;\n-\tcase QUOTE_PYTHON:\n-\t\tpython_quote_buf(&sb, v->s);\n-\t\tbreak;\n-\tcase QUOTE_TCL:\n-\t\ttcl_quote_buf(&sb, v->s);\n-\t\tbreak;\n-\t}\n-\tif (quote_style != QUOTE_NONE) {\n-\t\tfputs(sb.buf, stdout);\n-\t\tstrbuf_release(&sb);\n-\t}\n-}\n-\n-static int hex1(char ch)\n-{\n-\tif ('0' <= ch && ch <= '9')\n-\t\treturn ch - '0';\n-\telse if ('a' <= ch && ch <= 'f')\n-\t\treturn ch - 'a' + 10;\n-\telse if ('A' <= ch && ch <= 'F')\n-\t\treturn ch - 'A' + 10;\n-\treturn -1;\n-}\n-static int hex2(const char *cp)\n-{\n-\tif (cp[0] && cp[1])\n-\t\treturn (hex1(cp[0]) << 4) | hex1(cp[1]);\n-\telse\n-\t\treturn -1;\n-}\n-\n-static void emit(const char *cp, const char *ep)\n-{\n-\twhile (*cp && (!ep || cp < ep)) {\n-\t\tif (*cp == '%') {\n-\t\t\tif (cp[1] == '%')\n-\t\t\t\tcp++;\n-\t\t\telse {\n-\t\t\t\tint ch = hex2(cp + 1);\n-\t\t\t\tif (0 <= ch) {\n-\t\t\t\t\tputchar(ch);\n-\t\t\t\t\tcp += 3;\n-\t\t\t\t\tcontinue;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\t\tputchar(*cp);\n-\t\tcp++;\n-\t}\n-}\n-\n-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n-{\n-\tconst char *cp, *sp, *ep;\n-\n-\tfor (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n-\t\tstruct atom_value *atomv;\n-\n-\t\tep = strchr(sp, ')');\n-\t\tif (cp < sp)\n-\t\t\temit(cp, sp);\n-\t\tget_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n-\t\tprint_value(atomv, quote_style);\n-\t}\n-\tif (*cp) {\n-\t\tsp = cp + strlen(cp);\n-\t\temit(cp, sp);\n-\t}\n-\tif (need_color_reset_at_eol) {\n-\t\tstruct atom_value resetv;\n-\t\tchar color[COLOR_MAXLEN] = \"\";\n-\n-\t\tif (color_parse(\"reset\", color) < 0)\n-\t\t\tdie(\"BUG: couldn't parse 'reset' as a color\");\n-\t\tresetv.s = color;\n-\t\tprint_value(&resetv, quote_style);\n-\t}\n-\tputchar('\\n');\n-}\n-\n-/*  If no sorting option is given, use refname to sort as default */\n-struct ref_sorting *ref_default_sorting(void)\n-{\n-\tstatic const char cstr_name[] = \"refname\";\n-\n-\tstruct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));\n-\n-\tsorting->next = NULL;\n-\tsorting->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name));\n-\treturn sorting;\n-}\n-\n-int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)\n-{\n-\tstruct ref_sorting **sorting_tail = opt->value;\n-\tstruct ref_sorting *s;\n-\tint len;\n-\n-\tif (!arg) /* should --no-sort void the list ? */\n-\t\treturn -1;\n-\n-\ts = xcalloc(1, sizeof(*s));\n-\ts->next = *sorting_tail;\n-\t*sorting_tail = s;\n-\n-\tif (*arg == '-') {\n-\t\ts->reverse = 1;\n-\t\targ++;\n-\t}\n-\tlen = strlen(arg);\n-\ts->atom = parse_ref_filter_atom(arg, arg+len);\n-\treturn 0;\n-}\n-\n static char const * const for_each_ref_usage[] = {\n \tN_(\"git for-each-ref [<options>] [<pattern>]\"),\n \tNULL\n@@ -1129,7 +56,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \n \tmemset(&ref_cbdata, 0, sizeof(ref_cbdata));\n \tref_cbdata.filter.name_patterns = argv;\n-\tfor_each_rawref(ref_filter_handler, &ref_cbdata);\n+\tfilter_refs(for_each_rawref, &ref_cbdata);\n \n \tref_array_sort(sorting, &ref_cbdata.array);\n \ndiff --git a/ref-filter.c b/ref-filter.c\nnew file mode 100644\nindex 0000000..ece16a1\n--- /dev/null\n+++ b/ref-filter.c\n@@ -0,0 +1,1077 @@\n+#include \"builtin.h\"\n+#include \"cache.h\"\n+#include \"parse-options.h\"\n+#include \"refs.h\"\n+#include \"wildmatch.h\"\n+#include \"commit.h\"\n+#include \"remote.h\"\n+#include \"color.h\"\n+#include \"tag.h\"\n+#include \"quote.h\"\n+#include \"ref-filter.h\"\n+\n+typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n+\n+static struct {\n+\tconst char *name;\n+\tcmp_type cmp_type;\n+} valid_atom[] = {\n+\t{ \"refname\" },\n+\t{ \"objecttype\" },\n+\t{ \"objectsize\", FIELD_ULONG },\n+\t{ \"objectname\" },\n+\t{ \"tree\" },\n+\t{ \"parent\" },\n+\t{ \"numparent\", FIELD_ULONG },\n+\t{ \"object\" },\n+\t{ \"type\" },\n+\t{ \"tag\" },\n+\t{ \"author\" },\n+\t{ \"authorname\" },\n+\t{ \"authoremail\" },\n+\t{ \"authordate\", FIELD_TIME },\n+\t{ \"committer\" },\n+\t{ \"committername\" },\n+\t{ \"committeremail\" },\n+\t{ \"committerdate\", FIELD_TIME },\n+\t{ \"tagger\" },\n+\t{ \"taggername\" },\n+\t{ \"taggeremail\" },\n+\t{ \"taggerdate\", FIELD_TIME },\n+\t{ \"creator\" },\n+\t{ \"creatordate\", FIELD_TIME },\n+\t{ \"subject\" },\n+\t{ \"body\" },\n+\t{ \"contents\" },\n+\t{ \"contents:subject\" },\n+\t{ \"contents:body\" },\n+\t{ \"contents:signature\" },\n+\t{ \"upstream\" },\n+\t{ \"push\" },\n+\t{ \"symref\" },\n+\t{ \"flag\" },\n+\t{ \"HEAD\" },\n+\t{ \"color\" },\n+};\n+\n+/*\n+ * An atom is a valid field atom listed above, possibly prefixed with\n+ * a \"*\" to denote deref_tag().\n+ *\n+ * We parse given format string and sort specifiers, and make a list\n+ * of properties that we need to extract out of objects.  ref_array_item\n+ * structure will hold an array of values extracted that can be\n+ * indexed with the \"atom number\", which is an index into this\n+ * array.\n+ */\n+static const char **used_atom;\n+static cmp_type *used_atom_type;\n+static int used_atom_cnt, need_tagged, need_symref;\n+static int need_color_reset_at_eol;\n+\n+/*\n+ * Used to parse format string and sort specifiers\n+ */\n+int parse_ref_filter_atom(const char *atom, const char *ep)\n+{\n+\tconst char *sp;\n+\tint i, at;\n+\n+\tsp = atom;\n+\tif (*sp == '*' && sp < ep)\n+\t\tsp++; /* deref */\n+\tif (ep <= sp)\n+\t\tdie(\"malformed field name: %.*s\", (int)(ep-atom), atom);\n+\n+\t/* Do we have the atom already used elsewhere? */\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tint len = strlen(used_atom[i]);\n+\t\tif (len == ep - atom && !memcmp(used_atom[i], atom, len))\n+\t\t\treturn i;\n+\t}\n+\n+\t/* Is the atom a valid one? */\n+\tfor (i = 0; i < ARRAY_SIZE(valid_atom); i++) {\n+\t\tint len = strlen(valid_atom[i].name);\n+\t\t/*\n+\t\t * If the atom name has a colon, strip it and everything after\n+\t\t * it off - it specifies the format for this entry, and\n+\t\t * shouldn't be used for checking against the valid_atom\n+\t\t * table.\n+\t\t */\n+\t\tconst char *formatp = strchr(sp, ':');\n+\t\tif (!formatp || ep < formatp)\n+\t\t\tformatp = ep;\n+\t\tif (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))\n+\t\t\tbreak;\n+\t}\n+\n+\tif (ARRAY_SIZE(valid_atom) <= i)\n+\t\tdie(\"unknown field name: %.*s\", (int)(ep-atom), atom);\n+\n+\t/* Add it in, including the deref prefix */\n+\tat = used_atom_cnt;\n+\tused_atom_cnt++;\n+\tREALLOC_ARRAY(used_atom, used_atom_cnt);\n+\tREALLOC_ARRAY(used_atom_type, used_atom_cnt);\n+\tused_atom[at] = xmemdupz(atom, ep - atom);\n+\tused_atom_type[at] = valid_atom[i].cmp_type;\n+\tif (*atom == '*')\n+\t\tneed_tagged = 1;\n+\tif (!strcmp(used_atom[at], \"symref\"))\n+\t\tneed_symref = 1;\n+\treturn at;\n+}\n+\n+/*\n+ * In a format string, find the next occurrence of %(atom).\n+ */\n+static const char *find_next(const char *cp)\n+{\n+\twhile (*cp) {\n+\t\tif (*cp == '%') {\n+\t\t\t/*\n+\t\t\t * %( is the start of an atom;\n+\t\t\t * %% is a quoted per-cent.\n+\t\t\t */\n+\t\t\tif (cp[1] == '(')\n+\t\t\t\treturn cp;\n+\t\t\telse if (cp[1] == '%')\n+\t\t\t\tcp++; /* skip over two % */\n+\t\t\t/* otherwise this is a singleton, literal % */\n+\t\t}\n+\t\tcp++;\n+\t}\n+\treturn NULL;\n+}\n+\n+/*\n+ * Make sure the format string is well formed, and parse out\n+ * the used atoms.\n+ */\n+int verify_ref_format(const char *format)\n+{\n+\tconst char *cp, *sp;\n+\n+\tneed_color_reset_at_eol = 0;\n+\tfor (cp = format; *cp && (sp = find_next(cp)); ) {\n+\t\tconst char *color, *ep = strchr(sp, ')');\n+\t\tint at;\n+\n+\t\tif (!ep)\n+\t\t\treturn error(\"malformed format string %s\", sp);\n+\t\t/* sp points at \"%(\" and ep points at the closing \")\" */\n+\t\tat = parse_ref_filter_atom(sp + 2, ep);\n+\t\tcp = ep + 1;\n+\n+\t\tif (skip_prefix(used_atom[at], \"color:\", &color))\n+\t\t\tneed_color_reset_at_eol = !!strcmp(color, \"reset\");\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * Given an object name, read the object data and size, and return a\n+ * \"struct object\".  If the object data we are returning is also borrowed\n+ * by the \"struct object\" representation, set *eaten as well---it is a\n+ * signal from parse_object_buffer to us not to free the buffer.\n+ */\n+static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)\n+{\n+\tenum object_type type;\n+\tvoid *buf = read_sha1_file(sha1, &type, sz);\n+\n+\tif (buf)\n+\t\t*obj = parse_object_buffer(sha1, type, *sz, buf, eaten);\n+\telse\n+\t\t*obj = NULL;\n+\treturn buf;\n+}\n+\n+static int grab_objectname(const char *name, const unsigned char *sha1,\n+\t\t\t    struct atom_value *v)\n+{\n+\tif (!strcmp(name, \"objectname\")) {\n+\t\tchar *s = xmalloc(41);\n+\t\tstrcpy(s, sha1_to_hex(sha1));\n+\t\tv->s = s;\n+\t\treturn 1;\n+\t}\n+\tif (!strcmp(name, \"objectname:short\")) {\n+\t\tv->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+/* See grab_values */\n+static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (!strcmp(name, \"objecttype\"))\n+\t\t\tv->s = typename(obj->type);\n+\t\telse if (!strcmp(name, \"objectsize\")) {\n+\t\t\tchar *s = xmalloc(40);\n+\t\t\tsprintf(s, \"%lu\", sz);\n+\t\t\tv->ul = sz;\n+\t\t\tv->s = s;\n+\t\t}\n+\t\telse if (deref)\n+\t\t\tgrab_objectname(name, obj->sha1, v);\n+\t}\n+}\n+\n+/* See grab_values */\n+static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tstruct tag *tag = (struct tag *) obj;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (!strcmp(name, \"tag\"))\n+\t\t\tv->s = tag->tag;\n+\t\telse if (!strcmp(name, \"type\") && tag->tagged)\n+\t\t\tv->s = typename(tag->tagged->type);\n+\t\telse if (!strcmp(name, \"object\") && tag->tagged) {\n+\t\t\tchar *s = xmalloc(41);\n+\t\t\tstrcpy(s, sha1_to_hex(tag->tagged->sha1));\n+\t\t\tv->s = s;\n+\t\t}\n+\t}\n+}\n+\n+/* See grab_values */\n+static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tstruct commit *commit = (struct commit *) obj;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (!strcmp(name, \"tree\")) {\n+\t\t\tchar *s = xmalloc(41);\n+\t\t\tstrcpy(s, sha1_to_hex(commit->tree->object.sha1));\n+\t\t\tv->s = s;\n+\t\t}\n+\t\tif (!strcmp(name, \"numparent\")) {\n+\t\t\tchar *s = xmalloc(40);\n+\t\t\tv->ul = commit_list_count(commit->parents);\n+\t\t\tsprintf(s, \"%lu\", v->ul);\n+\t\t\tv->s = s;\n+\t\t}\n+\t\telse if (!strcmp(name, \"parent\")) {\n+\t\t\tint num = commit_list_count(commit->parents);\n+\t\t\tint i;\n+\t\t\tstruct commit_list *parents;\n+\t\t\tchar *s = xmalloc(41 * num + 1);\n+\t\t\tv->s = s;\n+\t\t\tfor (i = 0, parents = commit->parents;\n+\t\t\t     parents;\n+\t\t\t     parents = parents->next, i = i + 41) {\n+\t\t\t\tstruct commit *parent = parents->item;\n+\t\t\t\tstrcpy(s+i, sha1_to_hex(parent->object.sha1));\n+\t\t\t\tif (parents->next)\n+\t\t\t\t\ts[i+40] = ' ';\n+\t\t\t}\n+\t\t\tif (!i)\n+\t\t\t\t*s = '\\0';\n+\t\t}\n+\t}\n+}\n+\n+static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)\n+{\n+\tconst char *eol;\n+\twhile (*buf) {\n+\t\tif (!strncmp(buf, who, wholen) &&\n+\t\t    buf[wholen] == ' ')\n+\t\t\treturn buf + wholen + 1;\n+\t\teol = strchr(buf, '\\n');\n+\t\tif (!eol)\n+\t\t\treturn \"\";\n+\t\teol++;\n+\t\tif (*eol == '\\n')\n+\t\t\treturn \"\"; /* end of header */\n+\t\tbuf = eol;\n+\t}\n+\treturn \"\";\n+}\n+\n+static const char *copy_line(const char *buf)\n+{\n+\tconst char *eol = strchrnul(buf, '\\n');\n+\treturn xmemdupz(buf, eol - buf);\n+}\n+\n+static const char *copy_name(const char *buf)\n+{\n+\tconst char *cp;\n+\tfor (cp = buf; *cp && *cp != '\\n'; cp++) {\n+\t\tif (!strncmp(cp, \" <\", 2))\n+\t\t\treturn xmemdupz(buf, cp - buf);\n+\t}\n+\treturn \"\";\n+}\n+\n+static const char *copy_email(const char *buf)\n+{\n+\tconst char *email = strchr(buf, '<');\n+\tconst char *eoemail;\n+\tif (!email)\n+\t\treturn \"\";\n+\teoemail = strchr(email, '>');\n+\tif (!eoemail)\n+\t\treturn \"\";\n+\treturn xmemdupz(email, eoemail + 1 - email);\n+}\n+\n+static char *copy_subject(const char *buf, unsigned long len)\n+{\n+\tchar *r = xmemdupz(buf, len);\n+\tint i;\n+\n+\tfor (i = 0; i < len; i++)\n+\t\tif (r[i] == '\\n')\n+\t\t\tr[i] = ' ';\n+\n+\treturn r;\n+}\n+\n+static void grab_date(const char *buf, struct atom_value *v, const char *atomname)\n+{\n+\tconst char *eoemail = strstr(buf, \"> \");\n+\tchar *zone;\n+\tunsigned long timestamp;\n+\tlong tz;\n+\tenum date_mode date_mode = DATE_NORMAL;\n+\tconst char *formatp;\n+\n+\t/*\n+\t * We got here because atomname ends in \"date\" or \"date<something>\";\n+\t * it's not possible that <something> is not \":<format>\" because\n+\t * parse_ref_filter_atom() wouldn't have allowed it, so we can assume that no\n+\t * \":\" means no format is specified, and use the default.\n+\t */\n+\tformatp = strchr(atomname, ':');\n+\tif (formatp != NULL) {\n+\t\tformatp++;\n+\t\tdate_mode = parse_date_format(formatp);\n+\t}\n+\n+\tif (!eoemail)\n+\t\tgoto bad;\n+\ttimestamp = strtoul(eoemail + 2, &zone, 10);\n+\tif (timestamp == ULONG_MAX)\n+\t\tgoto bad;\n+\ttz = strtol(zone, NULL, 10);\n+\tif ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)\n+\t\tgoto bad;\n+\tv->s = xstrdup(show_date(timestamp, tz, date_mode));\n+\tv->ul = timestamp;\n+\treturn;\n+ bad:\n+\tv->s = \"\";\n+\tv->ul = 0;\n+}\n+\n+/* See grab_values */\n+static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tint wholen = strlen(who);\n+\tconst char *wholine = NULL;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (strncmp(who, name, wholen))\n+\t\t\tcontinue;\n+\t\tif (name[wholen] != 0 &&\n+\t\t    strcmp(name + wholen, \"name\") &&\n+\t\t    strcmp(name + wholen, \"email\") &&\n+\t\t    !starts_with(name + wholen, \"date\"))\n+\t\t\tcontinue;\n+\t\tif (!wholine)\n+\t\t\twholine = find_wholine(who, wholen, buf, sz);\n+\t\tif (!wholine)\n+\t\t\treturn; /* no point looking for it */\n+\t\tif (name[wholen] == 0)\n+\t\t\tv->s = copy_line(wholine);\n+\t\telse if (!strcmp(name + wholen, \"name\"))\n+\t\t\tv->s = copy_name(wholine);\n+\t\telse if (!strcmp(name + wholen, \"email\"))\n+\t\t\tv->s = copy_email(wholine);\n+\t\telse if (starts_with(name + wholen, \"date\"))\n+\t\t\tgrab_date(wholine, v, name);\n+\t}\n+\n+\t/*\n+\t * For a tag or a commit object, if \"creator\" or \"creatordate\" is\n+\t * requested, do something special.\n+\t */\n+\tif (strcmp(who, \"tagger\") && strcmp(who, \"committer\"))\n+\t\treturn; /* \"author\" for commit object is not wanted */\n+\tif (!wholine)\n+\t\twholine = find_wholine(who, wholen, buf, sz);\n+\tif (!wholine)\n+\t\treturn;\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\n+\t\tif (starts_with(name, \"creatordate\"))\n+\t\t\tgrab_date(wholine, v, name);\n+\t\telse if (!strcmp(name, \"creator\"))\n+\t\t\tv->s = copy_line(wholine);\n+\t}\n+}\n+\n+static void find_subpos(const char *buf, unsigned long sz,\n+\t\t\tconst char **sub, unsigned long *sublen,\n+\t\t\tconst char **body, unsigned long *bodylen,\n+\t\t\tunsigned long *nonsiglen,\n+\t\t\tconst char **sig, unsigned long *siglen)\n+{\n+\tconst char *eol;\n+\t/* skip past header until we hit empty line */\n+\twhile (*buf && *buf != '\\n') {\n+\t\teol = strchrnul(buf, '\\n');\n+\t\tif (*eol)\n+\t\t\teol++;\n+\t\tbuf = eol;\n+\t}\n+\t/* skip any empty lines */\n+\twhile (*buf == '\\n')\n+\t\tbuf++;\n+\n+\t/* parse signature first; we might not even have a subject line */\n+\t*sig = buf + parse_signature(buf, strlen(buf));\n+\t*siglen = strlen(*sig);\n+\n+\t/* subject is first non-empty line */\n+\t*sub = buf;\n+\t/* subject goes to first empty line */\n+\twhile (buf < *sig && *buf && *buf != '\\n') {\n+\t\teol = strchrnul(buf, '\\n');\n+\t\tif (*eol)\n+\t\t\teol++;\n+\t\tbuf = eol;\n+\t}\n+\t*sublen = buf - *sub;\n+\t/* drop trailing newline, if present */\n+\tif (*sublen && (*sub)[*sublen - 1] == '\\n')\n+\t\t*sublen -= 1;\n+\n+\t/* skip any empty lines */\n+\twhile (*buf == '\\n')\n+\t\tbuf++;\n+\t*body = buf;\n+\t*bodylen = strlen(buf);\n+\t*nonsiglen = *sig - buf;\n+}\n+\n+/* See grab_values */\n+static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tconst char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;\n+\tunsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (strcmp(name, \"subject\") &&\n+\t\t    strcmp(name, \"body\") &&\n+\t\t    strcmp(name, \"contents\") &&\n+\t\t    strcmp(name, \"contents:subject\") &&\n+\t\t    strcmp(name, \"contents:body\") &&\n+\t\t    strcmp(name, \"contents:signature\"))\n+\t\t\tcontinue;\n+\t\tif (!subpos)\n+\t\t\tfind_subpos(buf, sz,\n+\t\t\t\t    &subpos, &sublen,\n+\t\t\t\t    &bodypos, &bodylen, &nonsiglen,\n+\t\t\t\t    &sigpos, &siglen);\n+\n+\t\tif (!strcmp(name, \"subject\"))\n+\t\t\tv->s = copy_subject(subpos, sublen);\n+\t\telse if (!strcmp(name, \"contents:subject\"))\n+\t\t\tv->s = copy_subject(subpos, sublen);\n+\t\telse if (!strcmp(name, \"body\"))\n+\t\t\tv->s = xmemdupz(bodypos, bodylen);\n+\t\telse if (!strcmp(name, \"contents:body\"))\n+\t\t\tv->s = xmemdupz(bodypos, nonsiglen);\n+\t\telse if (!strcmp(name, \"contents:signature\"))\n+\t\t\tv->s = xmemdupz(sigpos, siglen);\n+\t\telse if (!strcmp(name, \"contents\"))\n+\t\t\tv->s = xstrdup(subpos);\n+\t}\n+}\n+\n+/*\n+ * We want to have empty print-string for field requests\n+ * that do not apply (e.g. \"authordate\" for a tag object)\n+ */\n+static void fill_missing_values(struct atom_value *val)\n+{\n+\tint i;\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (v->s == NULL)\n+\t\t\tv->s = \"\";\n+\t}\n+}\n+\n+/*\n+ * val is a list of atom_value to hold returned values.  Extract\n+ * the values for atoms in used_atom array out of (obj, buf, sz).\n+ * when deref is false, (obj, buf, sz) is the object that is\n+ * pointed at by the ref itself; otherwise it is the object the\n+ * ref (which is a tag) refers to.\n+ */\n+static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tgrab_common_values(val, deref, obj, buf, sz);\n+\tswitch (obj->type) {\n+\tcase OBJ_TAG:\n+\t\tgrab_tag_values(val, deref, obj, buf, sz);\n+\t\tgrab_sub_body_contents(val, deref, obj, buf, sz);\n+\t\tgrab_person(\"tagger\", val, deref, obj, buf, sz);\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\tgrab_commit_values(val, deref, obj, buf, sz);\n+\t\tgrab_sub_body_contents(val, deref, obj, buf, sz);\n+\t\tgrab_person(\"author\", val, deref, obj, buf, sz);\n+\t\tgrab_person(\"committer\", val, deref, obj, buf, sz);\n+\t\tbreak;\n+\tcase OBJ_TREE:\n+\t\t/* grab_tree_values(val, deref, obj, buf, sz); */\n+\t\tbreak;\n+\tcase OBJ_BLOB:\n+\t\t/* grab_blob_values(val, deref, obj, buf, sz); */\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"Eh?  Object of type %d?\", obj->type);\n+\t}\n+}\n+\n+static inline char *copy_advance(char *dst, const char *src)\n+{\n+\twhile (*src)\n+\t\t*dst++ = *src++;\n+\treturn dst;\n+}\n+\n+/*\n+ * Parse the object referred by ref, and grab needed value.\n+ */\n+static void populate_value(struct ref_array_item *ref)\n+{\n+\tvoid *buf;\n+\tstruct object *obj;\n+\tint eaten, i;\n+\tunsigned long size;\n+\tconst unsigned char *tagged;\n+\n+\tref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));\n+\n+\tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n+\t\tunsigned char unused1[20];\n+\t\tref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,\n+\t\t\t\t\t     unused1, NULL);\n+\t\tif (!ref->symref)\n+\t\t\tref->symref = \"\";\n+\t}\n+\n+\t/* Fill in specials first */\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &ref->value[i];\n+\t\tint deref = 0;\n+\t\tconst char *refname;\n+\t\tconst char *formatp;\n+\t\tstruct branch *branch = NULL;\n+\n+\t\tif (*name == '*') {\n+\t\t\tderef = 1;\n+\t\t\tname++;\n+\t\t}\n+\n+\t\tif (starts_with(name, \"refname\"))\n+\t\t\trefname = ref->refname;\n+\t\telse if (starts_with(name, \"symref\"))\n+\t\t\trefname = ref->symref ? ref->symref : \"\";\n+\t\telse if (starts_with(name, \"upstream\")) {\n+\t\t\tconst char *branch_name;\n+\t\t\t/* only local branches may have an upstream */\n+\t\t\tif (!skip_prefix(ref->refname, \"refs/heads/\",\n+\t\t\t\t\t &branch_name))\n+\t\t\t\tcontinue;\n+\t\t\tbranch = branch_get(branch_name);\n+\n+\t\t\trefname = branch_get_upstream(branch, NULL);\n+\t\t\tif (!refname)\n+\t\t\t\tcontinue;\n+\t\t} else if (starts_with(name, \"push\")) {\n+\t\t\tconst char *branch_name;\n+\t\t\tif (!skip_prefix(ref->refname, \"refs/heads/\",\n+\t\t\t\t\t &branch_name))\n+\t\t\t\tcontinue;\n+\t\t\tbranch = branch_get(branch_name);\n+\n+\t\t\trefname = branch_get_push(branch, NULL);\n+\t\t\tif (!refname)\n+\t\t\t\tcontinue;\n+\t\t} else if (starts_with(name, \"color:\")) {\n+\t\t\tchar color[COLOR_MAXLEN] = \"\";\n+\n+\t\t\tif (color_parse(name + 6, color) < 0)\n+\t\t\t\tdie(_(\"unable to parse format\"));\n+\t\t\tv->s = xstrdup(color);\n+\t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"flag\")) {\n+\t\t\tchar buf[256], *cp = buf;\n+\t\t\tif (ref->flag & REF_ISSYMREF)\n+\t\t\t\tcp = copy_advance(cp, \",symref\");\n+\t\t\tif (ref->flag & REF_ISPACKED)\n+\t\t\t\tcp = copy_advance(cp, \",packed\");\n+\t\t\tif (cp == buf)\n+\t\t\t\tv->s = \"\";\n+\t\t\telse {\n+\t\t\t\t*cp = '\\0';\n+\t\t\t\tv->s = xstrdup(buf + 1);\n+\t\t\t}\n+\t\t\tcontinue;\n+\t\t} else if (!deref && grab_objectname(name, ref->objectname, v)) {\n+\t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"HEAD\")) {\n+\t\t\tconst char *head;\n+\t\t\tunsigned char sha1[20];\n+\n+\t\t\thead = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING,\n+\t\t\t\t\t\t  sha1, NULL);\n+\t\t\tif (!strcmp(ref->refname, head))\n+\t\t\t\tv->s = \"*\";\n+\t\t\telse\n+\t\t\t\tv->s = \" \";\n+\t\t\tcontinue;\n+\t\t} else\n+\t\t\tcontinue;\n+\n+\t\tformatp = strchr(name, ':');\n+\t\tif (formatp) {\n+\t\t\tint num_ours, num_theirs;\n+\n+\t\t\tformatp++;\n+\t\t\tif (!strcmp(formatp, \"short\"))\n+\t\t\t\trefname = shorten_unambiguous_ref(refname,\n+\t\t\t\t\t\t      warn_ambiguous_refs);\n+\t\t\telse if (!strcmp(formatp, \"track\") &&\n+\t\t\t\t (starts_with(name, \"upstream\") ||\n+\t\t\t\t  starts_with(name, \"push\"))) {\n+\t\t\t\tchar buf[40];\n+\n+\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n+\t\t\t\t\t\t       &num_theirs, NULL))\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tif (!num_ours && !num_theirs)\n+\t\t\t\t\tv->s = \"\";\n+\t\t\t\telse if (!num_ours) {\n+\t\t\t\t\tsprintf(buf, \"[behind %d]\", num_theirs);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t} else if (!num_theirs) {\n+\t\t\t\t\tsprintf(buf, \"[ahead %d]\", num_ours);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t} else {\n+\t\t\t\t\tsprintf(buf, \"[ahead %d, behind %d]\",\n+\t\t\t\t\t\tnum_ours, num_theirs);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t}\n+\t\t\t\tcontinue;\n+\t\t\t} else if (!strcmp(formatp, \"trackshort\") &&\n+\t\t\t\t   (starts_with(name, \"upstream\") ||\n+\t\t\t\t    starts_with(name, \"push\"))) {\n+\t\t\t\tassert(branch);\n+\n+\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n+\t\t\t\t\t\t\t&num_theirs, NULL))\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tif (!num_ours && !num_theirs)\n+\t\t\t\t\tv->s = \"=\";\n+\t\t\t\telse if (!num_ours)\n+\t\t\t\t\tv->s = \"<\";\n+\t\t\t\telse if (!num_theirs)\n+\t\t\t\t\tv->s = \">\";\n+\t\t\t\telse\n+\t\t\t\t\tv->s = \"<>\";\n+\t\t\t\tcontinue;\n+\t\t\t} else\n+\t\t\t\tdie(\"unknown %.*s format %s\",\n+\t\t\t\t    (int)(formatp - name), name, formatp);\n+\t\t}\n+\n+\t\tif (!deref)\n+\t\t\tv->s = refname;\n+\t\telse {\n+\t\t\tint len = strlen(refname);\n+\t\t\tchar *s = xmalloc(len + 4);\n+\t\t\tsprintf(s, \"%s^{}\", refname);\n+\t\t\tv->s = s;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tstruct atom_value *v = &ref->value[i];\n+\t\tif (v->s == NULL)\n+\t\t\tgoto need_obj;\n+\t}\n+\treturn;\n+\n+ need_obj:\n+\tbuf = get_obj(ref->objectname, &obj, &size, &eaten);\n+\tif (!buf)\n+\t\tdie(\"missing object %s for %s\",\n+\t\t    sha1_to_hex(ref->objectname), ref->refname);\n+\tif (!obj)\n+\t\tdie(\"parse_object_buffer failed on %s for %s\",\n+\t\t    sha1_to_hex(ref->objectname), ref->refname);\n+\n+\tgrab_values(ref->value, 0, obj, buf, size);\n+\tif (!eaten)\n+\t\tfree(buf);\n+\n+\t/*\n+\t * If there is no atom that wants to know about tagged\n+\t * object, we are done.\n+\t */\n+\tif (!need_tagged || (obj->type != OBJ_TAG))\n+\t\treturn;\n+\n+\t/*\n+\t * If it is a tag object, see if we use a value that derefs\n+\t * the object, and if we do grab the object it refers to.\n+\t */\n+\ttagged = ((struct tag *)obj)->tagged->sha1;\n+\n+\t/*\n+\t * NEEDSWORK: This derefs tag only once, which\n+\t * is good to deal with chains of trust, but\n+\t * is not consistent with what deref_tag() does\n+\t * which peels the onion to the core.\n+\t */\n+\tbuf = get_obj(tagged, &obj, &size, &eaten);\n+\tif (!buf)\n+\t\tdie(\"missing object %s for %s\",\n+\t\t    sha1_to_hex(tagged), ref->refname);\n+\tif (!obj)\n+\t\tdie(\"parse_object_buffer failed on %s for %s\",\n+\t\t    sha1_to_hex(tagged), ref->refname);\n+\tgrab_values(ref->value, 1, obj, buf, size);\n+\tif (!eaten)\n+\t\tfree(buf);\n+}\n+\n+/*\n+ * Given a ref, return the value for the atom.  This lazily gets value\n+ * out of the object by calling populate value.\n+ */\n+static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom_value **v)\n+{\n+\tif (!ref->value) {\n+\t\tpopulate_value(ref);\n+\t\tfill_missing_values(ref->value);\n+\t}\n+\t*v = &ref->value[atom];\n+}\n+\n+/*\n+ * Given a refname, return 1 if the refname matches with one of the patterns\n+ * while the pattern is a pathname like 'refs/tags' or 'refs/heads/master'\n+ * and so on, else return 0. Supports use of wild characters.\n+ */\n+static int match_name_as_path(const char **pattern, const char *refname)\n+{\n+\tint namelen = strlen(refname);\n+\tfor (; *pattern; pattern++) {\n+\t\tconst char *p = *pattern;\n+\t\tint plen = strlen(p);\n+\n+\t\tif ((plen <= namelen) &&\n+\t\t    !strncmp(refname, p, plen) &&\n+\t\t    (refname[plen] == '\\0' ||\n+\t\t     refname[plen] == '/' ||\n+\t\t     p[plen-1] == '/'))\n+\t\t\treturn 1;\n+\t\tif (!wildmatch(p, refname, WM_PATHNAME, NULL))\n+\t\t\treturn 1;\n+\t}\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+\t\t\t\t\t\t int flag)\n+{\n+\tstruct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item));\n+\tref->refname = xstrdup(refname);\n+\thashcpy(ref->objectname, objectname);\n+\tref->flag = flag;\n+\n+\treturn ref;\n+}\n+\n+/*\n+ * A call-back given to for_each_ref().  Filter refs and keep them for\n+ * later object processing.\n+ */\n+int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+{\n+\tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n+\tstruct ref_filter *filter = &ref_cbdata->filter;\n+\tstruct ref_array_item *ref;\n+\n+\tif (flag & REF_BAD_NAME) {\n+\t\t  warning(\"ignoring ref with broken name %s\", refname);\n+\t\t  return 0;\n+\t}\n+\n+\tif (*filter->name_patterns && !match_name_as_path(filter->name_patterns, 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\n+\t * by maxcount logic.\n+\t */\n+\tref = new_ref_array_item(refname, oid->hash, flag);\n+\n+\tREALLOC_ARRAY(ref_cbdata->array.items, ref_cbdata->array.nr + 1);\n+\tref_cbdata->array.items[ref_cbdata->array.nr++] = ref;\n+\treturn 0;\n+}\n+\n+/*  Free memory allocated for a ref_array_item */\n+static void free_array_item(struct ref_array_item *item)\n+{\n+\tfree(item->symref);\n+\tfree(item->refname);\n+\tfree(item);\n+}\n+\n+/* Free all memory allocated for ref_array */\n+void ref_array_clear(struct ref_array *array)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < array->nr; i++)\n+\t\tfree_array_item(array->items[i]);\n+\tfree(array->items);\n+\tarray->items = NULL;\n+\tarray->nr = array->alloc = 0;\n+}\n+\n+static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)\n+{\n+\tstruct atom_value *va, *vb;\n+\tint cmp;\n+\tcmp_type cmp_type = used_atom_type[s->atom];\n+\n+\tget_ref_atom_value(a, s->atom, &va);\n+\tget_ref_atom_value(b, s->atom, &vb);\n+\tswitch (cmp_type) {\n+\tcase FIELD_STR:\n+\t\tcmp = strcmp(va->s, vb->s);\n+\t\tbreak;\n+\tdefault:\n+\t\tif (va->ul < vb->ul)\n+\t\t\tcmp = -1;\n+\t\telse if (va->ul == vb->ul)\n+\t\t\tcmp = 0;\n+\t\telse\n+\t\t\tcmp = 1;\n+\t\tbreak;\n+\t}\n+\treturn (s->reverse) ? -cmp : cmp;\n+}\n+\n+static struct ref_sorting *ref_sorting;\n+static int compare_refs(const void *a_, const void *b_)\n+{\n+\tstruct ref_array_item *a = *((struct ref_array_item **)a_);\n+\tstruct ref_array_item *b = *((struct ref_array_item **)b_);\n+\tstruct ref_sorting *s;\n+\n+\tfor (s = ref_sorting; s; s = s->next) {\n+\t\tint cmp = cmp_ref_sorting(s, a, b);\n+\t\tif (cmp)\n+\t\t\treturn cmp;\n+\t}\n+\treturn 0;\n+}\n+\n+void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n+{\n+\tref_sorting = sorting;\n+\tqsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);\n+}\n+\n+static void print_value(struct atom_value *v, int quote_style)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tswitch (quote_style) {\n+\tcase QUOTE_NONE:\n+\t\tfputs(v->s, stdout);\n+\t\tbreak;\n+\tcase QUOTE_SHELL:\n+\t\tsq_quote_buf(&sb, v->s);\n+\t\tbreak;\n+\tcase QUOTE_PERL:\n+\t\tperl_quote_buf(&sb, v->s);\n+\t\tbreak;\n+\tcase QUOTE_PYTHON:\n+\t\tpython_quote_buf(&sb, v->s);\n+\t\tbreak;\n+\tcase QUOTE_TCL:\n+\t\ttcl_quote_buf(&sb, v->s);\n+\t\tbreak;\n+\t}\n+\tif (quote_style != QUOTE_NONE) {\n+\t\tfputs(sb.buf, stdout);\n+\t\tstrbuf_release(&sb);\n+\t}\n+}\n+\n+static int hex1(char ch)\n+{\n+\tif ('0' <= ch && ch <= '9')\n+\t\treturn ch - '0';\n+\telse if ('a' <= ch && ch <= 'f')\n+\t\treturn ch - 'a' + 10;\n+\telse if ('A' <= ch && ch <= 'F')\n+\t\treturn ch - 'A' + 10;\n+\treturn -1;\n+}\n+static int hex2(const char *cp)\n+{\n+\tif (cp[0] && cp[1])\n+\t\treturn (hex1(cp[0]) << 4) | hex1(cp[1]);\n+\telse\n+\t\treturn -1;\n+}\n+\n+static void emit(const char *cp, const char *ep)\n+{\n+\twhile (*cp && (!ep || cp < ep)) {\n+\t\tif (*cp == '%') {\n+\t\t\tif (cp[1] == '%')\n+\t\t\t\tcp++;\n+\t\t\telse {\n+\t\t\t\tint ch = hex2(cp + 1);\n+\t\t\t\tif (0 <= ch) {\n+\t\t\t\t\tputchar(ch);\n+\t\t\t\t\tcp += 3;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tputchar(*cp);\n+\t\tcp++;\n+\t}\n+}\n+\n+void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n+{\n+\tconst char *cp, *sp, *ep;\n+\n+\tfor (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n+\t\tstruct atom_value *atomv;\n+\n+\t\tep = strchr(sp, ')');\n+\t\tif (cp < sp)\n+\t\t\temit(cp, sp);\n+\t\tget_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n+\t\tprint_value(atomv, quote_style);\n+\t}\n+\tif (*cp) {\n+\t\tsp = cp + strlen(cp);\n+\t\temit(cp, sp);\n+\t}\n+\tif (need_color_reset_at_eol) {\n+\t\tstruct atom_value resetv;\n+\t\tchar color[COLOR_MAXLEN] = \"\";\n+\n+\t\tif (color_parse(\"reset\", color) < 0)\n+\t\t\tdie(\"BUG: couldn't parse 'reset' as a color\");\n+\t\tresetv.s = color;\n+\t\tprint_value(&resetv, quote_style);\n+\t}\n+\tputchar('\\n');\n+}\n+\n+/*  If no sorting option is given, use refname to sort as default */\n+struct ref_sorting *ref_default_sorting(void)\n+{\n+\tstatic const char cstr_name[] = \"refname\";\n+\n+\tstruct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));\n+\n+\tsorting->next = NULL;\n+\tsorting->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name));\n+\treturn sorting;\n+}\n+\n+int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct ref_sorting **sorting_tail = opt->value;\n+\tstruct ref_sorting *s;\n+\tint len;\n+\n+\tif (!arg) /* should --no-sort void the list ? */\n+\t\treturn -1;\n+\n+\ts = xcalloc(1, sizeof(*s));\n+\ts->next = *sorting_tail;\n+\t*sorting_tail = s;\n+\n+\tif (*arg == '-') {\n+\t\ts->reverse = 1;\n+\t\targ++;\n+\t}\n+\tlen = strlen(arg);\n+\ts->atom = parse_ref_filter_atom(arg, arg+len);\n+\treturn 0;\n+}\ndiff --git a/ref-filter.h b/ref-filter.h\nindex c2c5d37..15e6766 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -46,8 +46,12 @@ struct ref_filter_cbdata {\n \tstruct ref_filter filter;\n };\n \n-/*  Callback function for for_each_*ref(). This filters the refs based on the filters set */\n-int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data);\n+/*\n+ * API for filtering a set of refs. The refs are provided and iterated\n+ * over using the for_each_ref_fn(). The refs are stored into and filtered\n+ * based on the ref_filter_cbdata structure.\n+ */\n+int filter_refs(int (for_each_ref_fn)(each_ref_fn, void *), struct ref_filter_cbdata *data);\n /*  Clear all memory allocated to ref_array */\n void ref_array_clear(struct ref_array *array);\n /*  Parse format string and sort specifiers */\n-- \n2.4.2\n"},{"id":"263113","messageId":"1433598496-31287-10-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 10/11] for-each-ref: introduce filter_refs()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:48:15Z","receivedAt":"2015-06-06T13:48:15Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Introduce filter_refs() which will act as an API for users to\ncall and provide a function which will iterate over a set of refs\nwhile filtering out the required refs.\n\nCurrently this will wrap around ref_filter_handler(). Hence,\nref_filter_handler is made file scope static.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\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 | 10 ++++++++++\n 1 file changed, 10 insertions(+)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex ece16a1..91a6ec9 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -904,6 +904,16 @@ void ref_array_clear(struct ref_array *array)\n \tarray->nr = array->alloc = 0;\n }\n \n+/*\n+ * API for filtering a set of refs. The refs are provided and iterated\n+ * over using the for_each_ref_fn(). The refs are stored into and filtered\n+ * based on the ref_filter_cbdata structure.\n+ */\n+int filter_refs(int (for_each_ref_fn)(each_ref_fn, void *), struct ref_filter_cbdata *data)\n+{\n+\treturn for_each_ref_fn(ref_filter_handler, data);\n+}\n+\n static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)\n {\n \tstruct atom_value *va, *vb;\n-- \n2.4.2\n"},{"id":"263111","messageId":"1433598496-31287-11-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 11/11] ref-filter: make 'ref_array_item' use a FLEX_ARRAY for refname","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-06T13:48:16Z","receivedAt":"2015-06-06T13:48:16Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"This would remove the need of using a pointer to store refname.\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 | 7 ++++---\n ref-filter.h | 2 +-\n 2 files changed, 5 insertions(+), 4 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 91a6ec9..745d3b3 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -846,8 +846,10 @@ static struct ref_array_item *new_ref_array_item(const char *refname,\n \t\t\t\t\t\t const unsigned char *objectname,\n \t\t\t\t\t\t int flag)\n {\n-\tstruct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item));\n-\tref->refname = xstrdup(refname);\n+\tsize_t len = strlen(refname);\n+\tstruct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1);\n+\tmemcpy(ref->refname, refname, len);\n+\tref->refname[len] = '\\0';\n \thashcpy(ref->objectname, objectname);\n \tref->flag = flag;\n \n@@ -888,7 +890,6 @@ int ref_filter_handler(const char *refname, const struct object_id *oid, int fla\n static void free_array_item(struct ref_array_item *item)\n {\n \tfree(item->symref);\n-\tfree(item->refname);\n \tfree(item);\n }\n \ndiff --git a/ref-filter.h b/ref-filter.h\nindex 15e6766..041a39a 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -29,7 +29,7 @@ struct ref_array_item {\n \tint flag;\n \tchar *symref;\n \tstruct atom_value *value;\n-\tchar *refname;\n+\tchar refname[FLEX_ARRAY];\n };\n \n struct ref_array {\n-- \n2.4.2\n"},{"id":"263144","messageId":"1433673294-16243-1-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-9-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 09/11] ref-filter: move code from 'for-each-ref'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-07T10:34:54Z","receivedAt":"2015-06-07T10:34:54Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Move most of the code from 'for-each-ref' to 'ref-filter' to make\nit publicly available to other commands, this is to unify the code\nof 'tag -l', 'branch -l' and 'for-each-ref' so that they can share\ntheir implementations with each other.\n\nAdd 'ref-filter' to the Makefile, this completes the movement of code\nfrom 'for-each-ref' to '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 Makefile               |    1 +\n builtin/for-each-ref.c | 1075 +----------------------------------------------\n ref-filter.c           | 1077 ++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 1079 insertions(+), 1074 deletions(-)\n create mode 100644 ref-filter.c\n\ndiff --git a/Makefile b/Makefile\nindex 54ec511..d715b66 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -762,6 +762,7 @@ LIB_OBJS += reachable.o\n LIB_OBJS += read-cache.o\n LIB_OBJS += reflog-walk.o\n LIB_OBJS += refs.o\n+LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace_object.o\n LIB_OBJS += rerere.o\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 063de00..4d2d024 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -2,1082 +2,9 @@\n #include \"cache.h\"\n #include \"refs.h\"\n #include \"object.h\"\n-#include \"tag.h\"\n-#include \"commit.h\"\n-#include \"tree.h\"\n-#include \"blob.h\"\n-#include \"quote.h\"\n #include \"parse-options.h\"\n-#include \"remote.h\"\n-#include \"color.h\"\n #include \"ref-filter.h\"\n \n-typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n-\n-static struct {\n-\tconst char *name;\n-\tcmp_type cmp_type;\n-} valid_atom[] = {\n-\t{ \"refname\" },\n-\t{ \"objecttype\" },\n-\t{ \"objectsize\", FIELD_ULONG },\n-\t{ \"objectname\" },\n-\t{ \"tree\" },\n-\t{ \"parent\" },\n-\t{ \"numparent\", FIELD_ULONG },\n-\t{ \"object\" },\n-\t{ \"type\" },\n-\t{ \"tag\" },\n-\t{ \"author\" },\n-\t{ \"authorname\" },\n-\t{ \"authoremail\" },\n-\t{ \"authordate\", FIELD_TIME },\n-\t{ \"committer\" },\n-\t{ \"committername\" },\n-\t{ \"committeremail\" },\n-\t{ \"committerdate\", FIELD_TIME },\n-\t{ \"tagger\" },\n-\t{ \"taggername\" },\n-\t{ \"taggeremail\" },\n-\t{ \"taggerdate\", FIELD_TIME },\n-\t{ \"creator\" },\n-\t{ \"creatordate\", FIELD_TIME },\n-\t{ \"subject\" },\n-\t{ \"body\" },\n-\t{ \"contents\" },\n-\t{ \"contents:subject\" },\n-\t{ \"contents:body\" },\n-\t{ \"contents:signature\" },\n-\t{ \"upstream\" },\n-\t{ \"push\" },\n-\t{ \"symref\" },\n-\t{ \"flag\" },\n-\t{ \"HEAD\" },\n-\t{ \"color\" },\n-};\n-\n-/*\n- * An atom is a valid field atom listed above, possibly prefixed with\n- * a \"*\" to denote deref_tag().\n- *\n- * We parse given format string and sort specifiers, and make a list\n- * of properties that we need to extract out of objects.  ref_array_item\n- * structure will hold an array of values extracted that can be\n- * indexed with the \"atom number\", which is an index into this\n- * array.\n- */\n-static const char **used_atom;\n-static cmp_type *used_atom_type;\n-static int used_atom_cnt, need_tagged, need_symref;\n-static int need_color_reset_at_eol;\n-\n-/*\n- * Used to parse format string and sort specifiers\n- */\n-int parse_ref_filter_atom(const char *atom, const char *ep)\n-{\n-\tconst char *sp;\n-\tint i, at;\n-\n-\tsp = atom;\n-\tif (*sp == '*' && sp < ep)\n-\t\tsp++; /* deref */\n-\tif (ep <= sp)\n-\t\tdie(\"malformed field name: %.*s\", (int)(ep-atom), atom);\n-\n-\t/* Do we have the atom already used elsewhere? */\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tint len = strlen(used_atom[i]);\n-\t\tif (len == ep - atom && !memcmp(used_atom[i], atom, len))\n-\t\t\treturn i;\n-\t}\n-\n-\t/* Is the atom a valid one? */\n-\tfor (i = 0; i < ARRAY_SIZE(valid_atom); i++) {\n-\t\tint len = strlen(valid_atom[i].name);\n-\t\t/*\n-\t\t * If the atom name has a colon, strip it and everything after\n-\t\t * it off - it specifies the format for this entry, and\n-\t\t * shouldn't be used for checking against the valid_atom\n-\t\t * table.\n-\t\t */\n-\t\tconst char *formatp = strchr(sp, ':');\n-\t\tif (!formatp || ep < formatp)\n-\t\t\tformatp = ep;\n-\t\tif (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))\n-\t\t\tbreak;\n-\t}\n-\n-\tif (ARRAY_SIZE(valid_atom) <= i)\n-\t\tdie(\"unknown field name: %.*s\", (int)(ep-atom), atom);\n-\n-\t/* Add it in, including the deref prefix */\n-\tat = used_atom_cnt;\n-\tused_atom_cnt++;\n-\tREALLOC_ARRAY(used_atom, used_atom_cnt);\n-\tREALLOC_ARRAY(used_atom_type, used_atom_cnt);\n-\tused_atom[at] = xmemdupz(atom, ep - atom);\n-\tused_atom_type[at] = valid_atom[i].cmp_type;\n-\tif (*atom == '*')\n-\t\tneed_tagged = 1;\n-\tif (!strcmp(used_atom[at], \"symref\"))\n-\t\tneed_symref = 1;\n-\treturn at;\n-}\n-\n-/*\n- * In a format string, find the next occurrence of %(atom).\n- */\n-static const char *find_next(const char *cp)\n-{\n-\twhile (*cp) {\n-\t\tif (*cp == '%') {\n-\t\t\t/*\n-\t\t\t * %( is the start of an atom;\n-\t\t\t * %% is a quoted per-cent.\n-\t\t\t */\n-\t\t\tif (cp[1] == '(')\n-\t\t\t\treturn cp;\n-\t\t\telse if (cp[1] == '%')\n-\t\t\t\tcp++; /* skip over two % */\n-\t\t\t/* otherwise this is a singleton, literal % */\n-\t\t}\n-\t\tcp++;\n-\t}\n-\treturn NULL;\n-}\n-\n-/*\n- * Make sure the format string is well formed, and parse out\n- * the used atoms.\n- */\n-int verify_ref_format(const char *format)\n-{\n-\tconst char *cp, *sp;\n-\n-\tneed_color_reset_at_eol = 0;\n-\tfor (cp = format; *cp && (sp = find_next(cp)); ) {\n-\t\tconst char *color, *ep = strchr(sp, ')');\n-\t\tint at;\n-\n-\t\tif (!ep)\n-\t\t\treturn error(\"malformed format string %s\", sp);\n-\t\t/* sp points at \"%(\" and ep points at the closing \")\" */\n-\t\tat = parse_ref_filter_atom(sp + 2, ep);\n-\t\tcp = ep + 1;\n-\n-\t\tif (skip_prefix(used_atom[at], \"color:\", &color))\n-\t\t\tneed_color_reset_at_eol = !!strcmp(color, \"reset\");\n-\t}\n-\treturn 0;\n-}\n-\n-/*\n- * Given an object name, read the object data and size, and return a\n- * \"struct object\".  If the object data we are returning is also borrowed\n- * by the \"struct object\" representation, set *eaten as well---it is a\n- * signal from parse_object_buffer to us not to free the buffer.\n- */\n-static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)\n-{\n-\tenum object_type type;\n-\tvoid *buf = read_sha1_file(sha1, &type, sz);\n-\n-\tif (buf)\n-\t\t*obj = parse_object_buffer(sha1, type, *sz, buf, eaten);\n-\telse\n-\t\t*obj = NULL;\n-\treturn buf;\n-}\n-\n-static int grab_objectname(const char *name, const unsigned char *sha1,\n-\t\t\t    struct atom_value *v)\n-{\n-\tif (!strcmp(name, \"objectname\")) {\n-\t\tchar *s = xmalloc(41);\n-\t\tstrcpy(s, sha1_to_hex(sha1));\n-\t\tv->s = s;\n-\t\treturn 1;\n-\t}\n-\tif (!strcmp(name, \"objectname:short\")) {\n-\t\tv->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));\n-\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n-/* See grab_values */\n-static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tint i;\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\t\tif (!strcmp(name, \"objecttype\"))\n-\t\t\tv->s = typename(obj->type);\n-\t\telse if (!strcmp(name, \"objectsize\")) {\n-\t\t\tchar *s = xmalloc(40);\n-\t\t\tsprintf(s, \"%lu\", sz);\n-\t\t\tv->ul = sz;\n-\t\t\tv->s = s;\n-\t\t}\n-\t\telse if (deref)\n-\t\t\tgrab_objectname(name, obj->sha1, v);\n-\t}\n-}\n-\n-/* See grab_values */\n-static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tint i;\n-\tstruct tag *tag = (struct tag *) obj;\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\t\tif (!strcmp(name, \"tag\"))\n-\t\t\tv->s = tag->tag;\n-\t\telse if (!strcmp(name, \"type\") && tag->tagged)\n-\t\t\tv->s = typename(tag->tagged->type);\n-\t\telse if (!strcmp(name, \"object\") && tag->tagged) {\n-\t\t\tchar *s = xmalloc(41);\n-\t\t\tstrcpy(s, sha1_to_hex(tag->tagged->sha1));\n-\t\t\tv->s = s;\n-\t\t}\n-\t}\n-}\n-\n-/* See grab_values */\n-static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tint i;\n-\tstruct commit *commit = (struct commit *) obj;\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\t\tif (!strcmp(name, \"tree\")) {\n-\t\t\tchar *s = xmalloc(41);\n-\t\t\tstrcpy(s, sha1_to_hex(commit->tree->object.sha1));\n-\t\t\tv->s = s;\n-\t\t}\n-\t\tif (!strcmp(name, \"numparent\")) {\n-\t\t\tchar *s = xmalloc(40);\n-\t\t\tv->ul = commit_list_count(commit->parents);\n-\t\t\tsprintf(s, \"%lu\", v->ul);\n-\t\t\tv->s = s;\n-\t\t}\n-\t\telse if (!strcmp(name, \"parent\")) {\n-\t\t\tint num = commit_list_count(commit->parents);\n-\t\t\tint i;\n-\t\t\tstruct commit_list *parents;\n-\t\t\tchar *s = xmalloc(41 * num + 1);\n-\t\t\tv->s = s;\n-\t\t\tfor (i = 0, parents = commit->parents;\n-\t\t\t     parents;\n-\t\t\t     parents = parents->next, i = i + 41) {\n-\t\t\t\tstruct commit *parent = parents->item;\n-\t\t\t\tstrcpy(s+i, sha1_to_hex(parent->object.sha1));\n-\t\t\t\tif (parents->next)\n-\t\t\t\t\ts[i+40] = ' ';\n-\t\t\t}\n-\t\t\tif (!i)\n-\t\t\t\t*s = '\\0';\n-\t\t}\n-\t}\n-}\n-\n-static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)\n-{\n-\tconst char *eol;\n-\twhile (*buf) {\n-\t\tif (!strncmp(buf, who, wholen) &&\n-\t\t    buf[wholen] == ' ')\n-\t\t\treturn buf + wholen + 1;\n-\t\teol = strchr(buf, '\\n');\n-\t\tif (!eol)\n-\t\t\treturn \"\";\n-\t\teol++;\n-\t\tif (*eol == '\\n')\n-\t\t\treturn \"\"; /* end of header */\n-\t\tbuf = eol;\n-\t}\n-\treturn \"\";\n-}\n-\n-static const char *copy_line(const char *buf)\n-{\n-\tconst char *eol = strchrnul(buf, '\\n');\n-\treturn xmemdupz(buf, eol - buf);\n-}\n-\n-static const char *copy_name(const char *buf)\n-{\n-\tconst char *cp;\n-\tfor (cp = buf; *cp && *cp != '\\n'; cp++) {\n-\t\tif (!strncmp(cp, \" <\", 2))\n-\t\t\treturn xmemdupz(buf, cp - buf);\n-\t}\n-\treturn \"\";\n-}\n-\n-static const char *copy_email(const char *buf)\n-{\n-\tconst char *email = strchr(buf, '<');\n-\tconst char *eoemail;\n-\tif (!email)\n-\t\treturn \"\";\n-\teoemail = strchr(email, '>');\n-\tif (!eoemail)\n-\t\treturn \"\";\n-\treturn xmemdupz(email, eoemail + 1 - email);\n-}\n-\n-static char *copy_subject(const char *buf, unsigned long len)\n-{\n-\tchar *r = xmemdupz(buf, len);\n-\tint i;\n-\n-\tfor (i = 0; i < len; i++)\n-\t\tif (r[i] == '\\n')\n-\t\t\tr[i] = ' ';\n-\n-\treturn r;\n-}\n-\n-static void grab_date(const char *buf, struct atom_value *v, const char *atomname)\n-{\n-\tconst char *eoemail = strstr(buf, \"> \");\n-\tchar *zone;\n-\tunsigned long timestamp;\n-\tlong tz;\n-\tenum date_mode date_mode = DATE_NORMAL;\n-\tconst char *formatp;\n-\n-\t/*\n-\t * We got here because atomname ends in \"date\" or \"date<something>\";\n-\t * it's not possible that <something> is not \":<format>\" because\n-\t * parse_ref_filter_atom() wouldn't have allowed it, so we can assume that no\n-\t * \":\" means no format is specified, and use the default.\n-\t */\n-\tformatp = strchr(atomname, ':');\n-\tif (formatp != NULL) {\n-\t\tformatp++;\n-\t\tdate_mode = parse_date_format(formatp);\n-\t}\n-\n-\tif (!eoemail)\n-\t\tgoto bad;\n-\ttimestamp = strtoul(eoemail + 2, &zone, 10);\n-\tif (timestamp == ULONG_MAX)\n-\t\tgoto bad;\n-\ttz = strtol(zone, NULL, 10);\n-\tif ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)\n-\t\tgoto bad;\n-\tv->s = xstrdup(show_date(timestamp, tz, date_mode));\n-\tv->ul = timestamp;\n-\treturn;\n- bad:\n-\tv->s = \"\";\n-\tv->ul = 0;\n-}\n-\n-/* See grab_values */\n-static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tint i;\n-\tint wholen = strlen(who);\n-\tconst char *wholine = NULL;\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\t\tif (strncmp(who, name, wholen))\n-\t\t\tcontinue;\n-\t\tif (name[wholen] != 0 &&\n-\t\t    strcmp(name + wholen, \"name\") &&\n-\t\t    strcmp(name + wholen, \"email\") &&\n-\t\t    !starts_with(name + wholen, \"date\"))\n-\t\t\tcontinue;\n-\t\tif (!wholine)\n-\t\t\twholine = find_wholine(who, wholen, buf, sz);\n-\t\tif (!wholine)\n-\t\t\treturn; /* no point looking for it */\n-\t\tif (name[wholen] == 0)\n-\t\t\tv->s = copy_line(wholine);\n-\t\telse if (!strcmp(name + wholen, \"name\"))\n-\t\t\tv->s = copy_name(wholine);\n-\t\telse if (!strcmp(name + wholen, \"email\"))\n-\t\t\tv->s = copy_email(wholine);\n-\t\telse if (starts_with(name + wholen, \"date\"))\n-\t\t\tgrab_date(wholine, v, name);\n-\t}\n-\n-\t/*\n-\t * For a tag or a commit object, if \"creator\" or \"creatordate\" is\n-\t * requested, do something special.\n-\t */\n-\tif (strcmp(who, \"tagger\") && strcmp(who, \"committer\"))\n-\t\treturn; /* \"author\" for commit object is not wanted */\n-\tif (!wholine)\n-\t\twholine = find_wholine(who, wholen, buf, sz);\n-\tif (!wholine)\n-\t\treturn;\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\n-\t\tif (starts_with(name, \"creatordate\"))\n-\t\t\tgrab_date(wholine, v, name);\n-\t\telse if (!strcmp(name, \"creator\"))\n-\t\t\tv->s = copy_line(wholine);\n-\t}\n-}\n-\n-static void find_subpos(const char *buf, unsigned long sz,\n-\t\t\tconst char **sub, unsigned long *sublen,\n-\t\t\tconst char **body, unsigned long *bodylen,\n-\t\t\tunsigned long *nonsiglen,\n-\t\t\tconst char **sig, unsigned long *siglen)\n-{\n-\tconst char *eol;\n-\t/* skip past header until we hit empty line */\n-\twhile (*buf && *buf != '\\n') {\n-\t\teol = strchrnul(buf, '\\n');\n-\t\tif (*eol)\n-\t\t\teol++;\n-\t\tbuf = eol;\n-\t}\n-\t/* skip any empty lines */\n-\twhile (*buf == '\\n')\n-\t\tbuf++;\n-\n-\t/* parse signature first; we might not even have a subject line */\n-\t*sig = buf + parse_signature(buf, strlen(buf));\n-\t*siglen = strlen(*sig);\n-\n-\t/* subject is first non-empty line */\n-\t*sub = buf;\n-\t/* subject goes to first empty line */\n-\twhile (buf < *sig && *buf && *buf != '\\n') {\n-\t\teol = strchrnul(buf, '\\n');\n-\t\tif (*eol)\n-\t\t\teol++;\n-\t\tbuf = eol;\n-\t}\n-\t*sublen = buf - *sub;\n-\t/* drop trailing newline, if present */\n-\tif (*sublen && (*sub)[*sublen - 1] == '\\n')\n-\t\t*sublen -= 1;\n-\n-\t/* skip any empty lines */\n-\twhile (*buf == '\\n')\n-\t\tbuf++;\n-\t*body = buf;\n-\t*bodylen = strlen(buf);\n-\t*nonsiglen = *sig - buf;\n-}\n-\n-/* See grab_values */\n-static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tint i;\n-\tconst char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;\n-\tunsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (!!deref != (*name == '*'))\n-\t\t\tcontinue;\n-\t\tif (deref)\n-\t\t\tname++;\n-\t\tif (strcmp(name, \"subject\") &&\n-\t\t    strcmp(name, \"body\") &&\n-\t\t    strcmp(name, \"contents\") &&\n-\t\t    strcmp(name, \"contents:subject\") &&\n-\t\t    strcmp(name, \"contents:body\") &&\n-\t\t    strcmp(name, \"contents:signature\"))\n-\t\t\tcontinue;\n-\t\tif (!subpos)\n-\t\t\tfind_subpos(buf, sz,\n-\t\t\t\t    &subpos, &sublen,\n-\t\t\t\t    &bodypos, &bodylen, &nonsiglen,\n-\t\t\t\t    &sigpos, &siglen);\n-\n-\t\tif (!strcmp(name, \"subject\"))\n-\t\t\tv->s = copy_subject(subpos, sublen);\n-\t\telse if (!strcmp(name, \"contents:subject\"))\n-\t\t\tv->s = copy_subject(subpos, sublen);\n-\t\telse if (!strcmp(name, \"body\"))\n-\t\t\tv->s = xmemdupz(bodypos, bodylen);\n-\t\telse if (!strcmp(name, \"contents:body\"))\n-\t\t\tv->s = xmemdupz(bodypos, nonsiglen);\n-\t\telse if (!strcmp(name, \"contents:signature\"))\n-\t\t\tv->s = xmemdupz(sigpos, siglen);\n-\t\telse if (!strcmp(name, \"contents\"))\n-\t\t\tv->s = xstrdup(subpos);\n-\t}\n-}\n-\n-/*\n- * We want to have empty print-string for field requests\n- * that do not apply (e.g. \"authordate\" for a tag object)\n- */\n-static void fill_missing_values(struct atom_value *val)\n-{\n-\tint i;\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (v->s == NULL)\n-\t\t\tv->s = \"\";\n-\t}\n-}\n-\n-/*\n- * val is a list of atom_value to hold returned values.  Extract\n- * the values for atoms in used_atom array out of (obj, buf, sz).\n- * when deref is false, (obj, buf, sz) is the object that is\n- * pointed at by the ref itself; otherwise it is the object the\n- * ref (which is a tag) refers to.\n- */\n-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n-{\n-\tgrab_common_values(val, deref, obj, buf, sz);\n-\tswitch (obj->type) {\n-\tcase OBJ_TAG:\n-\t\tgrab_tag_values(val, deref, obj, buf, sz);\n-\t\tgrab_sub_body_contents(val, deref, obj, buf, sz);\n-\t\tgrab_person(\"tagger\", val, deref, obj, buf, sz);\n-\t\tbreak;\n-\tcase OBJ_COMMIT:\n-\t\tgrab_commit_values(val, deref, obj, buf, sz);\n-\t\tgrab_sub_body_contents(val, deref, obj, buf, sz);\n-\t\tgrab_person(\"author\", val, deref, obj, buf, sz);\n-\t\tgrab_person(\"committer\", val, deref, obj, buf, sz);\n-\t\tbreak;\n-\tcase OBJ_TREE:\n-\t\t/* grab_tree_values(val, deref, obj, buf, sz); */\n-\t\tbreak;\n-\tcase OBJ_BLOB:\n-\t\t/* grab_blob_values(val, deref, obj, buf, sz); */\n-\t\tbreak;\n-\tdefault:\n-\t\tdie(\"Eh?  Object of type %d?\", obj->type);\n-\t}\n-}\n-\n-static inline char *copy_advance(char *dst, const char *src)\n-{\n-\twhile (*src)\n-\t\t*dst++ = *src++;\n-\treturn dst;\n-}\n-\n-/*\n- * Parse the object referred by ref, and grab needed value.\n- */\n-static void populate_value(struct ref_array_item *ref)\n-{\n-\tvoid *buf;\n-\tstruct object *obj;\n-\tint eaten, i;\n-\tunsigned long size;\n-\tconst unsigned char *tagged;\n-\n-\tref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));\n-\n-\tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n-\t\tunsigned char unused1[20];\n-\t\tref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,\n-\t\t\t\t\t     unused1, NULL);\n-\t\tif (!ref->symref)\n-\t\t\tref->symref = \"\";\n-\t}\n-\n-\t/* Fill in specials first */\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tconst char *name = used_atom[i];\n-\t\tstruct atom_value *v = &ref->value[i];\n-\t\tint deref = 0;\n-\t\tconst char *refname;\n-\t\tconst char *formatp;\n-\t\tstruct branch *branch = NULL;\n-\n-\t\tif (*name == '*') {\n-\t\t\tderef = 1;\n-\t\t\tname++;\n-\t\t}\n-\n-\t\tif (starts_with(name, \"refname\"))\n-\t\t\trefname = ref->refname;\n-\t\telse if (starts_with(name, \"symref\"))\n-\t\t\trefname = ref->symref ? ref->symref : \"\";\n-\t\telse if (starts_with(name, \"upstream\")) {\n-\t\t\tconst char *branch_name;\n-\t\t\t/* only local branches may have an upstream */\n-\t\t\tif (!skip_prefix(ref->refname, \"refs/heads/\",\n-\t\t\t\t\t &branch_name))\n-\t\t\t\tcontinue;\n-\t\t\tbranch = branch_get(branch_name);\n-\n-\t\t\trefname = branch_get_upstream(branch, NULL);\n-\t\t\tif (!refname)\n-\t\t\t\tcontinue;\n-\t\t} else if (starts_with(name, \"push\")) {\n-\t\t\tconst char *branch_name;\n-\t\t\tif (!skip_prefix(ref->refname, \"refs/heads/\",\n-\t\t\t\t\t &branch_name))\n-\t\t\t\tcontinue;\n-\t\t\tbranch = branch_get(branch_name);\n-\n-\t\t\trefname = branch_get_push(branch, NULL);\n-\t\t\tif (!refname)\n-\t\t\t\tcontinue;\n-\t\t} else if (starts_with(name, \"color:\")) {\n-\t\t\tchar color[COLOR_MAXLEN] = \"\";\n-\n-\t\t\tif (color_parse(name + 6, color) < 0)\n-\t\t\t\tdie(_(\"unable to parse format\"));\n-\t\t\tv->s = xstrdup(color);\n-\t\t\tcontinue;\n-\t\t} else if (!strcmp(name, \"flag\")) {\n-\t\t\tchar buf[256], *cp = buf;\n-\t\t\tif (ref->flag & REF_ISSYMREF)\n-\t\t\t\tcp = copy_advance(cp, \",symref\");\n-\t\t\tif (ref->flag & REF_ISPACKED)\n-\t\t\t\tcp = copy_advance(cp, \",packed\");\n-\t\t\tif (cp == buf)\n-\t\t\t\tv->s = \"\";\n-\t\t\telse {\n-\t\t\t\t*cp = '\\0';\n-\t\t\t\tv->s = xstrdup(buf + 1);\n-\t\t\t}\n-\t\t\tcontinue;\n-\t\t} else if (!deref && grab_objectname(name, ref->objectname, v)) {\n-\t\t\tcontinue;\n-\t\t} else if (!strcmp(name, \"HEAD\")) {\n-\t\t\tconst char *head;\n-\t\t\tunsigned char sha1[20];\n-\n-\t\t\thead = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING,\n-\t\t\t\t\t\t  sha1, NULL);\n-\t\t\tif (!strcmp(ref->refname, head))\n-\t\t\t\tv->s = \"*\";\n-\t\t\telse\n-\t\t\t\tv->s = \" \";\n-\t\t\tcontinue;\n-\t\t} else\n-\t\t\tcontinue;\n-\n-\t\tformatp = strchr(name, ':');\n-\t\tif (formatp) {\n-\t\t\tint num_ours, num_theirs;\n-\n-\t\t\tformatp++;\n-\t\t\tif (!strcmp(formatp, \"short\"))\n-\t\t\t\trefname = shorten_unambiguous_ref(refname,\n-\t\t\t\t\t\t      warn_ambiguous_refs);\n-\t\t\telse if (!strcmp(formatp, \"track\") &&\n-\t\t\t\t (starts_with(name, \"upstream\") ||\n-\t\t\t\t  starts_with(name, \"push\"))) {\n-\t\t\t\tchar buf[40];\n-\n-\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n-\t\t\t\t\t\t       &num_theirs, NULL))\n-\t\t\t\t\tcontinue;\n-\n-\t\t\t\tif (!num_ours && !num_theirs)\n-\t\t\t\t\tv->s = \"\";\n-\t\t\t\telse if (!num_ours) {\n-\t\t\t\t\tsprintf(buf, \"[behind %d]\", num_theirs);\n-\t\t\t\t\tv->s = xstrdup(buf);\n-\t\t\t\t} else if (!num_theirs) {\n-\t\t\t\t\tsprintf(buf, \"[ahead %d]\", num_ours);\n-\t\t\t\t\tv->s = xstrdup(buf);\n-\t\t\t\t} else {\n-\t\t\t\t\tsprintf(buf, \"[ahead %d, behind %d]\",\n-\t\t\t\t\t\tnum_ours, num_theirs);\n-\t\t\t\t\tv->s = xstrdup(buf);\n-\t\t\t\t}\n-\t\t\t\tcontinue;\n-\t\t\t} else if (!strcmp(formatp, \"trackshort\") &&\n-\t\t\t\t   (starts_with(name, \"upstream\") ||\n-\t\t\t\t    starts_with(name, \"push\"))) {\n-\t\t\t\tassert(branch);\n-\n-\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n-\t\t\t\t\t\t\t&num_theirs, NULL))\n-\t\t\t\t\tcontinue;\n-\n-\t\t\t\tif (!num_ours && !num_theirs)\n-\t\t\t\t\tv->s = \"=\";\n-\t\t\t\telse if (!num_ours)\n-\t\t\t\t\tv->s = \"<\";\n-\t\t\t\telse if (!num_theirs)\n-\t\t\t\t\tv->s = \">\";\n-\t\t\t\telse\n-\t\t\t\t\tv->s = \"<>\";\n-\t\t\t\tcontinue;\n-\t\t\t} else\n-\t\t\t\tdie(\"unknown %.*s format %s\",\n-\t\t\t\t    (int)(formatp - name), name, formatp);\n-\t\t}\n-\n-\t\tif (!deref)\n-\t\t\tv->s = refname;\n-\t\telse {\n-\t\t\tint len = strlen(refname);\n-\t\t\tchar *s = xmalloc(len + 4);\n-\t\t\tsprintf(s, \"%s^{}\", refname);\n-\t\t\tv->s = s;\n-\t\t}\n-\t}\n-\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tstruct atom_value *v = &ref->value[i];\n-\t\tif (v->s == NULL)\n-\t\t\tgoto need_obj;\n-\t}\n-\treturn;\n-\n- need_obj:\n-\tbuf = get_obj(ref->objectname, &obj, &size, &eaten);\n-\tif (!buf)\n-\t\tdie(\"missing object %s for %s\",\n-\t\t    sha1_to_hex(ref->objectname), ref->refname);\n-\tif (!obj)\n-\t\tdie(\"parse_object_buffer failed on %s for %s\",\n-\t\t    sha1_to_hex(ref->objectname), ref->refname);\n-\n-\tgrab_values(ref->value, 0, obj, buf, size);\n-\tif (!eaten)\n-\t\tfree(buf);\n-\n-\t/*\n-\t * If there is no atom that wants to know about tagged\n-\t * object, we are done.\n-\t */\n-\tif (!need_tagged || (obj->type != OBJ_TAG))\n-\t\treturn;\n-\n-\t/*\n-\t * If it is a tag object, see if we use a value that derefs\n-\t * the object, and if we do grab the object it refers to.\n-\t */\n-\ttagged = ((struct tag *)obj)->tagged->sha1;\n-\n-\t/*\n-\t * NEEDSWORK: This derefs tag only once, which\n-\t * is good to deal with chains of trust, but\n-\t * is not consistent with what deref_tag() does\n-\t * which peels the onion to the core.\n-\t */\n-\tbuf = get_obj(tagged, &obj, &size, &eaten);\n-\tif (!buf)\n-\t\tdie(\"missing object %s for %s\",\n-\t\t    sha1_to_hex(tagged), ref->refname);\n-\tif (!obj)\n-\t\tdie(\"parse_object_buffer failed on %s for %s\",\n-\t\t    sha1_to_hex(tagged), ref->refname);\n-\tgrab_values(ref->value, 1, obj, buf, size);\n-\tif (!eaten)\n-\t\tfree(buf);\n-}\n-\n-/*\n- * Given a ref, return the value for the atom.  This lazily gets value\n- * out of the object by calling populate value.\n- */\n-static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom_value **v)\n-{\n-\tif (!ref->value) {\n-\t\tpopulate_value(ref);\n-\t\tfill_missing_values(ref->value);\n-\t}\n-\t*v = &ref->value[atom];\n-}\n-\n-/*\n- * Given a refname, return 1 if the refname matches with one of the patterns\n- * while the pattern is a pathname like 'refs/tags' or 'refs/heads/master'\n- * and so on, else return 0. Supports use of wild characters.\n- */\n-static int match_name_as_path(const char **pattern, const char *refname)\n-{\n-\tint namelen = strlen(refname);\n-\tfor (; *pattern; pattern++) {\n-\t\tconst char *p = *pattern;\n-\t\tint plen = strlen(p);\n-\n-\t\tif ((plen <= namelen) &&\n-\t\t    !strncmp(refname, p, plen) &&\n-\t\t    (refname[plen] == '\\0' ||\n-\t\t     refname[plen] == '/' ||\n-\t\t     p[plen-1] == '/'))\n-\t\t\treturn 1;\n-\t\tif (!wildmatch(p, refname, WM_PATHNAME, NULL))\n-\t\t\treturn 1;\n-\t}\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-\t\t\t\t\t\t int flag)\n-{\n-\tstruct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item));\n-\tref->refname = xstrdup(refname);\n-\thashcpy(ref->objectname, objectname);\n-\tref->flag = flag;\n-\n-\treturn ref;\n-}\n-\n-/*\n- * A call-back given to for_each_ref().  Filter refs and keep them for\n- * later object processing.\n- */\n-int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n-{\n-\tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n-\tstruct ref_filter *filter = &ref_cbdata->filter;\n-\tstruct ref_array_item *ref;\n-\n-\tif (flag & REF_BAD_NAME) {\n-\t\t  warning(\"ignoring ref with broken name %s\", refname);\n-\t\t  return 0;\n-\t}\n-\n-\tif (*filter->name_patterns && !match_name_as_path(filter->name_patterns, 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\n-\t * by maxcount logic.\n-\t */\n-\tref = new_ref_array_item(refname, oid->hash, flag);\n-\n-\tREALLOC_ARRAY(ref_cbdata->array.items, ref_cbdata->array.nr + 1);\n-\tref_cbdata->array.items[ref_cbdata->array.nr++] = ref;\n-\treturn 0;\n-}\n-\n-/*  Free memory allocated for a ref_array_item */\n-static void free_array_item(struct ref_array_item *item)\n-{\n-\tfree(item->symref);\n-\tfree(item->refname);\n-\tfree(item);\n-}\n-\n-/* Free all memory allocated for ref_array */\n-void ref_array_clear(struct ref_array *array)\n-{\n-\tint i;\n-\n-\tfor (i = 0; i < array->nr; i++)\n-\t\tfree_array_item(array->items[i]);\n-\tfree(array->items);\n-\tarray->items = NULL;\n-\tarray->nr = array->alloc = 0;\n-}\n-\n-static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)\n-{\n-\tstruct atom_value *va, *vb;\n-\tint cmp;\n-\tcmp_type cmp_type = used_atom_type[s->atom];\n-\n-\tget_ref_atom_value(a, s->atom, &va);\n-\tget_ref_atom_value(b, s->atom, &vb);\n-\tswitch (cmp_type) {\n-\tcase FIELD_STR:\n-\t\tcmp = strcmp(va->s, vb->s);\n-\t\tbreak;\n-\tdefault:\n-\t\tif (va->ul < vb->ul)\n-\t\t\tcmp = -1;\n-\t\telse if (va->ul == vb->ul)\n-\t\t\tcmp = 0;\n-\t\telse\n-\t\t\tcmp = 1;\n-\t\tbreak;\n-\t}\n-\treturn (s->reverse) ? -cmp : cmp;\n-}\n-\n-static struct ref_sorting *ref_sorting;\n-static int compare_refs(const void *a_, const void *b_)\n-{\n-\tstruct ref_array_item *a = *((struct ref_array_item **)a_);\n-\tstruct ref_array_item *b = *((struct ref_array_item **)b_);\n-\tstruct ref_sorting *s;\n-\n-\tfor (s = ref_sorting; s; s = s->next) {\n-\t\tint cmp = cmp_ref_sorting(s, a, b);\n-\t\tif (cmp)\n-\t\t\treturn cmp;\n-\t}\n-\treturn 0;\n-}\n-\n-void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n-{\n-\tref_sorting = sorting;\n-\tqsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);\n-}\n-\n-static void print_value(struct atom_value *v, int quote_style)\n-{\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tswitch (quote_style) {\n-\tcase QUOTE_NONE:\n-\t\tfputs(v->s, stdout);\n-\t\tbreak;\n-\tcase QUOTE_SHELL:\n-\t\tsq_quote_buf(&sb, v->s);\n-\t\tbreak;\n-\tcase QUOTE_PERL:\n-\t\tperl_quote_buf(&sb, v->s);\n-\t\tbreak;\n-\tcase QUOTE_PYTHON:\n-\t\tpython_quote_buf(&sb, v->s);\n-\t\tbreak;\n-\tcase QUOTE_TCL:\n-\t\ttcl_quote_buf(&sb, v->s);\n-\t\tbreak;\n-\t}\n-\tif (quote_style != QUOTE_NONE) {\n-\t\tfputs(sb.buf, stdout);\n-\t\tstrbuf_release(&sb);\n-\t}\n-}\n-\n-static int hex1(char ch)\n-{\n-\tif ('0' <= ch && ch <= '9')\n-\t\treturn ch - '0';\n-\telse if ('a' <= ch && ch <= 'f')\n-\t\treturn ch - 'a' + 10;\n-\telse if ('A' <= ch && ch <= 'F')\n-\t\treturn ch - 'A' + 10;\n-\treturn -1;\n-}\n-static int hex2(const char *cp)\n-{\n-\tif (cp[0] && cp[1])\n-\t\treturn (hex1(cp[0]) << 4) | hex1(cp[1]);\n-\telse\n-\t\treturn -1;\n-}\n-\n-static void emit(const char *cp, const char *ep)\n-{\n-\twhile (*cp && (!ep || cp < ep)) {\n-\t\tif (*cp == '%') {\n-\t\t\tif (cp[1] == '%')\n-\t\t\t\tcp++;\n-\t\t\telse {\n-\t\t\t\tint ch = hex2(cp + 1);\n-\t\t\t\tif (0 <= ch) {\n-\t\t\t\t\tputchar(ch);\n-\t\t\t\t\tcp += 3;\n-\t\t\t\t\tcontinue;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\t\tputchar(*cp);\n-\t\tcp++;\n-\t}\n-}\n-\n-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n-{\n-\tconst char *cp, *sp, *ep;\n-\n-\tfor (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n-\t\tstruct atom_value *atomv;\n-\n-\t\tep = strchr(sp, ')');\n-\t\tif (cp < sp)\n-\t\t\temit(cp, sp);\n-\t\tget_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n-\t\tprint_value(atomv, quote_style);\n-\t}\n-\tif (*cp) {\n-\t\tsp = cp + strlen(cp);\n-\t\temit(cp, sp);\n-\t}\n-\tif (need_color_reset_at_eol) {\n-\t\tstruct atom_value resetv;\n-\t\tchar color[COLOR_MAXLEN] = \"\";\n-\n-\t\tif (color_parse(\"reset\", color) < 0)\n-\t\t\tdie(\"BUG: couldn't parse 'reset' as a color\");\n-\t\tresetv.s = color;\n-\t\tprint_value(&resetv, quote_style);\n-\t}\n-\tputchar('\\n');\n-}\n-\n-/*  If no sorting option is given, use refname to sort as default */\n-struct ref_sorting *ref_default_sorting(void)\n-{\n-\tstatic const char cstr_name[] = \"refname\";\n-\n-\tstruct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));\n-\n-\tsorting->next = NULL;\n-\tsorting->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name));\n-\treturn sorting;\n-}\n-\n-int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)\n-{\n-\tstruct ref_sorting **sorting_tail = opt->value;\n-\tstruct ref_sorting *s;\n-\tint len;\n-\n-\tif (!arg) /* should --no-sort void the list ? */\n-\t\treturn -1;\n-\n-\ts = xcalloc(1, sizeof(*s));\n-\ts->next = *sorting_tail;\n-\t*sorting_tail = s;\n-\n-\tif (*arg == '-') {\n-\t\ts->reverse = 1;\n-\t\targ++;\n-\t}\n-\tlen = strlen(arg);\n-\ts->atom = parse_ref_filter_atom(arg, arg+len);\n-\treturn 0;\n-}\n-\n static char const * const for_each_ref_usage[] = {\n \tN_(\"git for-each-ref [<options>] [<pattern>]\"),\n \tNULL\n@@ -1129,7 +56,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \n \tmemset(&ref_cbdata, 0, sizeof(ref_cbdata));\n \tref_cbdata.filter.name_patterns = argv;\n-\tfor_each_rawref(ref_filter_handler, &ref_cbdata);\n+\tfilter_refs(for_each_rawref, &ref_cbdata);\n \n \tref_array_sort(sorting, &ref_cbdata.array);\n \ndiff --git a/ref-filter.c b/ref-filter.c\nnew file mode 100644\nindex 0000000..ece16a1\n--- /dev/null\n+++ b/ref-filter.c\n@@ -0,0 +1,1077 @@\n+#include \"builtin.h\"\n+#include \"cache.h\"\n+#include \"parse-options.h\"\n+#include \"refs.h\"\n+#include \"wildmatch.h\"\n+#include \"commit.h\"\n+#include \"remote.h\"\n+#include \"color.h\"\n+#include \"tag.h\"\n+#include \"quote.h\"\n+#include \"ref-filter.h\"\n+\n+typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n+\n+static struct {\n+\tconst char *name;\n+\tcmp_type cmp_type;\n+} valid_atom[] = {\n+\t{ \"refname\" },\n+\t{ \"objecttype\" },\n+\t{ \"objectsize\", FIELD_ULONG },\n+\t{ \"objectname\" },\n+\t{ \"tree\" },\n+\t{ \"parent\" },\n+\t{ \"numparent\", FIELD_ULONG },\n+\t{ \"object\" },\n+\t{ \"type\" },\n+\t{ \"tag\" },\n+\t{ \"author\" },\n+\t{ \"authorname\" },\n+\t{ \"authoremail\" },\n+\t{ \"authordate\", FIELD_TIME },\n+\t{ \"committer\" },\n+\t{ \"committername\" },\n+\t{ \"committeremail\" },\n+\t{ \"committerdate\", FIELD_TIME },\n+\t{ \"tagger\" },\n+\t{ \"taggername\" },\n+\t{ \"taggeremail\" },\n+\t{ \"taggerdate\", FIELD_TIME },\n+\t{ \"creator\" },\n+\t{ \"creatordate\", FIELD_TIME },\n+\t{ \"subject\" },\n+\t{ \"body\" },\n+\t{ \"contents\" },\n+\t{ \"contents:subject\" },\n+\t{ \"contents:body\" },\n+\t{ \"contents:signature\" },\n+\t{ \"upstream\" },\n+\t{ \"push\" },\n+\t{ \"symref\" },\n+\t{ \"flag\" },\n+\t{ \"HEAD\" },\n+\t{ \"color\" },\n+};\n+\n+/*\n+ * An atom is a valid field atom listed above, possibly prefixed with\n+ * a \"*\" to denote deref_tag().\n+ *\n+ * We parse given format string and sort specifiers, and make a list\n+ * of properties that we need to extract out of objects.  ref_array_item\n+ * structure will hold an array of values extracted that can be\n+ * indexed with the \"atom number\", which is an index into this\n+ * array.\n+ */\n+static const char **used_atom;\n+static cmp_type *used_atom_type;\n+static int used_atom_cnt, need_tagged, need_symref;\n+static int need_color_reset_at_eol;\n+\n+/*\n+ * Used to parse format string and sort specifiers\n+ */\n+int parse_ref_filter_atom(const char *atom, const char *ep)\n+{\n+\tconst char *sp;\n+\tint i, at;\n+\n+\tsp = atom;\n+\tif (*sp == '*' && sp < ep)\n+\t\tsp++; /* deref */\n+\tif (ep <= sp)\n+\t\tdie(\"malformed field name: %.*s\", (int)(ep-atom), atom);\n+\n+\t/* Do we have the atom already used elsewhere? */\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tint len = strlen(used_atom[i]);\n+\t\tif (len == ep - atom && !memcmp(used_atom[i], atom, len))\n+\t\t\treturn i;\n+\t}\n+\n+\t/* Is the atom a valid one? */\n+\tfor (i = 0; i < ARRAY_SIZE(valid_atom); i++) {\n+\t\tint len = strlen(valid_atom[i].name);\n+\t\t/*\n+\t\t * If the atom name has a colon, strip it and everything after\n+\t\t * it off - it specifies the format for this entry, and\n+\t\t * shouldn't be used for checking against the valid_atom\n+\t\t * table.\n+\t\t */\n+\t\tconst char *formatp = strchr(sp, ':');\n+\t\tif (!formatp || ep < formatp)\n+\t\t\tformatp = ep;\n+\t\tif (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))\n+\t\t\tbreak;\n+\t}\n+\n+\tif (ARRAY_SIZE(valid_atom) <= i)\n+\t\tdie(\"unknown field name: %.*s\", (int)(ep-atom), atom);\n+\n+\t/* Add it in, including the deref prefix */\n+\tat = used_atom_cnt;\n+\tused_atom_cnt++;\n+\tREALLOC_ARRAY(used_atom, used_atom_cnt);\n+\tREALLOC_ARRAY(used_atom_type, used_atom_cnt);\n+\tused_atom[at] = xmemdupz(atom, ep - atom);\n+\tused_atom_type[at] = valid_atom[i].cmp_type;\n+\tif (*atom == '*')\n+\t\tneed_tagged = 1;\n+\tif (!strcmp(used_atom[at], \"symref\"))\n+\t\tneed_symref = 1;\n+\treturn at;\n+}\n+\n+/*\n+ * In a format string, find the next occurrence of %(atom).\n+ */\n+static const char *find_next(const char *cp)\n+{\n+\twhile (*cp) {\n+\t\tif (*cp == '%') {\n+\t\t\t/*\n+\t\t\t * %( is the start of an atom;\n+\t\t\t * %% is a quoted per-cent.\n+\t\t\t */\n+\t\t\tif (cp[1] == '(')\n+\t\t\t\treturn cp;\n+\t\t\telse if (cp[1] == '%')\n+\t\t\t\tcp++; /* skip over two % */\n+\t\t\t/* otherwise this is a singleton, literal % */\n+\t\t}\n+\t\tcp++;\n+\t}\n+\treturn NULL;\n+}\n+\n+/*\n+ * Make sure the format string is well formed, and parse out\n+ * the used atoms.\n+ */\n+int verify_ref_format(const char *format)\n+{\n+\tconst char *cp, *sp;\n+\n+\tneed_color_reset_at_eol = 0;\n+\tfor (cp = format; *cp && (sp = find_next(cp)); ) {\n+\t\tconst char *color, *ep = strchr(sp, ')');\n+\t\tint at;\n+\n+\t\tif (!ep)\n+\t\t\treturn error(\"malformed format string %s\", sp);\n+\t\t/* sp points at \"%(\" and ep points at the closing \")\" */\n+\t\tat = parse_ref_filter_atom(sp + 2, ep);\n+\t\tcp = ep + 1;\n+\n+\t\tif (skip_prefix(used_atom[at], \"color:\", &color))\n+\t\t\tneed_color_reset_at_eol = !!strcmp(color, \"reset\");\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * Given an object name, read the object data and size, and return a\n+ * \"struct object\".  If the object data we are returning is also borrowed\n+ * by the \"struct object\" representation, set *eaten as well---it is a\n+ * signal from parse_object_buffer to us not to free the buffer.\n+ */\n+static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)\n+{\n+\tenum object_type type;\n+\tvoid *buf = read_sha1_file(sha1, &type, sz);\n+\n+\tif (buf)\n+\t\t*obj = parse_object_buffer(sha1, type, *sz, buf, eaten);\n+\telse\n+\t\t*obj = NULL;\n+\treturn buf;\n+}\n+\n+static int grab_objectname(const char *name, const unsigned char *sha1,\n+\t\t\t    struct atom_value *v)\n+{\n+\tif (!strcmp(name, \"objectname\")) {\n+\t\tchar *s = xmalloc(41);\n+\t\tstrcpy(s, sha1_to_hex(sha1));\n+\t\tv->s = s;\n+\t\treturn 1;\n+\t}\n+\tif (!strcmp(name, \"objectname:short\")) {\n+\t\tv->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+/* See grab_values */\n+static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (!strcmp(name, \"objecttype\"))\n+\t\t\tv->s = typename(obj->type);\n+\t\telse if (!strcmp(name, \"objectsize\")) {\n+\t\t\tchar *s = xmalloc(40);\n+\t\t\tsprintf(s, \"%lu\", sz);\n+\t\t\tv->ul = sz;\n+\t\t\tv->s = s;\n+\t\t}\n+\t\telse if (deref)\n+\t\t\tgrab_objectname(name, obj->sha1, v);\n+\t}\n+}\n+\n+/* See grab_values */\n+static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tstruct tag *tag = (struct tag *) obj;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (!strcmp(name, \"tag\"))\n+\t\t\tv->s = tag->tag;\n+\t\telse if (!strcmp(name, \"type\") && tag->tagged)\n+\t\t\tv->s = typename(tag->tagged->type);\n+\t\telse if (!strcmp(name, \"object\") && tag->tagged) {\n+\t\t\tchar *s = xmalloc(41);\n+\t\t\tstrcpy(s, sha1_to_hex(tag->tagged->sha1));\n+\t\t\tv->s = s;\n+\t\t}\n+\t}\n+}\n+\n+/* See grab_values */\n+static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tstruct commit *commit = (struct commit *) obj;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (!strcmp(name, \"tree\")) {\n+\t\t\tchar *s = xmalloc(41);\n+\t\t\tstrcpy(s, sha1_to_hex(commit->tree->object.sha1));\n+\t\t\tv->s = s;\n+\t\t}\n+\t\tif (!strcmp(name, \"numparent\")) {\n+\t\t\tchar *s = xmalloc(40);\n+\t\t\tv->ul = commit_list_count(commit->parents);\n+\t\t\tsprintf(s, \"%lu\", v->ul);\n+\t\t\tv->s = s;\n+\t\t}\n+\t\telse if (!strcmp(name, \"parent\")) {\n+\t\t\tint num = commit_list_count(commit->parents);\n+\t\t\tint i;\n+\t\t\tstruct commit_list *parents;\n+\t\t\tchar *s = xmalloc(41 * num + 1);\n+\t\t\tv->s = s;\n+\t\t\tfor (i = 0, parents = commit->parents;\n+\t\t\t     parents;\n+\t\t\t     parents = parents->next, i = i + 41) {\n+\t\t\t\tstruct commit *parent = parents->item;\n+\t\t\t\tstrcpy(s+i, sha1_to_hex(parent->object.sha1));\n+\t\t\t\tif (parents->next)\n+\t\t\t\t\ts[i+40] = ' ';\n+\t\t\t}\n+\t\t\tif (!i)\n+\t\t\t\t*s = '\\0';\n+\t\t}\n+\t}\n+}\n+\n+static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)\n+{\n+\tconst char *eol;\n+\twhile (*buf) {\n+\t\tif (!strncmp(buf, who, wholen) &&\n+\t\t    buf[wholen] == ' ')\n+\t\t\treturn buf + wholen + 1;\n+\t\teol = strchr(buf, '\\n');\n+\t\tif (!eol)\n+\t\t\treturn \"\";\n+\t\teol++;\n+\t\tif (*eol == '\\n')\n+\t\t\treturn \"\"; /* end of header */\n+\t\tbuf = eol;\n+\t}\n+\treturn \"\";\n+}\n+\n+static const char *copy_line(const char *buf)\n+{\n+\tconst char *eol = strchrnul(buf, '\\n');\n+\treturn xmemdupz(buf, eol - buf);\n+}\n+\n+static const char *copy_name(const char *buf)\n+{\n+\tconst char *cp;\n+\tfor (cp = buf; *cp && *cp != '\\n'; cp++) {\n+\t\tif (!strncmp(cp, \" <\", 2))\n+\t\t\treturn xmemdupz(buf, cp - buf);\n+\t}\n+\treturn \"\";\n+}\n+\n+static const char *copy_email(const char *buf)\n+{\n+\tconst char *email = strchr(buf, '<');\n+\tconst char *eoemail;\n+\tif (!email)\n+\t\treturn \"\";\n+\teoemail = strchr(email, '>');\n+\tif (!eoemail)\n+\t\treturn \"\";\n+\treturn xmemdupz(email, eoemail + 1 - email);\n+}\n+\n+static char *copy_subject(const char *buf, unsigned long len)\n+{\n+\tchar *r = xmemdupz(buf, len);\n+\tint i;\n+\n+\tfor (i = 0; i < len; i++)\n+\t\tif (r[i] == '\\n')\n+\t\t\tr[i] = ' ';\n+\n+\treturn r;\n+}\n+\n+static void grab_date(const char *buf, struct atom_value *v, const char *atomname)\n+{\n+\tconst char *eoemail = strstr(buf, \"> \");\n+\tchar *zone;\n+\tunsigned long timestamp;\n+\tlong tz;\n+\tenum date_mode date_mode = DATE_NORMAL;\n+\tconst char *formatp;\n+\n+\t/*\n+\t * We got here because atomname ends in \"date\" or \"date<something>\";\n+\t * it's not possible that <something> is not \":<format>\" because\n+\t * parse_ref_filter_atom() wouldn't have allowed it, so we can assume that no\n+\t * \":\" means no format is specified, and use the default.\n+\t */\n+\tformatp = strchr(atomname, ':');\n+\tif (formatp != NULL) {\n+\t\tformatp++;\n+\t\tdate_mode = parse_date_format(formatp);\n+\t}\n+\n+\tif (!eoemail)\n+\t\tgoto bad;\n+\ttimestamp = strtoul(eoemail + 2, &zone, 10);\n+\tif (timestamp == ULONG_MAX)\n+\t\tgoto bad;\n+\ttz = strtol(zone, NULL, 10);\n+\tif ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)\n+\t\tgoto bad;\n+\tv->s = xstrdup(show_date(timestamp, tz, date_mode));\n+\tv->ul = timestamp;\n+\treturn;\n+ bad:\n+\tv->s = \"\";\n+\tv->ul = 0;\n+}\n+\n+/* See grab_values */\n+static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tint wholen = strlen(who);\n+\tconst char *wholine = NULL;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (strncmp(who, name, wholen))\n+\t\t\tcontinue;\n+\t\tif (name[wholen] != 0 &&\n+\t\t    strcmp(name + wholen, \"name\") &&\n+\t\t    strcmp(name + wholen, \"email\") &&\n+\t\t    !starts_with(name + wholen, \"date\"))\n+\t\t\tcontinue;\n+\t\tif (!wholine)\n+\t\t\twholine = find_wholine(who, wholen, buf, sz);\n+\t\tif (!wholine)\n+\t\t\treturn; /* no point looking for it */\n+\t\tif (name[wholen] == 0)\n+\t\t\tv->s = copy_line(wholine);\n+\t\telse if (!strcmp(name + wholen, \"name\"))\n+\t\t\tv->s = copy_name(wholine);\n+\t\telse if (!strcmp(name + wholen, \"email\"))\n+\t\t\tv->s = copy_email(wholine);\n+\t\telse if (starts_with(name + wholen, \"date\"))\n+\t\t\tgrab_date(wholine, v, name);\n+\t}\n+\n+\t/*\n+\t * For a tag or a commit object, if \"creator\" or \"creatordate\" is\n+\t * requested, do something special.\n+\t */\n+\tif (strcmp(who, \"tagger\") && strcmp(who, \"committer\"))\n+\t\treturn; /* \"author\" for commit object is not wanted */\n+\tif (!wholine)\n+\t\twholine = find_wholine(who, wholen, buf, sz);\n+\tif (!wholine)\n+\t\treturn;\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\n+\t\tif (starts_with(name, \"creatordate\"))\n+\t\t\tgrab_date(wholine, v, name);\n+\t\telse if (!strcmp(name, \"creator\"))\n+\t\t\tv->s = copy_line(wholine);\n+\t}\n+}\n+\n+static void find_subpos(const char *buf, unsigned long sz,\n+\t\t\tconst char **sub, unsigned long *sublen,\n+\t\t\tconst char **body, unsigned long *bodylen,\n+\t\t\tunsigned long *nonsiglen,\n+\t\t\tconst char **sig, unsigned long *siglen)\n+{\n+\tconst char *eol;\n+\t/* skip past header until we hit empty line */\n+\twhile (*buf && *buf != '\\n') {\n+\t\teol = strchrnul(buf, '\\n');\n+\t\tif (*eol)\n+\t\t\teol++;\n+\t\tbuf = eol;\n+\t}\n+\t/* skip any empty lines */\n+\twhile (*buf == '\\n')\n+\t\tbuf++;\n+\n+\t/* parse signature first; we might not even have a subject line */\n+\t*sig = buf + parse_signature(buf, strlen(buf));\n+\t*siglen = strlen(*sig);\n+\n+\t/* subject is first non-empty line */\n+\t*sub = buf;\n+\t/* subject goes to first empty line */\n+\twhile (buf < *sig && *buf && *buf != '\\n') {\n+\t\teol = strchrnul(buf, '\\n');\n+\t\tif (*eol)\n+\t\t\teol++;\n+\t\tbuf = eol;\n+\t}\n+\t*sublen = buf - *sub;\n+\t/* drop trailing newline, if present */\n+\tif (*sublen && (*sub)[*sublen - 1] == '\\n')\n+\t\t*sublen -= 1;\n+\n+\t/* skip any empty lines */\n+\twhile (*buf == '\\n')\n+\t\tbuf++;\n+\t*body = buf;\n+\t*bodylen = strlen(buf);\n+\t*nonsiglen = *sig - buf;\n+}\n+\n+/* See grab_values */\n+static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tconst char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;\n+\tunsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (strcmp(name, \"subject\") &&\n+\t\t    strcmp(name, \"body\") &&\n+\t\t    strcmp(name, \"contents\") &&\n+\t\t    strcmp(name, \"contents:subject\") &&\n+\t\t    strcmp(name, \"contents:body\") &&\n+\t\t    strcmp(name, \"contents:signature\"))\n+\t\t\tcontinue;\n+\t\tif (!subpos)\n+\t\t\tfind_subpos(buf, sz,\n+\t\t\t\t    &subpos, &sublen,\n+\t\t\t\t    &bodypos, &bodylen, &nonsiglen,\n+\t\t\t\t    &sigpos, &siglen);\n+\n+\t\tif (!strcmp(name, \"subject\"))\n+\t\t\tv->s = copy_subject(subpos, sublen);\n+\t\telse if (!strcmp(name, \"contents:subject\"))\n+\t\t\tv->s = copy_subject(subpos, sublen);\n+\t\telse if (!strcmp(name, \"body\"))\n+\t\t\tv->s = xmemdupz(bodypos, bodylen);\n+\t\telse if (!strcmp(name, \"contents:body\"))\n+\t\t\tv->s = xmemdupz(bodypos, nonsiglen);\n+\t\telse if (!strcmp(name, \"contents:signature\"))\n+\t\t\tv->s = xmemdupz(sigpos, siglen);\n+\t\telse if (!strcmp(name, \"contents\"))\n+\t\t\tv->s = xstrdup(subpos);\n+\t}\n+}\n+\n+/*\n+ * We want to have empty print-string for field requests\n+ * that do not apply (e.g. \"authordate\" for a tag object)\n+ */\n+static void fill_missing_values(struct atom_value *val)\n+{\n+\tint i;\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (v->s == NULL)\n+\t\t\tv->s = \"\";\n+\t}\n+}\n+\n+/*\n+ * val is a list of atom_value to hold returned values.  Extract\n+ * the values for atoms in used_atom array out of (obj, buf, sz).\n+ * when deref is false, (obj, buf, sz) is the object that is\n+ * pointed at by the ref itself; otherwise it is the object the\n+ * ref (which is a tag) refers to.\n+ */\n+static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tgrab_common_values(val, deref, obj, buf, sz);\n+\tswitch (obj->type) {\n+\tcase OBJ_TAG:\n+\t\tgrab_tag_values(val, deref, obj, buf, sz);\n+\t\tgrab_sub_body_contents(val, deref, obj, buf, sz);\n+\t\tgrab_person(\"tagger\", val, deref, obj, buf, sz);\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\tgrab_commit_values(val, deref, obj, buf, sz);\n+\t\tgrab_sub_body_contents(val, deref, obj, buf, sz);\n+\t\tgrab_person(\"author\", val, deref, obj, buf, sz);\n+\t\tgrab_person(\"committer\", val, deref, obj, buf, sz);\n+\t\tbreak;\n+\tcase OBJ_TREE:\n+\t\t/* grab_tree_values(val, deref, obj, buf, sz); */\n+\t\tbreak;\n+\tcase OBJ_BLOB:\n+\t\t/* grab_blob_values(val, deref, obj, buf, sz); */\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"Eh?  Object of type %d?\", obj->type);\n+\t}\n+}\n+\n+static inline char *copy_advance(char *dst, const char *src)\n+{\n+\twhile (*src)\n+\t\t*dst++ = *src++;\n+\treturn dst;\n+}\n+\n+/*\n+ * Parse the object referred by ref, and grab needed value.\n+ */\n+static void populate_value(struct ref_array_item *ref)\n+{\n+\tvoid *buf;\n+\tstruct object *obj;\n+\tint eaten, i;\n+\tunsigned long size;\n+\tconst unsigned char *tagged;\n+\n+\tref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));\n+\n+\tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n+\t\tunsigned char unused1[20];\n+\t\tref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,\n+\t\t\t\t\t     unused1, NULL);\n+\t\tif (!ref->symref)\n+\t\t\tref->symref = \"\";\n+\t}\n+\n+\t/* Fill in specials first */\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &ref->value[i];\n+\t\tint deref = 0;\n+\t\tconst char *refname;\n+\t\tconst char *formatp;\n+\t\tstruct branch *branch = NULL;\n+\n+\t\tif (*name == '*') {\n+\t\t\tderef = 1;\n+\t\t\tname++;\n+\t\t}\n+\n+\t\tif (starts_with(name, \"refname\"))\n+\t\t\trefname = ref->refname;\n+\t\telse if (starts_with(name, \"symref\"))\n+\t\t\trefname = ref->symref ? ref->symref : \"\";\n+\t\telse if (starts_with(name, \"upstream\")) {\n+\t\t\tconst char *branch_name;\n+\t\t\t/* only local branches may have an upstream */\n+\t\t\tif (!skip_prefix(ref->refname, \"refs/heads/\",\n+\t\t\t\t\t &branch_name))\n+\t\t\t\tcontinue;\n+\t\t\tbranch = branch_get(branch_name);\n+\n+\t\t\trefname = branch_get_upstream(branch, NULL);\n+\t\t\tif (!refname)\n+\t\t\t\tcontinue;\n+\t\t} else if (starts_with(name, \"push\")) {\n+\t\t\tconst char *branch_name;\n+\t\t\tif (!skip_prefix(ref->refname, \"refs/heads/\",\n+\t\t\t\t\t &branch_name))\n+\t\t\t\tcontinue;\n+\t\t\tbranch = branch_get(branch_name);\n+\n+\t\t\trefname = branch_get_push(branch, NULL);\n+\t\t\tif (!refname)\n+\t\t\t\tcontinue;\n+\t\t} else if (starts_with(name, \"color:\")) {\n+\t\t\tchar color[COLOR_MAXLEN] = \"\";\n+\n+\t\t\tif (color_parse(name + 6, color) < 0)\n+\t\t\t\tdie(_(\"unable to parse format\"));\n+\t\t\tv->s = xstrdup(color);\n+\t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"flag\")) {\n+\t\t\tchar buf[256], *cp = buf;\n+\t\t\tif (ref->flag & REF_ISSYMREF)\n+\t\t\t\tcp = copy_advance(cp, \",symref\");\n+\t\t\tif (ref->flag & REF_ISPACKED)\n+\t\t\t\tcp = copy_advance(cp, \",packed\");\n+\t\t\tif (cp == buf)\n+\t\t\t\tv->s = \"\";\n+\t\t\telse {\n+\t\t\t\t*cp = '\\0';\n+\t\t\t\tv->s = xstrdup(buf + 1);\n+\t\t\t}\n+\t\t\tcontinue;\n+\t\t} else if (!deref && grab_objectname(name, ref->objectname, v)) {\n+\t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"HEAD\")) {\n+\t\t\tconst char *head;\n+\t\t\tunsigned char sha1[20];\n+\n+\t\t\thead = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING,\n+\t\t\t\t\t\t  sha1, NULL);\n+\t\t\tif (!strcmp(ref->refname, head))\n+\t\t\t\tv->s = \"*\";\n+\t\t\telse\n+\t\t\t\tv->s = \" \";\n+\t\t\tcontinue;\n+\t\t} else\n+\t\t\tcontinue;\n+\n+\t\tformatp = strchr(name, ':');\n+\t\tif (formatp) {\n+\t\t\tint num_ours, num_theirs;\n+\n+\t\t\tformatp++;\n+\t\t\tif (!strcmp(formatp, \"short\"))\n+\t\t\t\trefname = shorten_unambiguous_ref(refname,\n+\t\t\t\t\t\t      warn_ambiguous_refs);\n+\t\t\telse if (!strcmp(formatp, \"track\") &&\n+\t\t\t\t (starts_with(name, \"upstream\") ||\n+\t\t\t\t  starts_with(name, \"push\"))) {\n+\t\t\t\tchar buf[40];\n+\n+\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n+\t\t\t\t\t\t       &num_theirs, NULL))\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tif (!num_ours && !num_theirs)\n+\t\t\t\t\tv->s = \"\";\n+\t\t\t\telse if (!num_ours) {\n+\t\t\t\t\tsprintf(buf, \"[behind %d]\", num_theirs);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t} else if (!num_theirs) {\n+\t\t\t\t\tsprintf(buf, \"[ahead %d]\", num_ours);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t} else {\n+\t\t\t\t\tsprintf(buf, \"[ahead %d, behind %d]\",\n+\t\t\t\t\t\tnum_ours, num_theirs);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t}\n+\t\t\t\tcontinue;\n+\t\t\t} else if (!strcmp(formatp, \"trackshort\") &&\n+\t\t\t\t   (starts_with(name, \"upstream\") ||\n+\t\t\t\t    starts_with(name, \"push\"))) {\n+\t\t\t\tassert(branch);\n+\n+\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n+\t\t\t\t\t\t\t&num_theirs, NULL))\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tif (!num_ours && !num_theirs)\n+\t\t\t\t\tv->s = \"=\";\n+\t\t\t\telse if (!num_ours)\n+\t\t\t\t\tv->s = \"<\";\n+\t\t\t\telse if (!num_theirs)\n+\t\t\t\t\tv->s = \">\";\n+\t\t\t\telse\n+\t\t\t\t\tv->s = \"<>\";\n+\t\t\t\tcontinue;\n+\t\t\t} else\n+\t\t\t\tdie(\"unknown %.*s format %s\",\n+\t\t\t\t    (int)(formatp - name), name, formatp);\n+\t\t}\n+\n+\t\tif (!deref)\n+\t\t\tv->s = refname;\n+\t\telse {\n+\t\t\tint len = strlen(refname);\n+\t\t\tchar *s = xmalloc(len + 4);\n+\t\t\tsprintf(s, \"%s^{}\", refname);\n+\t\t\tv->s = s;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tstruct atom_value *v = &ref->value[i];\n+\t\tif (v->s == NULL)\n+\t\t\tgoto need_obj;\n+\t}\n+\treturn;\n+\n+ need_obj:\n+\tbuf = get_obj(ref->objectname, &obj, &size, &eaten);\n+\tif (!buf)\n+\t\tdie(\"missing object %s for %s\",\n+\t\t    sha1_to_hex(ref->objectname), ref->refname);\n+\tif (!obj)\n+\t\tdie(\"parse_object_buffer failed on %s for %s\",\n+\t\t    sha1_to_hex(ref->objectname), ref->refname);\n+\n+\tgrab_values(ref->value, 0, obj, buf, size);\n+\tif (!eaten)\n+\t\tfree(buf);\n+\n+\t/*\n+\t * If there is no atom that wants to know about tagged\n+\t * object, we are done.\n+\t */\n+\tif (!need_tagged || (obj->type != OBJ_TAG))\n+\t\treturn;\n+\n+\t/*\n+\t * If it is a tag object, see if we use a value that derefs\n+\t * the object, and if we do grab the object it refers to.\n+\t */\n+\ttagged = ((struct tag *)obj)->tagged->sha1;\n+\n+\t/*\n+\t * NEEDSWORK: This derefs tag only once, which\n+\t * is good to deal with chains of trust, but\n+\t * is not consistent with what deref_tag() does\n+\t * which peels the onion to the core.\n+\t */\n+\tbuf = get_obj(tagged, &obj, &size, &eaten);\n+\tif (!buf)\n+\t\tdie(\"missing object %s for %s\",\n+\t\t    sha1_to_hex(tagged), ref->refname);\n+\tif (!obj)\n+\t\tdie(\"parse_object_buffer failed on %s for %s\",\n+\t\t    sha1_to_hex(tagged), ref->refname);\n+\tgrab_values(ref->value, 1, obj, buf, size);\n+\tif (!eaten)\n+\t\tfree(buf);\n+}\n+\n+/*\n+ * Given a ref, return the value for the atom.  This lazily gets value\n+ * out of the object by calling populate value.\n+ */\n+static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom_value **v)\n+{\n+\tif (!ref->value) {\n+\t\tpopulate_value(ref);\n+\t\tfill_missing_values(ref->value);\n+\t}\n+\t*v = &ref->value[atom];\n+}\n+\n+/*\n+ * Given a refname, return 1 if the refname matches with one of the patterns\n+ * while the pattern is a pathname like 'refs/tags' or 'refs/heads/master'\n+ * and so on, else return 0. Supports use of wild characters.\n+ */\n+static int match_name_as_path(const char **pattern, const char *refname)\n+{\n+\tint namelen = strlen(refname);\n+\tfor (; *pattern; pattern++) {\n+\t\tconst char *p = *pattern;\n+\t\tint plen = strlen(p);\n+\n+\t\tif ((plen <= namelen) &&\n+\t\t    !strncmp(refname, p, plen) &&\n+\t\t    (refname[plen] == '\\0' ||\n+\t\t     refname[plen] == '/' ||\n+\t\t     p[plen-1] == '/'))\n+\t\t\treturn 1;\n+\t\tif (!wildmatch(p, refname, WM_PATHNAME, NULL))\n+\t\t\treturn 1;\n+\t}\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+\t\t\t\t\t\t int flag)\n+{\n+\tstruct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item));\n+\tref->refname = xstrdup(refname);\n+\thashcpy(ref->objectname, objectname);\n+\tref->flag = flag;\n+\n+\treturn ref;\n+}\n+\n+/*\n+ * A call-back given to for_each_ref().  Filter refs and keep them for\n+ * later object processing.\n+ */\n+int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+{\n+\tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n+\tstruct ref_filter *filter = &ref_cbdata->filter;\n+\tstruct ref_array_item *ref;\n+\n+\tif (flag & REF_BAD_NAME) {\n+\t\t  warning(\"ignoring ref with broken name %s\", refname);\n+\t\t  return 0;\n+\t}\n+\n+\tif (*filter->name_patterns && !match_name_as_path(filter->name_patterns, 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\n+\t * by maxcount logic.\n+\t */\n+\tref = new_ref_array_item(refname, oid->hash, flag);\n+\n+\tREALLOC_ARRAY(ref_cbdata->array.items, ref_cbdata->array.nr + 1);\n+\tref_cbdata->array.items[ref_cbdata->array.nr++] = ref;\n+\treturn 0;\n+}\n+\n+/*  Free memory allocated for a ref_array_item */\n+static void free_array_item(struct ref_array_item *item)\n+{\n+\tfree(item->symref);\n+\tfree(item->refname);\n+\tfree(item);\n+}\n+\n+/* Free all memory allocated for ref_array */\n+void ref_array_clear(struct ref_array *array)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < array->nr; i++)\n+\t\tfree_array_item(array->items[i]);\n+\tfree(array->items);\n+\tarray->items = NULL;\n+\tarray->nr = array->alloc = 0;\n+}\n+\n+static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)\n+{\n+\tstruct atom_value *va, *vb;\n+\tint cmp;\n+\tcmp_type cmp_type = used_atom_type[s->atom];\n+\n+\tget_ref_atom_value(a, s->atom, &va);\n+\tget_ref_atom_value(b, s->atom, &vb);\n+\tswitch (cmp_type) {\n+\tcase FIELD_STR:\n+\t\tcmp = strcmp(va->s, vb->s);\n+\t\tbreak;\n+\tdefault:\n+\t\tif (va->ul < vb->ul)\n+\t\t\tcmp = -1;\n+\t\telse if (va->ul == vb->ul)\n+\t\t\tcmp = 0;\n+\t\telse\n+\t\t\tcmp = 1;\n+\t\tbreak;\n+\t}\n+\treturn (s->reverse) ? -cmp : cmp;\n+}\n+\n+static struct ref_sorting *ref_sorting;\n+static int compare_refs(const void *a_, const void *b_)\n+{\n+\tstruct ref_array_item *a = *((struct ref_array_item **)a_);\n+\tstruct ref_array_item *b = *((struct ref_array_item **)b_);\n+\tstruct ref_sorting *s;\n+\n+\tfor (s = ref_sorting; s; s = s->next) {\n+\t\tint cmp = cmp_ref_sorting(s, a, b);\n+\t\tif (cmp)\n+\t\t\treturn cmp;\n+\t}\n+\treturn 0;\n+}\n+\n+void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n+{\n+\tref_sorting = sorting;\n+\tqsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);\n+}\n+\n+static void print_value(struct atom_value *v, int quote_style)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tswitch (quote_style) {\n+\tcase QUOTE_NONE:\n+\t\tfputs(v->s, stdout);\n+\t\tbreak;\n+\tcase QUOTE_SHELL:\n+\t\tsq_quote_buf(&sb, v->s);\n+\t\tbreak;\n+\tcase QUOTE_PERL:\n+\t\tperl_quote_buf(&sb, v->s);\n+\t\tbreak;\n+\tcase QUOTE_PYTHON:\n+\t\tpython_quote_buf(&sb, v->s);\n+\t\tbreak;\n+\tcase QUOTE_TCL:\n+\t\ttcl_quote_buf(&sb, v->s);\n+\t\tbreak;\n+\t}\n+\tif (quote_style != QUOTE_NONE) {\n+\t\tfputs(sb.buf, stdout);\n+\t\tstrbuf_release(&sb);\n+\t}\n+}\n+\n+static int hex1(char ch)\n+{\n+\tif ('0' <= ch && ch <= '9')\n+\t\treturn ch - '0';\n+\telse if ('a' <= ch && ch <= 'f')\n+\t\treturn ch - 'a' + 10;\n+\telse if ('A' <= ch && ch <= 'F')\n+\t\treturn ch - 'A' + 10;\n+\treturn -1;\n+}\n+static int hex2(const char *cp)\n+{\n+\tif (cp[0] && cp[1])\n+\t\treturn (hex1(cp[0]) << 4) | hex1(cp[1]);\n+\telse\n+\t\treturn -1;\n+}\n+\n+static void emit(const char *cp, const char *ep)\n+{\n+\twhile (*cp && (!ep || cp < ep)) {\n+\t\tif (*cp == '%') {\n+\t\t\tif (cp[1] == '%')\n+\t\t\t\tcp++;\n+\t\t\telse {\n+\t\t\t\tint ch = hex2(cp + 1);\n+\t\t\t\tif (0 <= ch) {\n+\t\t\t\t\tputchar(ch);\n+\t\t\t\t\tcp += 3;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tputchar(*cp);\n+\t\tcp++;\n+\t}\n+}\n+\n+void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n+{\n+\tconst char *cp, *sp, *ep;\n+\n+\tfor (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n+\t\tstruct atom_value *atomv;\n+\n+\t\tep = strchr(sp, ')');\n+\t\tif (cp < sp)\n+\t\t\temit(cp, sp);\n+\t\tget_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n+\t\tprint_value(atomv, quote_style);\n+\t}\n+\tif (*cp) {\n+\t\tsp = cp + strlen(cp);\n+\t\temit(cp, sp);\n+\t}\n+\tif (need_color_reset_at_eol) {\n+\t\tstruct atom_value resetv;\n+\t\tchar color[COLOR_MAXLEN] = \"\";\n+\n+\t\tif (color_parse(\"reset\", color) < 0)\n+\t\t\tdie(\"BUG: couldn't parse 'reset' as a color\");\n+\t\tresetv.s = color;\n+\t\tprint_value(&resetv, quote_style);\n+\t}\n+\tputchar('\\n');\n+}\n+\n+/*  If no sorting option is given, use refname to sort as default */\n+struct ref_sorting *ref_default_sorting(void)\n+{\n+\tstatic const char cstr_name[] = \"refname\";\n+\n+\tstruct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));\n+\n+\tsorting->next = NULL;\n+\tsorting->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name));\n+\treturn sorting;\n+}\n+\n+int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct ref_sorting **sorting_tail = opt->value;\n+\tstruct ref_sorting *s;\n+\tint len;\n+\n+\tif (!arg) /* should --no-sort void the list ? */\n+\t\treturn -1;\n+\n+\ts = xcalloc(1, sizeof(*s));\n+\ts->next = *sorting_tail;\n+\t*sorting_tail = s;\n+\n+\tif (*arg == '-') {\n+\t\ts->reverse = 1;\n+\t\targ++;\n+\t}\n+\tlen = strlen(arg);\n+\ts->atom = parse_ref_filter_atom(arg, arg+len);\n+\treturn 0;\n+}\n-- \n2.4.2\n"},{"id":"263145","messageId":"1433673373-16441-1-git-send-email-karthik.188@gmail.com","threadId":"39543","inReplyTo":"1433598496-31287-10-git-send-email-karthik.188@gmail.com","subject":"[PATCH v6 10/11] for-each-ref: introduce filter_refs()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-07T10:36:13Z","receivedAt":"2015-06-07T10:36:13Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Introduce filter_refs() which will act as an API for users to\ncall and provide a function which will iterate over a set of refs\nwhile filtering out the required refs.\n\nCurrently this will wrap around ref_filter_handler(). Hence,\nref_filter_handler is made file scope static.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\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 | 12 +++++++++++-\n ref-filter.h |  8 ++++++--\n 2 files changed, 17 insertions(+), 3 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex ece16a1..886c3d7 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -858,7 +858,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname,\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n-int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+static int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_filter *filter = &ref_cbdata->filter;\n@@ -904,6 +904,16 @@ void ref_array_clear(struct ref_array *array)\n \tarray->nr = array->alloc = 0;\n }\n \n+/*\n+ * API for filtering a set of refs. The refs are provided and iterated\n+ * over using the for_each_ref_fn(). The refs are stored into and filtered\n+ * based on the ref_filter_cbdata structure.\n+ */\n+int filter_refs(int (for_each_ref_fn)(each_ref_fn, void *), struct ref_filter_cbdata *data)\n+{\n+\treturn for_each_ref_fn(ref_filter_handler, data);\n+}\n+\n static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)\n {\n \tstruct atom_value *va, *vb;\ndiff --git a/ref-filter.h b/ref-filter.h\nindex c2c5d37..15e6766 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -46,8 +46,12 @@ struct ref_filter_cbdata {\n \tstruct ref_filter filter;\n };\n \n-/*  Callback function for for_each_*ref(). This filters the refs based on the filters set */\n-int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data);\n+/*\n+ * API for filtering a set of refs. The refs are provided and iterated\n+ * over using the for_each_ref_fn(). The refs are stored into and filtered\n+ * based on the ref_filter_cbdata structure.\n+ */\n+int filter_refs(int (for_each_ref_fn)(each_ref_fn, void *), struct ref_filter_cbdata *data);\n /*  Clear all memory allocated to ref_array */\n void ref_array_clear(struct ref_array *array);\n /*  Parse format string and sort specifiers */\n-- \n2.4.2\n"},{"id":"263147","messageId":"5574331D.3020705@gmail.com","threadId":"39543","inReplyTo":"5572F904.1080608@gmail.com","subject":"Re: [PATCH v6 0/11] create ref-filter from for-each-ref","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-07T12:03:41Z","receivedAt":"2015-06-07T12:03:41Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On 06/06/2015 07:13 PM, Karthik Nayak wrote:\n> Version four of this patch can be found here :\n> http://www.mail-archive.com/git@vger.kernel.org/msg70280.html\n>\n> v5 : http://www.mail-archive.com/git@vger.kernel.org/msg70888.html\n>\n> Changes in v5:\n> *    Rename functions to better suit the code.\n> *    implement filter_refs()\n> *    use FLEX_ARRAY for refname\n> *    other small changes\n>\n> Changes in v6:\n> *    based on latest master branch (github.com/git/git)\n> *    rename variables named sort to sorting.\n\nPatch 09/11 and 10/11 had a some code swapped, weird. Have mailed\nnew patches.\n\n-- \nRegards,\nKarthik\n"},{"id":"263260","messageId":"vpq4mmiw2jy.fsf@anie.imag.fr","threadId":"39543","inReplyTo":"1433673294-16243-1-git-send-email-karthik.188@gmail.com","subject":"Re: [PATCH v6 09/11] ref-filter: move code from 'for-each-ref'","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-06-08T17:08:33Z","receivedAt":"2015-06-08T17:08:33Z","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> --- a/builtin/for-each-ref.c\n> +++ b/builtin/for-each-ref.c\n> @@ -1129,7 +56,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n>  \n>  \tmemset(&ref_cbdata, 0, sizeof(ref_cbdata));\n>  \tref_cbdata.filter.name_patterns = argv;\n> -\tfor_each_rawref(ref_filter_handler, &ref_cbdata);\n> +\tfilter_refs(for_each_rawref, &ref_cbdata);\n\nThis seems unrelated from the rest of the patch. And you haven't\nintroduced filter_refs yet!\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"263262","messageId":"5575CD35.4030705@gmail.com","threadId":"39543","inReplyTo":"vpq4mmiw2jy.fsf@anie.imag.fr","subject":"Re: [PATCH v6 09/11] ref-filter: move code from 'for-each-ref'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-08T17:13:25Z","receivedAt":"2015-06-08T17:13:25Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On 06/08/2015 10:38 PM, Matthieu Moy wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n> > --- a/builtin/for-each-ref.c\n> > +++ b/builtin/for-each-ref.c\n> > @@ -1129,7 +56,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n> >\n> >       memset(&ref_cbdata, 0, sizeof(ref_cbdata));\n> >       ref_cbdata.filter.name_patterns = argv;\n> > -    for_each_rawref(ref_filter_handler, &ref_cbdata);\n> > +    filter_refs(for_each_rawref, &ref_cbdata);\n>\n> This seems unrelated from the rest of the patch. And you haven't\n> introduced filter_refs yet!\n>\nThat definitely is, this happened after fixing merges I suppose.\nIgnore it for now, I'll fix it.\n\n-- \nRegards,\nKarthik\n"},{"id":"263264","messageId":"vpqoakqunnn.fsf@anie.imag.fr","threadId":"39543","inReplyTo":"5572F904.1080608@gmail.com","subject":"Re: [PATCH v6 0/11] create ref-filter from for-each-ref","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-06-08T17:15:40Z","receivedAt":"2015-06-08T17:15:40Z","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> Version four of this patch can be found here :\n> http://www.mail-archive.com/git@vger.kernel.org/msg70280.html\n>\n> v5 : http://www.mail-archive.com/git@vger.kernel.org/msg70888.html\n>\n> Changes in v5:\n> *    Rename functions to better suit the code.\n> *    implement filter_refs()\n> *    use FLEX_ARRAY for refname\n> *    other small changes\n>\n> Changes in v6:\n> *\tbased on latest master branch (github.com/git/git)\n> *\trename variables named sort to sorting.\n\nOther than the hunk in PATCH 9 that belongs to PATCH 10, and the\nmisleading commit message in PATCH 2, the series looks good to me.\n\n(BTW, an easy way to check that each patch is compilable is to run\n\"git rebase -i --exec make\". It would have noticed the issue with\nfilter_refs() automatically)\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"263265","messageId":"5575CE73.5010605@gmail.com","threadId":"39543","inReplyTo":"vpqoakqunnn.fsf@anie.imag.fr","subject":"Re: [PATCH v6 0/11] create ref-filter from for-each-ref","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-08T17:18:43Z","receivedAt":"2015-06-08T17:18:43Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On 06/08/2015 10:45 PM, Matthieu Moy wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n> > Version four of this patch can be found here :\n> > http://www.mail-archive.com/git@vger.kernel.org/msg70280.html\n> >\n> > v5 : http://www.mail-archive.com/git@vger.kernel.org/msg70888.html\n> >\n> > Changes in v5:\n> > *    Rename functions to better suit the code.\n> > *    implement filter_refs()\n> > *    use FLEX_ARRAY for refname\n> > *    other small changes\n> >\n> > Changes in v6:\n> > *    based on latest master branch (github.com/git/git)\n> > *    rename variables named sort to sorting.\n>\n> Other than the hunk in PATCH 9 that belongs to PATCH 10, and the\n> misleading commit message in PATCH 2, the series looks good to me.\n>\n> (BTW, an easy way to check that each patch is compilable is to run\n> \"git rebase -i --exec make\". It would have noticed the issue with\n> filter_refs() automatically)\n>\nThats brilliant, just what I needed! Thanks a lot :D\n\n-- \nRegards,\nKarthik\n"},{"id":"263276","messageId":"xmqqlhfuaxjw.fsf@gitster.dls.corp.google.com","threadId":"39543","inReplyTo":"1433598496-31287-1-git-send-email-karthik.188@gmail.com","subject":"Re: [PATCH v6 01/11] for-each-ref: extract helper functions out of grab_single_ref()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-08T18:02:11Z","receivedAt":"2015-06-08T18:02:11Z","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> + * Given a refname, return 1 if the refname matches with one of the patterns\n\nYou can \"match refname with pattern\".  But \"refname matches the\npattern\" without \"with\", I think.\n\nI am not a native, though.\n\n> + * while the pattern is a pathname like 'refs/tags' or 'refs/heads/master'\n> + * and so on, else return 0. Supports use of wild characters.\n\ns/wild/wildcard/.\n\n\tReturn 1 if the refname matches with one of the patterns,\n\totherwise 0.  The patterns can be literal prefix (e.g. a\n\trefname \"refs/heads/master\" matches a pattern \"refs/heads/\")\n\tor a wildcard (e.g. the same ref matches \"refs/heads/m*\",\n\ttoo).\n\nperhaps?\n"},{"id":"263277","messageId":"xmqqh9qiax8l.fsf@gitster.dls.corp.google.com","threadId":"39543","inReplyTo":"5575CD35.4030705@gmail.com","subject":"Re: [PATCH v6 09/11] ref-filter: move code from 'for-each-ref'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-08T18:08:58Z","receivedAt":"2015-06-08T18:08:58Z","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> On 06/08/2015 10:38 PM, Matthieu Moy wrote:\n>> Karthik Nayak <karthik.188@gmail.com> writes:\n>>\n>> > --- a/builtin/for-each-ref.c\n>> > +++ b/builtin/for-each-ref.c\n>> > @@ -1129,7 +56,7 @@ int cmd_for_each_ref(int argc, const char\n>> > **argv, const char *prefix)\n>> >\n>> >       memset(&ref_cbdata, 0, sizeof(ref_cbdata));\n>> >       ref_cbdata.filter.name_patterns = argv;\n>> > -    for_each_rawref(ref_filter_handler, &ref_cbdata);\n>> > +    filter_refs(for_each_rawref, &ref_cbdata);\n>>\n>> This seems unrelated from the rest of the patch. And you haven't\n>> introduced filter_refs yet!\n>>\n> That definitely is, this happened after fixing merges I suppose.\n> Ignore it for now, I'll fix it.\n\nI am not sure which one of two copies of 09/11 is being discussed,\nbut I'll stop here for now.  Most of the patches up to 08/11 looked\nsensible, if the renames were a bit too noisy.\n\nThanks.\n"},{"id":"263278","messageId":"xmqqd216awxz.fsf@gitster.dls.corp.google.com","threadId":"39543","inReplyTo":"1433673373-16441-1-git-send-email-karthik.188@gmail.com","subject":"Re: [PATCH v6 10/11] for-each-ref: introduce filter_refs()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-08T18:15:20Z","receivedAt":"2015-06-08T18:15:20Z","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> +/*\n> + * API for filtering a set of refs. The refs are provided and iterated\n> + * over using the for_each_ref_fn(). The refs are stored into and filtered\n> + * based on the ref_filter_cbdata structure.\n> + */\n> +int filter_refs(int (for_each_ref_fn)(each_ref_fn, void *), struct ref_filter_cbdata *data)\n> +{\n> +\treturn for_each_ref_fn(ref_filter_handler, data);\n> +}\n\nI do not think it is such a good idea to allow API callers to\nspecify for-each-ref-fn directly.  See my message in an earlier\nreview.\n\nI also think ref_filter_cbdata is an implementation detail of\nfilter_refs and may not have to be exposed to the API callers.\nIt probably is more sensible for them to pass\n\n - an array of refs to receive filtered results (your ref_array thing)\n - the criteria to use when filtering (your ref_filter thing)\n\nas two separate parameters to this function, together with other\nparameters that lets you (meaning the implementation of filter_refs())\nto decide which for-each-ref iterator to call, e.g. do you want to\nuse raw iteration?  do you want to iterate only over refs/heads? etc.\n\nIn other words, the caller of this API should not have to know that\nyou (meaning the implementation of filter_refs()) are internally\nusing for_each_ref() API.\n"},{"id":"263279","messageId":"5575DFDF.7000404@gmail.com","threadId":"39543","inReplyTo":"xmqqlhfuaxjw.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 01/11] for-each-ref: extract helper functions out of grab_single_ref()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-08T18:33:03Z","receivedAt":"2015-06-08T18:33:03Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On 06/08/2015 11:32 PM, Junio C Hamano wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> + * Given a refname, return 1 if the refname matches with one of the patterns\n>\n> You can \"match refname with pattern\".  But \"refname matches the\n> pattern\" without \"with\", I think.\n>\n> I am not a native, though.\n>\n>> + * while the pattern is a pathname like 'refs/tags' or 'refs/heads/master'\n>> + * and so on, else return 0. Supports use of wild characters.\n>\n> s/wild/wildcard/.\n>\n> \tReturn 1 if the refname matches with one of the patterns,\n> \totherwise 0.  The patterns can be literal prefix (e.g. a\n> \trefname \"refs/heads/master\" matches a pattern \"refs/heads/\")\n> \tor a wildcard (e.g. the same ref matches \"refs/heads/m*\",\n> \ttoo).\n>\n> perhaps?\n>\n\nSounds a lot better, thanks :)\n\n-- \nRegards,\nKarthik\n"},{"id":"263282","messageId":"5575E0EB.5030704@gmail.com","threadId":"39543","inReplyTo":"xmqqd216awxz.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 10/11] for-each-ref: introduce filter_refs()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-06-08T18:37:31Z","receivedAt":"2015-06-08T18:37:31Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On 06/08/2015 11:45 PM, Junio C Hamano wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> +/*\n>> + * API for filtering a set of refs. The refs are provided and iterated\n>> + * over using the for_each_ref_fn(). The refs are stored into and filtered\n>> + * based on the ref_filter_cbdata structure.\n>> + */\n>> +int filter_refs(int (for_each_ref_fn)(each_ref_fn, void *), struct ref_filter_cbdata *data)\n>> +{\n>> +\treturn for_each_ref_fn(ref_filter_handler, data);\n>> +}\n>\n> I do not think it is such a good idea to allow API callers to\n> specify for-each-ref-fn directly.  See my message in an earlier\n> review.\n >\n\nI did read your previous message. I misunderstood some things.\n\n>\n> I also think ref_filter_cbdata is an implementation detail of\n> filter_refs and may not have to be exposed to the API callers.\n> It probably is more sensible for them to pass\n>\n>   - an array of refs to receive filtered results (your ref_array thing)\n>   - the criteria to use when filtering (your ref_filter thing)\n>\n\nThis could be done.\n\n >\n> as two separate parameters to this function, together with other\n> parameters that lets you (meaning the implementation of filter_refs())\n> to decide which for-each-ref iterator to call, e.g. do you want to\n> use raw iteration?  do you want to iterate only over refs/heads? etc.\n>\n> In other words, the caller of this API should not have to know that\n> you (meaning the implementation of filter_refs()) are internally\n> using for_each_ref() API.\n>\n\nI'm a little confused about this, how exactly do you propose we go about \ndoing something like this? I mean, usually the user of the API\nknows what exactly they want, like in tag.c, branch.c and for-each-ref.c\nBut I'm not sure what you mean by \"parameters that lets you (meaning the \nimplementation of filter_refs()) to decide which for-each-ref iterator \nto call\". A small example maybe? Thanks!\n\n-- \nRegards,\nKarthik\n"}]}