{"thread":{"id":"42348","subject":"[PATCH v2 01/12] commit.c: use strchrnul() to scan for one line","startedAt":"2016-05-16T21:05:33Z","lastAt":"2016-05-18T18:36:07Z","messageCount":27,"participants":["Junio C Hamano","Stefan Beller","Eric Sunshine"],"isPatch":true,"patchVersion":2,"patchTotal":12},"messages":[{"id":"286708","messageId":"20160516210545.6591-1-gitster@pobox.com","threadId":"42348","inReplyTo":null,"subject":"[PATCH v2 00/12] revamping git_check_attr() API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:33Z","receivedAt":"2016-05-16T21:05:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"A common pattern to check N attributes for many paths is to\n\n (1) prepare an array A of N git_attr_check_elem items;\n (2) call git_attr() to intern the N attribute names and fill A;\n (3) repeatedly call git_check_attrs() for path with N and A;\n\nA look-up for these N attributes for a single path P scans the\nentire attr_stack, starting from the .git/info/attributes file and\nthen .gitattributes file in the directory the path P is in, going\nupwards to find .gitattributes file found in parent directories.\n\nAn earlier commit 06a604e6 (attr: avoid heavy work when we know the\nspecified attr is not defined, 2014-12-28) tried to optimize out\nthis scanning for one trivial special case: when the attribute being\nsought is known not to exist, we do not have to scan for it.  While\nthis may be a cheap and effective heuristic, it would not work well\nwhen N is (much) more than 1.\n\nWhat we would want is a more customized way to skip irrelevant\nentries in the attribute stack, and the definition of irrelevance\nis tied to the set of attributes passed to git_check_attrs() call,\ni.e. the set of attributes being sought.  The data necessary for\nthis optimization needs to live alongside the set of attributes, but\na simple array of git_attr_check_elem simply does not have any place\nfor that.\n\nIntroduce \"struct git_attr_check\" that contains N, the number of\nattributes being sought, and A, the array that holds N\ngit_attr_check_elem items, and a function git_check_attr() that\ntakes a path P and this structure as its parameters.  This structure\ncan later be extended to hold extra data necessary for optimization.\n\n    Side Note: While in this series, I am not interested in\n    specifying the exact optimization, it would help readers to give\n    what is envisioned.  Even when the caller is interested in only\n    a few attributes (say, \"diff\", \"text\" and \"group-doc\"), we'd walk\n    the attribute entries in various .gitattributes files, perform\n    the pattern match and collect attributes for the path.  An\n    earlier commit 06a604e6 (attr: avoid heavy work when we know the\n    specified attr is not defined, 2014-12-28) tried to optimize out\n    this scanning for one trivial special case: where an asked-for\n    attribute does not appear anywhere in these files, we know we do\n    not have to scan at all.\n\n    We can do much better.  We can scan these attribute entries that\n    came from .gitattributes files once and mark the ones that affect\n    one or more of the attributes we know the caller is interested\n    in.  Then we only go through the marked entries.  E.g. if the\n    caller is interested in \"diff\", \"text\" and \"group-doc\"\n    attributes, we can mark entries that talks about any of \"diff\",\n    \"text\", \"group-doc\" and \"binary\" attributes.  The last one is\n    because it is an attribute macro that expands to affect \"diff\"\n    and \"text\" (these attribute are unset by having \"binary\").\n\n    The way git_attr_check() API is structured expects that the\n    caller expresses the set of attributes it is interested in\n    upfront, and reuses to ask the same question about many paths,\n    and it is already optimized for the case where it does so in\n    in-order recursive descent of a tree hierarchy by having an\n    attr_stack that reads, pre-parses and caches contents of the\n    .gitattributes files found in each directory hierarchy.  The\n    cached optimization data that sits in \"struct git_attr_check\"\n    would be effective for this expected access pattern.\n\n    The optimization data cannot be file-scope static to attr.c,\n    even though that might be easier to implement, in anticipation\n    of having more than one \"struct git_attr_check\" working\n    alternatingly, e.g. having more than one pathspec with\n    \":(attr=X)\" pathspec magic, i.e.\n\n        git cmd \":(attr=X)dir/\" \":(attr=Y)dir/ectory/\"\n\n    Each pathspec element is expected to keep its own \"struct\n    git_attr_check\" in the custom data to support pathspec magic\n    to match with attributes, and the optimization data kept there\n    would survive repeated calls to git_check_attr() for the same\n    path with different set of attributes.\n\n\nThe patches in the earliest part of the series have been sent to the\nlist already; there is no substantial change (I think I made a\ntypofix in the commit log message found by Eric).\n\n  commit.c: use strchrnul() to scan for one line\n  attr.c: use strchrnul() to scan for one line\n  attr.c: update a stale comment on \"struct match_attr\"\n  attr.c: explain the lack of attr-name syntax check in parse_attr()\n  attr.c: complete a sentence in a comment\n  attr.c: mark where #if DEBUG ends more clearly\n  attr.c: simplify macroexpand_one()\n\nStep 8 is to make sure I can catch all the existing callers and\nupdate the API incrementally.\n\n  attr: rename function and struct related to checking attributes\n\nStep 9 is the most important one.  By updating the API to allow the\ncallers hold piece of information more than just a simple array of\n<attr, value> pair, it paves the way to add data structures that\nwill help optimizing the lookup.  As a demonstration of the new API,\nit converts one caller.  The immediate effect to the callers is that\nthey can lose moderate amount of code, but that is not the point of\nthis change.\n\n  attr: (re)introduce git_check_attr() and struct git_attr_check\n\nThe two patches that follow converts remaining callers to the enw\nAPI.\n\n  attr: convert git_all_attrs() to use \"struct git_attr_check\"\n  attr: convert git_check_attrs() callers to use the new API\n\nThe last step retires the old one and updates the document.\n\n  attr: retire git_check_attrs() API\n\n Documentation/technical/api-gitattributes.txt |  62 ++++++-----\n archive.c                                     |  24 ++---\n attr.c                                        | 143 +++++++++++++++++++-------\n attr.h                                        |  31 ++++--\n builtin/check-attr.c                          |  55 +++++-----\n builtin/pack-objects.c                        |  19 +---\n commit.c                                      |   3 +-\n convert.c                                     |  26 ++---\n ll-merge.c                                    |  33 +++---\n userdiff.c                                    |  19 ++--\n ws.c                                          |  19 ++--\n 11 files changed, 236 insertions(+), 198 deletions(-)\n\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286707","messageId":"20160516210545.6591-2-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 01/12] commit.c: use strchrnul() to scan for one line","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:34Z","receivedAt":"2016-05-16T21:05:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n commit.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex 3f4f371..1f9ee8a 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -415,8 +415,7 @@ int find_commit_subject(const char *commit_buffer, const char **subject)\n \t\tp++;\n \tif (*p) {\n \t\tp += 2;\n-\t\tfor (eol = p; *eol && *eol != '\\n'; eol++)\n-\t\t\t; /* do nothing */\n+\t\teol = strchrnul(p, '\\n');\n \t} else\n \t\teol = p;\n \n-- \n2.8.2-748-gfb85f76\n"},{"id":"286710","messageId":"20160516210545.6591-3-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 02/12] attr.c: use strchrnul() to scan for one line","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:35Z","receivedAt":"2016-05-16T21:05:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n attr.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex eec5d7d..45aec1b 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -402,8 +402,8 @@ static struct attr_stack *read_attr_from_index(const char *path, int macro_ok)\n \tfor (sp = buf; *sp; ) {\n \t\tchar *ep;\n \t\tint more;\n-\t\tfor (ep = sp; *ep && *ep != '\\n'; ep++)\n-\t\t\t;\n+\n+\t\tep = strchrnul(sp, '\\n');\n \t\tmore = (*ep == '\\n');\n \t\t*ep = '\\0';\n \t\thandle_attr_line(res, sp, path, ++lineno, macro_ok);\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286713","messageId":"20160516210545.6591-4-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 03/12] attr.c: update a stale comment on \"struct match_attr\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:36Z","receivedAt":"2016-05-16T21:05:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When 82dce998 (attr: more matching optimizations from .gitignore,\n2012-10-15) changed a pointer to a string \"*pattern\" into an\nembedded \"struct pattern\" in struct match_attr, it forgot to update\nthe comment that describes the structure.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n attr.c | 5 ++---\n 1 file changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 45aec1b..4ae7801 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -131,9 +131,8 @@ struct pattern {\n  * If is_macro is true, then u.attr is a pointer to the git_attr being\n  * defined.\n  *\n- * If is_macro is false, then u.pattern points at the filename pattern\n- * to which the rule applies.  (The memory pointed to is part of the\n- * memory block allocated for the match_attr instance.)\n+ * If is_macro is false, then u.pat is the filename pattern to which the\n+ * rule applies.\n  *\n  * In either case, num_attr is the number of attributes affected by\n  * this rule, and state is an array listing them.  The attributes are\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286709","messageId":"20160516210545.6591-5-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 04/12] attr.c: explain the lack of attr-name syntax check in parse_attr()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:37Z","receivedAt":"2016-05-16T21:05:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n attr.c | 6 ++++++\n 1 file changed, 6 insertions(+)\n\ndiff --git a/attr.c b/attr.c\nindex 4ae7801..05db667 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -183,6 +183,12 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,\n \t\t\treturn NULL;\n \t\t}\n \t} else {\n+\t\t/*\n+\t\t * As this function is always called twice, once with\n+\t\t * e == NULL in the first pass and then e != NULL in\n+\t\t * the second pass, no need for invalid_attr_name()\n+\t\t * check here.\n+\t\t */\n \t\tif (*cp == '-' || *cp == '!') {\n \t\t\te->setto = (*cp == '-') ? ATTR__FALSE : ATTR__UNSET;\n \t\t\tcp++;\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286712","messageId":"20160516210545.6591-6-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 05/12] attr.c: complete a sentence in a comment","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:38Z","receivedAt":"2016-05-16T21:05:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n attr.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/attr.c b/attr.c\nindex 05db667..a7f2c3f 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -300,7 +300,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,\n  * directory (again, reading the file from top to bottom) down to the\n  * current directory, and then scan the list backwards to find the first match.\n  * This is exactly the same as what is_excluded() does in dir.c to deal with\n- * .gitignore\n+ * .gitignore file and info/excludes file as a fallback.\n  */\n \n static struct attr_stack {\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286711","messageId":"20160516210545.6591-7-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 06/12] attr.c: mark where #if DEBUG ends more clearly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:39Z","receivedAt":"2016-05-16T21:05:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n attr.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/attr.c b/attr.c\nindex a7f2c3f..95416d3 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -469,7 +469,7 @@ static void debug_set(const char *what, const char *match, struct git_attr *attr\n #define debug_push(a) do { ; } while (0)\n #define debug_pop(a) do { ; } while (0)\n #define debug_set(a,b,c,d) do { ; } while (0)\n-#endif\n+#endif /* DEBUG_ATTR */\n \n static void drop_attr_stack(void)\n {\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286714","messageId":"20160516210545.6591-8-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 07/12] attr.c: simplify macroexpand_one()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:40Z","receivedAt":"2016-05-16T21:05:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The double-loop wants to do an early return immediately when one\nmatching macro is found.  Eliminate the extra variable 'a' used for\nthat purpose and rewrite the \"assign the found item to 'a' to make\nit non-NULL and force the loop(s) to terminate\" with a direct return\nfrom there.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n attr.c | 11 ++++-------\n 1 file changed, 4 insertions(+), 7 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 95416d3..7bfeef3 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -701,24 +701,21 @@ static int fill(const char *path, int pathlen, int basename_offset,\n static int macroexpand_one(int nr, int rem)\n {\n \tstruct attr_stack *stk;\n-\tstruct match_attr *a = NULL;\n \tint i;\n \n \tif (check_all_attr[nr].value != ATTR__TRUE ||\n \t    !check_all_attr[nr].attr->maybe_macro)\n \t\treturn rem;\n \n-\tfor (stk = attr_stack; !a && stk; stk = stk->prev)\n-\t\tfor (i = stk->num_matches - 1; !a && 0 <= i; i--) {\n+\tfor (stk = attr_stack; stk; stk = stk->prev) {\n+\t\tfor (i = stk->num_matches - 1; 0 <= i; i--) {\n \t\t\tstruct match_attr *ma = stk->attrs[i];\n \t\t\tif (!ma->is_macro)\n \t\t\t\tcontinue;\n \t\t\tif (ma->u.attr->attr_nr == nr)\n-\t\t\t\ta = ma;\n+\t\t\t\treturn fill_one(\"expand\", ma, rem);\n \t\t}\n-\n-\tif (a)\n-\t\trem = fill_one(\"expand\", a, rem);\n+\t}\n \n \treturn rem;\n }\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286715","messageId":"20160516210545.6591-9-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 08/12] attr: rename function and struct related to checking attributes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:41Z","receivedAt":"2016-05-16T21:05:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The traditional API to check attributes is to prepare an N-element\narray of \"struct git_attr_check\" and pass N and the array to the\nfunction \"git_check_attr()\" as arguments.\n\nIn preparation to revamp the API to pass a single structure, in\nwhich these N elements are held, rename the type used for these\nindividual array elements to \"struct git_attr_check_elem\" and rename\nthe function to \"git_check_attrs()\".\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n archive.c              |  6 +++---\n attr.c                 | 12 ++++++------\n attr.h                 |  8 ++++----\n builtin/check-attr.c   | 19 ++++++++++---------\n builtin/pack-objects.c |  6 +++---\n convert.c              | 12 ++++++------\n ll-merge.c             | 10 +++++-----\n userdiff.c             |  4 ++--\n ws.c                   |  6 +++---\n 9 files changed, 42 insertions(+), 41 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 5d735ae..0f6acc5 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -87,7 +87,7 @@ void *sha1_file_to_archive(const struct archiver_args *args,\n \treturn buffer;\n }\n \n-static void setup_archive_check(struct git_attr_check *check)\n+static void setup_archive_check(struct git_attr_check_elem *check)\n {\n \tstatic struct git_attr *attr_export_ignore;\n \tstatic struct git_attr *attr_export_subst;\n@@ -123,7 +123,7 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,\n \tstruct archiver_context *c = context;\n \tstruct archiver_args *args = c->args;\n \twrite_archive_entry_fn_t write_entry = c->write_entry;\n-\tstruct git_attr_check check[2];\n+\tstruct git_attr_check_elem check[2];\n \tconst char *path_without_prefix;\n \tint err;\n \n@@ -138,7 +138,7 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,\n \tpath_without_prefix = path.buf + args->baselen;\n \n \tsetup_archive_check(check);\n-\tif (!git_check_attr(path_without_prefix, ARRAY_SIZE(check), check)) {\n+\tif (!git_check_attrs(path_without_prefix, ARRAY_SIZE(check), check)) {\n \t\tif (ATTR_TRUE(check[0].value))\n \t\t\treturn 0;\n \t\targs->convert = ATTR_TRUE(check[1].value);\ndiff --git a/attr.c b/attr.c\nindex 7bfeef3..8aa346c 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -40,7 +40,7 @@ struct git_attr {\n static int attr_nr;\n static int cannot_trust_maybe_real;\n \n-static struct git_attr_check *check_all_attr;\n+static struct git_attr_check_elem *check_all_attr;\n static struct git_attr *(git_attr_hash[HASHSIZE]);\n \n char *git_attr_name(struct git_attr *attr)\n@@ -661,7 +661,7 @@ static int macroexpand_one(int attr_nr, int rem);\n \n static int fill_one(const char *what, struct match_attr *a, int rem)\n {\n-\tstruct git_attr_check *check = check_all_attr;\n+\tstruct git_attr_check_elem *check = check_all_attr;\n \tint i;\n \n \tfor (i = a->num_attr - 1; 0 < rem && 0 <= i; i--) {\n@@ -726,7 +726,7 @@ static int macroexpand_one(int nr, int rem)\n  * collected. Otherwise all attributes are collected.\n  */\n static void collect_some_attrs(const char *path, int num,\n-\t\t\t       struct git_attr_check *check)\n+\t\t\t       struct git_attr_check_elem *check)\n \n {\n \tstruct attr_stack *stk;\n@@ -754,7 +754,7 @@ static void collect_some_attrs(const char *path, int num,\n \t\trem = 0;\n \t\tfor (i = 0; i < num; i++) {\n \t\t\tif (!check[i].attr->maybe_real) {\n-\t\t\t\tstruct git_attr_check *c;\n+\t\t\t\tstruct git_attr_check_elem *c;\n \t\t\t\tc = check_all_attr + check[i].attr->attr_nr;\n \t\t\t\tc->value = ATTR__UNSET;\n \t\t\t\trem++;\n@@ -769,7 +769,7 @@ static void collect_some_attrs(const char *path, int num,\n \t\trem = fill(path, pathlen, basename_offset, stk, rem);\n }\n \n-int git_check_attr(const char *path, int num, struct git_attr_check *check)\n+int git_check_attrs(const char *path, int num, struct git_attr_check_elem *check)\n {\n \tint i;\n \n@@ -785,7 +785,7 @@ int git_check_attr(const char *path, int num, struct git_attr_check *check)\n \treturn 0;\n }\n \n-int git_all_attrs(const char *path, int *num, struct git_attr_check **check)\n+int git_all_attrs(const char *path, int *num, struct git_attr_check_elem **check)\n {\n \tint i, count, j;\n \ndiff --git a/attr.h b/attr.h\nindex 8b08d33..cab82ec 100644\n--- a/attr.h\n+++ b/attr.h\n@@ -20,11 +20,11 @@ extern const char git_attr__false[];\n #define ATTR_UNSET(v) ((v) == NULL)\n \n /*\n- * Send one or more git_attr_check to git_check_attr(), and\n+ * Send one or more git_attr_check to git_check_attrs(), and\n  * each 'value' member tells what its value is.\n  * Unset one is returned as NULL.\n  */\n-struct git_attr_check {\n+struct git_attr_check_elem {\n \tstruct git_attr *attr;\n \tconst char *value;\n };\n@@ -36,7 +36,7 @@ struct git_attr_check {\n  */\n char *git_attr_name(struct git_attr *);\n \n-int git_check_attr(const char *path, int, struct git_attr_check *);\n+int git_check_attrs(const char *path, int, struct git_attr_check_elem *);\n \n /*\n  * Retrieve all attributes that apply to the specified path.  *num\n@@ -45,7 +45,7 @@ int git_check_attr(const char *path, int, struct git_attr_check *);\n  * objects describing the attributes and their values.  *check must be\n  * free()ed by the caller.\n  */\n-int git_all_attrs(const char *path, int *num, struct git_attr_check **check);\n+int git_all_attrs(const char *path, int *num, struct git_attr_check_elem **check);\n \n enum git_attr_direction {\n \tGIT_ATTR_CHECKIN,\ndiff --git a/builtin/check-attr.c b/builtin/check-attr.c\nindex 53a5a18..97e3837 100644\n--- a/builtin/check-attr.c\n+++ b/builtin/check-attr.c\n@@ -24,8 +24,8 @@ static const struct option check_attr_options[] = {\n \tOPT_END()\n };\n \n-static void output_attr(int cnt, struct git_attr_check *check,\n-\tconst char *file)\n+static void output_attr(int cnt, struct git_attr_check_elem *check,\n+\t\t\tconst char *file)\n {\n \tint j;\n \tfor (j = 0; j < cnt; j++) {\n@@ -51,14 +51,15 @@ static void output_attr(int cnt, struct git_attr_check *check,\n \t}\n }\n \n-static void check_attr(const char *prefix, int cnt,\n-\tstruct git_attr_check *check, const char *file)\n+static void check_attr(const char *prefix,\n+\t\t       int cnt, struct git_attr_check_elem *check,\n+\t\t       const char *file)\n {\n \tchar *full_path =\n \t\tprefix_path(prefix, prefix ? strlen(prefix) : 0, file);\n \tif (check != NULL) {\n-\t\tif (git_check_attr(full_path, cnt, check))\n-\t\t\tdie(\"git_check_attr died\");\n+\t\tif (git_check_attrs(full_path, cnt, check))\n+\t\t\tdie(\"git_check_attrs died\");\n \t\toutput_attr(cnt, check, file);\n \t} else {\n \t\tif (git_all_attrs(full_path, &cnt, &check))\n@@ -69,8 +70,8 @@ static void check_attr(const char *prefix, int cnt,\n \tfree(full_path);\n }\n \n-static void check_attr_stdin_paths(const char *prefix, int cnt,\n-\tstruct git_attr_check *check)\n+static void check_attr_stdin_paths(const char *prefix,\n+\t\t\t\t   int cnt, struct git_attr_check_elem *check)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct strbuf unquoted = STRBUF_INIT;\n@@ -99,7 +100,7 @@ static NORETURN void error_with_usage(const char *msg)\n \n int cmd_check_attr(int argc, const char **argv, const char *prefix)\n {\n-\tstruct git_attr_check *check;\n+\tstruct git_attr_check_elem *check;\n \tint cnt, i, doubledash, filei;\n \n \tif (!is_bare_repository())\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex a27de5b..167c301 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -886,7 +886,7 @@ static void write_pack_file(void)\n \t\t\twritten, nr_result);\n }\n \n-static void setup_delta_attr_check(struct git_attr_check *check)\n+static void setup_delta_attr_check(struct git_attr_check_elem *check)\n {\n \tstatic struct git_attr *attr_delta;\n \n@@ -898,10 +898,10 @@ static void setup_delta_attr_check(struct git_attr_check *check)\n \n static int no_try_delta(const char *path)\n {\n-\tstruct git_attr_check check[1];\n+\tstruct git_attr_check_elem check[1];\n \n \tsetup_delta_attr_check(check);\n-\tif (git_check_attr(path, ARRAY_SIZE(check), check))\n+\tif (git_check_attrs(path, ARRAY_SIZE(check), check))\n \t\treturn 0;\n \tif (ATTR_FALSE(check->value))\n \t\treturn 1;\ndiff --git a/convert.c b/convert.c\nindex f524b8d..058da86 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -703,7 +703,7 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,\n \treturn 1;\n }\n \n-static enum crlf_action git_path_check_crlf(struct git_attr_check *check)\n+static enum crlf_action git_path_check_crlf(struct git_attr_check_elem *check)\n {\n \tconst char *value = check->value;\n \n@@ -720,7 +720,7 @@ static enum crlf_action git_path_check_crlf(struct git_attr_check *check)\n \treturn CRLF_UNDEFINED;\n }\n \n-static enum eol git_path_check_eol(struct git_attr_check *check)\n+static enum eol git_path_check_eol(struct git_attr_check_elem *check)\n {\n \tconst char *value = check->value;\n \n@@ -733,7 +733,7 @@ static enum eol git_path_check_eol(struct git_attr_check *check)\n \treturn EOL_UNSET;\n }\n \n-static struct convert_driver *git_path_check_convert(struct git_attr_check *check)\n+static struct convert_driver *git_path_check_convert(struct git_attr_check_elem *check)\n {\n \tconst char *value = check->value;\n \tstruct convert_driver *drv;\n@@ -746,7 +746,7 @@ static struct convert_driver *git_path_check_convert(struct git_attr_check *chec\n \treturn NULL;\n }\n \n-static int git_path_check_ident(struct git_attr_check *check)\n+static int git_path_check_ident(struct git_attr_check_elem *check)\n {\n \tconst char *value = check->value;\n \n@@ -768,7 +768,7 @@ static const char *conv_attr_name[] = {\n static void convert_attrs(struct conv_attrs *ca, const char *path)\n {\n \tint i;\n-\tstatic struct git_attr_check ccheck[NUM_CONV_ATTRS];\n+\tstatic struct git_attr_check_elem ccheck[NUM_CONV_ATTRS];\n \n \tif (!ccheck[0].attr) {\n \t\tfor (i = 0; i < NUM_CONV_ATTRS; i++)\n@@ -777,7 +777,7 @@ static void convert_attrs(struct conv_attrs *ca, const char *path)\n \t\tgit_config(read_convert_config, NULL);\n \t}\n \n-\tif (!git_check_attr(path, NUM_CONV_ATTRS, ccheck)) {\n+\tif (!git_check_attrs(path, NUM_CONV_ATTRS, ccheck)) {\n \t\tca->crlf_action = git_path_check_crlf(ccheck + 4);\n \t\tif (ca->crlf_action == CRLF_UNDEFINED)\n \t\t\tca->crlf_action = git_path_check_crlf(ccheck + 0);\ndiff --git a/ll-merge.c b/ll-merge.c\nindex ff4a43a..772b14e 100644\n--- a/ll-merge.c\n+++ b/ll-merge.c\n@@ -334,13 +334,13 @@ static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr\n \treturn &ll_merge_drv[LL_TEXT_MERGE];\n }\n \n-static int git_path_check_merge(const char *path, struct git_attr_check check[2])\n+static int git_path_check_merge(const char *path, struct git_attr_check_elem check[2])\n {\n \tif (!check[0].attr) {\n \t\tcheck[0].attr = git_attr(\"merge\");\n \t\tcheck[1].attr = git_attr(\"conflict-marker-size\");\n \t}\n-\treturn git_check_attr(path, 2, check);\n+\treturn git_check_attrs(path, 2, check);\n }\n \n static void normalize_file(mmfile_t *mm, const char *path)\n@@ -360,7 +360,7 @@ int ll_merge(mmbuffer_t *result_buf,\n \t     mmfile_t *theirs, const char *their_label,\n \t     const struct ll_merge_options *opts)\n {\n-\tstatic struct git_attr_check check[2];\n+\tstatic struct git_attr_check_elem check[2];\n \tstatic const struct ll_merge_options default_opts;\n \tconst char *ll_driver_name = NULL;\n \tint marker_size = DEFAULT_CONFLICT_MARKER_SIZE;\n@@ -392,12 +392,12 @@ int ll_merge(mmbuffer_t *result_buf,\n \n int ll_merge_marker_size(const char *path)\n {\n-\tstatic struct git_attr_check check;\n+\tstatic struct git_attr_check_elem check;\n \tint marker_size = DEFAULT_CONFLICT_MARKER_SIZE;\n \n \tif (!check.attr)\n \t\tcheck.attr = git_attr(\"conflict-marker-size\");\n-\tif (!git_check_attr(path, 1, &check) && check.value) {\n+\tif (!git_check_attrs(path, 1, &check) && check.value) {\n \t\tmarker_size = atoi(check.value);\n \t\tif (marker_size <= 0)\n \t\t\tmarker_size = DEFAULT_CONFLICT_MARKER_SIZE;\ndiff --git a/userdiff.c b/userdiff.c\nindex 6bf2505..3560024 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -251,7 +251,7 @@ struct userdiff_driver *userdiff_find_by_name(const char *name) {\n struct userdiff_driver *userdiff_find_by_path(const char *path)\n {\n \tstatic struct git_attr *attr;\n-\tstruct git_attr_check check;\n+\tstruct git_attr_check_elem check;\n \n \tif (!attr)\n \t\tattr = git_attr(\"diff\");\n@@ -259,7 +259,7 @@ struct userdiff_driver *userdiff_find_by_path(const char *path)\n \n \tif (!path)\n \t\treturn NULL;\n-\tif (git_check_attr(path, 1, &check))\n+\tif (git_check_attrs(path, 1, &check))\n \t\treturn NULL;\n \n \tif (ATTR_TRUE(check.value))\ndiff --git a/ws.c b/ws.c\nindex ea4b2b1..7350905 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -71,7 +71,7 @@ unsigned parse_whitespace_rule(const char *string)\n \treturn rule;\n }\n \n-static void setup_whitespace_attr_check(struct git_attr_check *check)\n+static void setup_whitespace_attr_check(struct git_attr_check_elem *check)\n {\n \tstatic struct git_attr *attr_whitespace;\n \n@@ -82,10 +82,10 @@ static void setup_whitespace_attr_check(struct git_attr_check *check)\n \n unsigned whitespace_rule(const char *pathname)\n {\n-\tstruct git_attr_check attr_whitespace_rule;\n+\tstruct git_attr_check_elem attr_whitespace_rule;\n \n \tsetup_whitespace_attr_check(&attr_whitespace_rule);\n-\tif (!git_check_attr(pathname, 1, &attr_whitespace_rule)) {\n+\tif (!git_check_attrs(pathname, 1, &attr_whitespace_rule)) {\n \t\tconst char *value;\n \n \t\tvalue = attr_whitespace_rule.value;\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286717","messageId":"20160516210545.6591-10-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 09/12] attr: (re)introduce git_check_attr() and struct git_attr_check","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:42Z","receivedAt":"2016-05-16T21:05:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"A common pattern to check N attributes for many paths is to\n\n (1) prepare an array A of N git_attr_check_elem items;\n (2) call git_attr() to intern the N attribute names and fill A;\n (3) repeatedly call git_check_attrs() for path with N and A;\n\nA look-up for these N attributes for a single path P scans the\nentire attr_stack, starting from the .git/info/attributes file and\nthen .gitattributes file in the directory the path P is in, going\nupwards to find .gitattributes file found in parent directories.\n\nAn earlier commit 06a604e6 (attr: avoid heavy work when we know the\nspecified attr is not defined, 2014-12-28) tried to optimize out\nthis scanning for one trivial special case: when the attribute being\nsought is known not to exist, we do not have to scan for it.  While\nthis may be a cheap and effective heuristic, it would not work well\nwhen N is (much) more than 1.\n\nWhat we would want is a more customized way to skip irrelevant\nentries in the attribute stack, and the definition of irrelevance\nis tied to the set of attributes passed to git_check_attrs() call,\ni.e. the set of attributes being sought.  The data necessary for\nthis optimization needs to live alongside the set of attributes, but\na simple array of git_attr_check_elem simply does not have any place\nfor that.\n\nIntroduce \"struct git_attr_check\" that contains N, the number of\nattributes being sought, and A, the array that holds N\ngit_attr_check_elem items, and a function git_check_attr() that\ntakes a path P and this structure as its parameters.  This structure\ncan later be extended to hold extra data necessary for optimization.\n\nAlso, to make it easier to write the first two steps in common\ncases, introduce git_attr_check_initl() helper function, which takes\na NULL-terminated list of attribute names and initialize this\nstructure.\n\nAs an illustration of this new API, convert archive.c that asks for\nexport-subst and export-ignore attributes for each paths.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n archive.c | 24 ++++++------------------\n attr.c    | 34 ++++++++++++++++++++++++++++++++++\n attr.h    |  9 +++++++++\n 3 files changed, 49 insertions(+), 18 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 0f6acc5..7779af1 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -87,19 +87,6 @@ void *sha1_file_to_archive(const struct archiver_args *args,\n \treturn buffer;\n }\n \n-static void setup_archive_check(struct git_attr_check_elem *check)\n-{\n-\tstatic struct git_attr *attr_export_ignore;\n-\tstatic struct git_attr *attr_export_subst;\n-\n-\tif (!attr_export_ignore) {\n-\t\tattr_export_ignore = git_attr(\"export-ignore\");\n-\t\tattr_export_subst = git_attr(\"export-subst\");\n-\t}\n-\tcheck[0].attr = attr_export_ignore;\n-\tcheck[1].attr = attr_export_subst;\n-}\n-\n struct directory {\n \tstruct directory *up;\n \tstruct object_id oid;\n@@ -123,7 +110,7 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,\n \tstruct archiver_context *c = context;\n \tstruct archiver_args *args = c->args;\n \twrite_archive_entry_fn_t write_entry = c->write_entry;\n-\tstruct git_attr_check_elem check[2];\n+\tstatic struct git_attr_check *check;\n \tconst char *path_without_prefix;\n \tint err;\n \n@@ -137,11 +124,12 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,\n \t\tstrbuf_addch(&path, '/');\n \tpath_without_prefix = path.buf + args->baselen;\n \n-\tsetup_archive_check(check);\n-\tif (!git_check_attrs(path_without_prefix, ARRAY_SIZE(check), check)) {\n-\t\tif (ATTR_TRUE(check[0].value))\n+\tif (!check)\n+\t\tcheck = git_attr_check_initl(\"export-ignore\", \"export-subst\", NULL);\n+\tif (!git_check_attr(path_without_prefix, check)) {\n+\t\tif (ATTR_TRUE(check->check[0].value))\n \t\t\treturn 0;\n-\t\targs->convert = ATTR_TRUE(check[1].value);\n+\t\targs->convert = ATTR_TRUE(check->check[1].value);\n \t}\n \n \tif (S_ISDIR(mode) || S_ISGITLINK(mode)) {\ndiff --git a/attr.c b/attr.c\nindex 8aa346c..285fc58 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -825,3 +825,37 @@ void git_attr_set_direction(enum git_attr_direction new, struct index_state *ist\n \t\tdrop_attr_stack();\n \tuse_index = istate;\n }\n+\n+int git_check_attr(const char *path, struct git_attr_check *check)\n+{\n+\treturn git_check_attrs(path, check->check_nr, check->check);\n+}\n+\n+struct git_attr_check *git_attr_check_initl(const char *one, ...)\n+{\n+\tstruct git_attr_check *check;\n+\tint cnt;\n+\tva_list params;\n+\tconst char *param;\n+\n+\tva_start(params, one);\n+\tfor (cnt = 1; (param = va_arg(params, const char *)) != NULL; cnt++)\n+\t\t;\n+\tva_end(params);\n+\tcheck = xcalloc(1,\n+\t\t\tsizeof(*check) + cnt * sizeof(*(check->check)));\n+\tcheck->check_nr = cnt;\n+\tcheck->check = (struct git_attr_check_elem *)(check + 1);\n+\n+\tcheck->check[0].attr = git_attr(one);\n+\tva_start(params, one);\n+\tfor (cnt = 1; cnt < check->check_nr; cnt++) {\n+\t\tparam = va_arg(params, const char *);\n+\t\tif (!param)\n+\t\t\tdie(\"BUG: counted %d != ended at %d\",\n+\t\t\t    check->check_nr, cnt);\n+\t\tcheck->check[cnt].attr = git_attr(param);\n+\t}\n+\tva_end(params);\n+\treturn check;\n+}\ndiff --git a/attr.h b/attr.h\nindex cab82ec..3ed89e5 100644\n--- a/attr.h\n+++ b/attr.h\n@@ -29,6 +29,15 @@ struct git_attr_check_elem {\n \tconst char *value;\n };\n \n+struct git_attr_check {\n+\tint check_nr;\n+\tint check_alloc;\n+\tstruct git_attr_check_elem *check;\n+};\n+\n+extern struct git_attr_check *git_attr_check_initl(const char *, ...);\n+extern int git_check_attr(const char *path, struct git_attr_check *);\n+\n /*\n  * Return the name of the attribute represented by the argument.  The\n  * return value is a pointer to a null-delimited string that is part\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286716","messageId":"20160516210545.6591-11-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 10/12] attr: convert git_all_attrs() to use \"struct git_attr_check\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:43Z","receivedAt":"2016-05-16T21:05:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This updates the other two ways the attribute check is done via an\narray of \"struct git_attr_check_elem\" elements.  These two niches\nappear only in \"git check-attr\".\n\n * The caller does not know offhand what attributes it wants to ask\n   about and cannot use git_attr_check_initl() to prepare the\n   git_attr_check structure.\n\n * The caller may not know what attributes it wants to ask at all,\n   and instead wants to learn everything that the given path has.\n\nSuch a caller can call git_attr_check_alloc() to allocate an empty\ngit_attr_check, and then call git_attr_check_append() to add\nattribute names one by one.  A new attribute can be appended until\ngit_attr_check structure is \"finalized\", which happens when it is\nused to ask for attributes for any path by calling git_check_attr()\nor git_all_attrs().  A git_attr_check structure that is initialized\nby git_attr_check_initl() is already finalized when it is returned.\n\nI am not at all happy with the way git_all_attrs() API turned out to\nbe, but it is only to support one niche caller (\"check-attr --all\"),\nso I'll stop here for now.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n attr.c               | 70 +++++++++++++++++++++++++++++++++++++---------------\n attr.h               | 16 +++++++-----\n builtin/check-attr.c | 54 ++++++++++++++++++----------------------\n 3 files changed, 84 insertions(+), 56 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 285fc58..9c187bc 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -785,32 +785,21 @@ int git_check_attrs(const char *path, int num, struct git_attr_check_elem *check\n \treturn 0;\n }\n \n-int git_all_attrs(const char *path, int *num, struct git_attr_check_elem **check)\n+void git_all_attrs(const char *path, struct git_attr_check *check)\n {\n-\tint i, count, j;\n+\tint i;\n \n+\tgit_attr_check_clear(check);\n \tcollect_some_attrs(path, 0, NULL);\n \n-\t/* Count the number of attributes that are set. */\n-\tcount = 0;\n-\tfor (i = 0; i < attr_nr; i++) {\n-\t\tconst char *value = check_all_attr[i].value;\n-\t\tif (value != ATTR__UNSET && value != ATTR__UNKNOWN)\n-\t\t\t++count;\n-\t}\n-\t*num = count;\n-\tALLOC_ARRAY(*check, count);\n-\tj = 0;\n \tfor (i = 0; i < attr_nr; i++) {\n+\t\tconst char *name = check_all_attr[i].attr->name;\n \t\tconst char *value = check_all_attr[i].value;\n-\t\tif (value != ATTR__UNSET && value != ATTR__UNKNOWN) {\n-\t\t\t(*check)[j].attr = check_all_attr[i].attr;\n-\t\t\t(*check)[j].value = value;\n-\t\t\t++j;\n-\t\t}\n+\t\tif (value == ATTR__UNSET || value == ATTR__UNKNOWN)\n+\t\t\tcontinue;\n+\t\tgit_attr_check_append(check, name);\n+\t\tcheck->check[check->check_nr - 1].value = value;\n \t}\n-\n-\treturn 0;\n }\n \n void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate)\n@@ -828,6 +817,7 @@ void git_attr_set_direction(enum git_attr_direction new, struct index_state *ist\n \n int git_check_attr(const char *path, struct git_attr_check *check)\n {\n+\tcheck->finalized = 1;\n \treturn git_check_attrs(path, check->check_nr, check->check);\n }\n \n@@ -845,17 +835,57 @@ struct git_attr_check *git_attr_check_initl(const char *one, ...)\n \tcheck = xcalloc(1,\n \t\t\tsizeof(*check) + cnt * sizeof(*(check->check)));\n \tcheck->check_nr = cnt;\n+\tcheck->finalized = 1;\n \tcheck->check = (struct git_attr_check_elem *)(check + 1);\n \n \tcheck->check[0].attr = git_attr(one);\n \tva_start(params, one);\n \tfor (cnt = 1; cnt < check->check_nr; cnt++) {\n+\t\tstruct git_attr *attr;\n \t\tparam = va_arg(params, const char *);\n \t\tif (!param)\n \t\t\tdie(\"BUG: counted %d != ended at %d\",\n \t\t\t    check->check_nr, cnt);\n-\t\tcheck->check[cnt].attr = git_attr(param);\n+\t\tattr = git_attr(param);\n+\t\tif (!attr)\n+\t\t\tdie(\"BUG: %s: not a valid attribute name\", param);\n+\t\tcheck->check[cnt].attr = attr;\n \t}\n \tva_end(params);\n \treturn check;\n }\n+\n+struct git_attr_check *git_attr_check_alloc(void)\n+{\n+\treturn xcalloc(1, sizeof(struct git_attr_check));\n+}\n+\n+void git_attr_check_append(struct git_attr_check *check, const char *name)\n+{\n+\tstruct git_attr *attr;\n+\n+\tif (check->finalized)\n+\t\tdie(\"BUG: append after git_attr_check structure is finalized\");\n+\n+\tattr = git_attr(name);\n+\tif (!attr)\n+\t\tdie(\"%s: not a valid attribute name\", name);\n+\tALLOC_GROW(check->check, check->check_nr + 1, check->check_alloc);\n+\tcheck->check[check->check_nr++].attr = attr;\n+}\n+\n+void git_attr_check_clear(struct git_attr_check *check)\n+{\n+\tif ((void *)(check->check) == (void *)(check + 1))\n+\t\tdie(\"BUG: clearing a statically initialized git_attr_check\");\n+\tfree(check->check);\n+\tcheck->check_nr = 0;\n+\tcheck->check_alloc = 0;\n+\tcheck->finalized = 0;\n+}\n+\n+void git_attr_check_free(struct git_attr_check *check)\n+{\n+\tgit_attr_check_clear(check);\n+\tfree(check);\n+}\ndiff --git a/attr.h b/attr.h\nindex 3ed89e5..b5557ae 100644\n--- a/attr.h\n+++ b/attr.h\n@@ -30,6 +30,7 @@ struct git_attr_check_elem {\n };\n \n struct git_attr_check {\n+\tint finalized;\n \tint check_nr;\n \tint check_alloc;\n \tstruct git_attr_check_elem *check;\n@@ -38,6 +39,12 @@ struct git_attr_check {\n extern struct git_attr_check *git_attr_check_initl(const char *, ...);\n extern int git_check_attr(const char *path, struct git_attr_check *);\n \n+extern struct git_attr_check *git_attr_check_alloc(void);\n+extern void git_attr_check_append(struct git_attr_check *, const char *);\n+\n+extern void git_attr_check_clear(struct git_attr_check *);\n+extern void git_attr_check_free(struct git_attr_check *);\n+\n /*\n  * Return the name of the attribute represented by the argument.  The\n  * return value is a pointer to a null-delimited string that is part\n@@ -48,13 +55,10 @@ char *git_attr_name(struct git_attr *);\n int git_check_attrs(const char *path, int, struct git_attr_check_elem *);\n \n /*\n- * Retrieve all attributes that apply to the specified path.  *num\n- * will be set to the number of attributes on the path; **check will\n- * be set to point at a newly-allocated array of git_attr_check\n- * objects describing the attributes and their values.  *check must be\n- * free()ed by the caller.\n+ * Retrieve all attributes that apply to the specified path.\n+ * check holds the attributes and their values.\n  */\n-int git_all_attrs(const char *path, int *num, struct git_attr_check_elem **check);\n+void git_all_attrs(const char *path, struct git_attr_check *check);\n \n enum git_attr_direction {\n \tGIT_ATTR_CHECKIN,\ndiff --git a/builtin/check-attr.c b/builtin/check-attr.c\nindex 97e3837..700700f 100644\n--- a/builtin/check-attr.c\n+++ b/builtin/check-attr.c\n@@ -24,12 +24,13 @@ static const struct option check_attr_options[] = {\n \tOPT_END()\n };\n \n-static void output_attr(int cnt, struct git_attr_check_elem *check,\n-\t\t\tconst char *file)\n+static void output_attr(struct git_attr_check *check, const char *file)\n {\n \tint j;\n+\tint cnt = check->check_nr;\n+\n \tfor (j = 0; j < cnt; j++) {\n-\t\tconst char *value = check[j].value;\n+\t\tconst char *value = check->check[j].value;\n \n \t\tif (ATTR_TRUE(value))\n \t\t\tvalue = \"set\";\n@@ -42,36 +43,37 @@ static void output_attr(int cnt, struct git_attr_check_elem *check,\n \t\t\tprintf(\"%s%c\" /* path */\n \t\t\t       \"%s%c\" /* attrname */\n \t\t\t       \"%s%c\" /* attrvalue */,\n-\t\t\t       file, 0, git_attr_name(check[j].attr), 0, value, 0);\n+\t\t\t       file, 0,\n+\t\t\t       git_attr_name(check->check[j].attr), 0, value, 0);\n \t\t} else {\n \t\t\tquote_c_style(file, NULL, stdout, 0);\n-\t\t\tprintf(\": %s: %s\\n\", git_attr_name(check[j].attr), value);\n+\t\t\tprintf(\": %s: %s\\n\",\n+\t\t\t       git_attr_name(check->check[j].attr), value);\n \t\t}\n-\n \t}\n }\n \n static void check_attr(const char *prefix,\n-\t\t       int cnt, struct git_attr_check_elem *check,\n+\t\t       struct git_attr_check *check,\n \t\t       const char *file)\n {\n \tchar *full_path =\n \t\tprefix_path(prefix, prefix ? strlen(prefix) : 0, file);\n \tif (check != NULL) {\n-\t\tif (git_check_attrs(full_path, cnt, check))\n-\t\t\tdie(\"git_check_attrs died\");\n-\t\toutput_attr(cnt, check, file);\n+\t\tif (git_check_attr(full_path, check))\n+\t\t\tdie(\"git_check_attr died\");\n+\t\toutput_attr(check, file);\n \t} else {\n-\t\tif (git_all_attrs(full_path, &cnt, &check))\n-\t\t\tdie(\"git_all_attrs died\");\n-\t\toutput_attr(cnt, check, file);\n-\t\tfree(check);\n+\t\tcheck = git_attr_check_alloc();\n+\t\tgit_all_attrs(full_path, check);\n+\t\toutput_attr(check, file);\n+\t\tgit_attr_check_free(check);\n \t}\n \tfree(full_path);\n }\n \n static void check_attr_stdin_paths(const char *prefix,\n-\t\t\t\t   int cnt, struct git_attr_check_elem *check)\n+\t\t\t\t   struct git_attr_check *check)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct strbuf unquoted = STRBUF_INIT;\n@@ -85,7 +87,7 @@ static void check_attr_stdin_paths(const char *prefix,\n \t\t\t\tdie(\"line is badly quoted\");\n \t\t\tstrbuf_swap(&buf, &unquoted);\n \t\t}\n-\t\tcheck_attr(prefix, cnt, check, buf.buf);\n+\t\tcheck_attr(prefix, check, buf.buf);\n \t\tmaybe_flush_or_die(stdout, \"attribute to stdout\");\n \t}\n \tstrbuf_release(&buf);\n@@ -100,7 +102,7 @@ static NORETURN void error_with_usage(const char *msg)\n \n int cmd_check_attr(int argc, const char **argv, const char *prefix)\n {\n-\tstruct git_attr_check_elem *check;\n+\tstruct git_attr_check *check;\n \tint cnt, i, doubledash, filei;\n \n \tif (!is_bare_repository())\n@@ -163,24 +165,16 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)\n \tif (all_attrs) {\n \t\tcheck = NULL;\n \t} else {\n-\t\tcheck = xcalloc(cnt, sizeof(*check));\n-\t\tfor (i = 0; i < cnt; i++) {\n-\t\t\tconst char *name;\n-\t\t\tstruct git_attr *a;\n-\t\t\tname = argv[i];\n-\t\t\ta = git_attr(name);\n-\t\t\tif (!a)\n-\t\t\t\treturn error(\"%s: not a valid attribute name\",\n-\t\t\t\t\tname);\n-\t\t\tcheck[i].attr = a;\n-\t\t}\n+\t\tcheck = git_attr_check_alloc();\n+\t\tfor (i = 0; i < cnt; i++)\n+\t\t\tgit_attr_check_append(check, argv[i]);\n \t}\n \n \tif (stdin_paths)\n-\t\tcheck_attr_stdin_paths(prefix, cnt, check);\n+\t\tcheck_attr_stdin_paths(prefix, check);\n \telse {\n \t\tfor (i = filei; i < argc; i++)\n-\t\t\tcheck_attr(prefix, cnt, check, argv[i]);\n+\t\t\tcheck_attr(prefix, check, argv[i]);\n \t\tmaybe_flush_or_die(stdout, \"attribute to stdout\");\n \t}\n \treturn 0;\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286719","messageId":"20160516210545.6591-12-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 11/12] attr: convert git_check_attrs() callers to use the new API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:44Z","receivedAt":"2016-05-16T21:05:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The remaining callers are all simple \"I have N attributes I am\ninterested in.  I'll ask about them with various paths one by one\".\n\nAfter this step, no caller to git_check_attrs() remains.  After\nremoving it, we can extend \"struct git_attr_check\" struct with data\nthat can be used in optimizing the query for the specific N\nattributes it contains.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/pack-objects.c | 19 +++++--------------\n convert.c              | 18 +++++++-----------\n ll-merge.c             | 33 ++++++++++++++-------------------\n userdiff.c             | 19 ++++++++-----------\n ws.c                   | 19 ++++++-------------\n 5 files changed, 40 insertions(+), 68 deletions(-)\n\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 167c301..c6c2a69 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -886,24 +886,15 @@ static void write_pack_file(void)\n \t\t\twritten, nr_result);\n }\n \n-static void setup_delta_attr_check(struct git_attr_check_elem *check)\n-{\n-\tstatic struct git_attr *attr_delta;\n-\n-\tif (!attr_delta)\n-\t\tattr_delta = git_attr(\"delta\");\n-\n-\tcheck[0].attr = attr_delta;\n-}\n-\n static int no_try_delta(const char *path)\n {\n-\tstruct git_attr_check_elem check[1];\n+\tstatic struct git_attr_check *check;\n \n-\tsetup_delta_attr_check(check);\n-\tif (git_check_attrs(path, ARRAY_SIZE(check), check))\n+\tif (!check)\n+\t\tcheck = git_attr_check_initl(\"delta\", NULL);\n+\tif (git_check_attr(path, check))\n \t\treturn 0;\n-\tif (ATTR_FALSE(check->value))\n+\tif (ATTR_FALSE(check->check[0].value))\n \t\treturn 1;\n \treturn 0;\n }\ndiff --git a/convert.c b/convert.c\nindex 058da86..510d1b9 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -760,24 +760,20 @@ struct conv_attrs {\n \tint ident;\n };\n \n-static const char *conv_attr_name[] = {\n-\t\"crlf\", \"ident\", \"filter\", \"eol\", \"text\",\n-};\n-#define NUM_CONV_ATTRS ARRAY_SIZE(conv_attr_name)\n-\n static void convert_attrs(struct conv_attrs *ca, const char *path)\n {\n-\tint i;\n-\tstatic struct git_attr_check_elem ccheck[NUM_CONV_ATTRS];\n+\tstatic struct git_attr_check *check;\n \n-\tif (!ccheck[0].attr) {\n-\t\tfor (i = 0; i < NUM_CONV_ATTRS; i++)\n-\t\t\tccheck[i].attr = git_attr(conv_attr_name[i]);\n+\tif (!check) {\n+\t\tcheck = git_attr_check_initl(\"crlf\", \"ident\",\n+\t\t\t\t\t     \"filter\", \"eol\", \"text\",\n+\t\t\t\t\t     NULL);\n \t\tuser_convert_tail = &user_convert;\n \t\tgit_config(read_convert_config, NULL);\n \t}\n \n-\tif (!git_check_attrs(path, NUM_CONV_ATTRS, ccheck)) {\n+\tif (!git_check_attr(path, check)) {\n+\t\tstruct git_attr_check_elem *ccheck = check->check;\n \t\tca->crlf_action = git_path_check_crlf(ccheck + 4);\n \t\tif (ca->crlf_action == CRLF_UNDEFINED)\n \t\t\tca->crlf_action = git_path_check_crlf(ccheck + 0);\ndiff --git a/ll-merge.c b/ll-merge.c\nindex 772b14e..d4380c4 100644\n--- a/ll-merge.c\n+++ b/ll-merge.c\n@@ -334,15 +334,6 @@ static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr\n \treturn &ll_merge_drv[LL_TEXT_MERGE];\n }\n \n-static int git_path_check_merge(const char *path, struct git_attr_check_elem check[2])\n-{\n-\tif (!check[0].attr) {\n-\t\tcheck[0].attr = git_attr(\"merge\");\n-\t\tcheck[1].attr = git_attr(\"conflict-marker-size\");\n-\t}\n-\treturn git_check_attrs(path, 2, check);\n-}\n-\n static void normalize_file(mmfile_t *mm, const char *path)\n {\n \tstruct strbuf strbuf = STRBUF_INIT;\n@@ -360,7 +351,7 @@ int ll_merge(mmbuffer_t *result_buf,\n \t     mmfile_t *theirs, const char *their_label,\n \t     const struct ll_merge_options *opts)\n {\n-\tstatic struct git_attr_check_elem check[2];\n+\tstatic struct git_attr_check *check;\n \tstatic const struct ll_merge_options default_opts;\n \tconst char *ll_driver_name = NULL;\n \tint marker_size = DEFAULT_CONFLICT_MARKER_SIZE;\n@@ -374,10 +365,14 @@ int ll_merge(mmbuffer_t *result_buf,\n \t\tnormalize_file(ours, path);\n \t\tnormalize_file(theirs, path);\n \t}\n-\tif (!git_path_check_merge(path, check)) {\n-\t\tll_driver_name = check[0].value;\n-\t\tif (check[1].value) {\n-\t\t\tmarker_size = atoi(check[1].value);\n+\n+\tif (!check)\n+\t\tcheck = git_attr_check_initl(\"merge\", \"conflict-marker-size\", NULL);\n+\n+\tif (!git_check_attr(path, check)) {\n+\t\tll_driver_name = check->check[0].value;\n+\t\tif (check->check[1].value) {\n+\t\t\tmarker_size = atoi(check->check[1].value);\n \t\t\tif (marker_size <= 0)\n \t\t\t\tmarker_size = DEFAULT_CONFLICT_MARKER_SIZE;\n \t\t}\n@@ -392,13 +387,13 @@ int ll_merge(mmbuffer_t *result_buf,\n \n int ll_merge_marker_size(const char *path)\n {\n-\tstatic struct git_attr_check_elem check;\n+\tstatic struct git_attr_check *check;\n \tint marker_size = DEFAULT_CONFLICT_MARKER_SIZE;\n \n-\tif (!check.attr)\n-\t\tcheck.attr = git_attr(\"conflict-marker-size\");\n-\tif (!git_check_attrs(path, 1, &check) && check.value) {\n-\t\tmarker_size = atoi(check.value);\n+\tif (!check)\n+\t\tcheck = git_attr_check_initl(\"conflict-marker-size\", NULL);\n+\tif (!git_check_attr(path, check) && check->check[0].value) {\n+\t\tmarker_size = atoi(check->check[0].value);\n \t\tif (marker_size <= 0)\n \t\t\tmarker_size = DEFAULT_CONFLICT_MARKER_SIZE;\n \t}\ndiff --git a/userdiff.c b/userdiff.c\nindex 3560024..5473063 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -250,25 +250,22 @@ struct userdiff_driver *userdiff_find_by_name(const char *name) {\n \n struct userdiff_driver *userdiff_find_by_path(const char *path)\n {\n-\tstatic struct git_attr *attr;\n-\tstruct git_attr_check_elem check;\n-\n-\tif (!attr)\n-\t\tattr = git_attr(\"diff\");\n-\tcheck.attr = attr;\n+\tstatic struct git_attr_check *check;\n \n+\tif (!check)\n+\t\tcheck = git_attr_check_initl(\"diff\", NULL);\n \tif (!path)\n \t\treturn NULL;\n-\tif (git_check_attrs(path, 1, &check))\n+\tif (git_check_attr(path, check))\n \t\treturn NULL;\n \n-\tif (ATTR_TRUE(check.value))\n+\tif (ATTR_TRUE(check->check[0].value))\n \t\treturn &driver_true;\n-\tif (ATTR_FALSE(check.value))\n+\tif (ATTR_FALSE(check->check[0].value))\n \t\treturn &driver_false;\n-\tif (ATTR_UNSET(check.value))\n+\tif (ATTR_UNSET(check->check[0].value))\n \t\treturn NULL;\n-\treturn userdiff_find_by_name(check.value);\n+\treturn userdiff_find_by_name(check->check[0].value);\n }\n \n struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver)\ndiff --git a/ws.c b/ws.c\nindex 7350905..bb3270c 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -71,24 +71,17 @@ unsigned parse_whitespace_rule(const char *string)\n \treturn rule;\n }\n \n-static void setup_whitespace_attr_check(struct git_attr_check_elem *check)\n-{\n-\tstatic struct git_attr *attr_whitespace;\n-\n-\tif (!attr_whitespace)\n-\t\tattr_whitespace = git_attr(\"whitespace\");\n-\tcheck[0].attr = attr_whitespace;\n-}\n-\n unsigned whitespace_rule(const char *pathname)\n {\n-\tstruct git_attr_check_elem attr_whitespace_rule;\n+\tstatic struct git_attr_check *attr_whitespace_rule;\n+\n+\tif (!attr_whitespace_rule)\n+\t\tattr_whitespace_rule = git_attr_check_initl(\"whitespace\", NULL);\n \n-\tsetup_whitespace_attr_check(&attr_whitespace_rule);\n-\tif (!git_check_attrs(pathname, 1, &attr_whitespace_rule)) {\n+\tif (!git_check_attr(pathname, attr_whitespace_rule)) {\n \t\tconst char *value;\n \n-\t\tvalue = attr_whitespace_rule.value;\n+\t\tvalue = attr_whitespace_rule->check[0].value;\n \t\tif (ATTR_TRUE(value)) {\n \t\t\t/* true (whitespace) */\n \t\t\tunsigned all_rule = ws_tab_width(whitespace_rule_cfg);\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286718","messageId":"20160516210545.6591-13-gitster@pobox.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"[PATCH v2 12/12] attr: retire git_check_attrs() API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T21:05:45Z","receivedAt":"2016-05-16T21:05:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Since nobody uses the old API, make it file-scope static, and update\nthe documentation to describe the new API.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/technical/api-gitattributes.txt | 62 +++++++++++++++------------\n attr.c                                        |  3 +-\n attr.h                                        |  2 -\n 3 files changed, 36 insertions(+), 31 deletions(-)\n\ndiff --git a/Documentation/technical/api-gitattributes.txt b/Documentation/technical/api-gitattributes.txt\nindex 2602668..6f13f94 100644\n--- a/Documentation/technical/api-gitattributes.txt\n+++ b/Documentation/technical/api-gitattributes.txt\n@@ -16,10 +16,15 @@ Data Structure\n \tof no interest to the calling programs.  The name of the\n \tattribute can be retrieved by calling `git_attr_name()`.\n \n+`struct git_attr_check_elem`::\n+\n+\tThis structure represents one attribute and its value.\n+\n `struct git_attr_check`::\n \n-\tThis structure represents a set of attributes to check in a call\n-\tto `git_check_attr()` function, and receives the results.\n+\tThis structure represents a collection of `git_attr_check_elem`.\n+\tIt is passed to `git_check_attr()` function, specifying the\n+\tattributes to check, and receives their values.\n \n \n Attribute Values\n@@ -48,49 +53,47 @@ value of the attribute for the path.\n Querying Specific Attributes\n ----------------------------\n \n-* Prepare an array of `struct git_attr_check` to define the list of\n-  attributes you would want to check.  To populate this array, you would\n-  need to define necessary attributes by calling `git_attr()` function.\n+* Prepare `struct git_attr_check` using git_attr_check_initl()\n+  function, enumerating the names of attributes whose values you are\n+  interested in, terminated with a NULL pointer.\n \n * Call `git_check_attr()` to check the attributes for the path.\n \n-* Inspect `git_attr_check` structure to see how each of the attribute in\n-  the array is defined for the path.\n+* Inspect `git_attr_check` structure to see how each of the\n+  attribute in the array is defined for the path.\n \n \n Example\n -------\n \n-To see how attributes \"crlf\" and \"indent\" are set for different paths.\n+To see how attributes \"crlf\" and \"ident\" are set for different paths.\n \n-. Prepare an array of `struct git_attr_check` with two elements (because\n-  we are checking two attributes).  Initialize their `attr` member with\n-  pointers to `struct git_attr` obtained by calling `git_attr()`:\n+. Prepare a `struct git_attr_check` with two elements (because\n+  we are checking two attributes):\n \n ------------\n-static struct git_attr_check check[2];\n+static struct git_attr_check *check;\n static void setup_check(void)\n {\n-\tif (check[0].attr)\n+\tif (check)\n \t\treturn; /* already done */\n-\tcheck[0].attr = git_attr(\"crlf\");\n-\tcheck[1].attr = git_attr(\"ident\");\n+\tcheck = git_attr_check_initl(\"crlf\", \"ident\", NULL);\n }\n ------------\n \n-. Call `git_check_attr()` with the prepared array of `struct git_attr_check`:\n+. Call `git_check_attr()` with the prepared `struct git_attr_check`:\n \n ------------\n \tconst char *path;\n \n \tsetup_check();\n-\tgit_check_attr(path, ARRAY_SIZE(check), check);\n+\tgit_check_attr(path, check);\n ------------\n \n-. Act on `.value` member of the result, left in `check[]`:\n+. Act on `.value` member of the result, left in `check->check[]`:\n \n ------------\n-\tconst char *value = check[0].value;\n+\tconst char *value = check->check[0].value;\n \n \tif (ATTR_TRUE(value)) {\n \t\tThe attribute is Set, by listing only the name of the\n@@ -115,14 +118,17 @@ Querying All Attributes\n \n To get the values of all attributes associated with a file:\n \n-* Call `git_all_attrs()`, which returns an array of `git_attr_check`\n-  structures.\n+* Prepare an empty `git_attr_check` structure by calling\n+  `git_attr_check_alloc()`.\n+\n+* Call `git_all_attrs()`, which populates the `git_attr_check`\n+  with the attributes attached to the path.\n \n-* Iterate over the `git_attr_check` array to examine the attribute\n-  names and values.  The name of the attribute described by a\n-  `git_attr_check` object can be retrieved via\n-  `git_attr_name(check[i].attr)`.  (Please note that no items will be\n-  returned for unset attributes, so `ATTR_UNSET()` will return false\n-  for all returned `git_array_check` objects.)\n+* Iterate over the `git_attr_check.check[]` array to examine\n+  the attribute names and values.  The name of the attribute\n+  described by a  `git_attr_check.check[]` object can be retrieved via\n+  `git_attr_name(check->check[i].attr)`.  (Please note that no items\n+  will be returned for unset attributes, so `ATTR_UNSET()` will return\n+  false for all returned `git_array_check` objects.)\n \n-* Free the `git_array_check` array.\n+* Free the `git_array_check` by calling `git_attr_check_free()`.\ndiff --git a/attr.c b/attr.c\nindex 9c187bc..c3295a9 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -769,7 +769,8 @@ static void collect_some_attrs(const char *path, int num,\n \t\trem = fill(path, pathlen, basename_offset, stk, rem);\n }\n \n-int git_check_attrs(const char *path, int num, struct git_attr_check_elem *check)\n+static int git_check_attrs(const char *path, int num,\n+\t\t\t   struct git_attr_check_elem *check)\n {\n \tint i;\n \ndiff --git a/attr.h b/attr.h\nindex b5557ae..7dc49f8 100644\n--- a/attr.h\n+++ b/attr.h\n@@ -52,8 +52,6 @@ extern void git_attr_check_free(struct git_attr_check *);\n  */\n char *git_attr_name(struct git_attr *);\n \n-int git_check_attrs(const char *path, int, struct git_attr_check_elem *);\n-\n /*\n  * Retrieve all attributes that apply to the specified path.\n  * check holds the attributes and their values.\n-- \n2.8.2-748-gfb85f76\n"},{"id":"286737","messageId":"CAGZ79kbeXCsCs35hva2FWUhOD2F37ZDrzupEZLGBVbqnMxPjXQ@mail.gmail.com","threadId":"42348","inReplyTo":"20160516210545.6591-2-gitster@pobox.com","subject":"Re: [PATCH v2 01/12] commit.c: use strchrnul() to scan for one line","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-05-16T23:19:45Z","receivedAt":"2016-05-16T23:19:45Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, May 16, 2016 at 2:05 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  commit.c | 3 +--\n>  1 file changed, 1 insertion(+), 2 deletions(-)\n>\n> diff --git a/commit.c b/commit.c\n> index 3f4f371..1f9ee8a 100644\n> --- a/commit.c\n> +++ b/commit.c\n> @@ -415,8 +415,7 @@ int find_commit_subject(const char *commit_buffer, const char **subject)\n>                 p++;\n>         if (*p) {\n>                 p += 2;\n> -               for (eol = p; *eol && *eol != '\\n'; eol++)\n> -                       ; /* do nothing */\n> +               eol = strchrnul(p, '\\n');\n\nNit:\nYou could include the +2 into the arg of  strchrnul,\nsuch that you can drop the braces.\n\n>         } else\n>                 eol = p;\n>\n> --\n> 2.8.2-748-gfb85f76\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"286738","messageId":"CAGZ79kbSFp5e6-AeSbqk4hFxcuzTo-4oSG_z5nGTpAY0uvLAuw@mail.gmail.com","threadId":"42348","inReplyTo":"20160516210545.6591-3-gitster@pobox.com","subject":"Re: [PATCH v2 02/12] attr.c: use strchrnul() to scan for one line","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-05-16T23:28:54Z","receivedAt":"2016-05-16T23:28:54Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, May 16, 2016 at 2:05 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  attr.c | 4 ++--\n>  1 file changed, 2 insertions(+), 2 deletions(-)\n>\n> diff --git a/attr.c b/attr.c\n> index eec5d7d..45aec1b 100644\n> --- a/attr.c\n> +++ b/attr.c\n> @@ -402,8 +402,8 @@ static struct attr_stack *read_attr_from_index(const char *path, int macro_ok)\n>         for (sp = buf; *sp; ) {\n>                 char *ep;\n>                 int more;\n> -               for (ep = sp; *ep && *ep != '\\n'; ep++)\n> -                       ;\n> +\n> +               ep = strchrnul(sp, '\\n');\n\n(Even lesser nit as in patch 1)\nYou could directly assign ep and more when declaring them.\n\n>                 more = (*ep == '\\n');\n>                 *ep = '\\0';\n>                 handle_attr_line(res, sp, path, ++lineno, macro_ok);\n> --\n> 2.8.2-748-gfb85f76\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"286739","messageId":"CAGZ79kZW6GRC8As5oX6CXbTarcU86x_+e8=ksABQubXJGkVhiQ@mail.gmail.com","threadId":"42348","inReplyTo":"20160516210545.6591-4-gitster@pobox.com","subject":"Re: [PATCH v2 03/12] attr.c: update a stale comment on \"struct match_attr\"","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-05-16T23:34:53Z","receivedAt":"2016-05-16T23:34:53Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, May 16, 2016 at 2:05 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> When 82dce998 (attr: more matching optimizations from .gitignore,\n> 2012-10-15) changed a pointer to a string \"*pattern\" into an\n> embedded \"struct pattern\" in struct match_attr, it forgot to update\n> the comment that describes the structure.\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  attr.c | 5 ++---\n>  1 file changed, 2 insertions(+), 3 deletions(-)\n>\n> diff --git a/attr.c b/attr.c\n> index 45aec1b..4ae7801 100644\n> --- a/attr.c\n> +++ b/attr.c\n> @@ -131,9 +131,8 @@ struct pattern {\n>   * If is_macro is true, then u.attr is a pointer to the git_attr being\n>   * defined.\n>   *\n> - * If is_macro is false, then u.pattern points at the filename pattern\n> - * to which the rule applies.  (The memory pointed to is part of the\n> - * memory block allocated for the match_attr instance.)\n> + * If is_macro is false, then u.pat is the filename pattern to which the\n> + * rule applies.\n\nand we don't need to talk about memory ownership here as that\nis clear for 'struct pattern' documented elsewhere?\n\n>   *\n>   * In either case, num_attr is the number of attributes affected by\n>   * this rule, and state is an array listing them.  The attributes are\n> --\n> 2.8.2-748-gfb85f76\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"286740","messageId":"CAPc5daU248vqPwWiNzJf3gKJnO=SqngUSpT-BEdT2t91ScbBCg@mail.gmail.com","threadId":"42348","inReplyTo":"CAGZ79kZW6GRC8As5oX6CXbTarcU86x_+e8=ksABQubXJGkVhiQ@mail.gmail.com","subject":"Re: [PATCH v2 03/12] attr.c: update a stale comment on \"struct match_attr\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T23:39:52Z","receivedAt":"2016-05-16T23:39:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Mon, May 16, 2016 at 4:34 PM, Stefan Beller <sbeller@google.com> wrote:\n>> - * If is_macro is false, then u.pattern points at the filename pattern\n>> - * to which the rule applies.  (The memory pointed to is part of the\n>> - * memory block allocated for the match_attr instance.)\n>> + * If is_macro is false, then u.pat is the filename pattern to which the\n>> + * rule applies.\n>\n> and we don't need to talk about memory ownership here as that\n> is clear for 'struct pattern' documented elsewhere?\n\nIt is embedded in the structure which you cannot lose independently, no?\n"},{"id":"286742","messageId":"CAPc5daXM9a=W2R6YE=4PSuHWKXoZs_-VT+48ajh3mq1RoqYncA@mail.gmail.com","threadId":"42348","inReplyTo":"CAGZ79kbeXCsCs35hva2FWUhOD2F37ZDrzupEZLGBVbqnMxPjXQ@mail.gmail.com","subject":"Re: [PATCH v2 01/12] commit.c: use strchrnul() to scan for one line","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T23:41:57Z","receivedAt":"2016-05-16T23:41:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Mon, May 16, 2016 at 4:19 PM, Stefan Beller <sbeller@google.com> wrote:\n>>         if (*p) {\n>>                 p += 2;\n>> -               for (eol = p; *eol && *eol != '\\n'; eol++)\n>> -                       ; /* do nothing */\n>> +               eol = strchrnul(p, '\\n');\n>\n> Nit:\n> You could include the +2 into the arg of  strchrnul,\n> such that you can drop the braces.\n\nYou're right. With or without braces,\n\n  eol = strchrnul(p + 2, '\\n');\n\nwould be easier to understand (I do not know offhand if the value of p\nmatters after this part, as I am responding from GMail Web UI without\naccess to the wider context in the source, though).\n"},{"id":"286743","messageId":"CAPc5daXz59fX6MGfmAZ31GR_=Ph=_zZY35W4vfXkD1noK571dg@mail.gmail.com","threadId":"42348","inReplyTo":"CAGZ79kbSFp5e6-AeSbqk4hFxcuzTo-4oSG_z5nGTpAY0uvLAuw@mail.gmail.com","subject":"Re: [PATCH v2 02/12] attr.c: use strchrnul() to scan for one line","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-16T23:45:13Z","receivedAt":"2016-05-16T23:45:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Mon, May 16, 2016 at 4:28 PM, Stefan Beller <sbeller@google.com> wrote:\n>>         for (sp = buf; *sp; ) {\n>>                 char *ep;\n>>                 int more;\n>> -               for (ep = sp; *ep && *ep != '\\n'; ep++)\n>> -                       ;\n>> +\n>> +               ep = strchrnul(sp, '\\n');\n>\n> (Even lesser nit as in patch 1)\n> You could directly assign ep and more when declaring them.\n\nI'd prefer not to.\n\n\"A declaration block, blank and statement\" pattern allows you\nto have the declaration of variables to group related things\ntogether and in an order that makes it easier to explain.\nIf you throw in initialization there, you cannot do that anymore\n(e.g. ep must be initialized before you can compute more).\n"},{"id":"286744","messageId":"CAGZ79kZMsHrw+B29i6e4eWjVhEgH01H8QuNPA9_-CikGmQKVvQ@mail.gmail.com","threadId":"42348","inReplyTo":"CAPc5daXM9a=W2R6YE=4PSuHWKXoZs_-VT+48ajh3mq1RoqYncA@mail.gmail.com","subject":"Re: [PATCH v2 01/12] commit.c: use strchrnul() to scan for one line","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-05-16T23:46:56Z","receivedAt":"2016-05-16T23:46:56Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, May 16, 2016 at 4:41 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> On Mon, May 16, 2016 at 4:19 PM, Stefan Beller <sbeller@google.com> wrote:\n>>>         if (*p) {\n>>>                 p += 2;\n>>> -               for (eol = p; *eol && *eol != '\\n'; eol++)\n>>> -                       ; /* do nothing */\n>>> +               eol = strchrnul(p, '\\n');\n>>\n>> Nit:\n>> You could include the +2 into the arg of  strchrnul,\n>> such that you can drop the braces.\n>\n> You're right. With or without braces,\n>\n>   eol = strchrnul(p + 2, '\\n');\n>\n> would be easier to understand (I do not know offhand if the value of p\n> matters after this part, as I am responding from GMail Web UI without\n> access to the wider context in the source, though).\n\nHeh, good point, I did not think it through apparently. `p` matters.\n"},{"id":"286775","messageId":"CAPig+cR5ksdATefkYkG7-+a_nK0w+Fdu8ae-Z4=PTp8yAYhiQQ@mail.gmail.com","threadId":"42348","inReplyTo":"20160516210545.6591-10-gitster@pobox.com","subject":"Re: [PATCH v2 09/12] attr: (re)introduce git_check_attr() and struct git_attr_check","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-05-17T04:19:59Z","receivedAt":"2016-05-17T04:19:59Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, May 16, 2016 at 5:05 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> A common pattern to check N attributes for many paths is to\n>\n>  (1) prepare an array A of N git_attr_check_elem items;\n>  (2) call git_attr() to intern the N attribute names and fill A;\n>  (3) repeatedly call git_check_attrs() for path with N and A;\n> [...snip...]\n> Introduce \"struct git_attr_check\" that contains N, the number of\n> attributes being sought, and A, the array that holds N\n> git_attr_check_elem items, and a function git_check_attr() that\n> takes a path P and this structure as its parameters.  This structure\n> can later be extended to hold extra data necessary for optimization.\n>\n> Also, to make it easier to write the first two steps in common\n> cases, introduce git_attr_check_initl() helper function, which takes\n> a NULL-terminated list of attribute names and initialize this\n> structure.\n> [...snip...]\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n> diff --git a/attr.c b/attr.c\n> @@ -825,3 +825,37 @@ void git_attr_set_direction(enum git_attr_direction new, struct index_state *ist\n> +struct git_attr_check *git_attr_check_initl(const char *one, ...)\n> +{\n> +       [...]\n> +       check = xcalloc(1,\n> +                       sizeof(*check) + cnt * sizeof(*(check->check)));\n> +       check->check_nr = cnt;\n> +       check->check = (struct git_attr_check_elem *)(check + 1);\n> +       [...]\n> +       return check;\n> +}\n> diff --git a/attr.h b/attr.h\n> @@ -29,6 +29,15 @@ struct git_attr_check_elem {\n> +struct git_attr_check {\n> +       int check_nr;\n> +       int check_alloc;\n\nThis field is unused, and git_attr_check_initl() neglects to\ninitialize it (if it is intended to be used in the future).\n\n> +       struct git_attr_check_elem *check;\n> +};\n"},{"id":"286778","messageId":"xmqqr3d1xoho.fsf@gitster.mtv.corp.google.com","threadId":"42348","inReplyTo":"CAPig+cR5ksdATefkYkG7-+a_nK0w+Fdu8ae-Z4=PTp8yAYhiQQ@mail.gmail.com","subject":"Re: [PATCH v2 09/12] attr: (re)introduce git_check_attr() and struct git_attr_check","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-17T05:06:11Z","receivedAt":"2016-05-17T05:06:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> This field is unused, and git_attr_check_initl() neglects to\n> initialize it (if it is intended to be used in the future).\n\nThere are two patterns to use the new API:\n\n - initl() variant is \"initialize it once, never extend\"; it is very\n   much deliberate.  This is used by the majority of current\n   callers, that know that they want a fixed set of attributes and\n   can say what attributes they want upfront.\n\n - alloc then append variant will use the usual ALLOC_GROW() pattern\n   with nr/alloc.\n"},{"id":"286798","messageId":"xmqqeg90y5yz.fsf@gitster.mtv.corp.google.com","threadId":"42348","inReplyTo":"20160516210545.6591-11-gitster@pobox.com","subject":"Re: [PATCH v2 10/12] attr: convert git_all_attrs() to use \"struct git_attr_check\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-17T17:00:52Z","receivedAt":"2016-05-17T17:00:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> +struct git_attr_check *git_attr_check_alloc(void)\n> +{\n> +\treturn xcalloc(1, sizeof(struct git_attr_check));\n> +}\n> +\n> +void git_attr_check_append(struct git_attr_check *check, const char *name)\n> +{\n> +\tstruct git_attr *attr;\n> +\n> +\tif (check->finalized)\n> +\t\tdie(\"BUG: append after git_attr_check structure is finalized\");\n> +\n> +\tattr = git_attr(name);\n> +\tif (!attr)\n> +\t\tdie(\"%s: not a valid attribute name\", name);\n> +\tALLOC_GROW(check->check, check->check_nr + 1, check->check_alloc);\n> +\tcheck->check[check->check_nr++].attr = attr;\n> +}\n\nGiven that one of the two expected callers, namely, \"check-attr\" and\nStefan's pathspec label magic, of this \"alloc and then append\" side\nof the API wants to have an access to git_attr(name), I think\nthe function signature for this one should be updated to take not\n\"const char *name\" but instead take \"struct git_attr *attr\", i.e.\n\nvoid git_attr_check_append(struct git_attr_check *check,\n\t\t\t   struct git_attr *attr);\n\nThen the loop in check-attr below\n\n> +\t\tcheck = git_attr_check_alloc();\n> +\t\tfor (i = 0; i < cnt; i++)\n> +\t\t\tgit_attr_check_append(check, argv[i]);\n\nwould become\n\n\t\tcheck = git_attr_check_alloc();\n\t\tfor (i = 0; i < cnt; i++)\n\t\t\tgit_attr_check_append(check, git_attr(argv[i]));\n\nAnd this part in $gmane/294855\n\n    Then while parsing \":(attr:VAR1=VAL1 -VAR2 VAR3...)path/to/dir/\",\n    you would first do:\n\n            p->attr_check = git_attr_check_alloc();\n\n    once, and then send each of VAR1=VAL2, -VAR2, VAR3... to your\n    parse_one_item() helper function which would:\n\n     * parse the match-mode like your code does;\n\n     * parse out the attribute name (i.e. VAR1, VAR2 and VAR3), and\n       instead of keeping it as a \"(const) char *\", call git_attr()\n       to intern it (and keep it in local variable \"attr\"), and save\n       it in p->attr_match[p->attr_nr].attr;\n\n     * call git_attr_check_append(p->attr_check, git_attr_name(attr))\n\nwould look like\n\n\t... saw \":(attr:\", expect \"VAR1=VAL1 -VAR2...)\" to follow;\n        ... char *scan points at byte after attr: now.\n\n\titem->attr_check = git_attr_check_alloc();\n\twhile (scan) {\n\t\tconst char *var, *val;\n                enum attr_match_mode mode;\n\t\tstruct git_attr *attr;\n\t\tstruct attr_match *match;\n\n        \tscan = parse_attr_match(scan, &var, &val, &mode);\n                ... var points at VAR1, val points at VAL,\n                ... mode becomes MATCH_VALUE; scan skips\n                ... forward to pint at -VAR2.  parse_attr_match()\n                ... would return NULL once input runs out.\n\n                ALLOC_GROW(item->attr_match, ...);\n                match = &item->attr_match[item->attr_match_nr++];\n\n        \tattr = git_attr(var);\n\n\t\tmatch->attr = attr;\n                match->value = val;\n                match->match_mode = mode;\n                \n                git_attr_check_append(item->attr_check, attr);\n\t}\n\nThen matching part would get the \"item\" and would do:\n\n\t... after making sure that the path matches\n        ... the pathspec the magic is attached to ...\n\n\tgit_check_attr(path, item->attr_check);\n\tfor (i = 0; i < item->attr_match_nr; i++) {\n\t\tstruct git_attr *attr = item->attr_check->check[i].attr;\n\t\tconst char *value = item->attr_check->check[i].value;\n                struct attr_match *match = &item->attr_match[i];\n\n                ... compare what \"match\" expects with <attr, value> ...\n        }\n"},{"id":"286836","messageId":"xmqq7fesv1d6.fsf@gitster.mtv.corp.google.com","threadId":"42348","inReplyTo":"xmqqeg90y5yz.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 10/12] attr: convert git_all_attrs() to use \"struct git_attr_check\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-17T21:08:37Z","receivedAt":"2016-05-17T21:08:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Given that one of the two expected callers, namely, \"check-attr\" and\n> Stefan's pathspec label magic, of this \"alloc and then append\" side\n> of the API wants to have an access to git_attr(name), I think\n> the function signature for this one should be updated to take not\n> \"const char *name\" but instead take \"struct git_attr *attr\", i.e.\n\nPerhaps this can be squashed into 12/12 to update the tutorial part\nto cover this less often used form.\n\n Documentation/technical/api-gitattributes.txt | 22 +++++++++++++++++++++-\n 1 file changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/technical/api-gitattributes.txt b/Documentation/technical/api-gitattributes.txt\nindex 6f13f94..92fc32a 100644\n--- a/Documentation/technical/api-gitattributes.txt\n+++ b/Documentation/technical/api-gitattributes.txt\n@@ -55,7 +55,11 @@ Querying Specific Attributes\n \n * Prepare `struct git_attr_check` using git_attr_check_initl()\n   function, enumerating the names of attributes whose values you are\n-  interested in, terminated with a NULL pointer.\n+  interested in, terminated with a NULL pointer.  Alternatively, an\n+  empty `struct git_attr_check` can be prepared by calling\n+  `git_attr_check_alloc()` function and then attributes you want to\n+  ask about can be added to it with `git_attr_check_append()`\n+  function.\n \n * Call `git_check_attr()` to check the attributes for the path.\n \n@@ -112,6 +116,22 @@ static void setup_check(void)\n \t}\n ------------\n \n+To see how attributes in argv[] are set for different paths, only\n+the first step in the above would be different.\n+\n+------------\n+static struct git_attr_check *check;\n+static void setup_check(const char **argv)\n+{\n+\tcheck = git_attr_check_alloc();\n+\twhile (*argv) {\n+\t\tstruct git_attr *attr = git_attr(*argv);\n+\t\tgit_attr_check_append(check, attr);\n+\t\targv++;\n+\t}\n+}\n+------------\n+\n \n Querying All Attributes\n -----------------------\n"},{"id":"286892","messageId":"CAGZ79kZOFxRDAjXExVy5B4h=zSnd7VfxY6K3W5Y3-EQNQ8XMJA@mail.gmail.com","threadId":"42348","inReplyTo":"xmqq7fesv1d6.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 10/12] attr: convert git_all_attrs() to use \"struct git_attr_check\"","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-05-18T16:34:22Z","receivedAt":"2016-05-18T16:34:22Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, May 17, 2016 at 2:08 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> Given that one of the two expected callers, namely, \"check-attr\" and\n>> Stefan's pathspec label magic, of this \"alloc and then append\" side\n>> of the API wants to have an access to git_attr(name), I think\n>> the function signature for this one should be updated to take not\n>> \"const char *name\" but instead take \"struct git_attr *attr\", i.e.\n>\n> Perhaps this can be squashed into 12/12 to update the tutorial part\n> to cover this less often used form.\n\nThat would be great!\n\nThanks,\nStefan\n\n>\n>  Documentation/technical/api-gitattributes.txt | 22 +++++++++++++++++++++-\n>  1 file changed, 21 insertions(+), 1 deletion(-)\n>\n> diff --git a/Documentation/technical/api-gitattributes.txt b/Documentation/technical/api-gitattributes.txt\n> index 6f13f94..92fc32a 100644\n> --- a/Documentation/technical/api-gitattributes.txt\n> +++ b/Documentation/technical/api-gitattributes.txt\n> @@ -55,7 +55,11 @@ Querying Specific Attributes\n>\n>  * Prepare `struct git_attr_check` using git_attr_check_initl()\n>    function, enumerating the names of attributes whose values you are\n> -  interested in, terminated with a NULL pointer.\n> +  interested in, terminated with a NULL pointer.  Alternatively, an\n> +  empty `struct git_attr_check` can be prepared by calling\n> +  `git_attr_check_alloc()` function and then attributes you want to\n> +  ask about can be added to it with `git_attr_check_append()`\n> +  function.\n>\n>  * Call `git_check_attr()` to check the attributes for the path.\n>\n> @@ -112,6 +116,22 @@ static void setup_check(void)\n>         }\n>  ------------\n>\n> +To see how attributes in argv[] are set for different paths, only\n> +the first step in the above would be different.\n> +\n> +------------\n> +static struct git_attr_check *check;\n> +static void setup_check(const char **argv)\n> +{\n> +       check = git_attr_check_alloc();\n> +       while (*argv) {\n> +               struct git_attr *attr = git_attr(*argv);\n> +               git_attr_check_append(check, attr);\n> +               argv++;\n> +       }\n> +}\n> +------------\n> +\n>\n>  Querying All Attributes\n>  -----------------------\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"286896","messageId":"CAGZ79kYLVDkeHUMCxOB57YYeJt3f4O8csEp_tyO-oWMTzJ-9hw@mail.gmail.com","threadId":"42348","inReplyTo":"20160516210545.6591-1-gitster@pobox.com","subject":"Re: [PATCH v2 00/12] revamping git_check_attr() API","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-05-18T16:51:23Z","receivedAt":"2016-05-18T16:51:23Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"> The patches in the earliest part of the series have been sent to the\n> list already; there is no substantial change (I think I made a\n> typofix in the commit log message found by Eric).\n\nand a new patch got added here:\n\n    attr.c: tighten constness around \"git_attr\" structure\n\nI cannot find it on the list though?\n(I only see it in the \"What's cooking\" email and on origin/jc/attr)\n\nMinor nit:\nSo I wondered when you prefix the subject of the patches with \"attr.c:\"\nand when with \"attr:\". All patches with \"attr.c\" prefixed touch only that\nfile, the others will change the attr system beyond that file scope.\nHowever the \"tighten constness\" touches the header file, too. So\nyou'd maybe want to loose the \".c\".\n\nThanks,\nStefan\n"},{"id":"286908","messageId":"xmqqk2irrz6w.fsf@gitster.mtv.corp.google.com","threadId":"42348","inReplyTo":"CAGZ79kYLVDkeHUMCxOB57YYeJt3f4O8csEp_tyO-oWMTzJ-9hw@mail.gmail.com","subject":"Re: [PATCH v2 00/12] revamping git_check_attr() API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-18T18:36:07Z","receivedAt":"2016-05-18T18:36:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n>> The patches in the earliest part of the series have been sent to the\n>> list already; there is no substantial change (I think I made a\n>> typofix in the commit log message found by Eric).\n>\n> and a new patch got added here:\n>\n>     attr.c: tighten constness around \"git_attr\" structure\n>\n> I cannot find it on the list though?\n\nJust like everybody else has private \"wip\" version to be sent to the\nlist, I have my own.  I haven't had enough time to cook v3 yet into\na publishable state yet.\n\n> So I wondered when you prefix the subject of the patches with \"attr.c:\"\n> and when with \"attr:\".\n\nThe early ones with label attr.c are meant to be \"good clean-ups,\nwhether we decide to go with API update or not, that can live\nstandalone outside the series\"; the remainders may have been better\nlabeled with fully-spelled \"attributes:\", perhaps, as that is not\nabout the attr.c file alone, but is about the whole attributes\nsubsystem.\n"}]}