{"thread":{"id":"39418","subject":"[WIP][Patch v2 0/2] Ref-filter: unification of 'tag -l', 'branch -l' and 'for-each-ref'","startedAt":"2015-05-25T12:39:20Z","lastAt":"2015-05-28T11:26:54Z","messageCount":10,"participants":["Karthik Nayak","Junio C Hamano","Matthieu Moy"],"isPatch":true,"patchVersion":2,"patchTotal":2},"messages":[{"id":"262023","messageId":"556317F8.2070609@gmail.com","threadId":"39418","inReplyTo":null,"subject":"[WIP][Patch v2 0/2] Ref-filter: unification of 'tag -l', 'branch -l' and 'for-each-ref'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-05-25T12:39:20Z","receivedAt":"2015-05-25T12:39:20Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hello,\n\nAfter the 1st version of the WIP patch ($gmane/269461) this is a follow up.\n\nChanges since 1st version :\n*\trestructure to have 'struct ref_filter_data' which has a list of refs \n'struct ref_list list', and the filters to apply 'struct ref_filter filter'\n*\treformat the commit to be better structured and easier to track\nand more 'git blame' friendly.\n*\tother small changes.\n\nAlso I'll be maintaining a repo on Github where I'll be pushing code \nmore often. https://github.com/KarthikNayak/git\n\n--\n\nRegards,\nKarthik\n"},{"id":"262025","messageId":"1432557943-25337-1-git-send-email-karthik.188@gmail.com","threadId":"39418","inReplyTo":"556317F8.2070609@gmail.com","subject":"[PATCH v2 1/2] for-each-ref: re-structure code for moving to 'ref-filter'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-05-25T12:45:42Z","receivedAt":"2015-05-25T12:45:42Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Re-factoring and renaming of code to make meaningful, targeted\nservices available to other commands in efforts to change private\nscope of the code to public API by moving it to 'ref-filter'.\nRestructuring of code done to further modularize it to make API\nprovisions simple and effective.\n\nRename 'refinfo' to 'ref_filter_item' and intoduce 'ref_filter_data'\nwhich will hold 'ref_filter' (Conditions to filter the refs on) and\n'ref_list' (The list of ref_filter_items).\n\nModify the code to use 'ref_filter_data' and make some of the\nfunctions publically available by removind the static file scope.\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 | 212 ++++++++++++++++++++++++++++---------------------\n 1 file changed, 121 insertions(+), 91 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 83f9cf9..eb502a1 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -31,12 +31,26 @@ struct ref_sort {\n \tunsigned reverse : 1;\n };\n \n-struct refinfo {\n-\tchar *refname;\n-\tunsigned char objectname[20];\n-\tint flag;\n+struct ref_filter_item {\n+\tunsigned char sha1[20];\n+\tint flags;\n \tconst char *symref;\n \tstruct atom_value *value;\n+\tchar *name;\n+};\n+\n+struct ref_list {\n+\tint nr, alloc;\n+\tstruct ref_filter_item **items;\n+};\n+\n+struct ref_filter {\n+\tconst char **name_patterns;\n+};\n+\n+struct ref_filter_data {\n+\tstruct ref_list list;\n+\tstruct ref_filter filter;\n };\n \n static struct {\n@@ -85,7 +99,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_filter_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@@ -98,7 +112,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_atom(const char *atom, const char *ep)\n {\n \tconst char *sp;\n \tint i, at;\n@@ -175,7 +189,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_format(const char *format)\n {\n \tconst char *cp, *sp;\n \n@@ -622,7 +636,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_filter_item *ref)\n {\n \tvoid *buf;\n \tstruct object *obj;\n@@ -632,9 +646,9 @@ static void populate_value(struct refinfo *ref)\n \n \tref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));\n \n-\tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n+\tif (need_symref && (ref->flags & REF_ISSYMREF) && !ref->symref) {\n \t\tunsigned char unused1[20];\n-\t\tref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,\n+\t\tref->symref = resolve_refdup(ref->name, RESOLVE_REF_READING,\n \t\t\t\t\t     unused1, NULL);\n \t\tif (!ref->symref)\n \t\t\tref->symref = \"\";\n@@ -655,14 +669,14 @@ static void populate_value(struct refinfo *ref)\n \t\t}\n \n \t\tif (starts_with(name, \"refname\"))\n-\t\t\trefname = ref->refname;\n+\t\t\trefname = ref->name;\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\t/* only local branches may have an upstream */\n-\t\t\tif (!starts_with(ref->refname, \"refs/heads/\"))\n+\t\t\tif (!starts_with(ref->name, \"refs/heads/\"))\n \t\t\t\tcontinue;\n-\t\t\tbranch = branch_get(ref->refname + 11);\n+\t\t\tbranch = branch_get(ref->name + 11);\n \n \t\t\tif (!branch || !branch->merge || !branch->merge[0] ||\n \t\t\t    !branch->merge[0]->dst)\n@@ -677,9 +691,9 @@ static void populate_value(struct refinfo *ref)\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\tif (ref->flags & REF_ISSYMREF)\n \t\t\t\tcp = copy_advance(cp, \",symref\");\n-\t\t\tif (ref->flag & REF_ISPACKED)\n+\t\t\tif (ref->flags & REF_ISPACKED)\n \t\t\t\tcp = copy_advance(cp, \",packed\");\n \t\t\tif (cp == buf)\n \t\t\t\tv->s = \"\";\n@@ -688,7 +702,7 @@ static void populate_value(struct refinfo *ref)\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} else if (!deref && grab_objectname(name, ref->sha1, v)) {\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"HEAD\")) {\n \t\t\tconst char *head;\n@@ -696,7 +710,7 @@ static void populate_value(struct refinfo *ref)\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\tif (!strcmp(ref->name, head))\n \t\t\t\tv->s = \"*\";\n \t\t\telse\n \t\t\t\tv->s = \" \";\n@@ -774,13 +788,13 @@ static void populate_value(struct refinfo *ref)\n \treturn;\n \n  need_obj:\n-\tbuf = get_obj(ref->objectname, &obj, &size, &eaten);\n+\tbuf = get_obj(ref->sha1, &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+\t\t    sha1_to_hex(ref->sha1), ref->name);\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+\t\t    sha1_to_hex(ref->sha1), ref->name);\n \n \tgrab_values(ref->value, 0, obj, buf, size);\n \tif (!eaten)\n@@ -808,10 +822,10 @@ static void populate_value(struct refinfo *ref)\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+\t\t    sha1_to_hex(tagged), ref->name);\n \tif (!obj)\n \t\tdie(\"parse_object_buffer failed on %s for %s\",\n-\t\t    sha1_to_hex(tagged), ref->refname);\n+\t\t    sha1_to_hex(tagged), ref->name);\n \tgrab_values(ref->value, 1, obj, buf, size);\n \tif (!eaten)\n \t\tfree(buf);\n@@ -821,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_filter_item *ref, int atom, struct atom_value **v)\n {\n \tif (!ref->value) {\n \t\tpopulate_value(ref);\n@@ -830,65 +844,81 @@ static void get_value(struct refinfo *ref, int atom, struct atom_value **v)\n \t*v = &ref->value[atom];\n }\n \n-struct grab_ref_cbdata {\n-\tstruct refinfo **grab_array;\n-\tconst char **grab_pattern;\n-\tint grab_cnt;\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_filter_item and copy the sha1 and flags to it */\n+static struct ref_filter_item *new_ref_filter_item(const char *refname,\n+\t\t\t\t\t\t   const unsigned char *sha1,\n+\t\t\t\t\t\t   int flag)\n+{\n+\tstruct ref_filter_item *ref = xcalloc(1, sizeof(struct ref_filter_item));\n+\tref->name = xstrdup(refname);\n+\thashcpy(ref->sha1, sha1);\n+\tref->flags = 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+ * A call-back given to for_each_ref(). Process the refs and store only required refs\n+ * into the ref_filter_data structure.\n  */\n-static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n+int ref_filter_handler(const char *refname, const unsigned char *sha1, int flag, void *data)\n {\n-\tstruct grab_ref_cbdata *cb = cb_data;\n-\tstruct refinfo *ref;\n-\tint cnt;\n+\tstruct ref_filter_data *ref_data = data;\n+\tstruct ref_filter_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\twarning(\"ignoring ref with broken name %s\", refname);\n+\t\treturn 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 (*ref_data->filter.name_patterns && !match_name_as_path(ref_data->filter.name_patterns, refname))\n+\t\treturn 0;\n+\n+\tref = new_ref_filter_item(refname, sha1, flag);\n+\n+\tREALLOC_ARRAY(ref_data->list.items, ref_data->list.nr + 1);\n+\tref_data->list.items[ref_data->list.nr++] = ref;\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, sha1);\n-\tref->flag = 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 \treturn 0;\n }\n \n-static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)\n+/* Free all memory allocated for ref_filter_data */\n+void ref_filter_clear(struct ref_filter_data *ref_data)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < ref_data->list.nr; i++)\n+\t\tfree(ref_data->list.items[i]);\n+\tfree(ref_data->list.items);\n+\tref_data->list.items = NULL;\n+\tref_data->list.nr = ref_data->list.alloc = 0;\n+}\n+\n+static int cmp_ref_sort(struct ref_sort *s, struct ref_filter_item *a, struct ref_filter_item *b)\n {\n \tstruct atom_value *va, *vb;\n \tint cmp;\n@@ -915,8 +945,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_filter_item *a = *((struct ref_filter_item **)a_);\n+\tstruct ref_filter_item *b = *((struct ref_filter_item **)b_);\n \tstruct ref_sort *s;\n \n \tfor (s = ref_sort; s; s = s->next) {\n@@ -927,10 +957,11 @@ 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+void sort_refs(struct ref_sort *sort, struct ref_filter_data *ref_data)\n {\n \tref_sort = sort;\n-\tqsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);\n+\tqsort(ref_data->list.items, ref_data->list.nr,\n+\t      sizeof(struct ref_filter_item *), compare_refs);\n }\n \n static void print_value(struct atom_value *v, int quote_style)\n@@ -997,7 +1028,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+void show_ref(struct ref_filter_item *info, const char *format, int quote_style)\n {\n \tconst char *cp, *sp, *ep;\n \n@@ -1026,7 +1057,8 @@ static void show_ref(struct refinfo *info, const char *format, int quote_style)\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_sort *ref_default_sort(void)\n {\n \tstatic const char cstr_name[] = \"refname\";\n \n@@ -1037,7 +1069,7 @@ static struct ref_sort *default_sort(void)\n \treturn sort;\n }\n \n-static int opt_parse_sort(const struct option *opt, const char *arg, int unset)\n+int ref_opt_parse_sort(const struct option *opt, const char *arg, int unset)\n {\n \tstruct ref_sort **sort_tail = opt->value;\n \tstruct ref_sort *s;\n@@ -1066,12 +1098,12 @@ 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 refinfo **refs;\n-\tstruct grab_ref_cbdata cbdata;\n+\tstruct ref_filter_data ref_data;\n+\tmemset(&ref_data, 0, sizeof(ref_data));\n \n \tstruct option opts[] = {\n \t\tOPT_BIT('s', \"shell\", &quote_style,\n@@ -1087,7 +1119,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\"), &ref_opt_parse_sort),\n \t\tOPT_END(),\n \t};\n \n@@ -1104,22 +1136,20 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(for_each_ref_usage, opts);\n \n \tif (!sort)\n-\t\tsort = default_sort();\n+\t\tsort = ref_default_sort();\n \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+\tref_data.filter.name_patterns = argv;\n+\tfor_each_rawref(ref_filter_handler, &ref_data);\n \n-\tsort_refs(sort, refs, num_refs);\n+\tsort_refs(sort, &ref_data);\n \n-\tif (!maxcount || num_refs < maxcount)\n-\t\tmaxcount = num_refs;\n+\tif (!maxcount || ref_data.list.nr < maxcount)\n+\t\tmaxcount = ref_data.list.nr;\n \tfor (i = 0; i < maxcount; i++)\n-\t\tshow_ref(refs[i], format, quote_style);\n+\t\tshow_ref(ref_data.list.items[i], format, quote_style);\n+\tref_filter_clear(&ref_data);\n \treturn 0;\n }\n-- \n2.4.1\n"},{"id":"262024","messageId":"1432557943-25337-2-git-send-email-karthik.188@gmail.com","threadId":"39418","inReplyTo":"556317F8.2070609@gmail.com","subject":"[PATCH v2 2/2] ref-filter: move code from 'for-each-ref'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-05-25T12:45:43Z","receivedAt":"2015-05-25T12:45:43Z","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 publically 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\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 | 1089 +-----------------------------------------------\n ref-filter.c           | 1051 ++++++++++++++++++++++++++++++++++++++++++++++\n ref-filter.h           |   65 +++\n 4 files changed, 1118 insertions(+), 1088 deletions(-)\n create mode 100644 ref-filter.c\n create mode 100644 ref-filter.h\n\ndiff --git a/Makefile b/Makefile\nindex 36655d5..068d313 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -761,6 +761,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 eb502a1..dc95f8d 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -1,1095 +1,8 @@\n #include \"builtin.h\"\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 \"ref-filter.h\"\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-\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_sort {\n-\tstruct ref_sort *next;\n-\tint atom; /* index into used_atom array */\n-\tunsigned reverse : 1;\n-};\n-\n-struct ref_filter_item {\n-\tunsigned char sha1[20];\n-\tint flags;\n-\tconst char *symref;\n-\tstruct atom_value *value;\n-\tchar *name;\n-};\n-\n-struct ref_list {\n-\tint nr, alloc;\n-\tstruct ref_filter_item **items;\n-};\n-\n-struct ref_filter {\n-\tconst char **name_patterns;\n-};\n-\n-struct ref_filter_data {\n-\tstruct ref_list list;\n-\tstruct ref_filter filter;\n-};\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{ \"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_filter_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_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_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_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_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_filter_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->flags & REF_ISSYMREF) && !ref->symref) {\n-\t\tunsigned char unused1[20];\n-\t\tref->symref = resolve_refdup(ref->name, 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->name;\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\t/* only local branches may have an upstream */\n-\t\t\tif (!starts_with(ref->name, \"refs/heads/\"))\n-\t\t\t\tcontinue;\n-\t\t\tbranch = branch_get(ref->name + 11);\n-\n-\t\t\tif (!branch || !branch->merge || !branch->merge[0] ||\n-\t\t\t    !branch->merge[0]->dst)\n-\t\t\t\tcontinue;\n-\t\t\trefname = branch->merge[0]->dst;\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->flags & REF_ISSYMREF)\n-\t\t\t\tcp = copy_advance(cp, \",symref\");\n-\t\t\tif (ref->flags & 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->sha1, 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->name, 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\tchar buf[40];\n-\n-\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n-\t\t\t\t\t\t       &num_theirs) != 1)\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\tassert(branch);\n-\n-\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n-\t\t\t\t\t\t\t&num_theirs) != 1)\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->sha1, &obj, &size, &eaten);\n-\tif (!buf)\n-\t\tdie(\"missing object %s for %s\",\n-\t\t    sha1_to_hex(ref->sha1), ref->name);\n-\tif (!obj)\n-\t\tdie(\"parse_object_buffer failed on %s for %s\",\n-\t\t    sha1_to_hex(ref->sha1), ref->name);\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->name);\n-\tif (!obj)\n-\t\tdie(\"parse_object_buffer failed on %s for %s\",\n-\t\t    sha1_to_hex(tagged), ref->name);\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_value(struct ref_filter_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_filter_item and copy the sha1 and flags to it */\n-static struct ref_filter_item *new_ref_filter_item(const char *refname,\n-\t\t\t\t\t\t   const unsigned char *sha1,\n-\t\t\t\t\t\t   int flag)\n-{\n-\tstruct ref_filter_item *ref = xcalloc(1, sizeof(struct ref_filter_item));\n-\tref->name = xstrdup(refname);\n-\thashcpy(ref->sha1, sha1);\n-\tref->flags = flag;\n-\n-\treturn ref;\n-}\n-\n-/*\n- * A call-back given to for_each_ref(). Process the refs and store only required refs\n- * into the ref_filter_data structure.\n- */\n-int ref_filter_handler(const char *refname, const unsigned char *sha1, int flag, void *data)\n-{\n-\tstruct ref_filter_data *ref_data = data;\n-\tstruct ref_filter_item *ref;\n-\n-\tif (flag & REF_BAD_NAME) {\n-\t\twarning(\"ignoring ref with broken name %s\", refname);\n-\t\treturn 0;\n-\t}\n-\n-\tif (*ref_data->filter.name_patterns && !match_name_as_path(ref_data->filter.name_patterns, refname))\n-\t\treturn 0;\n-\n-\tref = new_ref_filter_item(refname, sha1, flag);\n-\n-\tREALLOC_ARRAY(ref_data->list.items, ref_data->list.nr + 1);\n-\tref_data->list.items[ref_data->list.nr++] = ref;\n-\n-\treturn 0;\n-}\n-\n-/* Free all memory allocated for ref_filter_data */\n-void ref_filter_clear(struct ref_filter_data *ref_data)\n-{\n-\tint i;\n-\n-\tfor (i = 0; i < ref_data->list.nr; i++)\n-\t\tfree(ref_data->list.items[i]);\n-\tfree(ref_data->list.items);\n-\tref_data->list.items = NULL;\n-\tref_data->list.nr = ref_data->list.alloc = 0;\n-}\n-\n-static int cmp_ref_sort(struct ref_sort *s, struct ref_filter_item *a, struct ref_filter_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-\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_sort *ref_sort;\n-static int compare_refs(const void *a_, const void *b_)\n-{\n-\tstruct ref_filter_item *a = *((struct ref_filter_item **)a_);\n-\tstruct ref_filter_item *b = *((struct ref_filter_item **)b_);\n-\tstruct ref_sort *s;\n-\n-\tfor (s = ref_sort; s; s = s->next) {\n-\t\tint cmp = cmp_ref_sort(s, a, b);\n-\t\tif (cmp)\n-\t\t\treturn cmp;\n-\t}\n-\treturn 0;\n-}\n-\n-void sort_refs(struct ref_sort *sort, struct ref_filter_data *ref_data)\n-{\n-\tref_sort = sort;\n-\tqsort(ref_data->list.items, ref_data->list.nr,\n-\t      sizeof(struct ref_filter_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(struct ref_filter_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_value(info, parse_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_sort *ref_default_sort(void)\n-{\n-\tstatic const char cstr_name[] = \"refname\";\n-\n-\tstruct ref_sort *sort = xcalloc(1, sizeof(*sort));\n-\n-\tsort->next = NULL;\n-\tsort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));\n-\treturn sort;\n-}\n-\n-int ref_opt_parse_sort(const struct option *opt, const char *arg, int unset)\n-{\n-\tstruct ref_sort **sort_tail = opt->value;\n-\tstruct ref_sort *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 = *sort_tail;\n-\t*sort_tail = s;\n-\n-\tif (*arg == '-') {\n-\t\ts->reverse = 1;\n-\t\targ++;\n-\t}\n-\tlen = strlen(arg);\n-\ts->atom = parse_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>]\"),\ndiff --git a/ref-filter.c b/ref-filter.c\nnew file mode 100644\nindex 0000000..eb9eff9\n--- /dev/null\n+++ b/ref-filter.c\n@@ -0,0 +1,1051 @@\n+#include \"builtin.h\"\n+#include \"cache.h\"\n+#include \"parse-options.h\"\n+#include \"refs.h\"\n+#include \"ref-filter.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+\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{ \"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_filter_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_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_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_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_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_filter_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->flags & REF_ISSYMREF) && !ref->symref) {\n+\t\tunsigned char unused1[20];\n+\t\tref->symref = resolve_refdup(ref->name, 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->name;\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\t/* only local branches may have an upstream */\n+\t\t\tif (!starts_with(ref->name, \"refs/heads/\"))\n+\t\t\t\tcontinue;\n+\t\t\tbranch = branch_get(ref->name + 11);\n+\n+\t\t\tif (!branch || !branch->merge || !branch->merge[0] ||\n+\t\t\t    !branch->merge[0]->dst)\n+\t\t\t\tcontinue;\n+\t\t\trefname = branch->merge[0]->dst;\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->flags & REF_ISSYMREF)\n+\t\t\t\tcp = copy_advance(cp, \",symref\");\n+\t\t\tif (ref->flags & 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->sha1, 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->name, 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\tchar buf[40];\n+\n+\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n+\t\t\t\t\t\t       &num_theirs) != 1)\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\tassert(branch);\n+\n+\t\t\t\tif (stat_tracking_info(branch, &num_ours,\n+\t\t\t\t\t\t\t&num_theirs) != 1)\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->sha1, &obj, &size, &eaten);\n+\tif (!buf)\n+\t\tdie(\"missing object %s for %s\",\n+\t\t    sha1_to_hex(ref->sha1), ref->name);\n+\tif (!obj)\n+\t\tdie(\"parse_object_buffer failed on %s for %s\",\n+\t\t    sha1_to_hex(ref->sha1), ref->name);\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->name);\n+\tif (!obj)\n+\t\tdie(\"parse_object_buffer failed on %s for %s\",\n+\t\t    sha1_to_hex(tagged), ref->name);\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_value(struct ref_filter_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_filter_item and copy the sha1 and flags to it */\n+static struct ref_filter_item *new_ref_filter_item(const char *refname,\n+\t\t\t\t\t\t   const unsigned char *sha1,\n+\t\t\t\t\t\t   int flag)\n+{\n+\tstruct ref_filter_item *ref = xcalloc(1, sizeof(struct ref_filter_item));\n+\tref->name = xstrdup(refname);\n+\thashcpy(ref->sha1, sha1);\n+\tref->flags = flag;\n+\n+\treturn ref;\n+}\n+\n+/*\n+ * A call-back given to for_each_ref(). Process the refs and store only required refs\n+ * into the ref_filter_data structure.\n+ */\n+int ref_filter_handler(const char *refname, const unsigned char *sha1, int flag, void *data)\n+{\n+\tstruct ref_filter_data *ref_data = data;\n+\tstruct ref_filter_item *ref;\n+\n+\tif (flag & REF_BAD_NAME) {\n+\t\twarning(\"ignoring ref with broken name %s\", refname);\n+\t\treturn 0;\n+\t}\n+\n+\tif (*ref_data->filter.name_patterns && !match_name_as_path(ref_data->filter.name_patterns, refname))\n+\t\treturn 0;\n+\n+\tref = new_ref_filter_item(refname, sha1, flag);\n+\n+\tREALLOC_ARRAY(ref_data->list.items, ref_data->list.nr + 1);\n+\tref_data->list.items[ref_data->list.nr++] = ref;\n+\n+\treturn 0;\n+}\n+\n+/* Free all memory allocated for ref_filter_data */\n+void ref_filter_clear(struct ref_filter_data *ref_data)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < ref_data->list.nr; i++)\n+\t\tfree(ref_data->list.items[i]);\n+\tfree(ref_data->list.items);\n+\tref_data->list.items = NULL;\n+\tref_data->list.nr = ref_data->list.alloc = 0;\n+}\n+\n+static int cmp_ref_sort(struct ref_sort *s, struct ref_filter_item *a, struct ref_filter_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+\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_sort *ref_sort;\n+static int compare_refs(const void *a_, const void *b_)\n+{\n+\tstruct ref_filter_item *a = *((struct ref_filter_item **)a_);\n+\tstruct ref_filter_item *b = *((struct ref_filter_item **)b_);\n+\tstruct ref_sort *s;\n+\n+\tfor (s = ref_sort; s; s = s->next) {\n+\t\tint cmp = cmp_ref_sort(s, a, b);\n+\t\tif (cmp)\n+\t\t\treturn cmp;\n+\t}\n+\treturn 0;\n+}\n+\n+void sort_refs(struct ref_sort *sort, struct ref_filter_data *ref_data)\n+{\n+\tref_sort = sort;\n+\tqsort(ref_data->list.items, ref_data->list.nr,\n+\t      sizeof(struct ref_filter_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(struct ref_filter_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_value(info, parse_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_sort *ref_default_sort(void)\n+{\n+\tstatic const char cstr_name[] = \"refname\";\n+\n+\tstruct ref_sort *sort = xcalloc(1, sizeof(*sort));\n+\n+\tsort->next = NULL;\n+\tsort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));\n+\treturn sort;\n+}\n+\n+int ref_opt_parse_sort(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct ref_sort **sort_tail = opt->value;\n+\tstruct ref_sort *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 = *sort_tail;\n+\t*sort_tail = s;\n+\n+\tif (*arg == '-') {\n+\t\ts->reverse = 1;\n+\t\targ++;\n+\t}\n+\tlen = strlen(arg);\n+\ts->atom = parse_atom(arg, arg+len);\n+\treturn 0;\n+}\ndiff --git a/ref-filter.h b/ref-filter.h\nnew file mode 100644\nindex 0000000..edf9c26\n--- /dev/null\n+++ b/ref-filter.h\n@@ -0,0 +1,65 @@\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_sort {\n+\tstruct ref_sort *next;\n+\tint atom; /* index into used_atom array */\n+\tunsigned reverse : 1;\n+};\n+\n+struct ref_filter_item {\n+\tunsigned char sha1[20];\n+\tint flags;\n+\tconst char *symref;\n+\tstruct atom_value *value;\n+\tchar *name;\n+};\n+\n+/*  Holds the list of refs either after filtering or for filtering */\n+struct ref_list {\n+\tint nr, alloc;\n+\tstruct ref_filter_item **items;\n+};\n+\n+/*  The filters to be applied to get the desired set of refs  */\n+struct ref_filter {\n+\tconst char **name_patterns;\n+};\n+\n+/*  The main structure which holds the list of refs and the filters */\n+struct ref_filter_data {\n+\tstruct ref_list list;\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 unsigned char *sha1, int flag, void *data);\n+/*  Clear all memory allocated to ref_filter_data */\n+void ref_filter_clear(struct ref_filter_data *ref_data);\n+\n+/*  Formatting and sorting functions */\n+int parse_atom(const char *atom, const char *ep);\n+int verify_format(const char *format);\n+void sort_refs(struct ref_sort *sort, struct ref_filter_data *ref_data);\n+void show_ref(struct ref_filter_item *info, const char *format, int quote_style);\n+int ref_opt_parse_sort(const struct option *opt, const char *arg, int unset);\n+struct ref_sort *ref_default_sort(void);\n+\n+#endif /*  REF_FILTER_H  */\n-- \n2.4.1\n"},{"id":"262032","messageId":"xmqqegm4bmtg.fsf@gitster.dls.corp.google.com","threadId":"39418","inReplyTo":"1432557943-25337-1-git-send-email-karthik.188@gmail.com","subject":"Re: [PATCH v2 1/2] for-each-ref: re-structure code for moving to 'ref-filter'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-05-25T17:15:39Z","receivedAt":"2015-05-25T17:15:39Z","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> -struct refinfo {\n> -\tchar *refname;\n> -\tunsigned char objectname[20];\n> -\tint flag;\n> +struct ref_filter_item {\n> +\tunsigned char sha1[20];\n> +\tint flags;\n>  \tconst char *symref;\n>  \tstruct atom_value *value;\n> +\tchar *name;\n> +};\n\nI do not see much point in renaming between these two.  The latter\nmakes it sound as if this is only for \"filtering\" and from that\nangle of view is probably a worse name.  If you do not think of a\nbetter one, and if you are going to name the array that contains\nthis thing \"ref_list\", calling \"ref_list_item\" would be following\nsuit to what string-list did.\n\nI somehow had an impression that we are trying to move away from\ncalling the name of objects as \"sha1[]\" as a longer term goal?  I do\nnot think it is particularly a good idea to start using \"struct\nobject_id\" in this series (it can be done after everything is done\nand you still have time left in GSoC), but I do not see how much\nvalue this interim renaming (because eventually we would change not\njust name but type, and the final name will _not_ be sha1[] but more\ncloser to \"object name\") adds value.\n\nYou didn't explain why you reordered the fields, either.  Were you\nplanning to make the name[] field to flex-array to reduce need for\none level of redirection or something?\n\n> +\n> +struct ref_list {\n> +\tint nr, alloc;\n> +\tstruct ref_filter_item **items;\n> +};\n> +\n> +struct ref_filter {\n> +\tconst char **name_patterns;\n> +};\n> +\n> +struct ref_filter_data {\n> +\tstruct ref_list list;\n> +\tstruct ref_filter filter;\n>  };\n\nI agree that \"grab\" part of \"grab_ref_cbdata\" sounds unprofessional,\nand using \"ref_filter_\" to signal that this callback data is about\nref-filter API might be a good idea (I do not think the original is\ntoo bad, either, though).  I do not think you would use this struct\nanywhere other than as the callback data; you would want to end its\nname with \"_cbdata\", not just \"_data\", to make it clear why these\ntwo unrelated things are in a single struct (the only time these two\nconcepts, operation and operatee, meet is when they need to be\npassed to an \"apply operation to operatee\" function; there is no\nsuch \"this set of operatee always are for this operation\"\nassociation between them---which is what I mean by 'two unrelated\nthings').\n\n> @@ -98,7 +112,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_atom(const char *atom, const char *ep)\n\nIt was perfectly good name as a file-scope static; within the\ncontext of 'for-each-ref' implementation, when every somebody says\n\"atom\", you would know it is those %(atomic-data-item) things, and\nparse_atom() would be a helper function to do so.\n\nBut it is *WAY* too generic a name to make public, where you are\nnaming things in the whole context of Git implementation.  If you\nused the word \"atom\" while discussing formatting done with \"git\nfor-each-ref\" with somebody else, it would be unambiguously clear\nwhat you mean; you wouldn't say \"I am writing a function to parse\n'atoms' in Git\"---that's too broad and you will get \"'atom', in what\nsense?\" in response.\n\n> @@ -175,7 +189,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_format(const char *format)\n\nDitto.\n\n> @@ -622,7 +636,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_filter_item *ref)\n>  {\n\nAs long as this will stay private within the new ref-filter.c after\nthe move, this name is OK.\n\n> @@ -655,14 +669,14 @@ static void populate_value(struct refinfo *ref)\n>  \t\t}\n>  \n>  \t\tif (starts_with(name, \"refname\"))\n> -\t\t\trefname = ref->refname;\n> +\t\t\trefname = ref->name;\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\t/* only local branches may have an upstream */\n> -\t\t\tif (!starts_with(ref->refname, \"refs/heads/\"))\n> +\t\t\tif (!starts_with(ref->name, \"refs/heads/\"))\n>  \t\t\t\tcontinue;\n> -\t\t\tbranch = branch_get(ref->refname + 11);\n> +\t\t\tbranch = branch_get(ref->name + 11);\n>  \n>  \t\t\tif (!branch || !branch->merge || !branch->merge[0] ||\n>  \t\t\t    !branch->merge[0]->dst)\n> @@ -677,9 +691,9 @@ static void populate_value(struct refinfo *ref)\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\tif (ref->flags & REF_ISSYMREF)\n>  \t\t\t\tcp = copy_advance(cp, \",symref\");\n> -\t\t\tif (ref->flag & REF_ISPACKED)\n> +\t\t\tif (ref->flags & REF_ISPACKED)\n>  \t\t\t\tcp = copy_advance(cp, \",packed\");\n>  \t\t\tif (cp == buf)\n>  \t\t\t\tv->s = \"\";\n\nI see fallouts from the two renamed fields in the above hunks.  Was\nthe rename necessary?\n\nrefinfo keeps two names (ref and object) and calling one \"refname\"\nmade perfect sense (and calling other \"objectname\" did, too).  Has\nanything around its use changed to invalidate that rationale after\nthe structure was renamed?\n\nWhen we say 'flag', it is obvious that it is a \"flag word\", i.e. a\nword that holds collection of flags.  Otherwise, we would have named\neach \"unsigned foo_flag : 1\" with meaningful names.  Was it\nnecessary to make the field name longer?\n\n> @@ -688,7 +702,7 @@ static void populate_value(struct refinfo *ref)\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} else if (!deref && grab_objectname(name, ref->sha1, v)) {\n>  \t\t\tcontinue;\n\nMental note: grab_objectname() still remains, so I'd guess that it\nwill not be moved from this file or it will stay private after it is\nmoved.\n\nAnother mental note: it was a consistent naming for the function to\ngrab objectname to store the result into objectname[] field.  Now it\nstores into sha1[] field.\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\nYuck; I can see what you are doing but can you imitate what the more\nexperienced people (e.g. peff, mhagger) do when restructuring\nexisting code and do things in smaller increments?  For example, I\nthink it should be a separate preparatory patch, even before these\nrenames of structures, fields and functions, to extract this helper\nfunction out of grab_single_ref() function.\n\n> +\n> +/* Allocate space for a new ref_filter_item and copy the sha1 and flags to it */\n> +static struct ref_filter_item *new_ref_filter_item(const char *refname,\n> +\t\t\t\t\t\t   const unsigned char *sha1,\n> +\t\t\t\t\t\t   int flag)\n> +{\n> +\tstruct ref_filter_item *ref = xcalloc(1, sizeof(struct ref_filter_item));\n> +\tref->name = xstrdup(refname);\n> +\thashcpy(ref->sha1, sha1);\n> +\tref->flags = flag;\n> +\n> +\treturn ref;\n> +}\n\nAnd extracting this could be another separate preparatory step\nbefore renames (but I didn't look too closely).\n\nI won't be commenting on the remainder of the patch in this message,\nas I need to step out.  I see quite a many instances of the same\n\"overly generic public names\" in your \"s/static //\".\n"},{"id":"262035","messageId":"55636301.7060803@gmail.com","threadId":"39418","inReplyTo":"xmqqegm4bmtg.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 1/2] for-each-ref: re-structure code for moving to 'ref-filter'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-05-25T17:59:29Z","receivedAt":"2015-05-25T17:59:29Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":" >\n> I do not see much point in renaming between these two.  The latter\n> makes it sound as if this is only for \"filtering\" and from that\n> angle of view is probably a worse name.  If you do not think of a\n> better one, and if you are going to name the array that contains\n> this thing \"ref_list\", calling \"ref_list_item\" would be following\n> suit to what string-list did.\n >\n\nWell I just wanted to keep it related to 'ref-filter', I think\n'ref_list_item'\nsounds better after seeing your point of view.\n\n>\n> I somehow had an impression that we are trying to move away from\n> calling the name of objects as \"sha1[]\" as a longer term goal?  I do\n> not think it is particularly a good idea to start using \"struct\n> object_id\" in this series (it can be done after everything is done\n> and you still have time left in GSoC), but I do not see how much\n> value this interim renaming (because eventually we would change not\n> just name but type, and the final name will _not_ be sha1[] but more\n> closer to \"object name\") adds value.\n >\n\nI did that to resemble whats usually being used in similar structures,\na simple grep of \"sha1[20];\" resulted in 344 uses.\nI didn't know about the \"we are trying to move away from calling the \nname of objects as \"sha1[]\"\". Will leave it as objectname then.\n\n>\n> You didn't explain why you reordered the fields, either.  Were you\n> planning to make the name[] field to flex-array to reduce need for\n> one level of redirection or something?\n>\n\nYes! exactly why the re-order, was going to rebase it and squash it in, \nif the code seemed to be up and running.\n\n\n>\n> I agree that \"grab\" part of \"grab_ref_cbdata\" sounds unprofessional,\n> and using \"ref_filter_\" to signal that this callback data is about\n> ref-filter API might be a good idea (I do not think the original is\n> too bad, either, though).  I do not think you would use this struct\n> anywhere other than as the callback data; you would want to end its\n> name with \"_cbdata\", not just \"_data\", to make it clear why these\n> two unrelated things are in a single struct (the only time these two\n> concepts, operation and operatee, meet is when they need to be\n> passed to an \"apply operation to operatee\" function; there is no\n> such \"this set of operatee always are for this operation\"\n> association between them---which is what I mean by 'two unrelated\n> things').\n>\n\nsure, will do :) thanks for putting that out.\n\n>\n> It was perfectly good name as a file-scope static; within the\n> context of 'for-each-ref' implementation, when every somebody says\n> \"atom\", you would know it is those %(atomic-data-item) things, and\n> parse_atom() would be a helper function to do so.\n>\n> But it is *WAY* too generic a name to make public, where you are\n> naming things in the whole context of Git implementation.  If you\n> used the word \"atom\" while discussing formatting done with \"git\n> for-each-ref\" with somebody else, it would be unambiguously clear\n> what you mean; you wouldn't say \"I am writing a function to parse\n> 'atoms' in Git\"---that's too broad and you will get \"'atom', in what\n> sense?\" in response.\n>\n\n>\n> Ditto.\n>\n\nYes, that does seem to be too vague for a public function name, will \namend it.\n\n>\n> As long as this will stay private within the new ref-filter.c after\n> the move, this name is OK.\n>\n\nThat'll mostly stay private, if required will change the name along.\n\n>\n> I see fallouts from the two renamed fields in the above hunks.  Was\n> the rename necessary?\n>\n> refinfo keeps two names (ref and object) and calling one \"refname\"\n> made perfect sense (and calling other \"objectname\" did, too).  Has\n> anything around its use changed to invalidate that rationale after\n> the structure was renamed?\n>\n\nI guess it was unnecessary, my bad.\n\n >\n> When we say 'flag', it is obvious that it is a \"flag word\", i.e. a\n> word that holds collection of flags.  Otherwise, we would have named\n> each \"unsigned foo_flag : 1\" with meaningful names.  Was it\n> necessary to make the field name longer?\n>\n\nJust felt flags to be more descriptive, well \"Otherwise, we would have \nnamed each \"unsigned foo_flag : 1\" with meaningful names. \" makes sense.\n\n> > @@ -688,7 +702,7 @@ static void populate_value(struct refinfo *ref)\n> >                   v->s = xstrdup(buf + 1);\n> >               }\n> >               continue;\n> > -        } else if (!deref && grab_objectname(name, ref->objectname,\n> v)) {\n> > +        } else if (!deref && grab_objectname(name, ref->sha1, v)) {\n> >               continue;\n>\n> Mental note: grab_objectname() still remains, so I'd guess that it\n> will not be moved from this file or it will stay private after it is\n> moved.\n>\n\nIt will be private.\n\n >\n> Another mental note: it was a consistent naming for the function to\n> grab objectname to store the result into objectname[] field.  Now it\n> stores into sha1[] field.\n>\n\nyes, seems a bit off.\n\n>\n> Yuck; I can see what you are doing but can you imitate what the more\n> experienced people (e.g. peff, mhagger) do when restructuring\n> existing code and do things in smaller increments?  For example, I\n> think it should be a separate preparatory patch, even before these\n> renames of structures, fields and functions, to extract this helper\n> function out of grab_single_ref() function.\n>\n\nShall do so.\n\n>\n> And extracting this could be another separate preparatory step\n> before renames (but I didn't look too closely).\n>\n\nSure.\n\n >\n> I won't be commenting on the remainder of the patch in this message,\n> as I need to step out.  I see quite a many instances of the same\n> \"overly generic public names\" in your \"s/static //\".\n>\n\nWill work on it, thanks for the suggestions.\n\n-- \nRegards,\nKarthik\n"},{"id":"262094","messageId":"xmqq617gbg58.fsf@gitster.dls.corp.google.com","threadId":"39418","inReplyTo":"55636301.7060803@gmail.com","subject":"Re: [PATCH v2 1/2] for-each-ref: re-structure code for moving to 'ref-filter'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-05-25T19:39:47Z","receivedAt":"2015-05-25T19:39:47Z","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>> I do not see much point in renaming between these two.  The latter\n>> makes it sound as if this is only for \"filtering\" and from that\n>> angle of view is probably a worse name.  If you do not think of a\n>> better one, and if you are going to name the array that contains\n>> this thing \"ref_list\", calling \"ref_list_item\" would be following\n>> suit to what string-list did.\n>>\n>\n> Well I just wanted to keep it related to 'ref-filter', I think\n> 'ref_list_item'\n> sounds better after seeing your point of view.\n\nAlso I think Matthieu already commented that \"filter\" was out of\nplace for that struct.  I still think your ref_list is better called\nref_array, but that is a minor point.  Use of \"foo_list\" in our\ncodebase is predominantly (because we use \"commit_list\" very often\nin the core part of the system) for a linear linked list where you\ndo not have a random access to the items.  string-list is misnomer,\nI would think.\n\n> I didn't know about the \"we are trying to move away from calling the\n> name of objects as \"sha1[]\"\". Will leave it as objectname then.\n\nI think you now know after seeing that 56-patch series ;-)\n\n>> You didn't explain why you reordered the fields, either.  Were you\n>> planning to make the name[] field to flex-array to reduce need for\n>> one level of redirection or something?\n>>\n>\n> Yes! exactly why the re-order, was going to rebase it and squash it\n> in, if the code seemed to be up and running.\n\nIf that is the case, I would suggest making that \"turn it flex array\"\na separate step.\n"},{"id":"262118","messageId":"556419B3.5030902@gmail.com","threadId":"39418","inReplyTo":"xmqq617gbg58.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 1/2] for-each-ref: re-structure code for moving to 'ref-filter'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-05-26T06:58:59Z","receivedAt":"2015-05-26T06:58:59Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":">\n> Also I think Matthieu already commented that \"filter\" was out of\n> place for that struct.  I still think your ref_list is better called\n> ref_array, but that is a minor point.  Use of \"foo_list\" in our\n> codebase is predominantly (because we use \"commit_list\" very often\n> in the core part of the system) for a linear linked list where you\n> do not have a random access to the items.  string-list is misnomer,\n> I would think.\n>\n\nref_array also sounds good, yes! there might be confusion and might be\nconsidered a linked list rather than an array. Will change.\n\n>\n> I think you now know after seeing that 56-patch series ;-)\n>\n\nHaha, That definitely helped.\n\n>\n> If that is the case, I would suggest making that \"turn it flex array\"\n> a separate step.\n>\n\nSure.\n\n-- \nRegards,\nKarthik\n"},{"id":"262132","messageId":"vpqpp5nfiea.fsf@anie.imag.fr","threadId":"39418","inReplyTo":"xmqqegm4bmtg.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 1/2] for-each-ref: re-structure code for moving to 'ref-filter'","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-05-26T15:49:49Z","receivedAt":"2015-05-26T15:49:49Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Yuck; I can see what you are doing but can you imitate what the more\n> experienced people (e.g. peff, mhagger) do when restructuring\n> existing code and do things in smaller increments?\n\nSeconded. Some reasons/guide to split:\n\n* Split trivial and non-trivial stuff. I can quickly review a\n  \"rename-only\" patch even if it's a bit long (essentially, I'll check\n  that you did find-and-replace properly), but reviewing a mix of\n  renames and actual code change is hard.\n\n* Split controversial and non-controversial stuff. For example, you\n  changed the ordering of fields in a struct. Perhaps it was not a good\n  idea. Perhaps it was a good idea, but then you want this reordering to\n  be alone in its patch so that you can explain why it's a good idea in\n  the commit message (you'll see me use the word \"why\" a lot when\n  talking about commit messages; not a coincidence).\n\n* Split code movement and other stuff. For example extraction of\n  match_name_as_path() would be trivial if made in its own patch.\n\nI'd also make a separate patch \"introduce the ref_list data-structure\"\nto introduce struct ref_list and basic helper functions (constructors,\nmutators, destructors).\n\nIt will take time and may appear to be counter-productive at first, but\nyou'll get used to it, and you'll end up being actually more productive\nthis way (well, maybe not \"you\" but the set \"you + reviewers\").\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"262307","messageId":"5566BEF2.2000301@gmail.com","threadId":"39418","inReplyTo":"vpqpp5nfiea.fsf@anie.imag.fr","subject":"Re: [PATCH v2 1/2] for-each-ref: re-structure code for moving to 'ref-filter'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-05-28T07:08:34Z","receivedAt":"2015-05-28T07:08:34Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On 05/26/2015 09:19 PM, Matthieu Moy wrote:\n> Seconded. Some reasons/guide to split:\n>\n> * Split trivial and non-trivial stuff. I can quickly review a\n>    \"rename-only\" patch even if it's a bit long (essentially, I'll check\n>    that you did find-and-replace properly), but reviewing a mix of\n>    renames and actual code change is hard.\n>\n> * Split controversial and non-controversial stuff. For example, you\n>    changed the ordering of fields in a struct. Perhaps it was not a good\n>    idea. Perhaps it was a good idea, but then you want this reordering to\n>    be alone in its patch so that you can explain why it's a good idea in\n>    the commit message (you'll see me use the word \"why\" a lot when\n>    talking about commit messages; not a coincidence).u\n\nSince one of the patches is to restructure and rename 'for-each-ref', I thought\nIt would be ideal to introduce the data structures within that patch, What do you\nthink?\n\n>\n> * Split code movement and other stuff. For example extraction of\n>    match_name_as_path() would be trivial if made in its own patch.\n>\n> I'd also make a separate patch \"introduce the ref_list data-structure\"\n> to introduce struct ref_list and basic helper functions (constructors,\n> mutators, destructors).\n>\n> It will take time and may appear to be counter-productive at first, but\n> you'll get used to it, and you'll end up being actually more productive\n> this way (well, maybe not \"you\" but the set \"you + reviewers\").\n>\n\nThanks for this.\n\n-- \nRegards,\nKarthik\n"},{"id":"262318","messageId":"vpq1ti1vt6p.fsf@anie.imag.fr","threadId":"39418","inReplyTo":"5566BEF2.2000301@gmail.com","subject":"Re: [PATCH v2 1/2] for-each-ref: re-structure code for moving to 'ref-filter'","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-05-28T11:26:54Z","receivedAt":"2015-05-28T11:26:54Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> On 05/26/2015 09:19 PM, Matthieu Moy wrote:\n>> Seconded. Some reasons/guide to split:\n>>\n>> * Split trivial and non-trivial stuff. I can quickly review a\n>>    \"rename-only\" patch even if it's a bit long (essentially, I'll check\n>>    that you did find-and-replace properly), but reviewing a mix of\n>>    renames and actual code change is hard.\n>>\n>> * Split controversial and non-controversial stuff. For example, you\n>>    changed the ordering of fields in a struct. Perhaps it was not a good\n>>    idea. Perhaps it was a good idea, but then you want this reordering to\n>>    be alone in its patch so that you can explain why it's a good idea in\n>>    the commit message (you'll see me use the word \"why\" a lot when\n>>    talking about commit messages; not a coincidence).\n>\n> Since one of the patches is to restructure and rename 'for-each-ref', I thought\n> It would be ideal to introduce the data structures within that patch, What do you\n> think?\n\nI don't have a universal answer: in general I prefer (let's say \"this\nlist prefers\") splitting as much as possible. It may make sense to group\n\"add data structure X\" with \"use data-structure X\" to make sure that\nfunctions you introduce have a caller.\n\nWhat's clear is that your PATCH 1/2 is not split enough. Just go through\nit, you'll see code movement (a pain to review in patch format),\nstraigthforward renamings (easy to review as-is, but disturbs the\nreviewer when mixed with something else) and actual new code.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"}]}