{"thread":{"id":"30375","subject":"[RFC 1/7] check_refname_component(): iterate via index rather than via pointer","startedAt":"2012-04-30T23:02:48Z","lastAt":"2012-04-30T23:02:55Z","messageCount":8,"participants":["mhagger@alum.mit.edu"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"190365","messageId":"1335826975-3093-1-git-send-email-mhagger@alum.mit.edu","threadId":"30375","inReplyTo":null,"subject":"[RFC 0/7] Make check_refname_format() more flexible","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-04-30T23:02:48Z","receivedAt":"2012-04-30T23:02:48Z","isPatch":false,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nHere are some patches that I have had kicking around for a while to\nmake check_refname_format() more flexible.  In particular, they add a\nREFNAME_FULL option (for checking that the reference name is a valid\nfull reference name (i.e., either starts with \"refs/\" or is ALL_CAPS)\nand a REFNAME_RELAXED option (which relaxes the rules to the level\nthat they only prevent \"dangerous\" refnames, not just confusing ones).\n\nREFNAME_RELAXED is the name I chose instead of REFNAME_NONSTRICT as\ndiscussed on the mailing list.  (I decided that \"REFNAME_NONSTRICT\"\nlooks too much like \"REFNAME_STRICT\" when reading quickly over it,\nplus it avoids double negatives.)\n\nPatch 2 fixes a big with the handling of the REFNAME_DOT_COMPONENT\noption.  (I believe that this option is anyway obsoleted by the\nchanges that removed extra_refs; I'll double-check.)\n\nThe patches also do some refactoring, like extracting a new function\nparse_refname_prefix().  If some of these fixes will be needed on\nolder branches, then they should be redone without the refactorings.\n\nIdeally all of the options to check_refname_format() should be covered\nby tests; on the other hand I don't think it is a good idea to cruft\nup the interface to \"git check-ref-format\" with options that are\nprobably not useful outside of the test suite.  Is there a precedent\nfor how to add tests for such code?\n\nMichael Haggerty (7):\n  check_refname_component(): iterate via index rather than via pointer\n  check_refname_component(): fix check with REFNAME_DOT_COMPONENT\n  parse_refname_component(): accept a length parameter\n  parse_refname_component(): parse until terminator\n  parse_refname_prefix(): new function\n  check_refname_format(): add REFNAME_FULL option\n  check_refname_format(): implement REFNAME_RELAXED option\n\n Documentation/git-check-ref-format.txt |   14 ++++\n builtin/check-ref-format.c             |    4 +\n refs.c                                 |  136 +++++++++++++++++++++++---------\n refs.h                                 |   16 +++-\n t/t1402-check-ref-format.sh            |   31 ++++++++\n 5 files changed, 158 insertions(+), 43 deletions(-)\n\n-- \n1.7.10\n"},{"id":"190364","messageId":"1335826975-3093-2-git-send-email-mhagger@alum.mit.edu","threadId":"30375","inReplyTo":"1335826975-3093-1-git-send-email-mhagger@alum.mit.edu","subject":"[RFC 1/7] check_refname_component(): iterate via index rather than via pointer","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-04-30T23:02:49Z","receivedAt":"2012-04-30T23:02:49Z","isPatch":false,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nThis will make later changes easier.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 09322fe..927d36c 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -35,12 +35,12 @@ static inline int bad_ref_char(int ch)\n  */\n static int check_refname_component(const char *refname, int flags)\n {\n-\tconst char *cp;\n+\tint i, len = strlen(refname);\n \tchar last = '\\0';\n \n-\tfor (cp = refname; ; cp++) {\n-\t\tchar ch = *cp;\n-\t\tif (ch == '\\0' || ch == '/')\n+\tfor (i = 0; i < len; i++) {\n+\t\tchar ch = refname[i];\n+\t\tif (ch == '/')\n \t\t\tbreak;\n \t\tif (bad_ref_char(ch))\n \t\t\treturn -1; /* Illegal character in refname. */\n@@ -50,7 +50,7 @@ static int check_refname_component(const char *refname, int flags)\n \t\t\treturn -1; /* Refname contains \"@{\". */\n \t\tlast = ch;\n \t}\n-\tif (cp == refname)\n+\tif (i == 0)\n \t\treturn 0; /* Component has zero length. */\n \tif (refname[0] == '.') {\n \t\tif (!(flags & REFNAME_DOT_COMPONENT))\n@@ -59,12 +59,12 @@ static int check_refname_component(const char *refname, int flags)\n \t\t * Even if leading dots are allowed, don't allow \".\"\n \t\t * as a component (\"..\" is prevented by a rule above).\n \t\t */\n-\t\tif (refname[1] == '\\0')\n+\t\tif (len == 1)\n \t\t\treturn -1; /* Component equals \".\". */\n \t}\n-\tif (cp - refname >= 5 && !memcmp(cp - 5, \".lock\", 5))\n+\tif (i >= 5 && !memcmp(&refname[i - 5], \".lock\", 5))\n \t\treturn -1; /* Refname ends with \".lock\". */\n-\treturn cp - refname;\n+\treturn i;\n }\n \n int check_refname_format(const char *refname, int flags)\n-- \n1.7.10\n"},{"id":"190371","messageId":"1335826975-3093-3-git-send-email-mhagger@alum.mit.edu","threadId":"30375","inReplyTo":"1335826975-3093-1-git-send-email-mhagger@alum.mit.edu","subject":"[RFC 2/7] check_refname_component(): fix check with REFNAME_DOT_COMPONENT","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-04-30T23:02:50Z","receivedAt":"2012-04-30T23:02:50Z","isPatch":false,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nWhen REFNAME_DOT_COMPONENT is set, we want to allow refname components\nthat start with '.' but not equal to \".\".  The old code would have\nallowed refname components equal to \".\" if they were not the last\ncomponent of the refname.  Fix the test.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/refs.c b/refs.c\nindex 927d36c..c293fd2 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -59,7 +59,7 @@ static int check_refname_component(const char *refname, int flags)\n \t\t * Even if leading dots are allowed, don't allow \".\"\n \t\t * as a component (\"..\" is prevented by a rule above).\n \t\t */\n-\t\tif (len == 1)\n+\t\tif (i == 1)\n \t\t\treturn -1; /* Component equals \".\". */\n \t}\n \tif (i >= 5 && !memcmp(&refname[i - 5], \".lock\", 5))\n-- \n1.7.10\n"},{"id":"190368","messageId":"1335826975-3093-4-git-send-email-mhagger@alum.mit.edu","threadId":"30375","inReplyTo":"1335826975-3093-1-git-send-email-mhagger@alum.mit.edu","subject":"[RFC 3/7] parse_refname_component(): accept a length parameter","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-04-30T23:02:51Z","receivedAt":"2012-04-30T23:02:51Z","isPatch":false,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   16 +++++++++-------\n 1 file changed, 9 insertions(+), 7 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex c293fd2..39008f2 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -29,16 +29,16 @@ static inline int bad_ref_char(int ch)\n }\n \n /*\n- * Try to read one refname component from the front of refname.  Return\n- * the length of the component found, or -1 if the component is not\n- * legal.\n+ * Try to read one refname component from the first refnamelen\n+ * characters of refname.  Return the length of the component found,\n+ * or -1 if the component is not legal.\n  */\n-static int check_refname_component(const char *refname, int flags)\n+static int check_refname_component(const char *refname, int refnamelen, int flags)\n {\n-\tint i, len = strlen(refname);\n+\tint i;\n \tchar last = '\\0';\n \n-\tfor (i = 0; i < len; i++) {\n+\tfor (i = 0; i < refnamelen; i++) {\n \t\tchar ch = refname[i];\n \t\tif (ch == '/')\n \t\t\tbreak;\n@@ -69,11 +69,12 @@ static int check_refname_component(const char *refname, int flags)\n \n int check_refname_format(const char *refname, int flags)\n {\n+\tint refnamelen = strlen(refname);\n \tint component_len, component_count = 0;\n \n \twhile (1) {\n \t\t/* We are at the start of a path component. */\n-\t\tcomponent_len = check_refname_component(refname, flags);\n+\t\tcomponent_len = check_refname_component(refname, refnamelen, flags);\n \t\tif (component_len <= 0) {\n \t\t\tif ((flags & REFNAME_REFSPEC_PATTERN) &&\n \t\t\t\t\trefname[0] == '*' &&\n@@ -90,6 +91,7 @@ int check_refname_format(const char *refname, int flags)\n \t\t\tbreak;\n \t\t/* Skip to next component. */\n \t\trefname += component_len + 1;\n+\t\trefnamelen -= component_len + 1;\n \t}\n \n \tif (refname[component_len - 1] == '.')\n-- \n1.7.10\n"},{"id":"190367","messageId":"1335826975-3093-5-git-send-email-mhagger@alum.mit.edu","threadId":"30375","inReplyTo":"1335826975-3093-1-git-send-email-mhagger@alum.mit.edu","subject":"[RFC 4/7] parse_refname_component(): parse until terminator","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-04-30T23:02:52Z","receivedAt":"2012-04-30T23:02:52Z","isPatch":false,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nRename check_refname_component() to parse_refname_component() and\nchange it to parse whatever part of the beginning of the string that\ncan be considered a valid refname component.  The old version reported\nan error if a refname component was terminated by anything other then\nend-of-string or '/'; this version just reports the length of the part\nof the string that can be considered a valid refname component\n(whether it is terminated by end-of-string, '/', \"..\", \"@{\", or some\ncharacter that is illegal in a refname component).\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   64 +++++++++++++++++++++++++++++++++++++++++++---------------------\n 1 file changed, 43 insertions(+), 21 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 39008f2..6e4cba0 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -29,25 +29,32 @@ static inline int bad_ref_char(int ch)\n }\n \n /*\n- * Try to read one refname component from the first refnamelen\n- * characters of refname.  Return the length of the component found,\n- * or -1 if the component is not legal.\n+ * Try to read one refname component from the front of refname.\n+ * Return the length of the part of the string that could constitute a\n+ * valid refname component (which might be zero), or -1 if the start\n+ * of the string looks like a refname component but is formatted\n+ * illegally.  A refname component can be terminated by the end of the\n+ * string, '/', any character forbidden by bad_ref_char(), \"..\", or\n+ * \"@{\".\n  */\n-static int check_refname_component(const char *refname, int refnamelen, int flags)\n+static int parse_refname_component(const char *refname, int refnamelen, int flags)\n {\n \tint i;\n \tchar last = '\\0';\n \n \tfor (i = 0; i < refnamelen; i++) {\n \t\tchar ch = refname[i];\n-\t\tif (ch == '/')\n+\t\tif (ch == '/') {\n \t\t\tbreak;\n-\t\tif (bad_ref_char(ch))\n-\t\t\treturn -1; /* Illegal character in refname. */\n-\t\tif (last == '.' && ch == '.')\n-\t\t\treturn -1; /* Refname contains \"..\". */\n-\t\tif (last == '@' && ch == '{')\n-\t\t\treturn -1; /* Refname contains \"@{\". */\n+\t\t} else if (bad_ref_char(ch)) {\n+\t\t\tbreak; /* Component terminated by illegal character. */\n+\t\t} else if (last == '.' && ch == '.') {\n+\t\t\ti--;\n+\t\t\tbreak; /* Component terminated by \"..\". */\n+\t\t} else if (last == '@' && ch == '{') {\n+\t\t\ti--;\n+\t\t\tbreak; /* Refname terminated by \"@{\". */\n+\t\t}\n \t\tlast = ch;\n \t}\n \tif (i == 0)\n@@ -57,13 +64,14 @@ static int check_refname_component(const char *refname, int refnamelen, int flag\n \t\t\treturn -1; /* Component starts with '.'. */\n \t\t/*\n \t\t * Even if leading dots are allowed, don't allow \".\"\n-\t\t * as a component (\"..\" is prevented by a rule above).\n+\t\t * as a component (\"..\" is treated as a terminator by\n+\t\t * a rule above).\n \t\t */\n \t\tif (i == 1)\n \t\t\treturn -1; /* Component equals \".\". */\n \t}\n \tif (i >= 5 && !memcmp(&refname[i - 5], \".lock\", 5))\n-\t\treturn -1; /* Refname ends with \".lock\". */\n+\t\treturn -1; /* Component is not allowed to end with \".lock\". */\n \treturn i;\n }\n \n@@ -74,11 +82,14 @@ int check_refname_format(const char *refname, int flags)\n \n \twhile (1) {\n \t\t/* We are at the start of a path component. */\n-\t\tcomponent_len = check_refname_component(refname, refnamelen, flags);\n-\t\tif (component_len <= 0) {\n+\t\tcomponent_len = parse_refname_component(refname, refnamelen, flags);\n+\t\tif (component_len < 0) {\n+\t\t\t/* This case includes a refname that ends with '/': */\n+\t\t\treturn -1;\n+\t\t} else if (component_len == 0) {\n \t\t\tif ((flags & REFNAME_REFSPEC_PATTERN) &&\n-\t\t\t\t\trefname[0] == '*' &&\n-\t\t\t\t\t(refname[1] == '\\0' || refname[1] == '/')) {\n+\t\t\t\t\trefnamelen >= 1 && refname[0] == '*' &&\n+\t\t\t\t\t(refnamelen == 1 || refname[1] == '/')) {\n \t\t\t\t/* Accept one wildcard as a full refname component. */\n \t\t\t\tflags &= ~REFNAME_REFSPEC_PATTERN;\n \t\t\t\tcomponent_len = 1;\n@@ -87,11 +98,22 @@ int check_refname_format(const char *refname, int flags)\n \t\t\t}\n \t\t}\n \t\tcomponent_count++;\n-\t\tif (refname[component_len] == '\\0')\n+\t\t/* See what terminated the component: */\n+\t\tif (component_len == refnamelen) {\n+\t\t\t/* We've parsed the whole string: */\n \t\t\tbreak;\n-\t\t/* Skip to next component. */\n-\t\trefname += component_len + 1;\n-\t\trefnamelen -= component_len + 1;\n+\t\t} else if (refname[component_len] == '/') {\n+\t\t\t/* Skip to the start of the next component: */\n+\t\t\trefname += component_len + 1;\n+\t\t\trefnamelen -= component_len + 1;\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * The component was ended by something else\n+\t\t\t * (something that cannot be part of a legal\n+\t\t\t * refname).\n+\t\t\t */\n+\t\t\treturn -1;\n+\t\t}\n \t}\n \n \tif (refname[component_len - 1] == '.')\n-- \n1.7.10\n"},{"id":"190370","messageId":"1335826975-3093-6-git-send-email-mhagger@alum.mit.edu","threadId":"30375","inReplyTo":"1335826975-3093-1-git-send-email-mhagger@alum.mit.edu","subject":"[RFC 5/7] parse_refname_prefix(): new function","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-04-30T23:02:53Z","receivedAt":"2012-04-30T23:02:53Z","isPatch":false,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nAdd a new function, parse_refname_prefix(), which looks for a possible\nrefname at the front of a string and returns its length.  Use this\nfunction to implement check_refname_format().\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   39 ++++++++++++++++++++++++++-------------\n 1 file changed, 26 insertions(+), 13 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 6e4cba0..70578ee 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -75,21 +75,28 @@ static int parse_refname_component(const char *refname, int refnamelen, int flag\n \treturn i;\n }\n \n-int check_refname_format(const char *refname, int flags)\n+/*\n+ * Try to interpret the beginning of string refname as a reference\n+ * name.  Return the length of the part of the string that could\n+ * constitute a valid refname, or -1 if the start of the string cannot\n+ * possibly be interpreted as a refname.  flags has the same\n+ * interpretation as for check_refname_format().\n+ */\n+static int parse_refname_prefix(const char *refname, int refnamelen, int flags)\n {\n-\tint refnamelen = strlen(refname);\n+\tconst char *p = refname;\n+\tint len = refnamelen;\n \tint component_len, component_count = 0;\n \n \twhile (1) {\n \t\t/* We are at the start of a path component. */\n-\t\tcomponent_len = parse_refname_component(refname, refnamelen, flags);\n+\t\tcomponent_len = parse_refname_component(p, len, flags);\n \t\tif (component_len < 0) {\n-\t\t\t/* This case includes a refname that ends with '/': */\n \t\t\treturn -1;\n \t\t} else if (component_len == 0) {\n \t\t\tif ((flags & REFNAME_REFSPEC_PATTERN) &&\n-\t\t\t\t\trefnamelen >= 1 && refname[0] == '*' &&\n-\t\t\t\t\t(refnamelen == 1 || refname[1] == '/')) {\n+\t\t\t\t\tlen >= 1 && p[0] == '*' &&\n+\t\t\t\t\t(len == 1 || p[1] == '/')) {\n \t\t\t\t/* Accept one wildcard as a full refname component. */\n \t\t\t\tflags &= ~REFNAME_REFSPEC_PATTERN;\n \t\t\t\tcomponent_len = 1;\n@@ -99,28 +106,34 @@ int check_refname_format(const char *refname, int flags)\n \t\t}\n \t\tcomponent_count++;\n \t\t/* See what terminated the component: */\n-\t\tif (component_len == refnamelen) {\n+\t\tif (component_len == len) {\n \t\t\t/* We've parsed the whole string: */\n \t\t\tbreak;\n-\t\t} else if (refname[component_len] == '/') {\n+\t\t} else if (p[component_len] == '/') {\n \t\t\t/* Skip to the start of the next component: */\n-\t\t\trefname += component_len + 1;\n-\t\t\trefnamelen -= component_len + 1;\n+\t\t\tp += component_len + 1;\n+\t\t\tlen -= component_len + 1;\n \t\t} else {\n \t\t\t/*\n \t\t\t * The component was ended by something else\n \t\t\t * (something that cannot be part of a legal\n \t\t\t * refname).\n \t\t\t */\n-\t\t\treturn -1;\n+\t\t\tbreak;\n \t\t}\n \t}\n \n-\tif (refname[component_len - 1] == '.')\n+\tif (p[component_len - 1] == '.')\n \t\treturn -1; /* Refname ends with '.'. */\n \tif (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)\n \t\treturn -1; /* Refname has only one component. */\n-\treturn 0;\n+\treturn p + component_len - refname;\n+}\n+\n+int check_refname_format(const char *refname, int flags)\n+{\n+\tint refnamelen = strlen(refname);\n+\treturn parse_refname_prefix(refname, refnamelen, flags) == refnamelen ? 0 : -1;\n }\n \n struct ref_entry;\n-- \n1.7.10\n"},{"id":"190369","messageId":"1335826975-3093-7-git-send-email-mhagger@alum.mit.edu","threadId":"30375","inReplyTo":"1335826975-3093-1-git-send-email-mhagger@alum.mit.edu","subject":"[RFC 6/7] check_refname_format(): add REFNAME_FULL option","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-04-30T23:02:54Z","receivedAt":"2012-04-30T23:02:54Z","isPatch":false,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nAdd a REFNAME_FULL option that causes check_refname_format() to\nrequire full reference names.  A full refname has to start with\n\"refs/\" or consist entirely of capital letters and underscores.  If\nthe REFNAME_FULL bit is set in the flags, check_refname_format() and\nparse_refname_prefix() verify that the refname is a valid full\nrefname.\n\nAdd a \"--full\" option to \"git check-ref-format\" that can be used to\naccess the new option.  Use this to add some tests involving \"git\ncheck-ref-format --full\".\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n Documentation/git-check-ref-format.txt |   14 +++++++++++\n builtin/check-ref-format.c             |    4 +++\n refs.c                                 |   42 +++++++++++++++++++++-----------\n refs.h                                 |   12 ++++++---\n t/t1402-check-ref-format.sh            |   31 +++++++++++++++++++++++\n 5 files changed, 85 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\nindex 103e7b1..9a8aafb 100644\n--- a/Documentation/git-check-ref-format.txt\n+++ b/Documentation/git-check-ref-format.txt\n@@ -27,6 +27,11 @@ if refs are packed by `git gc`).\n \n git imposes the following rules on how references are named:\n \n+. They must either start with `refs/` (e.g., `refs/heads/master`) or\n+  consist entirely of capital letters and underscores (e.g., `HEAD` or\n+  `MERGE_HEAD`).  This rule is enforced if the `--full` option is\n+  used.\n+\n . They can include slash `/` for hierarchical (directory)\n   grouping, but no slash-separated component can begin with a\n   dot `.` or end with the sequence `.lock`.\n@@ -83,6 +88,15 @@ typed the branch name.\n \n OPTIONS\n -------\n+--full::\n+--no-full::\n+\tControls whether the refname must represent a full refname\n+\t(i.e., starting with `refs/`).  If `--full` and\n+\t`--allow-onelevel` are both specified, then special top-level\n+\trefnames consisting of capital letters and underscored (like\n+\t`HEAD` or `MERGE_HEAD`) are also allowed.  The default is\n+\t`--no-full`.\n+\n --allow-onelevel::\n --no-allow-onelevel::\n \tControls whether one-level refnames are accepted (i.e.,\ndiff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c\nindex 28a7320..a73626d 100644\n--- a/builtin/check-ref-format.c\n+++ b/builtin/check-ref-format.c\n@@ -64,6 +64,10 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix)\n \tfor (i = 1; i < argc && argv[i][0] == '-'; i++) {\n \t\tif (!strcmp(argv[i], \"--normalize\") || !strcmp(argv[i], \"--print\"))\n \t\t\tnormalize = 1;\n+\t\telse if (!strcmp(argv[i], \"--full\"))\n+\t\t\tflags |= REFNAME_FULL;\n+\t\telse if (!strcmp(argv[i], \"--no-full\"))\n+\t\t\tflags &= ~REFNAME_FULL;\n \t\telse if (!strcmp(argv[i], \"--allow-onelevel\"))\n \t\t\tflags |= REFNAME_ALLOW_ONELEVEL;\n \t\telse if (!strcmp(argv[i], \"--no-allow-onelevel\"))\ndiff --git a/refs.c b/refs.c\nindex 70578ee..c4ab64d 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -105,28 +105,42 @@ static int parse_refname_prefix(const char *refname, int refnamelen, int flags)\n \t\t\t}\n \t\t}\n \t\tcomponent_count++;\n-\t\t/* See what terminated the component: */\n-\t\tif (component_len == len) {\n-\t\t\t/* We've parsed the whole string: */\n+\n+\t\tif (component_len == len || p[component_len] != '/')\n \t\t\tbreak;\n-\t\t} else if (p[component_len] == '/') {\n-\t\t\t/* Skip to the start of the next component: */\n-\t\t\tp += component_len + 1;\n-\t\t\tlen -= component_len + 1;\n-\t\t} else {\n+\n+\t\tif (component_count == 1 && flags & REFNAME_FULL) {\n \t\t\t/*\n-\t\t\t * The component was ended by something else\n-\t\t\t * (something that cannot be part of a legal\n-\t\t\t * refname).\n+\t\t\t * That was the first of multiple components;\n+\t\t\t * make sure that it is equal to \"refs\":\n \t\t\t */\n-\t\t\tbreak;\n+\t\t\tif (component_len != 4 || prefixcmp(p, \"refs\"))\n+\t\t\t\treturn -1;\n \t\t}\n+\n+\t\t/* Skip to the start of the next component: */\n+\t\tp += component_len + 1;\n+\t\tlen -= component_len + 1;\n \t}\n \n \tif (p[component_len - 1] == '.')\n \t\treturn -1; /* Refname ends with '.'. */\n-\tif (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)\n-\t\treturn -1; /* Refname has only one component. */\n+\tif (component_count == 1) {\n+\t\tif (!(flags & REFNAME_ALLOW_ONELEVEL))\n+\t\t\treturn -1; /* Refname has only one component. */\n+\t\tif (flags & REFNAME_FULL) {\n+\t\t\t/*\n+\t\t\t * Make sure that the (single-component)\n+\t\t\t * refname is ALL_CAPS:\n+\t\t\t */\n+\t\t\tint i;\n+\t\t\tfor (i = 0; i < component_len; i++) {\n+\t\t\t\tchar ch = p[i];\n+\t\t\t\tif (!(('A' <= ch && ch <= 'Z') || ch == '_'))\n+\t\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t}\n \treturn p + component_len - refname;\n }\n \ndiff --git a/refs.h b/refs.h\nindex d6c2fe2..efe13bb 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -111,9 +111,10 @@ int for_each_recent_reflog_ent(const char *refname, each_reflog_ent_fn fn, long,\n  */\n extern int for_each_reflog(each_ref_fn, void *);\n \n-#define REFNAME_ALLOW_ONELEVEL 1\n-#define REFNAME_REFSPEC_PATTERN 2\n-#define REFNAME_DOT_COMPONENT 4\n+#define REFNAME_ALLOW_ONELEVEL 0x01\n+#define REFNAME_REFSPEC_PATTERN 0x02\n+#define REFNAME_DOT_COMPONENT 0x04\n+#define REFNAME_FULL 0x08\n \n /*\n  * Return 0 iff refname has the correct format for a refname according\n@@ -124,7 +125,10 @@ extern int for_each_reflog(each_ref_fn, void *);\n  * components.  No leading or repeated slashes are accepted.  If\n  * REFNAME_DOT_COMPONENT is set in flags, then allow refname\n  * components to start with \".\" (but not a whole component equal to\n- * \".\" or \"..\").\n+ * \".\" or \"..\").  If REFNAME_FULL is set in flags, then additionally\n+ * verify that the refname is a valid full refname--that it either\n+ * starts with \"refs/\" or that it consists of only capital letters and\n+ * underscores (like \"HEAD\" or \"MERGE_HEAD\").\n  */\n extern int check_refname_format(const char *refname, int flags);\n \ndiff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh\nindex 1ae4d87..2f8a48e 100755\n--- a/t/t1402-check-ref-format.sh\n+++ b/t/t1402-check-ref-format.sh\n@@ -70,6 +70,7 @@ invalid_ref \"$ref\" --refspec-pattern\n valid_ref \"$ref\" '--refspec-pattern --allow-onelevel'\n invalid_ref \"$ref\" --normalize\n valid_ref \"$ref\" '--allow-onelevel --normalize'\n+invalid_ref \"$ref\" '--full'\n \n ref='foo/bar'\n valid_ref \"$ref\"\n@@ -77,12 +78,19 @@ valid_ref \"$ref\" --allow-onelevel\n valid_ref \"$ref\" --refspec-pattern\n valid_ref \"$ref\" '--refspec-pattern --allow-onelevel'\n valid_ref \"$ref\" --normalize\n+invalid_ref \"$ref\" '--full'\n \n ref='foo/*'\n invalid_ref \"$ref\"\n invalid_ref \"$ref\" --allow-onelevel\n valid_ref \"$ref\" --refspec-pattern\n valid_ref \"$ref\" '--refspec-pattern --allow-onelevel'\n+invalid_ref \"$ref\" '--full'\n+invalid_ref \"$ref\" '--refspec-pattern --full'\n+\n+ref='refs/*'\n+invalid_ref \"$ref\" '--full'\n+valid_ref \"$ref\" '--refspec-pattern --full'\n \n ref='*/foo'\n invalid_ref \"$ref\"\n@@ -91,18 +99,23 @@ valid_ref \"$ref\" --refspec-pattern\n valid_ref \"$ref\" '--refspec-pattern --allow-onelevel'\n invalid_ref \"$ref\" --normalize\n valid_ref \"$ref\" '--refspec-pattern --normalize'\n+invalid_ref \"$ref\" '--full'\n+invalid_ref \"$ref\" '--refspec-pattern --full'\n \n ref='foo/*/bar'\n invalid_ref \"$ref\"\n invalid_ref \"$ref\" --allow-onelevel\n valid_ref \"$ref\" --refspec-pattern\n valid_ref \"$ref\" '--refspec-pattern --allow-onelevel'\n+invalid_ref \"$ref\" '--full'\n \n ref='*'\n invalid_ref \"$ref\"\n invalid_ref \"$ref\" --allow-onelevel\n invalid_ref \"$ref\" --refspec-pattern\n valid_ref \"$ref\" '--refspec-pattern --allow-onelevel'\n+invalid_ref \"$ref\" '--full'\n+invalid_ref \"$ref\" '--refspec-pattern --full'\n \n ref='foo/*/*'\n invalid_ref \"$ref\" --refspec-pattern\n@@ -126,6 +139,24 @@ valid_ref NOT_MINGW \"$ref\" '--allow-onelevel --normalize'\n invalid_ref NOT_MINGW \"$ref\" '--refspec-pattern --normalize'\n valid_ref NOT_MINGW \"$ref\" '--refspec-pattern --allow-onelevel --normalize'\n \n+ref='HEAD'\n+invalid_ref \"$ref\"\n+valid_ref \"$ref\" --allow-onelevel\n+invalid_ref \"$ref\" --full\n+valid_ref \"$ref\" '--allow-onelevel --full'\n+\n+valid_ref FETCH_HEAD '--allow-onelevel --full'\n+valid_ref refs/foo --full\n+invalid_ref HEAD/ '--allow-onelevel --full'\n+valid_ref HEAD/ '--allow-onelevel --full --normalize'\n+invalid_ref refs '--full'\n+invalid_ref refs '--allow-onelevel --full'\n+invalid_ref refs/ '--allow-onelevel --full'\n+invalid_ref HEAD/foo '--full'\n+invalid_ref head '--allow-onelevel --full'\n+valid_ref 'refs/foo/*' '--refspec-pattern --full'\n+valid_ref 'refs/*/foo' '--refspec-pattern --full'\n+\n test_expect_success \"check-ref-format --branch @{-1}\" '\n \tT=$(git write-tree) &&\n \tsha1=$(echo A | git commit-tree $T) &&\n-- \n1.7.10\n"},{"id":"190366","messageId":"1335826975-3093-8-git-send-email-mhagger@alum.mit.edu","threadId":"30375","inReplyTo":"1335826975-3093-1-git-send-email-mhagger@alum.mit.edu","subject":"[RFC 7/7] check_refname_format(): implement REFNAME_RELAXED option","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-04-30T23:02:55Z","receivedAt":"2012-04-30T23:02:55Z","isPatch":false,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nImplement a new option, REFNAME_RELAXED, which accepts \"slightly\"\nillegal refnames--those that might cause confusion with notations\nlike \"R1..R2\", \"^R1\", etc. but are unlikely to cause internal errors.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   31 +++++++++++++++++++------------\n refs.h |    6 +++++-\n 2 files changed, 24 insertions(+), 13 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex c4ab64d..3a35e5e 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -17,14 +17,20 @@\n  */\n \n /* Return true iff ch is not allowed in reference names. */\n-static inline int bad_ref_char(int ch)\n+static inline int bad_ref_char(int ch, int flags)\n {\n-\tif (((unsigned) ch) <= ' ' || ch == 0x7f ||\n-\t    ch == '~' || ch == '^' || ch == ':' || ch == '\\\\')\n-\t\treturn 1;\n-\t/* 2.13 Pattern Matching Notation */\n-\tif (ch == '*' || ch == '?' || ch == '[') /* Unsupported */\n-\t\treturn 1;\n+\tif (flags & REFNAME_RELAXED) {\n+\t\tif (ch == '\\0' || ch == '\\n' || ch == ' ' ||\n+\t\t    ch == ':' || ch == '\\\\')\n+\t\t\treturn 1;\n+\t} else {\n+\t\tif (((unsigned) ch) <= ' ' || ch == 0x7f ||\n+\t\t    ch == '~' || ch == '^' || ch == ':' || ch == '\\\\')\n+\t\t\treturn 1;\n+\t\t/* 2.13 Pattern Matching Notation */\n+\t\tif (ch == '*' || ch == '?' || ch == '[') /* Unsupported */\n+\t\t\treturn 1;\n+\t}\n \treturn 0;\n }\n \n@@ -46,12 +52,12 @@ static int parse_refname_component(const char *refname, int refnamelen, int flag\n \t\tchar ch = refname[i];\n \t\tif (ch == '/') {\n \t\t\tbreak;\n-\t\t} else if (bad_ref_char(ch)) {\n+\t\t} else if (bad_ref_char(ch, flags)) {\n \t\t\tbreak; /* Component terminated by illegal character. */\n-\t\t} else if (last == '.' && ch == '.') {\n+\t\t} else if (!(flags & REFNAME_RELAXED) && last == '.' && ch == '.') {\n \t\t\ti--;\n \t\t\tbreak; /* Component terminated by \"..\". */\n-\t\t} else if (last == '@' && ch == '{') {\n+\t\t} else if (!(flags & REFNAME_RELAXED) && last == '@' && ch == '{') {\n \t\t\ti--;\n \t\t\tbreak; /* Refname terminated by \"@{\". */\n \t\t}\n@@ -64,11 +70,12 @@ static int parse_refname_component(const char *refname, int refnamelen, int flag\n \t\t\treturn -1; /* Component starts with '.'. */\n \t\t/*\n \t\t * Even if leading dots are allowed, don't allow \".\"\n-\t\t * as a component (\"..\" is treated as a terminator by\n-\t\t * a rule above).\n+\t\t * or \"..\"  as a component.\n \t\t */\n \t\tif (i == 1)\n \t\t\treturn -1; /* Component equals \".\". */\n+\t\tif (i == 2 && refname[1] == '.')\n+\t\t\treturn -1; /* Component equals \"..\". */\n \t}\n \tif (i >= 5 && !memcmp(&refname[i - 5], \".lock\", 5))\n \t\treturn -1; /* Component is not allowed to end with \".lock\". */\ndiff --git a/refs.h b/refs.h\nindex efe13bb..dd6cfe5 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -115,6 +115,7 @@ extern int for_each_reflog(each_ref_fn, void *);\n #define REFNAME_REFSPEC_PATTERN 0x02\n #define REFNAME_DOT_COMPONENT 0x04\n #define REFNAME_FULL 0x08\n+#define REFNAME_RELAXED 0x10\n \n /*\n  * Return 0 iff refname has the correct format for a refname according\n@@ -128,7 +129,10 @@ extern int for_each_reflog(each_ref_fn, void *);\n  * \".\" or \"..\").  If REFNAME_FULL is set in flags, then additionally\n  * verify that the refname is a valid full refname--that it either\n  * starts with \"refs/\" or that it consists of only capital letters and\n- * underscores (like \"HEAD\" or \"MERGE_HEAD\").\n+ * underscores (like \"HEAD\" or \"MERGE_HEAD\").  If REFNAME_RELAXED is\n+ * set in flags, then relax the rules to allow control characters\n+ * except for SP and LF, wildcard characters '?', '*', and '[', \"..\"\n+ * (but not as a complete refname component), and \"@{\".\n  */\n extern int check_refname_format(const char *refname, int flags);\n \n-- \n1.7.10\n"}]}