{"thread":{"id":"25711","subject":"[PATCH 0/3] Support relative path in <blah>:path syntax","startedAt":"2010-11-11T14:08:01Z","lastAt":"2010-11-28T03:37:32Z","messageCount":12,"participants":["Nguyễn Thái Ngọc Duy","Thiago Farina","Sverre Rabbelier","Junio C Hamano","Nguyen Thai Ngoc Duy"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"155687","messageId":"1289484484-8632-1-git-send-email-pclouds@gmail.com","threadId":"25711","inReplyTo":null,"subject":"[PATCH 0/3] Support relative path in <blah>:path syntax","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-11-11T14:08:01Z","receivedAt":"2010-11-11T14:08:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Sorry Jonathan I lied. I did not pick up your fast-import changes.\nCould not find how to test it. And it seems fast-import only cares\nabout commits, not the target audience of this syntax.\n\nDocument is not updated because I think it's intuitive enough.\n\nNguyễn Thái Ngọc Duy (3):\n  setup: save prefix (original cwd relative to toplevel) in\n    startup_info\n  Make prefix_path() return char* without const\n  get_sha1: support relative path \":path\" syntax\n\n cache.h                        |    3 +-\n setup.c                        |    6 ++-\n sha1_name.c                    |   37 ++++++++++++++++++++++--\n t/t1506-rev-parse-diagnosis.sh |   62 ++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 102 insertions(+), 6 deletions(-)\n\n-- \n1.7.3.2.210.g045198\n"},{"id":"155688","messageId":"1289484484-8632-2-git-send-email-pclouds@gmail.com","threadId":"25711","inReplyTo":"1289484484-8632-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/3] setup: save prefix (original cwd relative to toplevel) in startup_info","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-11-11T14:08:02Z","receivedAt":"2010-11-11T14:08:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Save the path from the original cwd to the cwd at the end of the\nsetup procedure in the startup_info struct introduced in e37c1329\n(2010-08-05).  The value cannot vary from thread to thread anyway,\nsince the cwd is global.\n\nSo now in your builtin command, instead of passing prefix around,\nwhen you want to convert a user-supplied path to a cwd-relative\npath, you can use startup_info->prefix directly.\n\nCaveat: As with the return value from setup_git_directory_gently(),\nstartup_info->prefix would be NULL when the original cwd is not a\nsubdir of the toplevel.\n\nLonger term, this woiuld allow the prefix to be reused when several\nnoncooperating functions require access to the same repository (for\nexample, when accessing configuration before running a builtin).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n cache.h |    1 +\n setup.c |    4 +++-\n 2 files changed, 4 insertions(+), 1 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 33decd9..222d9cf 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1117,6 +1117,7 @@ const char *split_cmdline_strerror(int cmdline_errno);\n /* git.c */\n struct startup_info {\n \tint have_repository;\n+\tconst char *prefix;\n };\n extern struct startup_info *startup_info;\n \ndiff --git a/setup.c b/setup.c\nindex a3b76de..833db12 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -512,8 +512,10 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \tconst char *prefix;\n \n \tprefix = setup_git_directory_gently_1(nongit_ok);\n-\tif (startup_info)\n+\tif (startup_info) {\n \t\tstartup_info->have_repository = !nongit_ok || !*nongit_ok;\n+\t\tstartup_info->prefix = prefix;\n+\t}\n \treturn prefix;\n }\n \n-- \n1.7.3.2.210.g045198\n"},{"id":"155689","messageId":"1289484484-8632-3-git-send-email-pclouds@gmail.com","threadId":"25711","inReplyTo":"1289484484-8632-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/3] Make prefix_path() return char* without const","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-11-11T14:08:03Z","receivedAt":"2010-11-11T14:08:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"prefix_path() allocates new buffer. There's no reason for it to keep\nthe buffer for itself and waste memory.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n cache.h |    2 +-\n setup.c |    2 +-\n 2 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 222d9cf..bd181c6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -428,7 +428,7 @@ extern const char **get_pathspec(const char *prefix, const char **pathspec);\n extern void setup_work_tree(void);\n extern const char *setup_git_directory_gently(int *);\n extern const char *setup_git_directory(void);\n-extern const char *prefix_path(const char *prefix, int len, const char *path);\n+extern char *prefix_path(const char *prefix, int len, const char *path);\n extern const char *prefix_filename(const char *prefix, int len, const char *path);\n extern int check_filename(const char *prefix, const char *name);\n extern void verify_filename(const char *prefix, const char *name);\ndiff --git a/setup.c b/setup.c\nindex 833db12..f930dc0 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -4,7 +4,7 @@\n static int inside_git_dir = -1;\n static int inside_work_tree = -1;\n \n-const char *prefix_path(const char *prefix, int len, const char *path)\n+char *prefix_path(const char *prefix, int len, const char *path)\n {\n \tconst char *orig = path;\n \tchar *sanitized = xmalloc(len + strlen(path) + 1);\n-- \n1.7.3.2.210.g045198\n"},{"id":"155690","messageId":"1289484484-8632-4-git-send-email-pclouds@gmail.com","threadId":"25711","inReplyTo":"1289484484-8632-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/3] get_sha1: support relative path \":path\" syntax","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-11-11T14:08:04Z","receivedAt":"2010-11-11T14:08:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Currently :path and ref:path can be used to refer to a specific object\nin index or ref respectively. \"path\" component is absolute path. This\npatch allows \"path\" to be written as \"./path\" or \"../path\", which is\nrelative to user's original cwd.\n\nThis does not work in commands for which startup_info is NULL\n(i.e. non-builtin ones, it seems none of them needs this anyway).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n sha1_name.c                    |   37 ++++++++++++++++++++++--\n t/t1506-rev-parse-diagnosis.sh |   62 ++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 96 insertions(+), 3 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 484081d..22c1df9 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -1046,6 +1046,23 @@ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,\n \treturn ret;\n }\n \n+static char *resolve_relative_path(const char *rel)\n+{\n+\tif (prefixcmp(rel, \"./\") && prefixcmp(rel, \"../\"))\n+\t\treturn NULL;\n+\n+\tif (!startup_info)\n+\t\tdie(\"Relative path syntax is not supported in this command. Please report.\");\n+\n+\tif (!is_inside_work_tree())\n+\t\tdie(\"relative path syntax can't be used outside working tree.\");\n+\n+\t/* die() inside prefix_path() if resolved path is outside worktree */\n+\treturn prefix_path(startup_info->prefix,\n+\t\t\t   startup_info->prefix ? strlen(startup_info->prefix) : 0,\n+\t\t\t   rel);\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@@ -1060,25 +1077,31 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \tif (!ret)\n \t\treturn ret;\n \t/* sha1:path --> object name of path in ent sha1\n-\t * :path -> object name of path in index\n+\t * :path -> object name of absolute path in index\n+\t * :./path -> object name of path relative to cwd in index\n \t * :[0-3]:path -> object name of path in index at stage\n \t * :/foo -> recent commit matching foo\n \t */\n \tif (name[0] == ':') {\n \t\tint stage = 0;\n \t\tstruct cache_entry *ce;\n+\t\tchar *new_path = NULL;\n \t\tint pos;\n \t\tif (namelen > 2 && name[1] == '/')\n \t\t\treturn get_sha1_oneline(name + 2, sha1);\n \t\tif (namelen < 3 ||\n \t\t    name[2] != ':' ||\n-\t\t    name[1] < '0' || '3' < name[1])\n+\t\t    name[1] < '0' || '3' < name[1]) {\n \t\t\tcp = name + 1;\n+\t\t\tnew_path = resolve_relative_path(cp);\n+\t\t\tif (new_path)\n+\t\t\t\tcp = new_path;\n+\t\t}\n \t\telse {\n \t\t\tstage = name[1] - '0';\n \t\t\tcp = name + 3;\n \t\t}\n-\t\tnamelen = namelen - (cp - name);\n+\t\tnamelen = strlen(cp);\n \n \t\tstrncpy(oc->path, cp,\n \t\t\tsizeof(oc->path));\n@@ -1096,12 +1119,14 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\t\t\tbreak;\n \t\t\tif (ce_stage(ce) == stage) {\n \t\t\t\thashcpy(sha1, ce->sha1);\n+\t\t\t\tfree(new_path);\n \t\t\t\treturn 0;\n \t\t\t}\n \t\t\tpos++;\n \t\t}\n \t\tif (!gently)\n \t\t\tdiagnose_invalid_index_path(stage, prefix, cp);\n+\t\tfree(new_path);\n \t\treturn -1;\n \t}\n \tfor (cp = name, bracket_depth = 0; *cp; cp++) {\n@@ -1122,6 +1147,11 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\t}\n \t\tif (!get_sha1_1(name, cp-name, tree_sha1)) {\n \t\t\tconst char *filename = cp+1;\n+\t\t\tchar *new_filename = NULL;\n+\n+\t\t\tnew_filename = resolve_relative_path(filename);\n+\t\t\tif (new_filename)\n+\t\t\t\tfilename = new_filename;\n \t\t\tret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);\n \t\t\tif (!gently) {\n \t\t\t\tdiagnose_invalid_sha1_path(prefix, filename,\n@@ -1133,6 +1163,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\t\t\tsizeof(oc->path));\n \t\t\toc->path[sizeof(oc->path)-1] = '\\0';\n \n+\t\t\tfree(new_filename);\n \t\t\treturn ret;\n \t\t} else {\n \t\t\tif (!gently)\ndiff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh\nindex 0eeeb0e..f7a4076 100755\n--- a/t/t1506-rev-parse-diagnosis.sh\n+++ b/t/t1506-rev-parse-diagnosis.sh\n@@ -31,6 +31,43 @@ test_expect_success 'correct file objects' '\n \t test $HASH_file = $(git rev-parse :0:file.txt) )\n '\n \n+test_expect_success 'correct relative file objects (0)' '\n+\tgit rev-parse :file.txt >expected &&\n+\tgit rev-parse :./file.txt >result &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'correct relative file objects (1)' '\n+\tgit rev-parse HEAD:file.txt >expected &&\n+\tgit rev-parse HEAD:./file.txt >result &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'correct relative file objects (2)' '\n+\t(\n+\t\tcd subdir &&\n+\t\tgit rev-parse HEAD:../file.txt >result &&\n+\t\ttest_cmp ../expected result\n+\t)\n+'\n+\n+test_expect_success 'correct relative file objects (3)' '\n+\t(\n+\t\tcd subdir &&\n+\t\tgit rev-parse HEAD:../subdir/../file.txt >result &&\n+\t\ttest_cmp ../expected result\n+\t)\n+'\n+\n+test_expect_success 'correct relative file objects (4)' '\n+\tgit rev-parse HEAD:subdir/file.txt >expected &&\n+\t(\n+\t\tcd subdir &&\n+\t\tgit rev-parse HEAD:./file.txt >result &&\n+\t\ttest_cmp ../expected result\n+\t)\n+'\n+\n test_expect_success 'incorrect revision id' '\n \ttest_must_fail git rev-parse foobar:file.txt 2>error &&\n \tgrep \"Invalid object name '\"'\"'foobar'\"'\"'.\" error &&\n@@ -75,4 +112,29 @@ test_expect_success 'invalid @{n} reference' '\n \tgrep \"fatal: Log for [^ ]* only has [0-9][0-9]* entries.\" error\n '\n \n+test_expect_success 'relative path not found' '\n+\t(\n+\t\tcd subdir &&\n+\t\ttest_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&\n+\t\tgrep subdir/nonexistent.txt error\n+\t)\n+'\n+\n+test_expect_success 'relative path outside worktree' '\n+\ttest_must_fail git rev-parse HEAD:../file.txt >output 2>error &&\n+\ttest -z \"$(cat output)\" &&\n+\tgrep \"outside repository\" error\n+'\n+\n+test_expect_success 'relative path when cwd is outside worktree' '\n+\ttest_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&\n+\ttest -z \"$(cat output)\" &&\n+\tgrep \"relative path syntax can.t be used outside working tree.\" error\n+'\n+\n+test_expect_success 'relative path when startup_info is NULL' '\n+\ttest_must_fail test-match-trees HEAD:./file.txt HEAD:./file.txt 2>error &&\n+\tgrep \"Relative path syntax is not supported in this command\" error\n+'\n+\n test_done\n-- \n1.7.3.2.210.g045198\n"},{"id":"155872","messageId":"AANLkTimJKQ=xc7az+FEM+18aTxQ5zQ24q7pfChnSwt6f@mail.gmail.com","threadId":"25711","inReplyTo":"1289484484-8632-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 3/3] get_sha1: support relative path \":path\" syntax","fromName":"Thiago Farina","fromEmail":"tfransosi@gmail.com","sentAt":"2010-11-14T20:22:14Z","receivedAt":"2010-11-14T20:22:14Z","isPatch":true,"sender":{"key":"tfransosi@gmail.com","avatar":"https://avatars.githubusercontent.com/u/970071?v=4"},"body":"2010/11/11 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n> Currently :path and ref:path can be used to refer to a specific object\n> in index or ref respectively. \"path\" component is absolute path. This\n> patch allows \"path\" to be written as \"./path\" or \"../path\", which is\n> relative to user's original cwd.\n>\n> This does not work in commands for which startup_info is NULL\n> (i.e. non-builtin ones, it seems none of them needs this anyway).\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  sha1_name.c                    |   37 ++++++++++++++++++++++--\n>  t/t1506-rev-parse-diagnosis.sh |   62 ++++++++++++++++++++++++++++++++++++++++\n>  2 files changed, 96 insertions(+), 3 deletions(-)\n>\n> diff --git a/sha1_name.c b/sha1_name.c\n> index 484081d..22c1df9 100644\n> --- a/sha1_name.c\n> +++ b/sha1_name.c\n> @@ -1046,6 +1046,23 @@ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,\n>        return ret;\n>  }\n>\n> +static char *resolve_relative_path(const char *rel)\n> +{\n> +       if (prefixcmp(rel, \"./\") && prefixcmp(rel, \"../\"))\n> +               return NULL;\n> +\n> +       if (!startup_info)\n> +               die(\"Relative path syntax is not supported in this command. Please report.\");\nIs the \"Please report.\" necessary? Report to who? Where? (I know we\nknow these answers, but maybe a new user won't know them).\n\n> +\n> +       if (!is_inside_work_tree())\n> +               die(\"relative path syntax can't be used outside working tree.\");\nnit: s/relative/Relative ?\n"},{"id":"155906","messageId":"1289793369-10653-1-git-send-email-pclouds@gmail.com","threadId":"25711","inReplyTo":"AANLkTimJKQ=xc7az+FEM+18aTxQ5zQ24q7pfChnSwt6f@mail.gmail.com","subject":"[PATCH] get_sha1: support relative path \":path\" syntax","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-11-15T03:56:09Z","receivedAt":"2010-11-15T03:56:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Currently :path and ref:path can be used to refer to a specific object\nin index or ref respectively. \"path\" component is absolute path. This\npatch allows \"path\" to be written as \"./path\" or \"../path\", which is\nrelative to user's original cwd.\n\nThis does not work in commands for which startup_info is NULL\n(i.e. non-builtin ones).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Fixed the error messages in resolve_relative_path()\n\n sha1_name.c                    |   38 ++++++++++++++++++++++--\n t/t1506-rev-parse-diagnosis.sh |   62 ++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 97 insertions(+), 3 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 484081d..1d227d5 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -1046,6 +1046,24 @@ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,\n \treturn ret;\n }\n \n+static char *resolve_relative_path(const char *rel)\n+{\n+\tif (prefixcmp(rel, \"./\") && prefixcmp(rel, \"../\"))\n+\t\treturn NULL;\n+\n+\tif (!startup_info)\n+\t\tdie(\"Relative path syntax is not supported in this command.\\n\"\n+\t\t    \"Please report to git@vger.kernel.org.\");\n+\n+\tif (!is_inside_work_tree())\n+\t\tdie(\"Relative path syntax can't be used outside working tree.\");\n+\n+\t/* die() inside prefix_path() if resolved path is outside worktree */\n+\treturn prefix_path(startup_info->prefix,\n+\t\t\t   startup_info->prefix ? strlen(startup_info->prefix) : 0,\n+\t\t\t   rel);\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@@ -1060,25 +1078,31 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \tif (!ret)\n \t\treturn ret;\n \t/* sha1:path --> object name of path in ent sha1\n-\t * :path -> object name of path in index\n+\t * :path -> object name of absolute path in index\n+\t * :./path -> object name of path relative to cwd in index\n \t * :[0-3]:path -> object name of path in index at stage\n \t * :/foo -> recent commit matching foo\n \t */\n \tif (name[0] == ':') {\n \t\tint stage = 0;\n \t\tstruct cache_entry *ce;\n+\t\tchar *new_path = NULL;\n \t\tint pos;\n \t\tif (namelen > 2 && name[1] == '/')\n \t\t\treturn get_sha1_oneline(name + 2, sha1);\n \t\tif (namelen < 3 ||\n \t\t    name[2] != ':' ||\n-\t\t    name[1] < '0' || '3' < name[1])\n+\t\t    name[1] < '0' || '3' < name[1]) {\n \t\t\tcp = name + 1;\n+\t\t\tnew_path = resolve_relative_path(cp);\n+\t\t\tif (new_path)\n+\t\t\t\tcp = new_path;\n+\t\t}\n \t\telse {\n \t\t\tstage = name[1] - '0';\n \t\t\tcp = name + 3;\n \t\t}\n-\t\tnamelen = namelen - (cp - name);\n+\t\tnamelen = strlen(cp);\n \n \t\tstrncpy(oc->path, cp,\n \t\t\tsizeof(oc->path));\n@@ -1096,12 +1120,14 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\t\t\tbreak;\n \t\t\tif (ce_stage(ce) == stage) {\n \t\t\t\thashcpy(sha1, ce->sha1);\n+\t\t\t\tfree(new_path);\n \t\t\t\treturn 0;\n \t\t\t}\n \t\t\tpos++;\n \t\t}\n \t\tif (!gently)\n \t\t\tdiagnose_invalid_index_path(stage, prefix, cp);\n+\t\tfree(new_path);\n \t\treturn -1;\n \t}\n \tfor (cp = name, bracket_depth = 0; *cp; cp++) {\n@@ -1122,6 +1148,11 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\t}\n \t\tif (!get_sha1_1(name, cp-name, tree_sha1)) {\n \t\t\tconst char *filename = cp+1;\n+\t\t\tchar *new_filename = NULL;\n+\n+\t\t\tnew_filename = resolve_relative_path(filename);\n+\t\t\tif (new_filename)\n+\t\t\t\tfilename = new_filename;\n \t\t\tret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);\n \t\t\tif (!gently) {\n \t\t\t\tdiagnose_invalid_sha1_path(prefix, filename,\n@@ -1133,6 +1164,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\t\t\tsizeof(oc->path));\n \t\t\toc->path[sizeof(oc->path)-1] = '\\0';\n \n+\t\t\tfree(new_filename);\n \t\t\treturn ret;\n \t\t} else {\n \t\t\tif (!gently)\ndiff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh\nindex 0eeeb0e..f7a4076 100755\n--- a/t/t1506-rev-parse-diagnosis.sh\n+++ b/t/t1506-rev-parse-diagnosis.sh\n@@ -31,6 +31,43 @@ test_expect_success 'correct file objects' '\n \t test $HASH_file = $(git rev-parse :0:file.txt) )\n '\n \n+test_expect_success 'correct relative file objects (0)' '\n+\tgit rev-parse :file.txt >expected &&\n+\tgit rev-parse :./file.txt >result &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'correct relative file objects (1)' '\n+\tgit rev-parse HEAD:file.txt >expected &&\n+\tgit rev-parse HEAD:./file.txt >result &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'correct relative file objects (2)' '\n+\t(\n+\t\tcd subdir &&\n+\t\tgit rev-parse HEAD:../file.txt >result &&\n+\t\ttest_cmp ../expected result\n+\t)\n+'\n+\n+test_expect_success 'correct relative file objects (3)' '\n+\t(\n+\t\tcd subdir &&\n+\t\tgit rev-parse HEAD:../subdir/../file.txt >result &&\n+\t\ttest_cmp ../expected result\n+\t)\n+'\n+\n+test_expect_success 'correct relative file objects (4)' '\n+\tgit rev-parse HEAD:subdir/file.txt >expected &&\n+\t(\n+\t\tcd subdir &&\n+\t\tgit rev-parse HEAD:./file.txt >result &&\n+\t\ttest_cmp ../expected result\n+\t)\n+'\n+\n test_expect_success 'incorrect revision id' '\n \ttest_must_fail git rev-parse foobar:file.txt 2>error &&\n \tgrep \"Invalid object name '\"'\"'foobar'\"'\"'.\" error &&\n@@ -75,4 +112,29 @@ test_expect_success 'invalid @{n} reference' '\n \tgrep \"fatal: Log for [^ ]* only has [0-9][0-9]* entries.\" error\n '\n \n+test_expect_success 'relative path not found' '\n+\t(\n+\t\tcd subdir &&\n+\t\ttest_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&\n+\t\tgrep subdir/nonexistent.txt error\n+\t)\n+'\n+\n+test_expect_success 'relative path outside worktree' '\n+\ttest_must_fail git rev-parse HEAD:../file.txt >output 2>error &&\n+\ttest -z \"$(cat output)\" &&\n+\tgrep \"outside repository\" error\n+'\n+\n+test_expect_success 'relative path when cwd is outside worktree' '\n+\ttest_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&\n+\ttest -z \"$(cat output)\" &&\n+\tgrep \"relative path syntax can.t be used outside working tree.\" error\n+'\n+\n+test_expect_success 'relative path when startup_info is NULL' '\n+\ttest_must_fail test-match-trees HEAD:./file.txt HEAD:./file.txt 2>error &&\n+\tgrep \"Relative path syntax is not supported in this command\" error\n+'\n+\n test_done\n-- \n1.7.3.2.210.g045198\n"},{"id":"155925","messageId":"AANLkTi=aVyOiRkE-+y8DDwcBQK36bVFrAX5PFw3TjCey@mail.gmail.com","threadId":"25711","inReplyTo":"1289793369-10653-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH] get_sha1: support relative path \":path\" syntax","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2010-11-15T14:56:20Z","receivedAt":"2010-11-15T14:56:20Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\n2010/11/15 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n> +               die(\"Relative path syntax is not supported in this command.\\n\"\n> +                   \"Please report to git@vger.kernel.org.\");\n\nMight it save us a lot of debugging hassle later if we report what\n\"this command\" is? E.g., if the user is using some internal tool that\nhappens to dispatch to a command that doesn't support this, it could\nhelp us to know what command is being used?\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"155931","messageId":"7v4obimr5l.fsf@alter.siamese.dyndns.org","threadId":"25711","inReplyTo":"AANLkTi=aVyOiRkE-+y8DDwcBQK36bVFrAX5PFw3TjCey@mail.gmail.com","subject":"Re: [PATCH] get_sha1: support relative path \":path\" syntax","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-11-15T17:29:42Z","receivedAt":"2010-11-15T17:29:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Sverre Rabbelier <srabbelier@gmail.com> writes:\n\n> 2010/11/15 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n>> +               die(\"Relative path syntax is not supported in this command.\\n\"\n>> +                   \"Please report to git@vger.kernel.org.\");\n>\n> Might it save us a lot of debugging hassle later if we report what\n> \"this command\" is? E.g., if the user is using some internal tool that\n> happens to dispatch to a command that doesn't support this, it could\n> help us to know what command is being used?\n\nIn the absence of programming errors, should all git command support\n<tree>:./<path> syntax to name an object, or are there cases where the\nrelative does not make any sense?\n\nWhat I am trying to get at is that if this is diagnosing a user error, or\nif this is showing that the mechanism to implement the relative path is\nunnecessarily hard for the programmers to misuse.\n\nFor example, if \"git show HEAD:./Makefile\" in a bare repository is a user\nerror, it is not \"not supported in this command\" but \"not supported in\nthis situation (more specifically, in a bare repository)\", so the first\nline is wrong, and more importantly, if that is a user error, there is\nnothing to report to the list.\n\nIf on the other hand, a command that ought to allow use of relative path\ndidn't set up necessary startup_info, this is diagnosing a programming\nerror.  But if that is the case, shouldn't we be able to do much better to\navoid such mistakes in the first place than triggering a BUG() here when\nthe user happens to try using this particular feature?  In other words,\neven when this \"feature\" isn't used in a particular invocation of a\ncommand, a command that is capable of supporting the feature should always\nbe setting up startup_info, perhaps by the time its cmd_foo() is called,\nno?  Shouldn't we be putting a BUG() somewhere higher and more visible in\nthe callchain to help catching such bugs?\n"},{"id":"155936","messageId":"7voc9ql8fe.fsf@alter.siamese.dyndns.org","threadId":"25711","inReplyTo":"7v4obimr5l.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] get_sha1: support relative path \":path\" syntax","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-11-15T18:59:33Z","receivedAt":"2010-11-15T18:59:33Z","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> In the absence of programming errors, should all git command support\n> <tree>:./<path> syntax to name an object, or are there cases where the\n> relative does not make any sense?\n>\n> What I am trying to get at is that if this is diagnosing a user error, or\n> if this is showing that the mechanism to implement the relative path is\n> unnecessarily hard for the programmers to misuse.\n\nThe mistake should be obvious from the context, but I wanted to say with\nthe last sentence \"the mechanism is too hard for the programmers to use\nand easy to make mistakes.\"\n"},{"id":"156060","messageId":"7vk4kbg7jz.fsf@alter.siamese.dyndns.org","threadId":"25711","inReplyTo":"1289484484-8632-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 0/3] Support relative path in <blah>:path syntax","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-11-17T17:54:08Z","receivedAt":"2010-11-17T17:54:08Z","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> Sorry Jonathan I lied. I did not pick up your fast-import changes.\n> Could not find how to test it. And it seems fast-import only cares\n> about commits, not the target audience of this syntax.\n>\n> Document is not updated because I think it's intuitive enough.\n\nWhen you say <tree>:<path>, you would intuitively expect that the path is\nrelative to <tree>, but this patch deliberately breaks (in a good way)\nthat expectation by introducing a magic token \"./\".  Once you know that\n\"./\" magic _exists_, it is obvious what it means, but people may not even\nimagine that such a magic may exist in the first place (certainly old\ntimers won't), and would not know what the magic token is if you do not\ntell them.\n\nIt needs to be documented.\n"},{"id":"156075","messageId":"20101118014701.GA30452@do","threadId":"25711","inReplyTo":"7vk4kbg7jz.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 0/3] Support relative path in <blah>:path syntax","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-11-18T01:47:01Z","receivedAt":"2010-11-18T01:47:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Nov 17, 2010 at 09:54:08AM -0800, Junio C Hamano wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n> \n> > Sorry Jonathan I lied. I did not pick up your fast-import changes.\n> > Could not find how to test it. And it seems fast-import only cares\n> > about commits, not the target audience of this syntax.\n> >\n> > Document is not updated because I think it's intuitive enough.\n> \n> When you say <tree>:<path>, you would intuitively expect that the path is\n> relative to <tree>, but this patch deliberately breaks (in a good way)\n> that expectation by introducing a magic token \"./\".  Once you know that\n> \"./\" magic _exists_, it is obvious what it means, but people may not even\n> imagine that such a magic may exist in the first place (certainly old\n> timers won't), and would not know what the magic token is if you do not\n> tell them.\n> \n> It needs to be documented.\n\nOK. How about this, squashing in the last patch of this series?\n\n--8<--\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 3d4b79c..3fc3e8b 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -121,6 +121,10 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.\n   ':path' (with an empty part before the colon, e.g. `:README`)\n   is a special case of the syntax described next: content\n   recorded in the index at the given path.\n+  A path starting with './' or '../' is relative to current working directory.\n+  The given path will be converted to be relative to working tree's root directory.\n+  This is most useful to address a blob or tree from a commit or tree that has\n+  the same tree structure with the working tree.\n \n * A colon, optionally followed by a stage number (0 to 3) and a\n   colon, followed by a path (e.g. `:0:README`); this names a blob object in the\n--8<--\n"},{"id":"156746","messageId":"1290915452-6339-1-git-send-email-pclouds@gmail.com","threadId":"25711","inReplyTo":"7v4obimr5l.fsf@alter.siamese.dyndns.org","subject":"[PATCH] get_sha1: support relative path \":path\" syntax","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-11-28T03:37:32Z","receivedAt":"2010-11-28T03:37:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Currently :path and ref:path can be used to refer to a specific object\nin index or ref respectively. \"path\" component is absolute path. This\npatch allows \"path\" to be written as \"./path\" or \"../path\", which is\nrelative to user's original cwd.\n\nThis does not work in commands for which startup_info is NULL\n(i.e. non-builtin ones, it seems none of them needs this anyway).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Updated the message when startup_info == NULL\n\n Documentation/revisions.txt    |    4 ++\n sha1_name.c                    |   37 ++++++++++++++++++++++--\n t/t1506-rev-parse-diagnosis.sh |   62 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 100 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 3d4b79c..8b519d7 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -121,6 +121,10 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.\n   ':path' (with an empty part before the colon, e.g. `:README`)\n   is a special case of the syntax described next: content\n   recorded in the index at the given path.\n+  A path starting with './' or '../' is relative to current working directory.\n+  The given path will be converted to be relative to working tree's root directory.\n+  This is most useful to address a blob or tree from a commit or tree that has\n+  the same tree structure with the working tree.\n \n * A colon, optionally followed by a stage number (0 to 3) and a\n   colon, followed by a path (e.g. `:0:README`); this names a blob object in the\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 484081d..f918faf 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -1046,6 +1046,23 @@ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,\n \treturn ret;\n }\n \n+static char *resolve_relative_path(const char *rel)\n+{\n+\tif (prefixcmp(rel, \"./\") && prefixcmp(rel, \"../\"))\n+\t\treturn NULL;\n+\n+\tif (!startup_info)\n+\t\tdie(\"BUG: startup_info struct is not initialized.\");\n+\n+\tif (!is_inside_work_tree())\n+\t\tdie(\"relative path syntax can't be used outside working tree.\");\n+\n+\t/* die() inside prefix_path() if resolved path is outside worktree */\n+\treturn prefix_path(startup_info->prefix,\n+\t\t\t   startup_info->prefix ? strlen(startup_info->prefix) : 0,\n+\t\t\t   rel);\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@@ -1060,25 +1077,31 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \tif (!ret)\n \t\treturn ret;\n \t/* sha1:path --> object name of path in ent sha1\n-\t * :path -> object name of path in index\n+\t * :path -> object name of absolute path in index\n+\t * :./path -> object name of path relative to cwd in index\n \t * :[0-3]:path -> object name of path in index at stage\n \t * :/foo -> recent commit matching foo\n \t */\n \tif (name[0] == ':') {\n \t\tint stage = 0;\n \t\tstruct cache_entry *ce;\n+\t\tchar *new_path = NULL;\n \t\tint pos;\n \t\tif (namelen > 2 && name[1] == '/')\n \t\t\treturn get_sha1_oneline(name + 2, sha1);\n \t\tif (namelen < 3 ||\n \t\t    name[2] != ':' ||\n-\t\t    name[1] < '0' || '3' < name[1])\n+\t\t    name[1] < '0' || '3' < name[1]) {\n \t\t\tcp = name + 1;\n+\t\t\tnew_path = resolve_relative_path(cp);\n+\t\t\tif (new_path)\n+\t\t\t\tcp = new_path;\n+\t\t}\n \t\telse {\n \t\t\tstage = name[1] - '0';\n \t\t\tcp = name + 3;\n \t\t}\n-\t\tnamelen = namelen - (cp - name);\n+\t\tnamelen = strlen(cp);\n \n \t\tstrncpy(oc->path, cp,\n \t\t\tsizeof(oc->path));\n@@ -1096,12 +1119,14 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\t\t\tbreak;\n \t\t\tif (ce_stage(ce) == stage) {\n \t\t\t\thashcpy(sha1, ce->sha1);\n+\t\t\t\tfree(new_path);\n \t\t\t\treturn 0;\n \t\t\t}\n \t\t\tpos++;\n \t\t}\n \t\tif (!gently)\n \t\t\tdiagnose_invalid_index_path(stage, prefix, cp);\n+\t\tfree(new_path);\n \t\treturn -1;\n \t}\n \tfor (cp = name, bracket_depth = 0; *cp; cp++) {\n@@ -1122,6 +1147,11 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\t}\n \t\tif (!get_sha1_1(name, cp-name, tree_sha1)) {\n \t\t\tconst char *filename = cp+1;\n+\t\t\tchar *new_filename = NULL;\n+\n+\t\t\tnew_filename = resolve_relative_path(filename);\n+\t\t\tif (new_filename)\n+\t\t\t\tfilename = new_filename;\n \t\t\tret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);\n \t\t\tif (!gently) {\n \t\t\t\tdiagnose_invalid_sha1_path(prefix, filename,\n@@ -1133,6 +1163,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t\t\t\tsizeof(oc->path));\n \t\t\toc->path[sizeof(oc->path)-1] = '\\0';\n \n+\t\t\tfree(new_filename);\n \t\t\treturn ret;\n \t\t} else {\n \t\t\tif (!gently)\ndiff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh\nindex 0eeeb0e..f7a4076 100755\n--- a/t/t1506-rev-parse-diagnosis.sh\n+++ b/t/t1506-rev-parse-diagnosis.sh\n@@ -31,6 +31,43 @@ test_expect_success 'correct file objects' '\n \t test $HASH_file = $(git rev-parse :0:file.txt) )\n '\n \n+test_expect_success 'correct relative file objects (0)' '\n+\tgit rev-parse :file.txt >expected &&\n+\tgit rev-parse :./file.txt >result &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'correct relative file objects (1)' '\n+\tgit rev-parse HEAD:file.txt >expected &&\n+\tgit rev-parse HEAD:./file.txt >result &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'correct relative file objects (2)' '\n+\t(\n+\t\tcd subdir &&\n+\t\tgit rev-parse HEAD:../file.txt >result &&\n+\t\ttest_cmp ../expected result\n+\t)\n+'\n+\n+test_expect_success 'correct relative file objects (3)' '\n+\t(\n+\t\tcd subdir &&\n+\t\tgit rev-parse HEAD:../subdir/../file.txt >result &&\n+\t\ttest_cmp ../expected result\n+\t)\n+'\n+\n+test_expect_success 'correct relative file objects (4)' '\n+\tgit rev-parse HEAD:subdir/file.txt >expected &&\n+\t(\n+\t\tcd subdir &&\n+\t\tgit rev-parse HEAD:./file.txt >result &&\n+\t\ttest_cmp ../expected result\n+\t)\n+'\n+\n test_expect_success 'incorrect revision id' '\n \ttest_must_fail git rev-parse foobar:file.txt 2>error &&\n \tgrep \"Invalid object name '\"'\"'foobar'\"'\"'.\" error &&\n@@ -75,4 +112,29 @@ test_expect_success 'invalid @{n} reference' '\n \tgrep \"fatal: Log for [^ ]* only has [0-9][0-9]* entries.\" error\n '\n \n+test_expect_success 'relative path not found' '\n+\t(\n+\t\tcd subdir &&\n+\t\ttest_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&\n+\t\tgrep subdir/nonexistent.txt error\n+\t)\n+'\n+\n+test_expect_success 'relative path outside worktree' '\n+\ttest_must_fail git rev-parse HEAD:../file.txt >output 2>error &&\n+\ttest -z \"$(cat output)\" &&\n+\tgrep \"outside repository\" error\n+'\n+\n+test_expect_success 'relative path when cwd is outside worktree' '\n+\ttest_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&\n+\ttest -z \"$(cat output)\" &&\n+\tgrep \"relative path syntax can.t be used outside working tree.\" error\n+'\n+\n+test_expect_success 'relative path when startup_info is NULL' '\n+\ttest_must_fail test-match-trees HEAD:./file.txt HEAD:./file.txt 2>error &&\n+\tgrep \"Relative path syntax is not supported in this command\" error\n+'\n+\n test_done\n-- \n1.7.3.2.316.gda8b3\n"}]}