{"thread":{"id":"26033","subject":"[PATCH 1/3] get_sha1_oneline: do not leak or double free","startedAt":"2010-12-13T03:01:13Z","lastAt":"2010-12-16T00:23:13Z","messageCount":17,"participants":["Nguyễn Thái Ngọc Duy","Nguyen Thai Ngoc Duy","Jonathan Nieder","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"157911","messageId":"1292209275-17451-1-git-send-email-pclouds@gmail.com","threadId":"26033","inReplyTo":null,"subject":"[PATCH 1/3] get_sha1_oneline: do not leak or double free","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T03:01:13Z","receivedAt":"2010-12-13T03:01:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Double free can happen when commit->buffer == NULL in the first\niteration, then != NULL in the next two iterations.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n sha1_name.c |    3 ++-\n 1 files changed, 2 insertions(+), 1 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 2c3a5fb..13ee6f5 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -718,13 +718,13 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n \t\tcommit = pop_most_recent_commit(&list, ONELINE_SEEN);\n \t\tif (!parse_object(commit->object.sha1))\n \t\t\tcontinue;\n-\t\tfree(temp_commit_buffer);\n \t\tif (commit->buffer)\n \t\t\tp = commit->buffer;\n \t\telse {\n \t\t\tp = read_sha1_file(commit->object.sha1, &type, &size);\n \t\t\tif (!p)\n \t\t\t\tcontinue;\n+\t\t\tfree(temp_commit_buffer);\n \t\t\ttemp_commit_buffer = p;\n \t\t}\n \t\tif (!(p = strstr(p, \"\\n\\n\")))\n@@ -740,6 +740,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n \tfree_commit_list(list);\n \tfor (l = backup; l; l = l->next)\n \t\tclear_commit_marks(l->item, ONELINE_SEEN);\n+\tfree_commit_list(backup);\n \treturn retval;\n }\n \n-- \n1.7.3.3.476.g10a82\n"},{"id":"157912","messageId":"1292209275-17451-2-git-send-email-pclouds@gmail.com","threadId":"26033","inReplyTo":"1292209275-17451-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/3] get_sha1_oneline: let callers initialize the commit tips for traverse","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T03:01:14Z","receivedAt":"2010-12-13T03:01:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This gives callers more control, i.e. which ref will be searched from.\n\nget_sha1_oneline takes care of ONELINE_SEEN marks (which is now TMP_MARK)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n sha1_name.c |   62 +++++++++++++++++++++++++++++++---------------------------\n 1 files changed, 33 insertions(+), 29 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 13ee6f5..3c2c61c 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -6,6 +6,8 @@\n #include \"tree-walk.h\"\n #include \"refs.h\"\n #include \"remote.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n \n static int find_short_object_filename(int len, const char *name, unsigned char *sha1)\n {\n@@ -669,30 +671,10 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)\n  * For future extension, ':/!' is reserved. If you want to match a message\n  * beginning with a '!', you have to repeat the exclamation mark.\n  */\n-#define ONELINE_SEEN (1u<<20)\n-\n-static int handle_one_ref(const char *path,\n-\t\tconst unsigned char *sha1, int flag, void *cb_data)\n-{\n-\tstruct commit_list **list = cb_data;\n-\tstruct object *object = parse_object(sha1);\n-\tif (!object)\n-\t\treturn 0;\n-\tif (object->type == OBJ_TAG) {\n-\t\tobject = deref_tag(object, path, strlen(path));\n-\t\tif (!object)\n-\t\t\treturn 0;\n-\t}\n-\tif (object->type != OBJ_COMMIT)\n-\t\treturn 0;\n-\tinsert_by_date((struct commit *)object, list);\n-\tobject->flags |= ONELINE_SEEN;\n-\treturn 0;\n-}\n-\n-static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n+static int get_sha1_oneline(const char *prefix, unsigned char *sha1,\n+\t\t\t    struct commit_list *list)\n {\n-\tstruct commit_list *list = NULL, *backup = NULL, *l;\n+\tstruct commit_list *backup = NULL, *l;\n \tint retval = -1;\n \tchar *temp_commit_buffer = NULL;\n \tregex_t regex;\n@@ -706,16 +688,17 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n \tif (regcomp(&regex, prefix, REG_EXTENDED))\n \t\tdie(\"Invalid search pattern: %s\", prefix);\n \n-\tfor_each_ref(handle_one_ref, &list);\n-\tfor (l = list; l; l = l->next)\n+\tfor (l = list; l; l = l->next) {\n+\t\tl->item->object.flags |= TMP_MARK;\n \t\tcommit_list_insert(l->item, &backup);\n+\t}\n \twhile (list) {\n \t\tchar *p;\n \t\tstruct commit *commit;\n \t\tenum object_type type;\n \t\tunsigned long size;\n \n-\t\tcommit = pop_most_recent_commit(&list, ONELINE_SEEN);\n+\t\tcommit = pop_most_recent_commit(&list, TMP_MARK);\n \t\tif (!parse_object(commit->object.sha1))\n \t\t\tcontinue;\n \t\tif (commit->buffer)\n@@ -739,7 +722,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n \tfree(temp_commit_buffer);\n \tfree_commit_list(list);\n \tfor (l = backup; l; l = l->next)\n-\t\tclear_commit_marks(l->item, ONELINE_SEEN);\n+\t\tclear_commit_marks(l->item, TMP_MARK);\n \tfree_commit_list(backup);\n \treturn retval;\n }\n@@ -1067,6 +1050,24 @@ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,\n \treturn ret;\n }\n \n+static int handle_one_ref(const char *path,\n+\t\tconst unsigned char *sha1, int flag, void *cb_data)\n+{\n+\tstruct commit_list **list = cb_data;\n+\tstruct object *object = parse_object(sha1);\n+\tif (!object)\n+\t\treturn 0;\n+\tif (object->type == OBJ_TAG) {\n+\t\tobject = deref_tag(object, path, strlen(path));\n+\t\tif (!object)\n+\t\t\treturn 0;\n+\t}\n+\tif (object->type != OBJ_COMMIT)\n+\t\treturn 0;\n+\tinsert_by_date((struct commit *)object, list);\n+\treturn 0;\n+}\n+\n int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\t\t    struct object_context *oc,\n \t\t\t    int gently, const char *prefix)\n@@ -1089,9 +1090,12 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\tint stage = 0;\n \t\tstruct cache_entry *ce;\n \t\tint pos;\n-\t\tif (namelen > 2 && name[1] == '/')\n+\t\tif (namelen > 2 && name[1] == '/') {\n+\t\t\tstruct commit_list *list = NULL;\n+\t\t\tfor_each_ref(handle_one_ref, &list);\n \t\t\t/* don't need mode for commit */\n-\t\t\treturn get_sha1_oneline(name + 2, sha1);\n+\t\t\treturn get_sha1_oneline(name + 2, sha1, list);\n+\t\t}\n \t\tif (namelen < 3 ||\n \t\t    name[2] != ':' ||\n \t\t    name[1] < '0' || '3' < name[1])\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157913","messageId":"1292209275-17451-3-git-send-email-pclouds@gmail.com","threadId":"26033","inReplyTo":"1292209275-17451-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/3] get_sha1: support ref^{/regex} syntax","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T03:01:15Z","receivedAt":"2010-12-13T03:01:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This works like :/ syntax, but only limited to one ref.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/revisions.txt |    6 +++\n sha1_name.c                 |   34 +++++++++++++------\n t/t1511-rev-parse-caret.sh  |   73 +++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 102 insertions(+), 11 deletions(-)\n create mode 100755 t/t1511-rev-parse-caret.sh\n\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 3d4b79c..174fa8e 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -106,6 +106,12 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.\n   and dereference the tag recursively until a non-tag object is\n   found.\n \n+* A suffix '{caret}' to a revision parameter followed by a brace\n+  pair that contains a text led by a slash (e.g. `HEAD^{/fix nasty bug}`):\n+  this is the same as `:/fix nasty bug` syntax below except that\n+  it returns the youngest matching commit which is reachable from\n+  the ref before '{caret}'.\n+\n * A colon, followed by a slash, followed by a text (e.g. `:/fix nasty bug`): this names\n   a commit whose commit message matches the specified regular expression.\n   This name returns the youngest matching commit which is\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 3c2c61c..a7e1bcb 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -529,6 +529,7 @@ struct object *peel_to_type(const char *name, int namelen,\n \t}\n }\n \n+static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);\n static int peel_onion(const char *name, int len, unsigned char *sha1)\n {\n \tunsigned char outer[20];\n@@ -564,6 +565,8 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)\n \t\texpected_type = OBJ_BLOB;\n \telse if (sp[0] == '}')\n \t\texpected_type = OBJ_NONE;\n+\telse if (sp[0] == '/')\n+\t\texpected_type = OBJ_COMMIT;\n \telse\n \t\treturn -1;\n \n@@ -578,19 +581,28 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)\n \t\tif (!o || (!o->parsed && !parse_object(o->sha1)))\n \t\t\treturn -1;\n \t\thashcpy(sha1, o->sha1);\n+\t\treturn 0;\n \t}\n-\telse {\n-\t\t/*\n-\t\t * At this point, the syntax look correct, so\n-\t\t * if we do not get the needed object, we should\n-\t\t * barf.\n-\t\t */\n-\t\to = peel_to_type(name, len, o, expected_type);\n-\t\tif (o) {\n-\t\t\thashcpy(sha1, o->sha1);\n-\t\t\treturn 0;\n-\t\t}\n+\n+\t/*\n+\t * At this point, the syntax look correct, so if we do not get\n+\t * the needed object, we should barf.\n+\t */\n+\to = peel_to_type(name, len, o, expected_type);\n+\tif (!o)\n \t\treturn -1;\n+\n+\thashcpy(sha1, o->sha1);\n+\tif (sp[0] == '/') { /* ^{/foo} */\n+\t\tstruct commit_list *list = NULL;\n+\t\tchar *prefix;\n+\t\tint ret;\n+\n+\t\tcommit_list_insert((struct commit *)o, &list);\n+\t\tprefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));\n+\t\tret = get_sha1_oneline(prefix, sha1, list);\n+\t\tfree(prefix);\n+\t\treturn ret;\n \t}\n \treturn 0;\n }\ndiff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh\nnew file mode 100755\nindex 0000000..5c8439c\n--- /dev/null\n+++ b/t/t1511-rev-parse-caret.sh\n@@ -0,0 +1,73 @@\n+#!/bin/sh\n+\n+test_description='tests for ref^{stuff}'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\techo blob >a-blob &&\n+\tgit tag -a -m blob blob-tag `git hash-object -w a-blob`\n+\tmkdir a-tree &&\n+\techo moreblobs >a-tree/another-blob &&\n+\tgit add . &&\n+\tTREE_SHA1=`git write-tree` &&\n+\tgit tag -a -m tree tree-tag \"$TREE_SHA1\" &&\n+\tgit commit -m Initial &&\n+\tgit tag -a -m commit commit-tag &&\n+\tgit branch ref &&\n+\tgit checkout master &&\n+\techo modified >>a-blob &&\n+\tgit add -u &&\n+\tgit commit -m Modified\n+'\n+\n+test_expect_success 'ref^{non-existent}' '\n+\ttest_must_fail git rev-parse ref^{non-existent}\n+'\n+\n+test_expect_success 'ref^{}' '\n+\tgit rev-parse ref >expected &&\n+\tgit rev-parse ref^{} >actual &&\n+\ttest_cmp expected actual &&\n+\tgit rev-parse commit-tag^{} >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'ref^{commit}' '\n+\tgit rev-parse ref >expected &&\n+\tgit rev-parse ref^{commit} >actual &&\n+\ttest_cmp expected actual &&\n+\tgit rev-parse commit-tag^{commit} >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_must_fail git rev-parse tree-tag^{commit} &&\n+\ttest_must_fail git rev-parse blob-tag^{commit}\n+'\n+\n+test_expect_success 'ref^{tree}' '\n+\techo $TREE_SHA1 >expected &&\n+\tgit rev-parse ref^{tree} >actual &&\n+\ttest_cmp expected actual &&\n+\tgit rev-parse commit-tag^{tree} >actual &&\n+\ttest_cmp expected actual &&\n+\tgit rev-parse tree-tag^{tree} >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_must_fail git rev-parse blob-tag^{tree}\n+'\n+\n+test_expect_success 'ref^{/}' '\n+\tgit rev-parse master >expected &&\n+\tgit rev-parse master^{/} >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'ref^{/non-existent}' '\n+\ttest_must_fail git rev-parse master^{/non-existent}\n+'\n+\n+test_expect_success 'ref^{/Initial}' '\n+\tgit rev-parse ref >expected &&\n+\tgit rev-parse master^{/Initial} >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157914","messageId":"AANLkTik2M=PPCXqtepRhntVYofrA0PUgzBLvdMNeeP5P@mail.gmail.com","threadId":"26033","inReplyTo":"1292209275-17451-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 3/3] get_sha1: support ref^{/regex} syntax","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T03:10:11Z","receivedAt":"2010-12-13T03:10:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/12/13 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n> This works like :/ syntax, but only limited to one ref.\n\nArgh.. I read Jonthan's comment too late. Maybe \"limited to one commit\ntip (of a DAG)\"?\n-- \nDuy\n"},{"id":"157919","messageId":"20101213044031.GA20812@burratino","threadId":"26033","inReplyTo":"AANLkTik2M=PPCXqtepRhntVYofrA0PUgzBLvdMNeeP5P@mail.gmail.com","subject":"Re: [PATCH 3/3] get_sha1: support ref^{/regex} syntax","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-12-13T04:40:31Z","receivedAt":"2010-12-13T04:40:31Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Nguyen Thai Ngoc Duy wrote:\n> 2010/12/13 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n\n>> This works like :/ syntax, but only limited to one ref.\n>\n> Argh.. I read Jonthan's comment too late. Maybe \"limited to one commit\n> tip (of a DAG)\"?\n\nAs long as it doesn't filter into the documentation, I don't really\nmind. :)\n"},{"id":"157920","messageId":"7v1v5m6w26.fsf@alter.siamese.dyndns.org","threadId":"26033","inReplyTo":"1292209275-17451-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 1/3] get_sha1_oneline: do not leak or double free","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T06:12:01Z","receivedAt":"2010-12-13T06:12:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Double free can happen when commit->buffer == NULL in the first\n> iteration, then != NULL in the next two iterations.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  sha1_name.c |    3 ++-\n>  1 files changed, 2 insertions(+), 1 deletions(-)\n\nThanks.  First the later hunk:\n\n> diff --git a/sha1_name.c b/sha1_name.c\n> index 2c3a5fb..13ee6f5 100644\n> --- a/sha1_name.c\n> +++ b/sha1_name.c\n> @@ -740,6 +740,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n>  \tfree_commit_list(list);\n>  \tfor (l = backup; l; l = l->next)\n>  \t\tclear_commit_marks(l->item, ONELINE_SEEN);\n> +\tfree_commit_list(backup);\n>  \treturn retval;\n>  }\n\nThis is necessary, but is unrelated to the topic, no?\n\n> @@ -718,13 +718,13 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n>  \t\tcommit = pop_most_recent_commit(&list, ONELINE_SEEN);\n>  \t\tif (!parse_object(commit->object.sha1))\n>  \t\t\tcontinue;\n> -\t\tfree(temp_commit_buffer);\n>  \t\tif (commit->buffer)\n>  \t\t\tp = commit->buffer;\n>  \t\telse {\n>  \t\t\tp = read_sha1_file(commit->object.sha1, &type, &size);\n>  \t\t\tif (!p)\n>  \t\t\t\tcontinue;\n> +\t\t\tfree(temp_commit_buffer);\n>  \t\t\ttemp_commit_buffer = p;\n>  \t\t}\n>  \t\tif (!(p = strstr(p, \"\\n\\n\")))\n\nThis looks very convoluted.\n\nI think the \"temp-commit-buffer with a lifetime one iteration more than\nthe loop body itself\" is merely a misguided attempt to avoid sprinkling\nmany free() calls inside the loop that has irregular exit points with\ncontinue and break.\n\nIf you rewrite the loop to have more regular structure, there is no reason\nto have such a temporary variable with tricky lifespan.\n\nI think the following is easier to read and conveys what the code is\ntrying to do more clearly.  No?\n\n sha1_name.c |   24 +++++++++++++-----------\n 1 files changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 2c3a5fb..2cc7a42 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -693,8 +693,7 @@ static int handle_one_ref(const char *path,\n static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n {\n \tstruct commit_list *list = NULL, *backup = NULL, *l;\n-\tint retval = -1;\n-\tchar *temp_commit_buffer = NULL;\n+\tint found = 0;\n \tregex_t regex;\n \n \tif (prefix[0] == '!') {\n@@ -710,37 +709,40 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n \tfor (l = list; l; l = l->next)\n \t\tcommit_list_insert(l->item, &backup);\n \twhile (list) {\n-\t\tchar *p;\n+\t\tchar *p, *to_free = NULL;\n \t\tstruct commit *commit;\n \t\tenum object_type type;\n \t\tunsigned long size;\n+\t\tint matches;\n \n \t\tcommit = pop_most_recent_commit(&list, ONELINE_SEEN);\n \t\tif (!parse_object(commit->object.sha1))\n \t\t\tcontinue;\n-\t\tfree(temp_commit_buffer);\n \t\tif (commit->buffer)\n \t\t\tp = commit->buffer;\n \t\telse {\n \t\t\tp = read_sha1_file(commit->object.sha1, &type, &size);\n \t\t\tif (!p)\n \t\t\t\tcontinue;\n-\t\t\ttemp_commit_buffer = p;\n+\t\t\tto_free = p;\n \t\t}\n-\t\tif (!(p = strstr(p, \"\\n\\n\")))\n-\t\t\tcontinue;\n-\t\tif (!regexec(&regex, p + 2, 0, NULL, 0)) {\n+\n+\t\tp = strstr(p, \"\\n\\n\");\n+\t\tmatches = p && !regexec(&regex, p + 2, 0, NULL, 0);\n+\t\tfree(to_free);\n+\n+\t\tif (matches) {\n \t\t\thashcpy(sha1, commit->object.sha1);\n-\t\t\tretval = 0;\n+\t\t\tfound = 1;\n \t\t\tbreak;\n \t\t}\n \t}\n \tregfree(&regex);\n-\tfree(temp_commit_buffer);\n \tfree_commit_list(list);\n \tfor (l = backup; l; l = l->next)\n \t\tclear_commit_marks(l->item, ONELINE_SEEN);\n-\treturn retval;\n+\tfree_commit_list(backup);\n+\treturn found ? 0 : -1;\n }\n \n struct grab_nth_branch_switch_cbdata {\n"},{"id":"157921","messageId":"7vvd2y5h63.fsf@alter.siamese.dyndns.org","threadId":"26033","inReplyTo":"7v1v5m6w26.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/3] get_sha1_oneline: do not leak or double free","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T06:19:00Z","receivedAt":"2010-12-13T06:19:00Z","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> I think the following is easier to read and conveys what the code is\n> trying to do more clearly.  No?\n\nThis time with a proposed log message...\n\n-- >8 --\nSubject: [PATCH] get_sha1_oneline: fix lifespan rule of temp_commit_buffer variable\n\nThis is trying to free only what we ourselves read (as opposed to what\nwe borrowed from commit->buffer) but do so lazily only to work around\nthe fact that the code has many irregular exit points, and doing it right\nmakes it necessary to call free() from many different places in the loop.\n\nRewrite the structure of the code inside the loop so that the variable\nhas to live within a single iteration, ever.  This should make the logic\neasier to follow as well.\n\nAlso we didn't free a temporary commit list we kept to hold the original\nset of commits.  Free it.\n\nNoticed-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n sha1_name.c |   24 +++++++++++++-----------\n 1 files changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 2c3a5fb..2cc7a42 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -693,8 +693,7 @@ static int handle_one_ref(const char *path,\n static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n {\n \tstruct commit_list *list = NULL, *backup = NULL, *l;\n-\tint retval = -1;\n-\tchar *temp_commit_buffer = NULL;\n+\tint found = 0;\n \tregex_t regex;\n \n \tif (prefix[0] == '!') {\n@@ -710,37 +709,40 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n \tfor (l = list; l; l = l->next)\n \t\tcommit_list_insert(l->item, &backup);\n \twhile (list) {\n-\t\tchar *p;\n+\t\tchar *p, *to_free = NULL;\n \t\tstruct commit *commit;\n \t\tenum object_type type;\n \t\tunsigned long size;\n+\t\tint matches;\n \n \t\tcommit = pop_most_recent_commit(&list, ONELINE_SEEN);\n \t\tif (!parse_object(commit->object.sha1))\n \t\t\tcontinue;\n-\t\tfree(temp_commit_buffer);\n \t\tif (commit->buffer)\n \t\t\tp = commit->buffer;\n \t\telse {\n \t\t\tp = read_sha1_file(commit->object.sha1, &type, &size);\n \t\t\tif (!p)\n \t\t\t\tcontinue;\n-\t\t\ttemp_commit_buffer = p;\n+\t\t\tto_free = p;\n \t\t}\n-\t\tif (!(p = strstr(p, \"\\n\\n\")))\n-\t\t\tcontinue;\n-\t\tif (!regexec(&regex, p + 2, 0, NULL, 0)) {\n+\n+\t\tp = strstr(p, \"\\n\\n\");\n+\t\tmatches = p && !regexec(&regex, p + 2, 0, NULL, 0);\n+\t\tfree(to_free);\n+\n+\t\tif (matches) {\n \t\t\thashcpy(sha1, commit->object.sha1);\n-\t\t\tretval = 0;\n+\t\t\tfound = 1;\n \t\t\tbreak;\n \t\t}\n \t}\n \tregfree(&regex);\n-\tfree(temp_commit_buffer);\n \tfree_commit_list(list);\n \tfor (l = backup; l; l = l->next)\n \t\tclear_commit_marks(l->item, ONELINE_SEEN);\n-\treturn retval;\n+\tfree_commit_list(backup);\n+\treturn found ? 0 : -1;\n }\n \n struct grab_nth_branch_switch_cbdata {\n-- \n1.7.3.3.763.g91c7d\n"},{"id":"157922","messageId":"AANLkTimMb2ouZQbgOFB_JTuJb99vzC=2CzeUH3TnFU8M@mail.gmail.com","threadId":"26033","inReplyTo":"7vvd2y5h63.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/3] get_sha1_oneline: do not leak or double free","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T06:27:03Z","receivedAt":"2010-12-13T06:27:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/12/13 Junio C Hamano <gitster@pobox.com>:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> I think the following is easier to read and conveys what the code is\n>> trying to do more clearly.  No?\n>\n> This time with a proposed log message...\n\nMuch better. Thanks.\n-- \nDuy\n"},{"id":"157923","messageId":"7voc8q5gll.fsf@alter.siamese.dyndns.org","threadId":"26033","inReplyTo":"7vvd2y5h63.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/3] get_sha1_oneline: do not leak or double free","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T06:31:18Z","receivedAt":"2010-12-13T06:31:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"... and this is how your [2/3] would look on top of that.\n\nI didn't change the scratchpad bit assignment in this commit, as that is a\nlogically separate change and I didn't look at all the codepaths that can\ncall into this function to make sure they never use TMP_MARK themselves.\n\nThey shouldn't, but it is easier to revert the change if there were.\n\n-- >8 --\nFrom: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nDate: Mon, 13 Dec 2010 10:01:14 +0700\nSubject: [PATCH 2/3] get_sha1_oneline: make callers prepare the commit list to traverse\n\nThis gives callers more control, i.e. which ref will be searched from.\nThey must prepare the list ordered by committer date.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n sha1_name.c |   19 +++++++++++--------\n 1 files changed, 11 insertions(+), 8 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 2cc7a42..aefae1f 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -686,13 +686,13 @@ static int handle_one_ref(const char *path,\n \tif (object->type != OBJ_COMMIT)\n \t\treturn 0;\n \tinsert_by_date((struct commit *)object, list);\n-\tobject->flags |= ONELINE_SEEN;\n \treturn 0;\n }\n \n-static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n+static int get_sha1_oneline(const char *prefix, unsigned char *sha1,\n+\t\t\t    struct commit_list *list)\n {\n-\tstruct commit_list *list = NULL, *backup = NULL, *l;\n+\tstruct commit_list *backup = NULL, *l;\n \tint found = 0;\n \tregex_t regex;\n \n@@ -705,9 +705,10 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n \tif (regcomp(&regex, prefix, REG_EXTENDED))\n \t\tdie(\"Invalid search pattern: %s\", prefix);\n \n-\tfor_each_ref(handle_one_ref, &list);\n-\tfor (l = list; l; l = l->next)\n+\tfor (l = list; l; l = l->next) {\n+\t\tl->item->object.flags |= ONELINE_SEEN;\n \t\tcommit_list_insert(l->item, &backup);\n+\t}\n \twhile (list) {\n \t\tchar *p, *to_free = NULL;\n \t\tstruct commit *commit;\n@@ -1090,9 +1091,11 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\tint stage = 0;\n \t\tstruct cache_entry *ce;\n \t\tint pos;\n-\t\tif (namelen > 2 && name[1] == '/')\n-\t\t\t/* don't need mode for commit */\n-\t\t\treturn get_sha1_oneline(name + 2, sha1);\n+\t\tif (namelen > 2 && name[1] == '/') {\n+\t\t\tstruct commit_list *list = NULL;\n+\t\t\tfor_each_ref(handle_one_ref, &list);\n+\t\t\treturn get_sha1_oneline(name + 2, sha1, list);\n+\t\t}\n \t\tif (namelen < 3 ||\n \t\t    name[2] != ':' ||\n \t\t    name[1] < '0' || '3' < name[1])\n"},{"id":"157925","messageId":"7vk4je5g1n.fsf@alter.siamese.dyndns.org","threadId":"26033","inReplyTo":"7voc8q5gll.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/3] get_sha1_oneline: do not leak or double free","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T06:43:16Z","receivedAt":"2010-12-13T06:43:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"... and this is [3/3], whose sha1_name.c part needed some adjustment.\n\n-- >8 --\nFrom: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nDate: Mon, 13 Dec 2010 10:01:15 +0700\nSubject: [PATCH] get_sha1: support $commit^{/regex} syntax\n\nThis works like \":/regex\" syntax that finds a recently created commit\nstarting from all refs, but limits the discovery to those reachable from\nthe named commit.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/revisions.txt |    6 +++\n sha1_name.c                 |   37 +++++++++++++++------\n t/t1511-rev-parse-caret.sh  |   73 +++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 105 insertions(+), 11 deletions(-)\n create mode 100755 t/t1511-rev-parse-caret.sh\n\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 3d4b79c..174fa8e 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -106,6 +106,12 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.\n   and dereference the tag recursively until a non-tag object is\n   found.\n \n+* A suffix '{caret}' to a revision parameter followed by a brace\n+  pair that contains a text led by a slash (e.g. `HEAD^{/fix nasty bug}`):\n+  this is the same as `:/fix nasty bug` syntax below except that\n+  it returns the youngest matching commit which is reachable from\n+  the ref before '{caret}'.\n+\n * A colon, followed by a slash, followed by a text (e.g. `:/fix nasty bug`): this names\n   a commit whose commit message matches the specified regular expression.\n   This name returns the youngest matching commit which is\ndiff --git a/sha1_name.c b/sha1_name.c\nindex aefae1f..1ba4bc3 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -7,6 +7,8 @@\n #include \"refs.h\"\n #include \"remote.h\"\n \n+static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);\n+\n static int find_short_object_filename(int len, const char *name, unsigned char *sha1)\n {\n \tstruct alternate_object_database *alt;\n@@ -562,6 +564,8 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)\n \t\texpected_type = OBJ_BLOB;\n \telse if (sp[0] == '}')\n \t\texpected_type = OBJ_NONE;\n+\telse if (sp[0] == '/')\n+\t\texpected_type = OBJ_COMMIT;\n \telse\n \t\treturn -1;\n \n@@ -576,19 +580,30 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)\n \t\tif (!o || (!o->parsed && !parse_object(o->sha1)))\n \t\t\treturn -1;\n \t\thashcpy(sha1, o->sha1);\n+\t\treturn 0;\n \t}\n-\telse {\n-\t\t/*\n-\t\t * At this point, the syntax look correct, so\n-\t\t * if we do not get the needed object, we should\n-\t\t * barf.\n-\t\t */\n-\t\to = peel_to_type(name, len, o, expected_type);\n-\t\tif (o) {\n-\t\t\thashcpy(sha1, o->sha1);\n-\t\t\treturn 0;\n-\t\t}\n+\n+\t/*\n+\t * At this point, the syntax look correct, so\n+\t * if we do not get the needed object, we should\n+\t * barf.\n+\t */\n+\to = peel_to_type(name, len, o, expected_type);\n+\tif (!o)\n \t\treturn -1;\n+\n+\thashcpy(sha1, o->sha1);\n+\tif (sp[0] == '/') {\n+\t\t/* \"$commit^{/foo}\" */\n+\t\tchar *prefix;\n+\t\tint ret;\n+\t\tstruct commit_list *list = NULL;\n+\n+\t\tprefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));\n+\t\tcommit_list_insert((struct commit *)o, &list);\n+\t\tret = get_sha1_oneline(prefix, sha1, list);\n+\t\tfree(prefix);\n+\t\treturn ret;\n \t}\n \treturn 0;\n }\ndiff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh\nnew file mode 100755\nindex 0000000..5c8439c\n--- /dev/null\n+++ b/t/t1511-rev-parse-caret.sh\n@@ -0,0 +1,73 @@\n+#!/bin/sh\n+\n+test_description='tests for ref^{stuff}'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\techo blob >a-blob &&\n+\tgit tag -a -m blob blob-tag `git hash-object -w a-blob`\n+\tmkdir a-tree &&\n+\techo moreblobs >a-tree/another-blob &&\n+\tgit add . &&\n+\tTREE_SHA1=`git write-tree` &&\n+\tgit tag -a -m tree tree-tag \"$TREE_SHA1\" &&\n+\tgit commit -m Initial &&\n+\tgit tag -a -m commit commit-tag &&\n+\tgit branch ref &&\n+\tgit checkout master &&\n+\techo modified >>a-blob &&\n+\tgit add -u &&\n+\tgit commit -m Modified\n+'\n+\n+test_expect_success 'ref^{non-existent}' '\n+\ttest_must_fail git rev-parse ref^{non-existent}\n+'\n+\n+test_expect_success 'ref^{}' '\n+\tgit rev-parse ref >expected &&\n+\tgit rev-parse ref^{} >actual &&\n+\ttest_cmp expected actual &&\n+\tgit rev-parse commit-tag^{} >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'ref^{commit}' '\n+\tgit rev-parse ref >expected &&\n+\tgit rev-parse ref^{commit} >actual &&\n+\ttest_cmp expected actual &&\n+\tgit rev-parse commit-tag^{commit} >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_must_fail git rev-parse tree-tag^{commit} &&\n+\ttest_must_fail git rev-parse blob-tag^{commit}\n+'\n+\n+test_expect_success 'ref^{tree}' '\n+\techo $TREE_SHA1 >expected &&\n+\tgit rev-parse ref^{tree} >actual &&\n+\ttest_cmp expected actual &&\n+\tgit rev-parse commit-tag^{tree} >actual &&\n+\ttest_cmp expected actual &&\n+\tgit rev-parse tree-tag^{tree} >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_must_fail git rev-parse blob-tag^{tree}\n+'\n+\n+test_expect_success 'ref^{/}' '\n+\tgit rev-parse master >expected &&\n+\tgit rev-parse master^{/} >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'ref^{/non-existent}' '\n+\ttest_must_fail git rev-parse master^{/non-existent}\n+'\n+\n+test_expect_success 'ref^{/Initial}' '\n+\tgit rev-parse ref >expected &&\n+\tgit rev-parse master^{/Initial} >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n1.7.3.3.763.g91c7d\n"},{"id":"158130","messageId":"7vaak7rh9k.fsf@alter.siamese.dyndns.org","threadId":"26033","inReplyTo":"1292209275-17451-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 3/3] get_sha1: support ref^{/regex} syntax","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-15T00:50:31Z","receivedAt":"2010-12-15T00:50:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> +test_expect_success 'ref^{/}' '\n> +\tgit rev-parse master >expected &&\n> +\tgit rev-parse master^{/} >actual &&\n> +\ttest_cmp expected actual\n> +'\n\nThis test fails on FBSD 8, which refuses to regcomp(\"\")\nsaying \"empty (sub)expression\", which is somewhat bogus.\n\nI'd change it to \"/.\" for now ;-).\n"},{"id":"158131","messageId":"AANLkTikgXAg9+0TXzpFH7LT1yD2sV5Dp=025GbudnY5S@mail.gmail.com","threadId":"26033","inReplyTo":"7vaak7rh9k.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 3/3] get_sha1: support ref^{/regex} syntax","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-15T01:58:11Z","receivedAt":"2010-12-15T01:58:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/12/15 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> +test_expect_success 'ref^{/}' '\n>> +     git rev-parse master >expected &&\n>> +     git rev-parse master^{/} >actual &&\n>> +     test_cmp expected actual\n>> +'\n>\n> This test fails on FBSD 8, which refuses to regcomp(\"\")\n> saying \"empty (sub)expression\", which is somewhat bogus.\n>\n> I'd change it to \"/.\" for now ;-).\n\nPerhaps we should just reject this case. No sane body would ever use it.\n-- \nDuy\n"},{"id":"158132","messageId":"7v62uvrbfk.fsf@alter.siamese.dyndns.org","threadId":"26033","inReplyTo":"AANLkTikgXAg9+0TXzpFH7LT1yD2sV5Dp=025GbudnY5S@mail.gmail.com","subject":"Re: [PATCH 3/3] get_sha1: support ref^{/regex} syntax","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-15T02:56:31Z","receivedAt":"2010-12-15T02:56:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> Perhaps we should just reject this case. No sane body would ever use it.\n\nI'd have to say that dismissing before even trying is not a very\ndisciplined attitude.\n\nShouldn't an empty regexp simply match everything?  Even on FBSD8,\n\n    $ grep '' /etc/passwd\n\nseems to show everything.  How hard would it be to do this in the codepath\nwe are discussing?\n"},{"id":"158134","messageId":"AANLkTimy5MVkxDzXqzA8T4DHJmzsSAvwqVVzRvWw5KA0@mail.gmail.com","threadId":"26033","inReplyTo":"7v62uvrbfk.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 3/3] get_sha1: support ref^{/regex} syntax","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-15T03:12:59Z","receivedAt":"2010-12-15T03:12:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Dec 15, 2010 at 9:56 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> Perhaps we should just reject this case. No sane body would ever use it.\n>\n> I'd have to say that dismissing before even trying is not a very\n> disciplined attitude.\n>\n> Shouldn't an empty regexp simply match everything?  Even on FBSD8,\n>\n>    $ grep '' /etc/passwd\n>\n> seems to show everything.  How hard would it be to do this in the codepath\n> we are discussing?\n\nTo make '' match everything is easy. I'll cook up something when I get home.\n-- \nDuy\n"},{"id":"158148","messageId":"1292403774-361-1-git-send-email-pclouds@gmail.com","threadId":"26033","inReplyTo":"AANLkTimy5MVkxDzXqzA8T4DHJmzsSAvwqVVzRvWw5KA0@mail.gmail.com","subject":"[PATCH] get_sha1: handle special case $commit^{/}","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-15T09:02:54Z","receivedAt":"2010-12-15T09:02:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Empty regex pattern should always match. But the exact behavior of\nregexec() may vary. Because it always matches anyway, we can just\nreturn 'matched' without calling regex machinery.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n On top of nd/oneline-sha1-name-from-specific-ref\n\n sha1_name.c |    7 +++++++\n 1 files changed, 7 insertions(+), 0 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 1ba4bc3..c5c59ce 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -599,6 +599,13 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)\n \t\tint ret;\n \t\tstruct commit_list *list = NULL;\n \n+\t\t/*\n+\t\t * $commit^{/}. Some regex implementation may reject.\n+\t\t * We don't need regex anyway. '' pattern always matches.\n+\t\t */\n+\t\tif (sp[1] == '}')\n+\t\t\treturn 0;\n+\n \t\tprefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));\n \t\tcommit_list_insert((struct commit *)o, &list);\n \t\tret = get_sha1_oneline(prefix, sha1, list);\n-- \n1.7.3.3.476.g10a82\n"},{"id":"158198","messageId":"7v8vzqppo3.fsf@alter.siamese.dyndns.org","threadId":"26033","inReplyTo":"1292403774-361-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH] get_sha1: handle special case $commit^{/}","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-15T23:44:12Z","receivedAt":"2010-12-15T23:44:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> Empty regex pattern should always match. But the exact behavior of\n> regexec() may vary. Because it always matches anyway, we can just\n> return 'matched' without calling regex machinery.\n\nHmm, I just noticed that \"git grep -e '' Makefile\" fails on FBSD8 for\nthe same reason.\n\nI'd prefer a solution that is not about \"special case $commit^{/}\" but is\nabout \"work around regcomp that cannot compile an empty regexp\".\n\nWisdoms?\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  On top of nd/oneline-sha1-name-from-specific-ref\n>\n>  sha1_name.c |    7 +++++++\n>  1 files changed, 7 insertions(+), 0 deletions(-)\n>\n> diff --git a/sha1_name.c b/sha1_name.c\n> index 1ba4bc3..c5c59ce 100644\n> --- a/sha1_name.c\n> +++ b/sha1_name.c\n> @@ -599,6 +599,13 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)\n>  \t\tint ret;\n>  \t\tstruct commit_list *list = NULL;\n>  \n> +\t\t/*\n> +\t\t * $commit^{/}. Some regex implementation may reject.\n> +\t\t * We don't need regex anyway. '' pattern always matches.\n> +\t\t */\n> +\t\tif (sp[1] == '}')\n> +\t\t\treturn 0;\n> +\n>  \t\tprefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));\n>  \t\tcommit_list_insert((struct commit *)o, &list);\n>  \t\tret = get_sha1_oneline(prefix, sha1, list);\n> -- \n> 1.7.3.3.476.g10a82\n"},{"id":"158199","messageId":"AANLkTim3Y5jqwc9=StOLgvJYPPJS7B84Otz0=v1dAbc1@mail.gmail.com","threadId":"26033","inReplyTo":"7v8vzqppo3.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] get_sha1: handle special case $commit^{/}","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-16T00:23:13Z","receivedAt":"2010-12-16T00:23:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/12/16 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n>\n>> Empty regex pattern should always match. But the exact behavior of\n>> regexec() may vary. Because it always matches anyway, we can just\n>> return 'matched' without calling regex machinery.\n>\n> Hmm, I just noticed that \"git grep -e '' Makefile\" fails on FBSD8 for\n> the same reason.\n>\n> I'd prefer a solution that is not about \"special case $commit^{/}\" but is\n> about \"work around regcomp that cannot compile an empty regexp\".\n\nHmm.. if there is no compilation phase, we can redefine a macro to\nworkaround. Perhaps prefer compat/regex to FBSD's implementation?\n-- \nDuy\n"}]}