{"thread":{"id":"23879","subject":"[PATCH] ls-files: allow relative pathspec","startedAt":"2010-05-22T16:35:25Z","lastAt":"2010-05-26T21:05:24Z","messageCount":3,"participants":["Clemens Buchacher","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"142107","messageId":"20100522163525.GA12420@localhost","threadId":"23879","inReplyTo":null,"subject":"[PATCH] ls-files: allow relative pathspec","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-05-22T16:35:25Z","receivedAt":"2010-05-22T16:35:25Z","isPatch":true,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"git ls-files used to error out if given paths which point outside\nthe current working directory, such as '../'. We now allow such\npaths and the output is analogous to git grep -l.\n\nSigned-off-by: Clemens Buchacher <drizzd@aon.at>\n---\n builtin/ls-files.c |   61 +++++++++++++++++++++++++++++-----------------------\n t/t7010-setup.sh   |   12 +++------\n 2 files changed, 38 insertions(+), 35 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex c0fbcdc..441da81 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -26,7 +26,8 @@ static int show_killed;\n static int show_valid_bit;\n static int line_terminator = '\\n';\n \n-static int prefix_len;\n+static const char *prefix;\n+static int max_prefix_len;\n static int prefix_offset;\n static const char **pathspec;\n static int error_unmatch;\n@@ -43,10 +44,19 @@ static const char *tag_modified = \"\";\n static const char *tag_skip_worktree = \"\";\n static const char *tag_resolve_undo = \"\";\n \n+static void write_name(const char* name, size_t len)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tif (prefix_offset > 0)\n+\t\tquote_path_relative(name, len, &path, prefix);\n+\telse\n+\t\tstrbuf_add(&path, name, len);\n+\twrite_name_quoted(strbuf_detach(&path, NULL), stdout, line_terminator);\n+}\n+\n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n-\tint len = prefix_len;\n-\tint offset = prefix_offset;\n+\tint len = max_prefix_len;\n \n \tif (len >= ent->len)\n \t\tdie(\"git ls-files: internal error - directory entry not superset of prefix\");\n@@ -55,7 +65,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \t\treturn;\n \n \tfputs(tag, stdout);\n-\twrite_name_quoted(ent->name + offset, stdout, line_terminator);\n+\twrite_name(ent->name, ent->len);\n }\n \n static void show_other_files(struct dir_struct *dir)\n@@ -121,8 +131,7 @@ static void show_killed_files(struct dir_struct *dir)\n \n static void show_ce_entry(const char *tag, struct cache_entry *ce)\n {\n-\tint len = prefix_len;\n-\tint offset = prefix_offset;\n+\tint len = max_prefix_len;\n \n \tif (len >= ce_namelen(ce))\n \t\tdie(\"git ls-files: internal error - cache entry not superset of prefix\");\n@@ -156,20 +165,19 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)\n \t\t       find_unique_abbrev(ce->sha1,abbrev),\n \t\t       ce_stage(ce));\n \t}\n-\twrite_name_quoted(ce->name + offset, stdout, line_terminator);\n+\twrite_name(ce->name, ce_namelen(ce));\n }\n \n static int show_one_ru(struct string_list_item *item, void *cbdata)\n {\n-\tint offset = prefix_offset;\n \tconst char *path = item->string;\n \tstruct resolve_undo_info *ui = item->util;\n \tint i, len;\n \n \tlen = strlen(path);\n-\tif (len < prefix_len)\n+\tif (len < max_prefix_len)\n \t\treturn 0; /* outside of the prefix */\n-\tif (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))\n+\tif (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))\n \t\treturn 0; /* uninterested */\n \tfor (i = 0; i < 3; i++) {\n \t\tif (!ui->mode[i])\n@@ -177,19 +185,19 @@ static int show_one_ru(struct string_list_item *item, void *cbdata)\n \t\tprintf(\"%s%06o %s %d\\t\", tag_resolve_undo, ui->mode[i],\n \t\t       find_unique_abbrev(ui->sha1[i], abbrev),\n \t\t       i + 1);\n-\t\twrite_name_quoted(path + offset, stdout, line_terminator);\n+\t\twrite_name(path, len);\n \t}\n \treturn 0;\n }\n \n-static void show_ru_info(const char *prefix)\n+static void show_ru_info()\n {\n \tif (!the_index.resolve_undo)\n \t\treturn;\n \tfor_each_string_list(show_one_ru, the_index.resolve_undo, NULL);\n }\n \n-static void show_files(struct dir_struct *dir, const char *prefix)\n+static void show_files(struct dir_struct *dir)\n {\n \tint i;\n \n@@ -243,7 +251,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)\n  */\n static void prune_cache(const char *prefix)\n {\n-\tint pos = cache_name_pos(prefix, prefix_len);\n+\tint pos = cache_name_pos(prefix, max_prefix_len);\n \tunsigned int first, last;\n \n \tif (pos < 0)\n@@ -256,7 +264,7 @@ static void prune_cache(const char *prefix)\n \twhile (last > first) {\n \t\tint next = (last + first) >> 1;\n \t\tstruct cache_entry *ce = active_cache[next];\n-\t\tif (!strncmp(ce->name, prefix, prefix_len)) {\n+\t\tif (!strncmp(ce->name, prefix, max_prefix_len)) {\n \t\t\tfirst = next+1;\n \t\t\tcontinue;\n \t\t}\n@@ -265,7 +273,7 @@ static void prune_cache(const char *prefix)\n \tactive_nr = last;\n }\n \n-static const char *verify_pathspec(const char *prefix)\n+static const char *verify_pathspec(const char* prefix)\n {\n \tconst char **p, *n, *prev;\n \tunsigned long max;\n@@ -291,10 +299,7 @@ static const char *verify_pathspec(const char *prefix)\n \t\t}\n \t}\n \n-\tif (prefix_offset > max || memcmp(prev, prefix, prefix_offset))\n-\t\tdie(\"git ls-files: cannot generate relative filenames containing '..'\");\n-\n-\tprefix_len = max;\n+\tmax_prefix_len = max;\n \treturn max ? xmemdupz(prev, max) : NULL;\n }\n \n@@ -456,9 +461,10 @@ static int option_parse_exclude_standard(const struct option *opt,\n \treturn 0;\n }\n \n-int cmd_ls_files(int argc, const char **argv, const char *prefix)\n+int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n {\n \tint require_work_tree = 0, show_tag = 0;\n+\tconst char *max_prefix;\n \tstruct dir_struct dir;\n \tstruct option builtin_ls_files_options[] = {\n \t\t{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,\n@@ -516,6 +522,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t};\n \n \tmemset(&dir, 0, sizeof(dir));\n+\tprefix = cmd_prefix;\n \tif (prefix)\n \t\tprefix_offset = strlen(prefix);\n \tgit_config(git_default_config, NULL);\n@@ -557,7 +564,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \n \t/* Verify that the pathspec matches the prefix */\n \tif (pathspec)\n-\t\tprefix = verify_pathspec(prefix);\n+\t\tmax_prefix = verify_pathspec(prefix);\n \n \t/* Treat unmatching pathspec elements as errors */\n \tif (pathspec && error_unmatch) {\n@@ -575,8 +582,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t      show_killed | show_modified | show_resolve_undo))\n \t\tshow_cached = 1;\n \n-\tif (prefix)\n-\t\tprune_cache(prefix);\n+\tif (max_prefix)\n+\t\tprune_cache(max_prefix);\n \tif (with_tree) {\n \t\t/*\n \t\t * Basic sanity check; show-stages and show-unmerged\n@@ -584,11 +591,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\t */\n \t\tif (show_stage || show_unmerged)\n \t\t\tdie(\"ls-files --with-tree is incompatible with -s or -u\");\n-\t\toverlay_tree_on_cache(with_tree, prefix);\n+\t\toverlay_tree_on_cache(with_tree, max_prefix);\n \t}\n-\tshow_files(&dir, prefix);\n+\tshow_files(&dir);\n \tif (show_resolve_undo)\n-\t\tshow_ru_info(prefix);\n+\t\tshow_ru_info();\n \n \tif (ps_matched) {\n \t\tint bad;\ndiff --git a/t/t7010-setup.sh b/t/t7010-setup.sh\nindex d8a7c79..0335a9a 100755\n--- a/t/t7010-setup.sh\n+++ b/t/t7010-setup.sh\n@@ -103,14 +103,10 @@ test_expect_success 'git ls-files (relative #3)' '\n \tgit add a &&\n \t(\n \t\tcd a/b &&\n-\t\tif git ls-files \"../e/f\"\n-\t\tthen\n-\t\t\techo Gaah, should have failed\n-\t\t\texit 1\n-\t\telse\n-\t\t\t: happy\n-\t\tfi\n-\t)\n+\t\tgit ls-files \"../e/f\"\n+\t)  >current &&\n+\techo ../e/f >expect &&\n+\ttest_cmp expect current\n \n '\n \n-- \n1.7.0.5.3.ga76e\n"},{"id":"142310","messageId":"7v1vczqmhw.fsf@alter.siamese.dyndns.org","threadId":"23879","inReplyTo":"20100522163525.GA12420@localhost","subject":"Re: [PATCH] ls-files: allow relative pathspec","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-05-26T05:07:39Z","receivedAt":"2010-05-26T05:07:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Clemens Buchacher <drizzd@aon.at> writes:\n\n> git ls-files used to error out if given paths which point outside\n> the current working directory, such as '../'. We now allow such\n> paths and the output is analogous to git grep -l.\n>\n> Signed-off-by: Clemens Buchacher <drizzd@aon.at>\n> ---\n>  builtin/ls-files.c |   61 +++++++++++++++++++++++++++++-----------------------\n>  t/t7010-setup.sh   |   12 +++------\n>  2 files changed, 38 insertions(+), 35 deletions(-)\n>\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index c0fbcdc..441da81 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> ...\n> @@ -177,19 +185,19 @@ static int show_one_ru(struct string_list_item *item, void *cbdata)\n>  \t\tprintf(\"%s%06o %s %d\\t\", tag_resolve_undo, ui->mode[i],\n>  \t\t       find_unique_abbrev(ui->sha1[i], abbrev),\n>  \t\t       i + 1);\n> -\t\twrite_name_quoted(path + offset, stdout, line_terminator);\n> +\t\twrite_name(path, len);\n>  \t}\n>  \treturn 0;\n>  }\n>  \n> -static void show_ru_info(const char *prefix)\n> +static void show_ru_info()\n\ns/info()/info(void)/;\n\n> @@ -265,7 +273,7 @@ static void prune_cache(const char *prefix)\n>  \tactive_nr = last;\n>  }\n>  \n> -static const char *verify_pathspec(const char *prefix)\n> +static const char *verify_pathspec(const char* prefix)\n\nPlease don't; this is not C++.\n\n> @@ -456,9 +461,10 @@ static int option_parse_exclude_standard(const struct option *opt,\n>  \treturn 0;\n>  }\n>  \n> -int cmd_ls_files(int argc, const char **argv, const char *prefix)\n> +int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n>  {\n>  \tint require_work_tree = 0, show_tag = 0;\n> +\tconst char *max_prefix;\n\nThis variable is used uninitialized when pathspec is NULL, I think.\n\nOther than that, the patch feels sane.  Thanks.\n"},{"id":"142394","messageId":"20100526210523.GA1770@localhost","threadId":"23879","inReplyTo":"7v1vczqmhw.fsf@alter.siamese.dyndns.org","subject":"[PATCH v2] ls-files: allow relative pathspec","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-05-26T21:05:24Z","receivedAt":"2010-05-26T21:05:24Z","isPatch":true,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"git ls-files used to error out if given paths which point outside the current\nworking directory, such as '../'. We now allow such paths and the output is\nanalogous to git grep -l.\n\nSigned-off-by: Clemens Buchacher <drizzd@aon.at>\n---\nOn Tue, May 25, 2010 at 10:07:39PM -0700, Junio C Hamano wrote:\n> Clemens Buchacher <drizzd@aon.at> writes:\n> \n> > @@ -456,9 +461,10 @@ static int option_parse_exclude_standard(const struct option *opt,\n> >  \treturn 0;\n> >  }\n> >  \n> > -int cmd_ls_files(int argc, const char **argv, const char *prefix)\n> > +int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n> >  {\n> >  \tint require_work_tree = 0, show_tag = 0;\n> > +\tconst char *max_prefix;\n> \n> This variable is used uninitialized when pathspec is NULL, I think.\n\nIndeed, thank you for reviewing.\n\nI also renamed verify_pathspec to pathspec_prefix, since it does\nnot actually verify anything.\n\nClemens\n\n builtin/ls-files.c |   69 ++++++++++++++++++++++++++++++----------------------\n t/t7010-setup.sh   |   12 +++------\n 2 files changed, 44 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex c0fbcdc..30ebe2c 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -26,7 +26,8 @@ static int show_killed;\n static int show_valid_bit;\n static int line_terminator = '\\n';\n \n-static int prefix_len;\n+static const char *prefix;\n+static int max_prefix_len;\n static int prefix_offset;\n static const char **pathspec;\n static int error_unmatch;\n@@ -43,10 +44,19 @@ static const char *tag_modified = \"\";\n static const char *tag_skip_worktree = \"\";\n static const char *tag_resolve_undo = \"\";\n \n+static void write_name(const char* name, size_t len)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tif (prefix_offset > 0)\n+\t\tquote_path_relative(name, len, &path, prefix);\n+\telse\n+\t\tstrbuf_add(&path, name, len);\n+\twrite_name_quoted(strbuf_detach(&path, NULL), stdout, line_terminator);\n+}\n+\n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n-\tint len = prefix_len;\n-\tint offset = prefix_offset;\n+\tint len = max_prefix_len;\n \n \tif (len >= ent->len)\n \t\tdie(\"git ls-files: internal error - directory entry not superset of prefix\");\n@@ -55,7 +65,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \t\treturn;\n \n \tfputs(tag, stdout);\n-\twrite_name_quoted(ent->name + offset, stdout, line_terminator);\n+\twrite_name(ent->name, ent->len);\n }\n \n static void show_other_files(struct dir_struct *dir)\n@@ -121,8 +131,7 @@ static void show_killed_files(struct dir_struct *dir)\n \n static void show_ce_entry(const char *tag, struct cache_entry *ce)\n {\n-\tint len = prefix_len;\n-\tint offset = prefix_offset;\n+\tint len = max_prefix_len;\n \n \tif (len >= ce_namelen(ce))\n \t\tdie(\"git ls-files: internal error - cache entry not superset of prefix\");\n@@ -156,20 +165,19 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)\n \t\t       find_unique_abbrev(ce->sha1,abbrev),\n \t\t       ce_stage(ce));\n \t}\n-\twrite_name_quoted(ce->name + offset, stdout, line_terminator);\n+\twrite_name(ce->name, ce_namelen(ce));\n }\n \n static int show_one_ru(struct string_list_item *item, void *cbdata)\n {\n-\tint offset = prefix_offset;\n \tconst char *path = item->string;\n \tstruct resolve_undo_info *ui = item->util;\n \tint i, len;\n \n \tlen = strlen(path);\n-\tif (len < prefix_len)\n+\tif (len < max_prefix_len)\n \t\treturn 0; /* outside of the prefix */\n-\tif (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))\n+\tif (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))\n \t\treturn 0; /* uninterested */\n \tfor (i = 0; i < 3; i++) {\n \t\tif (!ui->mode[i])\n@@ -177,19 +185,19 @@ static int show_one_ru(struct string_list_item *item, void *cbdata)\n \t\tprintf(\"%s%06o %s %d\\t\", tag_resolve_undo, ui->mode[i],\n \t\t       find_unique_abbrev(ui->sha1[i], abbrev),\n \t\t       i + 1);\n-\t\twrite_name_quoted(path + offset, stdout, line_terminator);\n+\t\twrite_name(path, len);\n \t}\n \treturn 0;\n }\n \n-static void show_ru_info(const char *prefix)\n+static void show_ru_info(void)\n {\n \tif (!the_index.resolve_undo)\n \t\treturn;\n \tfor_each_string_list(show_one_ru, the_index.resolve_undo, NULL);\n }\n \n-static void show_files(struct dir_struct *dir, const char *prefix)\n+static void show_files(struct dir_struct *dir)\n {\n \tint i;\n \n@@ -243,7 +251,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)\n  */\n static void prune_cache(const char *prefix)\n {\n-\tint pos = cache_name_pos(prefix, prefix_len);\n+\tint pos = cache_name_pos(prefix, max_prefix_len);\n \tunsigned int first, last;\n \n \tif (pos < 0)\n@@ -256,7 +264,7 @@ static void prune_cache(const char *prefix)\n \twhile (last > first) {\n \t\tint next = (last + first) >> 1;\n \t\tstruct cache_entry *ce = active_cache[next];\n-\t\tif (!strncmp(ce->name, prefix, prefix_len)) {\n+\t\tif (!strncmp(ce->name, prefix, max_prefix_len)) {\n \t\t\tfirst = next+1;\n \t\t\tcontinue;\n \t\t}\n@@ -265,11 +273,16 @@ static void prune_cache(const char *prefix)\n \tactive_nr = last;\n }\n \n-static const char *verify_pathspec(const char *prefix)\n+static const char *pathspec_prefix(const char *prefix)\n {\n \tconst char **p, *n, *prev;\n \tunsigned long max;\n \n+\tif (!pathspec) {\n+\t\tmax_prefix_len = prefix ? strlen(prefix) : 0;\n+\t\treturn prefix;\n+\t}\n+\n \tprev = NULL;\n \tmax = PATH_MAX;\n \tfor (p = pathspec; (n = *p) != NULL; p++) {\n@@ -291,10 +304,7 @@ static const char *verify_pathspec(const char *prefix)\n \t\t}\n \t}\n \n-\tif (prefix_offset > max || memcmp(prev, prefix, prefix_offset))\n-\t\tdie(\"git ls-files: cannot generate relative filenames containing '..'\");\n-\n-\tprefix_len = max;\n+\tmax_prefix_len = max;\n \treturn max ? xmemdupz(prev, max) : NULL;\n }\n \n@@ -456,9 +466,10 @@ static int option_parse_exclude_standard(const struct option *opt,\n \treturn 0;\n }\n \n-int cmd_ls_files(int argc, const char **argv, const char *prefix)\n+int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n {\n \tint require_work_tree = 0, show_tag = 0;\n+\tconst char *max_prefix;\n \tstruct dir_struct dir;\n \tstruct option builtin_ls_files_options[] = {\n \t\t{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,\n@@ -516,6 +527,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t};\n \n \tmemset(&dir, 0, sizeof(dir));\n+\tprefix = cmd_prefix;\n \tif (prefix)\n \t\tprefix_offset = strlen(prefix);\n \tgit_config(git_default_config, NULL);\n@@ -555,9 +567,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \tif (pathspec)\n \t\tstrip_trailing_slash_from_submodules();\n \n-\t/* Verify that the pathspec matches the prefix */\n-\tif (pathspec)\n-\t\tprefix = verify_pathspec(prefix);\n+\t/* Find common prefix for all pathspec's */\n+\tmax_prefix = pathspec_prefix(prefix);\n \n \t/* Treat unmatching pathspec elements as errors */\n \tif (pathspec && error_unmatch) {\n@@ -575,8 +586,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t      show_killed | show_modified | show_resolve_undo))\n \t\tshow_cached = 1;\n \n-\tif (prefix)\n-\t\tprune_cache(prefix);\n+\tif (max_prefix)\n+\t\tprune_cache(max_prefix);\n \tif (with_tree) {\n \t\t/*\n \t\t * Basic sanity check; show-stages and show-unmerged\n@@ -584,11 +595,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\t */\n \t\tif (show_stage || show_unmerged)\n \t\t\tdie(\"ls-files --with-tree is incompatible with -s or -u\");\n-\t\toverlay_tree_on_cache(with_tree, prefix);\n+\t\toverlay_tree_on_cache(with_tree, max_prefix);\n \t}\n-\tshow_files(&dir, prefix);\n+\tshow_files(&dir);\n \tif (show_resolve_undo)\n-\t\tshow_ru_info(prefix);\n+\t\tshow_ru_info();\n \n \tif (ps_matched) {\n \t\tint bad;\ndiff --git a/t/t7010-setup.sh b/t/t7010-setup.sh\nindex d8a7c79..0335a9a 100755\n--- a/t/t7010-setup.sh\n+++ b/t/t7010-setup.sh\n@@ -103,14 +103,10 @@ test_expect_success 'git ls-files (relative #3)' '\n \tgit add a &&\n \t(\n \t\tcd a/b &&\n-\t\tif git ls-files \"../e/f\"\n-\t\tthen\n-\t\t\techo Gaah, should have failed\n-\t\t\texit 1\n-\t\telse\n-\t\t\t: happy\n-\t\tfi\n-\t)\n+\t\tgit ls-files \"../e/f\"\n+\t)  >current &&\n+\techo ../e/f >expect &&\n+\ttest_cmp expect current\n \n '\n \n-- \n1.7.0.5.3.ga76e\n"}]}