{"thread":{"id":"26036","subject":"[PATCH 00/19] nd/struct-pathspec (or pathspec unification [1])","startedAt":"2010-12-13T09:46:37Z","lastAt":"2010-12-14T17:11:15Z","messageCount":45,"participants":["Nguyễn Thái Ngọc Duy","Thiago Farina","Junio C Hamano","Nguyen Thai Ngoc Duy","Johannes Sixt"],"isPatch":true,"patchVersion":1,"patchTotal":19},"messages":[{"id":"157936","messageId":"1292233616-27692-1-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":null,"subject":"[PATCH 00/19] nd/struct-pathspec (or pathspec unification [1])","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:37Z","receivedAt":"2010-12-13T09:46:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Background:\n\npathspecs in git can be handled differently in three places\n\n 1. log family uses tree_entry_interesting() and ce_path_match()\n 2. most index-related operations use match_pathspec()\n 3. grep uses its own pathspec_matches()\n\nOut of three, #3 provides the most advanced functionalities, while #1\nhas a few good optimizations, but not as powerful as #3. #2 is sort of\ntrade-off between the other two.\n\nThis series brings all the #3 goodness to #1 and #2, then kills #3. I\ndon't want to kill #2 because it takes a list as input, while #1 takes\ntrees (ce_path_match() takes list though). There could be different\noptmizations based on different input type.\n\nSummary of patches:\n\n  Add struct pathspec\n  diff-no-index: use diff_tree_setup_paths()\n  pathspec: cache string length when initializing pathspec\n  Convert struct diff_options to use struct pathspec\n  tree_entry_interesting(): remove dependency on struct diff_options\n  Move tree_entry_interesting() to tree-walk.c and export it\n\nThis is unchanged from nd/struct-pathspec in pu. There is one patch\nfrom pu replaced later.\n\n  glossary: define pathspec\n\nThis is what I am aiming to. If I make mistakes, blame Jonathan\nbecause he mis-specifies it ;-)\n\n  pathspec: mark wildcard pathspecs from the beginning\n\n>From old nd/struct-pathspec, to recognize potential wildcard pathspecs\nearly.\n\n  tree-diff.c: reserve space in \"base\" for pathname concatenation\n\nThe (probably most) used operation in traversing trees is concatenate\ndirname and basename into full path (especially for wildcard matching).\nThis requires a new buffer every time. This patch ensures that the\ncaller prepares a writable buffer with dirname already filled. If the\ncallee wants full path, it does not have to allocate another buffer\n(and does shorter memcpy).\n\nThis patch is not strictly needed though.\n\n  tree_entry_interesting(): factor out most matching logic\n\nFor readibility of the next patches.\n\n  tree_entry_interesting: support depth limit\n\nGoodness from #3.\n\n  tree_entry_interesting(): support wildcard matching\n  tree_entry_interesting(): optimize fnmatch when base is matched\n\nThis is something t_e_i() lacks for so long. However, in order to make\nlog family commands work properly, ce_path_match() also needs to learn\nwildcards.\n\nThis changes tree_entry_interesting() interface, therefore breaks\nen/object-list-with-pathspec. I'll send fixes shortly.\n\n  Convert ce_path_match() use to match_pathspec()\n\nSo that log family now works with wildcards.\n\n  pathspec: add match_pathspec_depth()\n\nThis is new match_pathspec(). I don't want to replace the old one\nbecause it changes more places. But once it works, another patch to\nkill match_pathspec() should be easy.\n\n  grep: convert to use struct pathspec\n  grep: use match_pathspec_depth() for cache grepping\n  grep: use preallocated buffer for grep_tree()\n  grep: drop pathspec_matches() in favor of tree_entry_interesting()\n\ngrep (especially t7810) is how I test all these. I need to write more\ntests to make sure things work. But for now t7810 passes.\n\nHopefully I did not lose any optimizations in pathspec_matches().\n\nIt's time to rebase negative pathspec patches on top and get back to\nmy narrow clone.\n\n[1] https://git.wiki.kernel.org/index.php/SoC2010Ideas#Unify_Pathspec_Semantics\n\n Documentation/glossary-content.txt |   23 ++++\n builtin/diff-files.c               |    2 +-\n builtin/diff.c                     |    4 +-\n builtin/grep.c                     |  200 ++++++++---------------------\n builtin/log.c                      |    2 +-\n cache.h                            |   14 ++\n diff-lib.c                         |    2 +-\n diff-no-index.c                    |   13 +-\n diff.h                             |    4 +-\n dir.c                              |   98 ++++++++++++++\n dir.h                              |    4 +\n read-cache.c                       |   20 +---\n revision.c                         |    6 +-\n t/t4010-diff-pathspec.sh           |   14 ++\n tree-diff.c                        |  246 ++++++++----------------------------\n tree-walk.c                        |  186 +++++++++++++++++++++++++++\n tree-walk.h                        |    2 +\n 17 files changed, 461 insertions(+), 379 deletions(-)\n\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157937","messageId":"1292233616-27692-2-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 01/19] Add struct pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:38Z","receivedAt":"2010-12-13T09:46:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This struct for now is just a wrapper for the current pathspec form:\nconst char **. It is intended to be extended with more useful\npathspec-related information over time.\n\nThe data structure for passing pathspec around remains const char **,\nstruct pathspec will be initialized locally to be used and destroyed.\nHopefully all pathspec related code will be gradually migrated to pass\nthis struct instead.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n cache.h |    7 +++++++\n dir.c   |   18 ++++++++++++++++++\n 2 files changed, 25 insertions(+), 0 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 2ef2fa3..3330769 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -493,6 +493,13 @@ extern int index_name_is_other(const struct index_state *, const char *, int);\n extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);\n extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);\n \n+struct pathspec {\n+\tconst char **raw; /* get_pathspec() result, not freed by free_pathspec() */\n+\tint nr;\n+};\n+\n+extern int init_pathspec(struct pathspec *, const char **);\n+extern void free_pathspec(struct pathspec *);\n extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);\n extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);\n extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);\ndiff --git a/dir.c b/dir.c\nindex 133f472..205adc4 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1071,3 +1071,21 @@ int remove_path(const char *name)\n \treturn 0;\n }\n \n+int init_pathspec(struct pathspec *pathspec, const char **paths)\n+{\n+\tconst char **p = paths;\n+\n+\tmemset(pathspec, 0, sizeof(*pathspec));\n+\tif (!p)\n+\t\treturn 0;\n+\twhile (*p)\n+\t\tp++;\n+\tpathspec->raw = paths;\n+\tpathspec->nr = p - paths;\n+\treturn 0;\n+}\n+\n+void free_pathspec(struct pathspec *pathspec)\n+{\n+\t; /* do nothing */\n+}\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157938","messageId":"1292233616-27692-3-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 02/19] diff-no-index: use diff_tree_setup_paths()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:39Z","receivedAt":"2010-12-13T09:46:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"diff_options.{paths,nr_paths} will be removed later. Do not\nmodify them directly.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n diff-no-index.c |    9 +++++----\n 1 files changed, 5 insertions(+), 4 deletions(-)\n\ndiff --git a/diff-no-index.c b/diff-no-index.c\nindex ce9e783..e48ab92 100644\n--- a/diff-no-index.c\n+++ b/diff-no-index.c\n@@ -231,8 +231,9 @@ void diff_no_index(struct rev_info *revs,\n \n \tif (prefix) {\n \t\tint len = strlen(prefix);\n+\t\tconst char *paths[3];\n+\t\tmemset(paths, 0, sizeof(paths));\n \n-\t\trevs->diffopt.paths = xcalloc(2, sizeof(char *));\n \t\tfor (i = 0; i < 2; i++) {\n \t\t\tconst char *p = argv[argc - 2 + i];\n \t\t\t/*\n@@ -242,12 +243,12 @@ void diff_no_index(struct rev_info *revs,\n \t\t\tp = (strcmp(p, \"-\")\n \t\t\t     ? xstrdup(prefix_filename(prefix, len, p))\n \t\t\t     : p);\n-\t\t\trevs->diffopt.paths[i] = p;\n+\t\t\tpaths[i] = p;\n \t\t}\n+\t\tdiff_tree_setup_paths(paths, &revs->diffopt);\n \t}\n \telse\n-\t\trevs->diffopt.paths = argv + argc - 2;\n-\trevs->diffopt.nr_paths = 2;\n+\t\tdiff_tree_setup_paths(argv + argc - 2, &revs->diffopt);\n \trevs->diffopt.skip_stat_unmatch = 1;\n \tif (!revs->diffopt.output_format)\n \t\trevs->diffopt.output_format = DIFF_FORMAT_PATCH;\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157939","messageId":"1292233616-27692-4-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 03/19] pathspec: cache string length when initializing pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:40Z","receivedAt":"2010-12-13T09:46:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This field will be used when tree_entry_interesting() is converted to\nuse struct pathspec. Currently it uses pathlens[] in struct\ndiff_options to avoid calculating string over and over again.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n cache.h |    3 +++\n dir.c   |   13 ++++++++++++-\n 2 files changed, 15 insertions(+), 1 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 3330769..36819b6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -496,6 +496,9 @@ extern int ie_modified(const struct index_state *, struct cache_entry *, struct\n struct pathspec {\n \tconst char **raw; /* get_pathspec() result, not freed by free_pathspec() */\n \tint nr;\n+\tstruct pathspec_item {\n+\t\tint len;\n+\t} *items;\n };\n \n extern int init_pathspec(struct pathspec *, const char **);\ndiff --git a/dir.c b/dir.c\nindex 205adc4..646c79f 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1074,6 +1074,7 @@ int remove_path(const char *name)\n int init_pathspec(struct pathspec *pathspec, const char **paths)\n {\n \tconst char **p = paths;\n+\tint i;\n \n \tmemset(pathspec, 0, sizeof(*pathspec));\n \tif (!p)\n@@ -1082,10 +1083,20 @@ int init_pathspec(struct pathspec *pathspec, const char **paths)\n \t\tp++;\n \tpathspec->raw = paths;\n \tpathspec->nr = p - paths;\n+\tif (!pathspec->nr)\n+\t\treturn 0;\n+\n+\tpathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr);\n+\tfor (i = 0; i < pathspec->nr; i++) {\n+\t\tstruct pathspec_item *item = pathspec->items+i;\n+\n+\t\titem->len = strlen(paths[i]);\n+\t}\n \treturn 0;\n }\n \n void free_pathspec(struct pathspec *pathspec)\n {\n-\t; /* do nothing */\n+\tfree(pathspec->items);\n+\tpathspec->items = NULL;\n }\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157955","messageId":"1292233616-27692-5-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 04/19] Convert struct diff_options to use struct pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:41Z","receivedAt":"2010-12-13T09:46:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/diff-files.c |    2 +-\n builtin/diff.c       |    4 ++--\n builtin/log.c        |    2 +-\n diff-lib.c           |    2 +-\n diff-no-index.c      |    4 ++--\n diff.h               |    4 +---\n revision.c           |    6 +-----\n tree-diff.c          |   46 +++++++++++-----------------------------------\n 8 files changed, 20 insertions(+), 50 deletions(-)\n\ndiff --git a/builtin/diff-files.c b/builtin/diff-files.c\nindex 951c7c8..46085f8 100644\n--- a/builtin/diff-files.c\n+++ b/builtin/diff-files.c\n@@ -61,7 +61,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n \t    (rev.diffopt.output_format & DIFF_FORMAT_PATCH))\n \t\trev.combine_merges = rev.dense_combined_merges = 1;\n \n-\tif (read_cache_preload(rev.diffopt.paths) < 0) {\n+\tif (read_cache_preload(rev.diffopt.pathspec.raw) < 0) {\n \t\tperror(\"read_cache_preload\");\n \t\treturn -1;\n \t}\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex a43d326..76c42d8 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -135,7 +135,7 @@ static int builtin_diff_index(struct rev_info *revs,\n \t    revs->max_count != -1 || revs->min_age != -1 ||\n \t    revs->max_age != -1)\n \t\tusage(builtin_diff_usage);\n-\tif (read_cache_preload(revs->diffopt.paths) < 0) {\n+\tif (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {\n \t\tperror(\"read_cache_preload\");\n \t\treturn -1;\n \t}\n@@ -237,7 +237,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv\n \t\trevs->combine_merges = revs->dense_combined_merges = 1;\n \n \tsetup_work_tree();\n-\tif (read_cache_preload(revs->diffopt.paths) < 0) {\n+\tif (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {\n \t\tperror(\"read_cache_preload\");\n \t\treturn -1;\n \t}\ndiff --git a/builtin/log.c b/builtin/log.c\nindex eaa1ee0..92779a5 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -89,7 +89,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \t\trev->always_show_header = 0;\n \tif (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {\n \t\trev->always_show_header = 0;\n-\t\tif (rev->diffopt.nr_paths != 1)\n+\t\tif (rev->diffopt.pathspec.nr != 1)\n \t\t\tusage(\"git logs can only follow renames on one pathname at a time\");\n \t}\n \tfor (i = 1; i < argc; i++) {\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 392ce2b..3b809f2 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -501,7 +501,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)\n \tactive_nr = dst - active_cache;\n \n \tinit_revisions(&revs, NULL);\n-\trevs.prune_data = opt->paths;\n+\trevs.prune_data = opt->pathspec.raw;\n \ttree = parse_tree_indirect(tree_sha1);\n \tif (!tree)\n \t\tdie(\"bad tree object %s\", sha1_to_hex(tree_sha1));\ndiff --git a/diff-no-index.c b/diff-no-index.c\nindex e48ab92..3a36144 100644\n--- a/diff-no-index.c\n+++ b/diff-no-index.c\n@@ -260,8 +260,8 @@ void diff_no_index(struct rev_info *revs,\n \tif (diff_setup_done(&revs->diffopt) < 0)\n \t\tdie(\"diff_setup_done failed\");\n \n-\tif (queue_diff(&revs->diffopt, revs->diffopt.paths[0],\n-\t\t       revs->diffopt.paths[1]))\n+\tif (queue_diff(&revs->diffopt, revs->diffopt.pathspec.raw[0],\n+\t\t       revs->diffopt.pathspec.raw[1]))\n \t\texit(1);\n \tdiff_set_mnemonic_prefix(&revs->diffopt, \"1/\", \"2/\");\n \tdiffcore_std(&revs->diffopt);\ndiff --git a/diff.h b/diff.h\nindex bf2f44d..6497b71 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -133,9 +133,7 @@ struct diff_options {\n \tFILE *file;\n \tint close_file;\n \n-\tint nr_paths;\n-\tconst char **paths;\n-\tint *pathlens;\n+\tstruct pathspec pathspec;\n \tchange_fn_t change;\n \tadd_remove_fn_t add_remove;\n \tdiff_format_fn_t format_callback;\ndiff --git a/revision.c b/revision.c\nindex b1c1890..b2a5867 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -553,11 +553,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)\n \n \tleft_first = left_count < right_count;\n \tinit_patch_ids(&ids);\n-\tif (revs->diffopt.nr_paths) {\n-\t\tids.diffopts.nr_paths = revs->diffopt.nr_paths;\n-\t\tids.diffopts.paths = revs->diffopt.paths;\n-\t\tids.diffopts.pathlens = revs->diffopt.pathlens;\n-\t}\n+\tids.diffopts.pathspec = revs->diffopt.pathspec;\n \n \t/* Compute patch-ids for one side */\n \tfor (p = list; p; p = p->next) {\ndiff --git a/tree-diff.c b/tree-diff.c\nindex cd659c6..986c0f4 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -100,16 +100,16 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int\n \tint pathlen;\n \tint never_interesting = -1;\n \n-\tif (!opt->nr_paths)\n+\tif (!opt->pathspec.nr)\n \t\treturn 1;\n \n \tsha1 = tree_entry_extract(desc, &path, &mode);\n \n \tpathlen = tree_entry_len(path, sha1);\n \n-\tfor (i = 0; i < opt->nr_paths; i++) {\n-\t\tconst char *match = opt->paths[i];\n-\t\tint matchlen = opt->pathlens[i];\n+\tfor (i = 0; i < opt->pathspec.nr; i++) {\n+\t\tconst char *match = opt->pathspec.raw[i];\n+\t\tint matchlen = opt->pathspec.items[i].len;\n \t\tint m = -1; /* signals that we haven't called strncmp() */\n \n \t\tif (baselen >= matchlen) {\n@@ -289,7 +289,7 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, stru\n \t\tif (DIFF_OPT_TST(opt, QUICK) &&\n \t\t    DIFF_OPT_TST(opt, HAS_CHANGES))\n \t\t\tbreak;\n-\t\tif (opt->nr_paths) {\n+\t\tif (opt->pathspec.nr) {\n \t\t\tskip_uninteresting(t1, base, baselen, opt);\n \t\t\tskip_uninteresting(t2, base, baselen, opt);\n \t\t}\n@@ -348,7 +348,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co\n \tDIFF_OPT_SET(&diff_opts, RECURSIVE);\n \tDIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);\n \tdiff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;\n-\tdiff_opts.single_follow = opt->paths[0];\n+\tdiff_opts.single_follow = opt->pathspec.raw[0];\n \tdiff_opts.break_opt = opt->break_opt;\n \tpaths[0] = NULL;\n \tdiff_tree_setup_paths(paths, &diff_opts);\n@@ -368,15 +368,15 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co\n \t\t * diff_queued_diff, we will also use that as the path in\n \t\t * the future!\n \t\t */\n-\t\tif ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, opt->paths[0])) {\n+\t\tif ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, opt->pathspec.raw[0])) {\n \t\t\t/* Switch the file-pairs around */\n \t\t\tq->queue[i] = choice;\n \t\t\tchoice = p;\n \n \t\t\t/* Update the path we use from now on.. */\n \t\t\tdiff_tree_release_paths(opt);\n-\t\t\topt->paths[0] = xstrdup(p->one->path);\n-\t\t\tdiff_tree_setup_paths(opt->paths, opt);\n+\t\t\topt->pathspec.raw[0] = xstrdup(p->one->path);\n+\t\t\tdiff_tree_setup_paths(opt->pathspec.raw, opt);\n \n \t\t\t/*\n \t\t\t * The caller expects us to return a set of vanilla\n@@ -451,36 +451,12 @@ int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_\n \treturn retval;\n }\n \n-static int count_paths(const char **paths)\n-{\n-\tint i = 0;\n-\twhile (*paths++)\n-\t\ti++;\n-\treturn i;\n-}\n-\n void diff_tree_release_paths(struct diff_options *opt)\n {\n-\tfree(opt->pathlens);\n+\tfree_pathspec(&opt->pathspec);\n }\n \n void diff_tree_setup_paths(const char **p, struct diff_options *opt)\n {\n-\topt->nr_paths = 0;\n-\topt->pathlens = NULL;\n-\topt->paths = NULL;\n-\n-\tif (p) {\n-\t\tint i;\n-\n-\t\topt->paths = p;\n-\t\topt->nr_paths = count_paths(p);\n-\t\tif (opt->nr_paths == 0) {\n-\t\t\topt->pathlens = NULL;\n-\t\t\treturn;\n-\t\t}\n-\t\topt->pathlens = xmalloc(opt->nr_paths * sizeof(int));\n-\t\tfor (i=0; i < opt->nr_paths; i++)\n-\t\t\topt->pathlens[i] = strlen(p[i]);\n-\t}\n+\tinit_pathspec(&opt->pathspec, p);\n }\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157942","messageId":"1292233616-27692-6-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 05/19] tree_entry_interesting(): remove dependency on struct diff_options","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:42Z","receivedAt":"2010-12-13T09:46:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function can be potentially used in more places than just\ntree-diff.c. \"struct diff_options\" does not make much sense outside\ndiff_tree_sha1().\n\nWhile removing the use of diff_options, it also removes\ntree_entry_extract() call, which means S_ISDIR() uses the entry->mode\ndirectly, without being filtered by canon_mode() (called internally\ninside tree_entry_extract)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n tree-diff.c |   28 +++++++++++-----------------\n 1 files changed, 11 insertions(+), 17 deletions(-)\n\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 986c0f4..822d45e 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -91,25 +91,20 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const\n  *  - zero for no\n  *  - negative for \"no, and no subsequent entries will be either\"\n  */\n-static int tree_entry_interesting(struct tree_desc *desc, const char *base, int baselen, struct diff_options *opt)\n+static int tree_entry_interesting(const struct name_entry *entry, const char *base, int baselen, const struct pathspec *ps)\n {\n-\tconst char *path;\n-\tconst unsigned char *sha1;\n-\tunsigned mode;\n \tint i;\n \tint pathlen;\n \tint never_interesting = -1;\n \n-\tif (!opt->pathspec.nr)\n+\tif (!ps || !ps->nr)\n \t\treturn 1;\n \n-\tsha1 = tree_entry_extract(desc, &path, &mode);\n-\n-\tpathlen = tree_entry_len(path, sha1);\n+\tpathlen = tree_entry_len(entry->path, entry->sha1);\n \n-\tfor (i = 0; i < opt->pathspec.nr; i++) {\n-\t\tconst char *match = opt->pathspec.raw[i];\n-\t\tint matchlen = opt->pathspec.items[i].len;\n+\tfor (i = 0; i < ps->nr; i++) {\n+\t\tconst char *match = ps->raw[i];\n+\t\tint matchlen = ps->items[i].len;\n \t\tint m = -1; /* signals that we haven't called strncmp() */\n \n \t\tif (baselen >= matchlen) {\n@@ -147,7 +142,7 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int\n \t\t\t * Does match sort strictly earlier than path\n \t\t\t * with their common parts?\n \t\t\t */\n-\t\t\tm = strncmp(match, path,\n+\t\t\tm = strncmp(match, entry->path,\n \t\t\t\t    (matchlen < pathlen) ? matchlen : pathlen);\n \t\t\tif (m < 0)\n \t\t\t\tcontinue;\n@@ -174,7 +169,7 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int\n \t\tif (matchlen > pathlen) {\n \t\t\tif (match[pathlen] != '/')\n \t\t\t\tcontinue;\n-\t\t\tif (!S_ISDIR(mode))\n+\t\t\tif (!S_ISDIR(entry->mode))\n \t\t\t\tcontinue;\n \t\t}\n \n@@ -183,7 +178,7 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int\n \t\t\t * we cheated and did not do strncmp(), so we do\n \t\t\t * that here.\n \t\t\t */\n-\t\t\tm = strncmp(match, path, pathlen);\n+\t\t\tm = strncmp(match, entry->path, pathlen);\n \n \t\t/*\n \t\t * If common part matched earlier then it is a hit,\n@@ -206,8 +201,7 @@ static void show_tree(struct diff_options *opt, const char *prefix, struct tree_\n \t\tif (all_interesting)\n \t\t\tshow = 1;\n \t\telse {\n-\t\t\tshow = tree_entry_interesting(desc, base, baselen,\n-\t\t\t\t\t\t      opt);\n+\t\t\tshow = tree_entry_interesting(&desc->entry, base, baselen, &opt->pathspec);\n \t\t\tif (show == 2)\n \t\t\t\tall_interesting = 1;\n \t\t}\n@@ -266,7 +260,7 @@ static void skip_uninteresting(struct tree_desc *t, const char *base, int basele\n \t\tif (all_interesting)\n \t\t\tshow = 1;\n \t\telse {\n-\t\t\tshow = tree_entry_interesting(t, base, baselen, opt);\n+\t\t\tshow = tree_entry_interesting(&t->entry, base, baselen, &opt->pathspec);\n \t\t\tif (show == 2)\n \t\t\t\tall_interesting = 1;\n \t\t}\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157941","messageId":"1292233616-27692-7-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 06/19] Move tree_entry_interesting() to tree-walk.c and export it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:43Z","receivedAt":"2010-12-13T09:46:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n tree-diff.c |  109 ---------------------------------------------------------\n tree-walk.c |  111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n tree-walk.h |    2 +\n 3 files changed, 113 insertions(+), 109 deletions(-)\n\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 822d45e..50d7e6d 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -82,115 +82,6 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const\n \treturn 0;\n }\n \n-/*\n- * Is a tree entry interesting given the pathspec we have?\n- *\n- * Return:\n- *  - 2 for \"yes, and all subsequent entries will be\"\n- *  - 1 for yes\n- *  - zero for no\n- *  - negative for \"no, and no subsequent entries will be either\"\n- */\n-static int tree_entry_interesting(const struct name_entry *entry, const char *base, int baselen, const struct pathspec *ps)\n-{\n-\tint i;\n-\tint pathlen;\n-\tint never_interesting = -1;\n-\n-\tif (!ps || !ps->nr)\n-\t\treturn 1;\n-\n-\tpathlen = tree_entry_len(entry->path, entry->sha1);\n-\n-\tfor (i = 0; i < ps->nr; i++) {\n-\t\tconst char *match = ps->raw[i];\n-\t\tint matchlen = ps->items[i].len;\n-\t\tint m = -1; /* signals that we haven't called strncmp() */\n-\n-\t\tif (baselen >= matchlen) {\n-\t\t\t/* If it doesn't match, move along... */\n-\t\t\tif (strncmp(base, match, matchlen))\n-\t\t\t\tcontinue;\n-\n-\t\t\t/*\n-\t\t\t * If the base is a subdirectory of a path which\n-\t\t\t * was specified, all of them are interesting.\n-\t\t\t */\n-\t\t\tif (!matchlen ||\n-\t\t\t    base[matchlen] == '/' ||\n-\t\t\t    match[matchlen - 1] == '/')\n-\t\t\t\treturn 2;\n-\n-\t\t\t/* Just a random prefix match */\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\t/* Does the base match? */\n-\t\tif (strncmp(base, match, baselen))\n-\t\t\tcontinue;\n-\n-\t\tmatch += baselen;\n-\t\tmatchlen -= baselen;\n-\n-\t\tif (never_interesting) {\n-\t\t\t/*\n-\t\t\t * We have not seen any match that sorts later\n-\t\t\t * than the current path.\n-\t\t\t */\n-\n-\t\t\t/*\n-\t\t\t * Does match sort strictly earlier than path\n-\t\t\t * with their common parts?\n-\t\t\t */\n-\t\t\tm = strncmp(match, entry->path,\n-\t\t\t\t    (matchlen < pathlen) ? matchlen : pathlen);\n-\t\t\tif (m < 0)\n-\t\t\t\tcontinue;\n-\n-\t\t\t/*\n-\t\t\t * If we come here even once, that means there is at\n-\t\t\t * least one pathspec that would sort equal to or\n-\t\t\t * later than the path we are currently looking at.\n-\t\t\t * In other words, if we have never reached this point\n-\t\t\t * after iterating all pathspecs, it means all\n-\t\t\t * pathspecs are either outside of base, or inside the\n-\t\t\t * base but sorts strictly earlier than the current\n-\t\t\t * one.  In either case, they will never match the\n-\t\t\t * subsequent entries.  In such a case, we initialized\n-\t\t\t * the variable to -1 and that is what will be\n-\t\t\t * returned, allowing the caller to terminate early.\n-\t\t\t */\n-\t\t\tnever_interesting = 0;\n-\t\t}\n-\n-\t\tif (pathlen > matchlen)\n-\t\t\tcontinue;\n-\n-\t\tif (matchlen > pathlen) {\n-\t\t\tif (match[pathlen] != '/')\n-\t\t\t\tcontinue;\n-\t\t\tif (!S_ISDIR(entry->mode))\n-\t\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (m == -1)\n-\t\t\t/*\n-\t\t\t * we cheated and did not do strncmp(), so we do\n-\t\t\t * that here.\n-\t\t\t */\n-\t\t\tm = strncmp(match, entry->path, pathlen);\n-\n-\t\t/*\n-\t\t * If common part matched earlier then it is a hit,\n-\t\t * because we rejected the case where path is not a\n-\t\t * leading directory and is shorter than match.\n-\t\t */\n-\t\tif (!m)\n-\t\t\treturn 1;\n-\t}\n-\treturn never_interesting; /* No matches */\n-}\n-\n /* A whole sub-tree went away or appeared */\n static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen)\n {\ndiff --git a/tree-walk.c b/tree-walk.c\nindex a9bbf4e..01168ea 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -455,3 +455,114 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch\n \tfree(tree);\n \treturn retval;\n }\n+\n+/*\n+ * Is a tree entry interesting given the pathspec we have?\n+ *\n+ * Return:\n+ *  - 2 for \"yes, and all subsequent entries will be\"\n+ *  - 1 for yes\n+ *  - zero for no\n+ *  - negative for \"no, and no subsequent entries will be either\"\n+ */\n+int tree_entry_interesting(const struct name_entry *entry,\n+\t\t\t   const char *base, int baselen,\n+\t\t\t   const struct pathspec *ps)\n+{\n+\tint i;\n+\tint pathlen;\n+\tint never_interesting = -1;\n+\n+\tif (!ps || !ps->nr)\n+\t\treturn 1;\n+\n+\tpathlen = tree_entry_len(entry->path, entry->sha1);\n+\n+\tfor (i = 0; i < ps->nr; i++) {\n+\t\tconst char *match = ps->raw[i];\n+\t\tint matchlen = ps->items[i].len;\n+\t\tint m = -1; /* signals that we haven't called strncmp() */\n+\n+\t\tif (baselen >= matchlen) {\n+\t\t\t/* If it doesn't match, move along... */\n+\t\t\tif (strncmp(base, match, matchlen))\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * If the base is a subdirectory of a path which\n+\t\t\t * was specified, all of them are interesting.\n+\t\t\t */\n+\t\t\tif (!matchlen ||\n+\t\t\t    base[matchlen] == '/' ||\n+\t\t\t    match[matchlen - 1] == '/')\n+\t\t\t\treturn 2;\n+\n+\t\t\t/* Just a random prefix match */\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* Does the base match? */\n+\t\tif (strncmp(base, match, baselen))\n+\t\t\tcontinue;\n+\n+\t\tmatch += baselen;\n+\t\tmatchlen -= baselen;\n+\n+\t\tif (never_interesting) {\n+\t\t\t/*\n+\t\t\t * We have not seen any match that sorts later\n+\t\t\t * than the current path.\n+\t\t\t */\n+\n+\t\t\t/*\n+\t\t\t * Does match sort strictly earlier than path\n+\t\t\t * with their common parts?\n+\t\t\t */\n+\t\t\tm = strncmp(match, entry->path,\n+\t\t\t\t    (matchlen < pathlen) ? matchlen : pathlen);\n+\t\t\tif (m < 0)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * If we come here even once, that means there is at\n+\t\t\t * least one pathspec that would sort equal to or\n+\t\t\t * later than the path we are currently looking at.\n+\t\t\t * In other words, if we have never reached this point\n+\t\t\t * after iterating all pathspecs, it means all\n+\t\t\t * pathspecs are either outside of base, or inside the\n+\t\t\t * base but sorts strictly earlier than the current\n+\t\t\t * one.  In either case, they will never match the\n+\t\t\t * subsequent entries.  In such a case, we initialized\n+\t\t\t * the variable to -1 and that is what will be\n+\t\t\t * returned, allowing the caller to terminate early.\n+\t\t\t */\n+\t\t\tnever_interesting = 0;\n+\t\t}\n+\n+\t\tif (pathlen > matchlen)\n+\t\t\tcontinue;\n+\n+\t\tif (matchlen > pathlen) {\n+\t\t\tif (match[pathlen] != '/')\n+\t\t\t\tcontinue;\n+\t\t\tif (!S_ISDIR(entry->mode))\n+\t\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (m == -1)\n+\t\t\t/*\n+\t\t\t * we cheated and did not do strncmp(), so we do\n+\t\t\t * that here.\n+\t\t\t */\n+\t\t\tm = strncmp(match, entry->path, pathlen);\n+\n+\t\t/*\n+\t\t * If common part matched earlier then it is a hit,\n+\t\t * because we rejected the case where path is not a\n+\t\t * leading directory and is shorter than match.\n+\t\t */\n+\t\tif (!m)\n+\t\t\treturn 1;\n+\t}\n+\treturn never_interesting; /* No matches */\n+}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 7e3e0b5..c12f0a2 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -60,4 +60,6 @@ static inline int traverse_path_len(const struct traverse_info *info, const stru\n \treturn info->pathlen + tree_entry_len(n->path, n->sha1);\n }\n \n+extern int tree_entry_interesting(const struct name_entry *, const char *, int, const struct pathspec *ps);\n+\n #endif\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157944","messageId":"1292233616-27692-8-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 07/19] glossary: define pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:44Z","receivedAt":"2010-12-13T09:46:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Jonathan Nieder <jrnieder@gmail.com>\n\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/glossary-content.txt |   23 +++++++++++++++++++++++\n 1 files changed, 23 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt\nindex 1f029f8..4ed2a28 100644\n--- a/Documentation/glossary-content.txt\n+++ b/Documentation/glossary-content.txt\n@@ -273,6 +273,29 @@ This commit is referred to as a \"merge commit\", or sometimes just a\n \t<<def_pack,pack>>, to assist in efficiently accessing the contents of a\n \tpack.\n \n+[[def_pathspec]]pathspec::\n+       Pattern used to specify paths.\n++\n+Pathspecs are used on the command line of \"git ls-files\", \"git\n+ls-tree\", \"git grep\", \"git checkout\", and many other commands to\n+limit the scope of operations to some subset of the tree or\n+worktree.  See the documentation of each command for whether\n+paths are relative to the current directory or toplevel.  The\n+pathspec syntax is as follows:\n+\n+* any path matches itself\n+* the pathspec up to the last slash represents a\n+  directory prefix.  The scope of that pathspec is\n+  limited to that subtree.\n+* the rest of the pathspec is a pattern for the remainder\n+  of the pathname.  Paths relative to the directory\n+  prefix will be matched against that pattern using fnmatch(3);\n+  in particular, '*' and '?' _can_ match directory separators.\n++\n+For example, Documentation/*.jpg will match all .jpg files\n+in the Documentation subtree,\n+including Documentation/chapter_1/figure_1.jpg.\n+\n [[def_parent]]parent::\n \tA <<def_commit_object,commit object>> contains a (possibly empty) list\n \tof the logical predecessor(s) in the line of development, i.e. its\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157943","messageId":"1292233616-27692-9-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 08/19] pathspec: mark wildcard pathspecs from the beginning","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:45Z","receivedAt":"2010-12-13T09:46:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n prefix_len was used, but no longer. Still hesitate to remove it. It\n might get used again..\n\n cache.h |    4 +++-\n dir.c   |   13 +++++++++++--\n 2 files changed, 14 insertions(+), 3 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 36819b6..3a1acf1 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -496,8 +496,10 @@ extern int ie_modified(const struct index_state *, struct cache_entry *, struct\n struct pathspec {\n \tconst char **raw; /* get_pathspec() result, not freed by free_pathspec() */\n \tint nr;\n+\tint has_wildcard:1;\n \tstruct pathspec_item {\n-\t\tint len;\n+\t\tint len, prefix_len;\n+\t\tint has_wildcard:1;\n \t} *items;\n };\n \ndiff --git a/dir.c b/dir.c\nindex 646c79f..0987d0c 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1089,8 +1089,17 @@ int init_pathspec(struct pathspec *pathspec, const char **paths)\n \tpathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr);\n \tfor (i = 0; i < pathspec->nr; i++) {\n \t\tstruct pathspec_item *item = pathspec->items+i;\n-\n-\t\titem->len = strlen(paths[i]);\n+\t\tconst char *path = paths[i];\n+\n+\t\titem->len = strlen(path);\n+\t\titem->has_wildcard = !no_wildcard(path);\n+\t\tif (item->has_wildcard) {\n+\t\t\tpathspec->has_wildcard = 1;\n+\t\t\titem->prefix_len = 0;\n+\t\t\twhile (item->prefix_len < item->len &&\n+\t\t\t       strchr(\"*?[{\\\\\", path[item->prefix_len]) == NULL)\n+\t\t\t\titem->prefix_len++;\n+\t\t}\n \t}\n \treturn 0;\n }\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157945","messageId":"1292233616-27692-10-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 09/19] tree-diff.c: reserve space in \"base\" for pathname concatenation","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:46Z","receivedAt":"2010-12-13T09:46:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This patch make sure that \"base\" parameter is writable. The callees\nare free to modify it as long as base remains the same before\nentering and after leaving the callee.\n\nThis avoids quite a bit of malloc and memcpy().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n tree-diff.c |   87 +++++++++++++++++++++++++++-------------------------------\n 1 files changed, 41 insertions(+), 46 deletions(-)\n\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 50d7e6d..a870f6c 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -6,34 +6,17 @@\n #include \"diffcore.h\"\n #include \"tree.h\"\n \n-static char *malloc_base(const char *base, int baselen, const char *path, int pathlen)\n-{\n-\tchar *newbase = xmalloc(baselen + pathlen + 2);\n-\tmemcpy(newbase, base, baselen);\n-\tmemcpy(newbase + baselen, path, pathlen);\n-\tmemcpy(newbase + baselen + pathlen, \"/\", 2);\n-\treturn newbase;\n-}\n+static void show_entry(struct diff_options *opt, const char *prefix,\n+\t\t       struct tree_desc *desc, char *base, int baselen);\n \n-static char *malloc_fullname(const char *base, int baselen, const char *path, int pathlen)\n-{\n-\tchar *fullname = xmalloc(baselen + pathlen + 1);\n-\tmemcpy(fullname, base, baselen);\n-\tmemcpy(fullname + baselen, path, pathlen);\n-\tfullname[baselen + pathlen] = 0;\n-\treturn fullname;\n-}\n-\n-static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,\n-\t\t       const char *base, int baselen);\n-\n-static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, int baselen, struct diff_options *opt)\n+static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,\n+\t\t\t      char *base, int baselen,\n+\t\t\t      struct diff_options *opt)\n {\n \tunsigned mode1, mode2;\n \tconst char *path1, *path2;\n \tconst unsigned char *sha1, *sha2;\n \tint cmp, pathlen1, pathlen2;\n-\tchar *fullname;\n \n \tsha1 = tree_entry_extract(t1, &path1, &mode1);\n \tsha2 = tree_entry_extract(t2, &path2, &mode2);\n@@ -64,26 +47,31 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const\n \n \tif (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode1)) {\n \t\tint retval;\n-\t\tchar *newbase = malloc_base(base, baselen, path1, pathlen1);\n+\n+\t\tmemcpy(base + baselen, path1, pathlen1);\n+\t\tmemcpy(base + baselen + pathlen1, \"/\", 2);\n+\n \t\tif (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {\n-\t\t\tnewbase[baselen + pathlen1] = 0;\n+\t\t\tbase[baselen + pathlen1] = 0;\n \t\t\topt->change(opt, mode1, mode2,\n-\t\t\t\t    sha1, sha2, newbase, 0, 0);\n-\t\t\tnewbase[baselen + pathlen1] = '/';\n+\t\t\t\t    sha1, sha2, base, 0, 0);\n+\t\t\tbase[baselen + pathlen1] = '/';\n \t\t}\n-\t\tretval = diff_tree_sha1(sha1, sha2, newbase, opt);\n-\t\tfree(newbase);\n+\t\tretval = diff_tree_sha1(sha1, sha2, base, opt);\n+\t\tbase[baselen] = 0;\n \t\treturn retval;\n \t}\n \n-\tfullname = malloc_fullname(base, baselen, path1, pathlen1);\n-\topt->change(opt, mode1, mode2, sha1, sha2, fullname, 0, 0);\n-\tfree(fullname);\n+\tmemcpy(base + baselen, path1, pathlen1);\n+\tbase[baselen + pathlen1] = 0;\n+\topt->change(opt, mode1, mode2, sha1, sha2, base, 0, 0);\n+\tbase[baselen] = 0;\n \treturn 0;\n }\n \n /* A whole sub-tree went away or appeared */\n-static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen)\n+static void show_tree(struct diff_options *opt, const char *prefix,\n+\t\t      struct tree_desc *desc, char *base, int baselen)\n {\n \tint all_interesting = 0;\n \twhile (desc->size) {\n@@ -105,8 +93,8 @@ static void show_tree(struct diff_options *opt, const char *prefix, struct tree_\n }\n \n /* A file entry went away or appeared */\n-static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,\n-\t\t       const char *base, int baselen)\n+static void show_entry(struct diff_options *opt, const char *prefix,\n+\t\t       struct tree_desc *desc, char *base, int baselen)\n {\n \tunsigned mode;\n \tconst char *path;\n@@ -115,34 +103,38 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree\n \n \tif (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode)) {\n \t\tenum object_type type;\n-\t\tchar *newbase = malloc_base(base, baselen, path, pathlen);\n \t\tstruct tree_desc inner;\n \t\tvoid *tree;\n \t\tunsigned long size;\n \n+\t\tmemcpy(base + baselen, path, pathlen);\n+\t\tmemcpy(base + baselen + pathlen, \"/\", 2);\n+\n \t\ttree = read_sha1_file(sha1, &type, &size);\n \t\tif (!tree || type != OBJ_TREE)\n \t\t\tdie(\"corrupt tree sha %s\", sha1_to_hex(sha1));\n \n \t\tif (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {\n-\t\t\tnewbase[baselen + pathlen] = 0;\n-\t\t\topt->add_remove(opt, *prefix, mode, sha1, newbase, 0);\n-\t\t\tnewbase[baselen + pathlen] = '/';\n+\t\t\tbase[baselen + pathlen] = 0;\n+\t\t\topt->add_remove(opt, *prefix, mode, sha1, base, 0);\n+\t\t\tbase[baselen + pathlen] = '/';\n \t\t}\n \n \t\tinit_tree_desc(&inner, tree, size);\n-\t\tshow_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);\n+\t\tshow_tree(opt, prefix, &inner, base, baselen + 1 + pathlen);\n \n+\t\tbase[baselen] = 0;\n \t\tfree(tree);\n-\t\tfree(newbase);\n \t} else {\n-\t\tchar *fullname = malloc_fullname(base, baselen, path, pathlen);\n-\t\topt->add_remove(opt, prefix[0], mode, sha1, fullname, 0);\n-\t\tfree(fullname);\n+\t\tmemcpy(base + baselen, path, pathlen);\n+\t\tbase[baselen + pathlen] = 0;\n+\t\topt->add_remove(opt, prefix[0], mode, sha1, base, 0);\n+\t\tbase[baselen] = 0;\n \t}\n }\n \n-static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt)\n+static void skip_uninteresting(struct tree_desc *t, char *base,\n+\t\t\t       int baselen, struct diff_options *opt)\n {\n \tint all_interesting = 0;\n \twhile (t->size) {\n@@ -166,10 +158,13 @@ static void skip_uninteresting(struct tree_desc *t, const char *base, int basele\n \t}\n }\n \n-int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)\n+int diff_tree(struct tree_desc *t1, struct tree_desc *t2,\n+\t      const char *base_, struct diff_options *opt)\n {\n-\tint baselen = strlen(base);\n+\tchar base[PATH_MAX];\n+\tint baselen = strlen(base_);\n \n+\tmemcpy(base, base_, baselen+1);\n \tfor (;;) {\n \t\tif (DIFF_OPT_TST(opt, QUICK) &&\n \t\t    DIFF_OPT_TST(opt, HAS_CHANGES))\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157947","messageId":"1292233616-27692-11-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 10/19] tree_entry_interesting(): factor out most matching logic","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:47Z","receivedAt":"2010-12-13T09:46:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n tree-walk.c |  168 ++++++++++++++++++++++++++++++++--------------------------\n 1 files changed, 93 insertions(+), 75 deletions(-)\n\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 01168ea..40a4657 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -456,6 +456,91 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch\n \treturn retval;\n }\n \n+static int match_entry(const struct name_entry *entry, int pathlen,\n+\t\t       const char *match, int matchlen,\n+\t\t       int *never_interesting)\n+{\n+\tint m = -1; /* signals that we haven't called strncmp() */\n+\n+\tif (*never_interesting) {\n+\t\t/*\n+\t\t * We have not seen any match that sorts later\n+\t\t * than the current path.\n+\t\t */\n+\n+\t\t/*\n+\t\t * Does match sort strictly earlier than path\n+\t\t * with their common parts?\n+\t\t */\n+\t\tm = strncmp(match, entry->path,\n+\t\t\t    (matchlen < pathlen) ? matchlen : pathlen);\n+\t\tif (m < 0)\n+\t\t\treturn 0;\n+\n+\t\t/*\n+\t\t * If we come here even once, that means there is at\n+\t\t * least one pathspec that would sort equal to or\n+\t\t * later than the path we are currently looking at.\n+\t\t * In other words, if we have never reached this point\n+\t\t * after iterating all pathspecs, it means all\n+\t\t * pathspecs are either outside of base, or inside the\n+\t\t * base but sorts strictly earlier than the current\n+\t\t * one.  In either case, they will never match the\n+\t\t * subsequent entries.  In such a case, we initialized\n+\t\t * the variable to -1 and that is what will be\n+\t\t * returned, allowing the caller to terminate early.\n+\t\t */\n+\t\t*never_interesting = 0;\n+\t}\n+\n+\tif (pathlen > matchlen)\n+\t\treturn 0;\n+\n+\tif (matchlen > pathlen) {\n+\t\tif (match[pathlen] != '/')\n+\t\t\treturn 0;\n+\t\tif (!S_ISDIR(entry->mode))\n+\t\t\treturn 0;\n+\t}\n+\n+\tif (m == -1)\n+\t\t/*\n+\t\t * we cheated and did not do strncmp(), so we do\n+\t\t * that here.\n+\t\t */\n+\t\tm = strncmp(match, entry->path, pathlen);\n+\n+\t/*\n+\t * If common part matched earlier then it is a hit,\n+\t * because we rejected the case where path is not a\n+\t * leading directory and is shorter than match.\n+\t */\n+\tif (!m)\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n+static int match_dir_prefix(const char *base, int baselen,\n+\t\t\t    const char *match, int matchlen)\n+{\n+\t/* If it doesn't match, move along... */\n+\tif (strncmp(base, match, matchlen))\n+\t\treturn 0;\n+\n+\t/*\n+\t * If the base is a subdirectory of a path which\n+\t * was specified, all of them are interesting.\n+\t */\n+\tif (!matchlen ||\n+\t    base[matchlen] == '/' ||\n+\t    match[matchlen - 1] == '/')\n+\t\treturn 1;\n+\n+\t/* Just a random prefix match */\n+\treturn 0;\n+}\n+\n /*\n  * Is a tree entry interesting given the pathspec we have?\n  *\n@@ -481,88 +566,21 @@ int tree_entry_interesting(const struct name_entry *entry,\n \tfor (i = 0; i < ps->nr; i++) {\n \t\tconst char *match = ps->raw[i];\n \t\tint matchlen = ps->items[i].len;\n-\t\tint m = -1; /* signals that we haven't called strncmp() */\n \n \t\tif (baselen >= matchlen) {\n-\t\t\t/* If it doesn't match, move along... */\n-\t\t\tif (strncmp(base, match, matchlen))\n+\t\t\tif (!match_dir_prefix(base, baselen, match, matchlen))\n+\t\t\t\t/* Just a random prefix match */\n \t\t\t\tcontinue;\n-\n-\t\t\t/*\n-\t\t\t * If the base is a subdirectory of a path which\n-\t\t\t * was specified, all of them are interesting.\n-\t\t\t */\n-\t\t\tif (!matchlen ||\n-\t\t\t    base[matchlen] == '/' ||\n-\t\t\t    match[matchlen - 1] == '/')\n-\t\t\t\treturn 2;\n-\n-\t\t\t/* Just a random prefix match */\n-\t\t\tcontinue;\n+\t\t\treturn 2;\n \t\t}\n \n \t\t/* Does the base match? */\n-\t\tif (strncmp(base, match, baselen))\n-\t\t\tcontinue;\n-\n-\t\tmatch += baselen;\n-\t\tmatchlen -= baselen;\n-\n-\t\tif (never_interesting) {\n-\t\t\t/*\n-\t\t\t * We have not seen any match that sorts later\n-\t\t\t * than the current path.\n-\t\t\t */\n-\n-\t\t\t/*\n-\t\t\t * Does match sort strictly earlier than path\n-\t\t\t * with their common parts?\n-\t\t\t */\n-\t\t\tm = strncmp(match, entry->path,\n-\t\t\t\t    (matchlen < pathlen) ? matchlen : pathlen);\n-\t\t\tif (m < 0)\n-\t\t\t\tcontinue;\n-\n-\t\t\t/*\n-\t\t\t * If we come here even once, that means there is at\n-\t\t\t * least one pathspec that would sort equal to or\n-\t\t\t * later than the path we are currently looking at.\n-\t\t\t * In other words, if we have never reached this point\n-\t\t\t * after iterating all pathspecs, it means all\n-\t\t\t * pathspecs are either outside of base, or inside the\n-\t\t\t * base but sorts strictly earlier than the current\n-\t\t\t * one.  In either case, they will never match the\n-\t\t\t * subsequent entries.  In such a case, we initialized\n-\t\t\t * the variable to -1 and that is what will be\n-\t\t\t * returned, allowing the caller to terminate early.\n-\t\t\t */\n-\t\t\tnever_interesting = 0;\n+\t\tif (!strncmp(base, match, baselen)) {\n+\t\t\tif (match_entry(entry, pathlen,\n+\t\t\t\t\tmatch + baselen, matchlen - baselen,\n+\t\t\t\t\t&never_interesting))\n+\t\t\t\treturn 1;\n \t\t}\n-\n-\t\tif (pathlen > matchlen)\n-\t\t\tcontinue;\n-\n-\t\tif (matchlen > pathlen) {\n-\t\t\tif (match[pathlen] != '/')\n-\t\t\t\tcontinue;\n-\t\t\tif (!S_ISDIR(entry->mode))\n-\t\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (m == -1)\n-\t\t\t/*\n-\t\t\t * we cheated and did not do strncmp(), so we do\n-\t\t\t * that here.\n-\t\t\t */\n-\t\t\tm = strncmp(match, entry->path, pathlen);\n-\n-\t\t/*\n-\t\t * If common part matched earlier then it is a hit,\n-\t\t * because we rejected the case where path is not a\n-\t\t * leading directory and is shorter than match.\n-\t\t */\n-\t\tif (!m)\n-\t\t\treturn 1;\n \t}\n \treturn never_interesting; /* No matches */\n }\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157954","messageId":"1292233616-27692-12-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 11/19] tree_entry_interesting: support depth limit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:48Z","receivedAt":"2010-12-13T09:46:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is needed to replace pathspec_matches() in builtin/grep.c. Depth\nlimit is only effective when pathspec.recursive == 1\n\nmax_depth == -1 means unlimited depth.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pathspec.recursive is also needed for wildcard matching later on.\n\n cache.h     |    2 ++\n dir.c       |   15 +++++++++++++++\n dir.h       |    1 +\n tree-diff.c |    2 ++\n tree-walk.c |   19 +++++++++++++++++--\n 5 files changed, 37 insertions(+), 2 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 3a1acf1..56da281 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -497,6 +497,8 @@ struct pathspec {\n \tconst char **raw; /* get_pathspec() result, not freed by free_pathspec() */\n \tint nr;\n \tint has_wildcard:1;\n+\tint recursive:1;\n+\tint max_depth;\n \tstruct pathspec_item {\n \t\tint len, prefix_len;\n \t\tint has_wildcard:1;\ndiff --git a/dir.c b/dir.c\nindex 0987d0c..bb5076c 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -71,6 +71,21 @@ int fill_directory(struct dir_struct *dir, const char **pathspec)\n \treturn len;\n }\n \n+int within_depth(const char *name, int namelen,\n+\t\t\tint depth, int max_depth)\n+{\n+\tconst char *cp = name, *cpe = name + namelen;\n+\n+\twhile (cp < cpe) {\n+\t\tif (*cp++ != '/')\n+\t\t\tcontinue;\n+\t\tdepth++;\n+\t\tif (depth > max_depth)\n+\t\t\treturn 0;\n+\t}\n+\treturn 1;\n+}\n+\n /*\n  * Does 'match' match the given name?\n  * A match is found if\ndiff --git a/dir.h b/dir.h\nindex 278d84c..c71de08 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -65,6 +65,7 @@ struct dir_struct {\n #define MATCHED_FNMATCH 2\n #define MATCHED_EXACTLY 3\n extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);\n+extern int within_depth(const char *name, int namelen, int depth, int max_depth);\n \n extern int fill_directory(struct dir_struct *dir, const char **pathspec);\n extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec);\ndiff --git a/tree-diff.c b/tree-diff.c\nindex a870f6c..7c3b770 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -165,6 +165,8 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2,\n \tint baselen = strlen(base_);\n \n \tmemcpy(base, base_, baselen+1);\n+\topt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);\n+\topt->pathspec.max_depth = -1;\n \tfor (;;) {\n \t\tif (DIFF_OPT_TST(opt, QUICK) &&\n \t\t    DIFF_OPT_TST(opt, HAS_CHANGES))\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 40a4657..d28de30 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"tree-walk.h\"\n #include \"unpack-trees.h\"\n+#include \"dir.h\"\n #include \"tree.h\"\n \n static const char *get_mode(const char *str, unsigned int *modep)\n@@ -558,9 +559,17 @@ int tree_entry_interesting(const struct name_entry *entry,\n \tint pathlen;\n \tint never_interesting = -1;\n \n-\tif (!ps || !ps->nr)\n+\tif (!ps)\n \t\treturn 1;\n \n+\tif (!ps->nr) {\n+\t\tif (!ps->recursive || ps->max_depth == -1)\n+\t\t\treturn 1;\n+\t\treturn !!within_depth(base, baselen,\n+\t\t\t\t      !!S_ISDIR(entry->mode),\n+\t\t\t\t      ps->max_depth);\n+\t}\n+\n \tpathlen = tree_entry_len(entry->path, entry->sha1);\n \n \tfor (i = 0; i < ps->nr; i++) {\n@@ -571,7 +580,13 @@ int tree_entry_interesting(const struct name_entry *entry,\n \t\t\tif (!match_dir_prefix(base, baselen, match, matchlen))\n \t\t\t\t/* Just a random prefix match */\n \t\t\t\tcontinue;\n-\t\t\treturn 2;\n+\n+\t\t\tif (!ps->recursive || ps->max_depth == -1)\n+\t\t\t\treturn 2;\n+\n+\t\t\treturn !!within_depth(base+matchlen+1, baselen-matchlen-1,\n+\t\t\t\t\t      !!S_ISDIR(entry->mode),\n+\t\t\t\t\t      ps->max_depth);\n \t\t}\n \n \t\t/* Does the base match? */\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157946","messageId":"1292233616-27692-13-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 12/19] tree_entry_interesting(): support wildcard matching","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:49Z","receivedAt":"2010-12-13T09:46:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n tree-walk.c |   33 ++++++++++++++++++++++++++++++---\n tree-walk.h |    2 +-\n 2 files changed, 31 insertions(+), 4 deletions(-)\n\ndiff --git a/tree-walk.c b/tree-walk.c\nindex d28de30..b5ad42b 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -552,7 +552,7 @@ static int match_dir_prefix(const char *base, int baselen,\n  *  - negative for \"no, and no subsequent entries will be either\"\n  */\n int tree_entry_interesting(const struct name_entry *entry,\n-\t\t\t   const char *base, int baselen,\n+\t\t\t   char *base, int baselen,\n \t\t\t   const struct pathspec *ps)\n {\n \tint i;\n@@ -573,13 +573,15 @@ int tree_entry_interesting(const struct name_entry *entry,\n \tpathlen = tree_entry_len(entry->path, entry->sha1);\n \n \tfor (i = 0; i < ps->nr; i++) {\n+\t\tconst struct pathspec_item *item = ps->items+i;\n+\n \t\tconst char *match = ps->raw[i];\n-\t\tint matchlen = ps->items[i].len;\n+\t\tint matchlen = item->len;\n \n \t\tif (baselen >= matchlen) {\n \t\t\tif (!match_dir_prefix(base, baselen, match, matchlen))\n \t\t\t\t/* Just a random prefix match */\n-\t\t\t\tcontinue;\n+\t\t\t\tgoto match_wildcards;\n \n \t\t\tif (!ps->recursive || ps->max_depth == -1)\n \t\t\t\treturn 2;\n@@ -596,6 +598,31 @@ int tree_entry_interesting(const struct name_entry *entry,\n \t\t\t\t\t&never_interesting))\n \t\t\t\treturn 1;\n \t\t}\n+\n+match_wildcards:\n+\t\t/*\n+\t\t * Concatenate base and entry->path into one and do\n+\t\t * fnmatch() on it.\n+\t\t */\n+\n+\t\tif (!item->has_wildcard)\n+\t\t\tcontinue;\n+\n+\t\tnever_interesting = 0;\n+\t\tmemcpy(base + baselen, entry->path, pathlen+1);\n+\n+\t\tif (!fnmatch(match, base, 0)) {\n+\t\t\tbase[baselen] = 0;\n+\t\t\treturn 1;\n+\t\t}\n+\t\tbase[baselen] = 0;\n+\n+\t\t/*\n+\t\t * Match all directories. We'll try to match files\n+\t\t * later on.\n+\t\t */\n+\t\tif (ps->recursive && S_ISDIR(entry->mode))\n+\t\t\treturn 1;\n \t}\n \treturn never_interesting; /* No matches */\n }\ndiff --git a/tree-walk.h b/tree-walk.h\nindex c12f0a2..94e0ef4 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -60,6 +60,6 @@ static inline int traverse_path_len(const struct traverse_info *info, const stru\n \treturn info->pathlen + tree_entry_len(n->path, n->sha1);\n }\n \n-extern int tree_entry_interesting(const struct name_entry *, const char *, int, const struct pathspec *ps);\n+extern int tree_entry_interesting(const struct name_entry *, char *, int, const struct pathspec *ps);\n \n #endif\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157953","messageId":"1292233616-27692-14-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 13/19] tree_entry_interesting(): optimize fnmatch when base is matched","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:50Z","receivedAt":"2010-12-13T09:46:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If base is already matched, skip that part when calling\nfnmatch(). This happens quite often when users start a command from\nworktree's subdirectory and prefix is usually prepended to all\npathspecs.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t4010-diff-pathspec.sh |   14 ++++++++++++++\n tree-walk.c              |   15 +++++++++++++++\n 2 files changed, 29 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh\nindex 94df7ae..4b120f8 100755\n--- a/t/t4010-diff-pathspec.sh\n+++ b/t/t4010-diff-pathspec.sh\n@@ -70,4 +70,18 @@ test_expect_success 'diff-tree pathspec' '\n \ttest_cmp expected current\n '\n \n+EMPTY_TREE=4b825dc642cb6eb9a060e54bf8d69288fbee4904\n+\n+test_expect_success 'diff-tree with wildcard shows dir also matches' '\n+\tgit diff-tree --name-only $EMPTY_TREE $tree -- \"f*\" >result &&\n+\techo file0 >expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'diff-tree -r with wildcard' '\n+\tgit diff-tree -r --name-only $EMPTY_TREE $tree -- \"*file1\" >result &&\n+\techo path1/file1 >expected &&\n+\ttest_cmp expected result\n+'\n+\n test_done\ndiff --git a/tree-walk.c b/tree-walk.c\nindex b5ad42b..e1a18fc 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -597,6 +597,21 @@ int tree_entry_interesting(const struct name_entry *entry,\n \t\t\t\t\tmatch + baselen, matchlen - baselen,\n \t\t\t\t\t&never_interesting))\n \t\t\t\treturn 1;\n+\n+\t\t\tif (item->has_wildcard) {\n+\t\t\t\tnever_interesting = 0;\n+\t\t\t\tif (!fnmatch(match + baselen, entry->path, 0))\n+\t\t\t\t\treturn 1;\n+\n+\t\t\t\t/*\n+\t\t\t\t * Match all directories. We'll try to\n+\t\t\t\t * match files later on.\n+\t\t\t\t */\n+\t\t\t\tif (ps->recursive && S_ISDIR(entry->mode))\n+\t\t\t\t\treturn 1;\n+\t\t\t}\n+\n+\t\t\tcontinue;\n \t\t}\n \n match_wildcards:\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157956","messageId":"1292233616-27692-15-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 14/19] Convert ce_path_match() use to match_pathspec()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:51Z","receivedAt":"2010-12-13T09:46:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Previously ce_path_match() is used together with tree_entry_interesting().\nBoth do not support wildcards. tree_entry_interesting() understands\nwildcards now, so it's time to teach ce_path_match() to do the same.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n read-cache.c |   20 +-------------------\n 1 files changed, 1 insertions(+), 19 deletions(-)\n\ndiff --git a/read-cache.c b/read-cache.c\nindex 1f42473..cbabd8b 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -685,28 +685,10 @@ int ce_same_name(struct cache_entry *a, struct cache_entry *b)\n \n int ce_path_match(const struct cache_entry *ce, const char **pathspec)\n {\n-\tconst char *match, *name;\n-\tint len;\n-\n \tif (!pathspec)\n \t\treturn 1;\n \n-\tlen = ce_namelen(ce);\n-\tname = ce->name;\n-\twhile ((match = *pathspec++) != NULL) {\n-\t\tint matchlen = strlen(match);\n-\t\tif (matchlen > len)\n-\t\t\tcontinue;\n-\t\tif (memcmp(name, match, matchlen))\n-\t\t\tcontinue;\n-\t\tif (matchlen && name[matchlen-1] == '/')\n-\t\t\treturn 1;\n-\t\tif (name[matchlen] == '/' || !name[matchlen])\n-\t\t\treturn 1;\n-\t\tif (!matchlen)\n-\t\t\treturn 1;\n-\t}\n-\treturn 0;\n+\treturn match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL);\n }\n \n /*\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157948","messageId":"1292233616-27692-16-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 15/19] pathspec: add match_pathspec_depth()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:52Z","receivedAt":"2010-12-13T09:46:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"match_pathspec_depth() is similar to match_pathspec() except that it\ncan take depth limit.\n\nIn long term, match_pathspec() should be removed in favor of this\nfunction.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c |   45 +++++++++++++++++++++++++++++++++++++++++++++\n dir.h |    3 +++\n 2 files changed, 48 insertions(+), 0 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex bb5076c..e12dbdd 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -169,6 +169,51 @@ int match_pathspec(const char **pathspec, const char *name, int namelen,\n \treturn retval;\n }\n \n+int match_pathspec_depth(const char **pathspec, int max_depth,\n+\t\t\t const char *name, int namelen,\n+\t\t\t int prefix, char *seen)\n+{\n+\tint i, retval = 0;\n+\n+\tif (!pathspec) {\n+\t\tif (max_depth == -1)\n+\t\t\treturn MATCHED_RECURSIVELY;\n+\n+\t\tif (within_depth(name, namelen, 0, max_depth))\n+\t\t\treturn MATCHED_EXACTLY;\n+\t\telse\n+\t\t\treturn 0;\n+\t}\n+\n+\tname += prefix;\n+\tnamelen -= prefix;\n+\n+\tfor (i = 0; pathspec[i] != NULL; i++) {\n+\t\tint how;\n+\t\tconst char *match = pathspec[i] + prefix;\n+\t\tif (seen && seen[i] == MATCHED_EXACTLY)\n+\t\t\tcontinue;\n+\t\thow = match_one(match, name, namelen);\n+\t\tif (max_depth != -1 &&\n+\t\t    how && how != MATCHED_FNMATCH) {\n+\t\t\tint len = strlen(match);\n+\t\t\tif (name[len] == '/')\n+\t\t\t\tlen++;\n+\t\t\tif (within_depth(name+len, namelen-len, 0, max_depth))\n+\t\t\t\thow = MATCHED_EXACTLY;\n+\t\t\telse\n+\t\t\t\thow = 0;\n+\t\t}\n+\t\tif (how) {\n+\t\t\tif (retval < how)\n+\t\t\t\tretval = how;\n+\t\t\tif (seen && seen[i] < how)\n+\t\t\t\tseen[i] = how;\n+\t\t}\n+\t}\n+\treturn retval;\n+}\n+\n static int no_wildcard(const char *string)\n {\n \treturn string[strcspn(string, \"*?[{\\\\\")] == '\\0';\ndiff --git a/dir.h b/dir.h\nindex c71de08..9dc9592 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -65,6 +65,9 @@ struct dir_struct {\n #define MATCHED_FNMATCH 2\n #define MATCHED_EXACTLY 3\n extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);\n+extern int match_pathspec_depth(const char **pathspec, int max_depth,\n+\t\t\t\tconst char *name, int namelen,\n+\t\t\t\tint prefix, char *seen);\n extern int within_depth(const char *name, int namelen, int depth, int max_depth);\n \n extern int fill_directory(struct dir_struct *dir, const char **pathspec);\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157950","messageId":"1292233616-27692-17-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 16/19] grep: convert to use struct pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:53Z","receivedAt":"2010-12-13T09:46:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/grep.c |   30 ++++++++++++++++--------------\n 1 files changed, 16 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex da32f3d..4179af8 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -585,7 +585,7 @@ static void run_pager(struct grep_opt *opt, const char *prefix)\n \tfree(argv);\n }\n \n-static int grep_cache(struct grep_opt *opt, const char **paths, int cached)\n+static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached)\n {\n \tint hit = 0;\n \tint nr;\n@@ -595,7 +595,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)\n \t\tstruct cache_entry *ce = active_cache[nr];\n \t\tif (!S_ISREG(ce->ce_mode))\n \t\t\tcontinue;\n-\t\tif (!pathspec_matches(paths, ce->name, opt->max_depth))\n+\t\tif (!pathspec_matches(pathspec->raw, ce->name, opt->max_depth))\n \t\t\tcontinue;\n \t\t/*\n \t\t * If CE_VALID is on, we assume worktree file and its cache entry\n@@ -622,7 +622,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)\n \treturn hit;\n }\n \n-static int grep_tree(struct grep_opt *opt, const char **paths,\n+static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n \t\t     struct tree_desc *tree,\n \t\t     const char *tree_name, const char *base)\n {\n@@ -656,7 +656,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,\n \t\t\tstrbuf_addch(&pathbuf, '/');\n \n \t\tdown = pathbuf.buf + tn_len;\n-\t\tif (!pathspec_matches(paths, down, opt->max_depth))\n+\t\tif (!pathspec_matches(pathspec->raw, down, opt->max_depth))\n \t\t\t;\n \t\telse if (S_ISREG(entry.mode))\n \t\t\thit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);\n@@ -671,7 +671,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,\n \t\t\t\tdie(\"unable to read tree (%s)\",\n \t\t\t\t    sha1_to_hex(entry.sha1));\n \t\t\tinit_tree_desc(&sub, data, size);\n-\t\t\thit |= grep_tree(opt, paths, &sub, tree_name, down);\n+\t\t\thit |= grep_tree(opt, pathspec, &sub, tree_name, down);\n \t\t\tfree(data);\n \t\t}\n \t\tif (hit && opt->status_only)\n@@ -681,7 +681,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,\n \treturn hit;\n }\n \n-static int grep_object(struct grep_opt *opt, const char **paths,\n+static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,\n \t\t       struct object *obj, const char *name)\n {\n \tif (obj->type == OBJ_BLOB)\n@@ -696,14 +696,14 @@ static int grep_object(struct grep_opt *opt, const char **paths,\n \t\tif (!data)\n \t\t\tdie(\"unable to read tree (%s)\", sha1_to_hex(obj->sha1));\n \t\tinit_tree_desc(&tree, data, size);\n-\t\thit = grep_tree(opt, paths, &tree, name, \"\");\n+\t\thit = grep_tree(opt, pathspec, &tree, name, \"\");\n \t\tfree(data);\n \t\treturn hit;\n \t}\n \tdie(\"unable to grep from object of type %s\", typename(obj->type));\n }\n \n-static int grep_objects(struct grep_opt *opt, const char **paths,\n+static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,\n \t\t\tconst struct object_array *list)\n {\n \tunsigned int i;\n@@ -713,7 +713,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths,\n \tfor (i = 0; i < nr; i++) {\n \t\tstruct object *real_obj;\n \t\treal_obj = deref_tag(list->objects[i].item, NULL, 0);\n-\t\tif (grep_object(opt, paths, real_obj, list->objects[i].name)) {\n+\t\tif (grep_object(opt, pathspec, real_obj, list->objects[i].name)) {\n \t\t\thit = 1;\n \t\t\tif (opt->status_only)\n \t\t\t\tbreak;\n@@ -722,7 +722,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths,\n \treturn hit;\n }\n \n-static int grep_directory(struct grep_opt *opt, const char **paths)\n+static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec)\n {\n \tstruct dir_struct dir;\n \tint i, hit = 0;\n@@ -730,7 +730,7 @@ static int grep_directory(struct grep_opt *opt, const char **paths)\n \tmemset(&dir, 0, sizeof(dir));\n \tsetup_standard_excludes(&dir);\n \n-\tfill_directory(&dir, paths);\n+\tfill_directory(&dir, pathspec->raw);\n \tfor (i = 0; i < dir.nr; i++) {\n \t\thit |= grep_file(opt, dir.entries[i]->name);\n \t\tif (hit && opt->status_only)\n@@ -836,6 +836,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \tstruct grep_opt opt;\n \tstruct object_array list = OBJECT_ARRAY_INIT;\n \tconst char **paths = NULL;\n+\tstruct pathspec pathspec;\n \tstruct string_list path_list = STRING_LIST_INIT_NODUP;\n \tint i;\n \tint dummy;\n@@ -1063,6 +1064,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\tpaths[0] = prefix;\n \t\tpaths[1] = NULL;\n \t}\n+\tinit_pathspec(&pathspec, paths);\n \n \tif (show_in_pager && (cached || list.nr))\n \t\tdie(\"--open-files-in-pager only works on the worktree\");\n@@ -1093,16 +1095,16 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\t\tdie(\"--cached cannot be used with --no-index.\");\n \t\tif (list.nr)\n \t\t\tdie(\"--no-index cannot be used with revs.\");\n-\t\thit = grep_directory(&opt, paths);\n+\t\thit = grep_directory(&opt, &pathspec);\n \t} else if (!list.nr) {\n \t\tif (!cached)\n \t\t\tsetup_work_tree();\n \n-\t\thit = grep_cache(&opt, paths, cached);\n+\t\thit = grep_cache(&opt, &pathspec, cached);\n \t} else {\n \t\tif (cached)\n \t\t\tdie(\"both --cached and trees are given.\");\n-\t\thit = grep_objects(&opt, paths, &list);\n+\t\thit = grep_objects(&opt, &pathspec, &list);\n \t}\n \n \tif (use_threads)\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157949","messageId":"1292233616-27692-18-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 17/19] grep: use match_pathspec_depth() for cache/worktree grepping","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:54Z","receivedAt":"2010-12-13T09:46:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/grep.c |    3 ++-\n 1 files changed, 2 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 4179af8..1646e15 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -595,7 +595,8 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int\n \t\tstruct cache_entry *ce = active_cache[nr];\n \t\tif (!S_ISREG(ce->ce_mode))\n \t\t\tcontinue;\n-\t\tif (!pathspec_matches(pathspec->raw, ce->name, opt->max_depth))\n+\t\tif (!match_pathspec_depth(pathspec->raw, opt->max_depth,\n+\t\t\t\t\t  ce->name, ce_namelen(ce), 0, NULL))\n \t\t\tcontinue;\n \t\t/*\n \t\t * If CE_VALID is on, we assume worktree file and its cache entry\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157951","messageId":"1292233616-27692-19-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 18/19] grep: use preallocated buffer for grep_tree()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:55Z","receivedAt":"2010-12-13T09:46:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n because tree_entry_interesting() will need writable \"base\".\n\n builtin/grep.c |   59 +++++++++++++++++++++++++++++--------------------------\n 1 files changed, 31 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 1646e15..6bd5728 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -623,44 +623,38 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int\n \treturn hit;\n }\n \n+/*\n+ * \"base\" is a writable buffer where\n+ * - base[-tree_name_len..-1] contains tree_name\n+ * - base[0..baselen-1] contains tree base\n+ */\n static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n-\t\t     struct tree_desc *tree,\n-\t\t     const char *tree_name, const char *base)\n+\t\t     struct tree_desc *tree, char *base,\n+\t\t     int baselen, int tree_name_len)\n {\n-\tint len;\n \tint hit = 0;\n \tstruct name_entry entry;\n-\tchar *down;\n-\tint tn_len = strlen(tree_name);\n-\tstruct strbuf pathbuf;\n-\n-\tstrbuf_init(&pathbuf, PATH_MAX + tn_len);\n-\n-\tif (tn_len) {\n-\t\tstrbuf_add(&pathbuf, tree_name, tn_len);\n-\t\tstrbuf_addch(&pathbuf, ':');\n-\t\ttn_len = pathbuf.len;\n-\t}\n-\tstrbuf_addstr(&pathbuf, base);\n-\tlen = pathbuf.len;\n \n \twhile (tree_entry(tree, &entry)) {\n \t\tint te_len = tree_entry_len(entry.path, entry.sha1);\n-\t\tpathbuf.len = len;\n-\t\tstrbuf_add(&pathbuf, entry.path, te_len);\n+\t\tint len = baselen;\n+\t\tmemcpy(base + baselen, entry.path, te_len+1);\n \n-\t\tif (S_ISDIR(entry.mode))\n+\t\tlen += te_len;\n+\t\tif (S_ISDIR(entry.mode)) {\n \t\t\t/* Match \"abc/\" against pathspec to\n \t\t\t * decide if we want to descend into \"abc\"\n \t\t\t * directory.\n \t\t\t */\n-\t\t\tstrbuf_addch(&pathbuf, '/');\n+\t\t\tbase[len++] = '/';\n+\t\t\tbase[len] = 0;\n+\t\t}\n \n-\t\tdown = pathbuf.buf + tn_len;\n-\t\tif (!pathspec_matches(pathspec->raw, down, opt->max_depth))\n+\t\tif (!pathspec_matches(pathspec->raw, base, opt->max_depth))\n \t\t\t;\n-\t\telse if (S_ISREG(entry.mode))\n-\t\t\thit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);\n+\t\telse if (S_ISREG(entry.mode)) {\n+\t\t\thit |= grep_sha1(opt, entry.sha1, base-tree_name_len, tree_name_len);\n+\t\t}\n \t\telse if (S_ISDIR(entry.mode)) {\n \t\t\tenum object_type type;\n \t\t\tstruct tree_desc sub;\n@@ -672,13 +666,12 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n \t\t\t\tdie(\"unable to read tree (%s)\",\n \t\t\t\t    sha1_to_hex(entry.sha1));\n \t\t\tinit_tree_desc(&sub, data, size);\n-\t\t\thit |= grep_tree(opt, pathspec, &sub, tree_name, down);\n+\t\t\thit |= grep_tree(opt, pathspec, &sub, base, len, tree_name_len);\n \t\t\tfree(data);\n \t\t}\n \t\tif (hit && opt->status_only)\n \t\t\tbreak;\n \t}\n-\tstrbuf_release(&pathbuf);\n \treturn hit;\n }\n \n@@ -691,13 +684,23 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,\n \t\tstruct tree_desc tree;\n \t\tvoid *data;\n \t\tunsigned long size;\n-\t\tint hit;\n+\t\tint hit, len;\n+\t\tchar *base;\n+\n \t\tdata = read_object_with_reference(obj->sha1, tree_type,\n \t\t\t\t\t\t  &size, NULL);\n \t\tif (!data)\n \t\t\tdie(\"unable to read tree (%s)\", sha1_to_hex(obj->sha1));\n \t\tinit_tree_desc(&tree, data, size);\n-\t\thit = grep_tree(opt, pathspec, &tree, name, \"\");\n+\t\tlen = name ? strlen(name) : 0;\n+\t\tbase = xmalloc(PATH_MAX + len + 1);\n+\t\tif (len) {\n+\t\t\tmemcpy(base, name, len);\n+\t\t\tbase[len++] = ':';\n+\t\t}\n+\t\tbase[len] = 0;\n+\t\thit = grep_tree(opt, pathspec, &tree, base+len, 0, len);\n+\t\tfree(base);\n \t\tfree(data);\n \t\treturn hit;\n \t}\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157952","messageId":"1292233616-27692-20-git-send-email-pclouds@gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 19/19] grep: drop pathspec_matches() in favor of tree_entry_interesting()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-13T09:46:56Z","receivedAt":"2010-12-13T09:46:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/grep.c |  130 ++++++-------------------------------------------------\n 1 files changed, 15 insertions(+), 115 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 6bd5728..a56cdd6 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -333,106 +333,6 @@ static int grep_config(const char *var, const char *value, void *cb)\n \treturn 0;\n }\n \n-/*\n- * Return non-zero if max_depth is negative or path has no more then max_depth\n- * slashes.\n- */\n-static int accept_subdir(const char *path, int max_depth)\n-{\n-\tif (max_depth < 0)\n-\t\treturn 1;\n-\n-\twhile ((path = strchr(path, '/')) != NULL) {\n-\t\tmax_depth--;\n-\t\tif (max_depth < 0)\n-\t\t\treturn 0;\n-\t\tpath++;\n-\t}\n-\treturn 1;\n-}\n-\n-/*\n- * Return non-zero if name is a subdirectory of match and is not too deep.\n- */\n-static int is_subdir(const char *name, int namelen,\n-\t\tconst char *match, int matchlen, int max_depth)\n-{\n-\tif (matchlen > namelen || strncmp(name, match, matchlen))\n-\t\treturn 0;\n-\n-\tif (name[matchlen] == '\\0') /* exact match */\n-\t\treturn 1;\n-\n-\tif (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/')\n-\t\treturn accept_subdir(name + matchlen + 1, max_depth);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * git grep pathspecs are somewhat different from diff-tree pathspecs;\n- * pathname wildcards are allowed.\n- */\n-static int pathspec_matches(const char **paths, const char *name, int max_depth)\n-{\n-\tint namelen, i;\n-\tif (!paths || !*paths)\n-\t\treturn accept_subdir(name, max_depth);\n-\tnamelen = strlen(name);\n-\tfor (i = 0; paths[i]; i++) {\n-\t\tconst char *match = paths[i];\n-\t\tint matchlen = strlen(match);\n-\t\tconst char *cp, *meta;\n-\n-\t\tif (is_subdir(name, namelen, match, matchlen, max_depth))\n-\t\t\treturn 1;\n-\t\tif (!fnmatch(match, name, 0))\n-\t\t\treturn 1;\n-\t\tif (name[namelen-1] != '/')\n-\t\t\tcontinue;\n-\n-\t\t/* We are being asked if the directory (\"name\") is worth\n-\t\t * descending into.\n-\t\t *\n-\t\t * Find the longest leading directory name that does\n-\t\t * not have metacharacter in the pathspec; the name\n-\t\t * we are looking at must overlap with that directory.\n-\t\t */\n-\t\tfor (cp = match, meta = NULL; cp - match < matchlen; cp++) {\n-\t\t\tchar ch = *cp;\n-\t\t\tif (ch == '*' || ch == '[' || ch == '?') {\n-\t\t\t\tmeta = cp;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\t\tif (!meta)\n-\t\t\tmeta = cp; /* fully literal */\n-\n-\t\tif (namelen <= meta - match) {\n-\t\t\t/* Looking at \"Documentation/\" and\n-\t\t\t * the pattern says \"Documentation/howto/\", or\n-\t\t\t * \"Documentation/diff*.txt\".  The name we\n-\t\t\t * have should match prefix.\n-\t\t\t */\n-\t\t\tif (!memcmp(match, name, namelen))\n-\t\t\t\treturn 1;\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (meta - match < namelen) {\n-\t\t\t/* Looking at \"Documentation/howto/\" and\n-\t\t\t * the pattern says \"Documentation/h*\";\n-\t\t\t * match up to \"Do.../h\"; this avoids descending\n-\t\t\t * into \"Documentation/technical/\".\n-\t\t\t */\n-\t\t\tif (!memcmp(match, name, meta - match))\n-\t\t\t\treturn 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t}\n-\treturn 0;\n-}\n-\n static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)\n {\n \tvoid *data;\n@@ -632,39 +532,37 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n \t\t     struct tree_desc *tree, char *base,\n \t\t     int baselen, int tree_name_len)\n {\n-\tint hit = 0;\n+\tint hit = 0, matched = 0;\n \tstruct name_entry entry;\n \n \twhile (tree_entry(tree, &entry)) {\n \t\tint te_len = tree_entry_len(entry.path, entry.sha1);\n-\t\tint len = baselen;\n-\t\tmemcpy(base + baselen, entry.path, te_len+1);\n \n-\t\tlen += te_len;\n-\t\tif (S_ISDIR(entry.mode)) {\n-\t\t\t/* Match \"abc/\" against pathspec to\n-\t\t\t * decide if we want to descend into \"abc\"\n-\t\t\t * directory.\n-\t\t\t */\n-\t\t\tbase[len++] = '/';\n-\t\t\tbase[len] = 0;\n+\t\tif (matched != 2) {\n+\t\t\tmatched = tree_entry_interesting(&entry, base, baselen, pathspec);\n+\t\t\tif (matched == -1)\n+\t\t\t\tbreak; /* no more matches */\n+\t\t\tif (!matched)\n+\t\t\t\tcontinue;\n \t\t}\n \n-\t\tif (!pathspec_matches(pathspec->raw, base, opt->max_depth))\n-\t\t\t;\n-\t\telse if (S_ISREG(entry.mode)) {\n+\t\tmemcpy(base + baselen, entry.path, te_len+1);\n+\t\tif (S_ISREG(entry.mode))\n \t\t\thit |= grep_sha1(opt, entry.sha1, base-tree_name_len, tree_name_len);\n-\t\t}\n \t\telse if (S_ISDIR(entry.mode)) {\n \t\t\tenum object_type type;\n \t\t\tstruct tree_desc sub;\n \t\t\tvoid *data;\n \t\t\tunsigned long size;\n+\t\t\tint len = baselen + te_len;\n \n \t\t\tdata = lock_and_read_sha1_file(entry.sha1, &type, &size);\n \t\t\tif (!data)\n \t\t\t\tdie(\"unable to read tree (%s)\",\n \t\t\t\t    sha1_to_hex(entry.sha1));\n+\n+\t\t\tbase[len++] = '/';\n+\t\t\tbase[len] = 0;\n \t\t\tinit_tree_desc(&sub, data, size);\n \t\t\thit |= grep_tree(opt, pathspec, &sub, base, len, tree_name_len);\n \t\t\tfree(data);\n@@ -1069,6 +967,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\tpaths[1] = NULL;\n \t}\n \tinit_pathspec(&pathspec, paths);\n+\tpathspec.max_depth = opt.max_depth;\n+\tpathspec.recursive = 1;\n \n \tif (show_in_pager && (cached || list.nr))\n \t\tdie(\"--open-files-in-pager only works on the worktree\");\n-- \n1.7.3.3.476.g10a82\n"},{"id":"157974","messageId":"AANLkTim+6d+XZ7j4pxE+WwBCsAvcZfN2qYu4=1rs6AAn@mail.gmail.com","threadId":"26036","inReplyTo":"1292233616-27692-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 01/19] Add struct pathspec","fromName":"Thiago Farina","fromEmail":"tfransosi@gmail.com","sentAt":"2010-12-13T17:31:28Z","receivedAt":"2010-12-13T17:31:28Z","isPatch":true,"sender":{"key":"tfransosi@gmail.com","avatar":"https://avatars.githubusercontent.com/u/970071?v=4"},"body":"2010/12/13 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n> This struct for now is just a wrapper for the current pathspec form:\n> const char **. It is intended to be extended with more useful\n> pathspec-related information over time.\n>\n> The data structure for passing pathspec around remains const char **,\n> struct pathspec will be initialized locally to be used and destroyed.\n> Hopefully all pathspec related code will be gradually migrated to pass\n> this struct instead.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  cache.h |    7 +++++++\n>  dir.c   |   18 ++++++++++++++++++\n>  2 files changed, 25 insertions(+), 0 deletions(-)\n>\n> diff --git a/cache.h b/cache.h\n> index 2ef2fa3..3330769 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -493,6 +493,13 @@ extern int index_name_is_other(const struct index_state *, const char *, int);\n>  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);\n>  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);\n>\n> +struct pathspec {\n> +       const char **raw; /* get_pathspec() result, not freed by free_pathspec() */\n> +       int nr;\n> +};\n> +\n> +extern int init_pathspec(struct pathspec *, const char **);\n> +extern void free_pathspec(struct pathspec *);\n>  extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);\n>  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);\n>  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);\n> diff --git a/dir.c b/dir.c\n> index 133f472..205adc4 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -1071,3 +1071,21 @@ int remove_path(const char *name)\n>        return 0;\n>  }\n>\n> +int init_pathspec(struct pathspec *pathspec, const char **paths)\n> +{\n> +       const char **p = paths;\n> +\n> +       memset(pathspec, 0, sizeof(*pathspec));\n> +       if (!p)\n> +               return 0;\n> +       while (*p)\n> +               p++;\n> +       pathspec->raw = paths;\n> +       pathspec->nr = p - paths;\n> +       return 0;\n> +}\n> +\n> +void free_pathspec(struct pathspec *pathspec)\n> +{\n> +       ; /* do nothing */\n\nOf curse this ; here is not necessary :)\n"},{"id":"157976","messageId":"7vr5dl35os.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"1292233616-27692-9-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 08/19] pathspec: mark wildcard pathspecs from the beginning","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T18:09:55Z","receivedAt":"2010-12-13T18:09:55Z","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> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> diff --git a/dir.c b/dir.c\n> index 646c79f..0987d0c 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -1089,8 +1089,17 @@ int init_pathspec(struct pathspec *pathspec, const char **paths)\n>  \tpathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr);\n>  \tfor (i = 0; i < pathspec->nr; i++) {\n>  \t\tstruct pathspec_item *item = pathspec->items+i;\n> -\n> -\t\titem->len = strlen(paths[i]);\n> +\t\tconst char *path = paths[i];\n> +\n> +\t\titem->len = strlen(path);\n> +\t\titem->has_wildcard = !no_wildcard(path);\n> +\t\tif (item->has_wildcard) {\n> +\t\t\tpathspec->has_wildcard = 1;\n> +\t\t\titem->prefix_len = 0;\n> +\t\t\twhile (item->prefix_len < item->len &&\n> +\t\t\t       strchr(\"*?[{\\\\\", path[item->prefix_len]) == NULL)\n> +\t\t\t\titem->prefix_len++;\n> +\t\t}\n\nWould it make sense to use strcspn(3) here?\n\n>  \t}\n>  \treturn 0;\n>  }\n> -- \n> 1.7.3.3.476.g10a82\n"},{"id":"157977","messageId":"7vlj3t35ol.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"1292233616-27692-10-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 09/19] tree-diff.c: reserve space in \"base\" for pathname concatenation","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T18:10:02Z","receivedAt":"2010-12-13T18:10:02Z","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> This patch make sure that \"base\" parameter is writable. The callees\n> are free to modify it as long as base remains the same before\n> entering and after leaving the callee.\n>\n> This avoids quite a bit of malloc and memcpy().\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nI like what I see in this patch in general, but there is nothing that\nguarantees that you are \"reserving\" enough space.  Doesn't the buffer\noverflow with deep enough trees?\n"},{"id":"157978","messageId":"7vfwu135og.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"1292233616-27692-11-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 10/19] tree_entry_interesting(): factor out most matching logic","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T18:10:07Z","receivedAt":"2010-12-13T18:10:07Z","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> diff --git a/tree-walk.c b/tree-walk.c\n> index 01168ea..40a4657 100644\n> --- a/tree-walk.c\n> +++ b/tree-walk.c\n> @@ -481,88 +566,21 @@ int tree_entry_interesting(const struct name_entry *entry,\n>  \tfor (i = 0; i < ps->nr; i++) {\n>  \t\tconst char *match = ps->raw[i];\n>  \t\tint matchlen = ps->items[i].len;\n> -\t\tint m = -1; /* signals that we haven't called strncmp() */\n>  \n>  \t\tif (baselen >= matchlen) {\n> -\t\t\t/* If it doesn't match, move along... */\n\nWhy lose this comment?  That is exactly what this simplified part is\nabout.  If match-dir-prefix says it doesn't match, we move along.\n\nKeep this comment here, and drop the one you copied to the beginning of\nmatch-dir-prefix, which is not about \"moving along\" anymore.  The function\nis about deciding if they match or not.\n\n> -\t\t\tif (strncmp(base, match, matchlen))\n> +\t\t\tif (!match_dir_prefix(base, baselen, match, matchlen))\n> +\t\t\t\t/* Just a random prefix match */\n>  \t\t\t\tcontinue;\n\nThis comment no longer holds true, even though the same comment at the end\nof match-dir-prefix function is correct.  This should probably say \"does\nnot match\", but that is something you already said.\n\nOther than that, this is a very nice refactoring.\n"},{"id":"157980","messageId":"7vaak935ob.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"1292233616-27692-13-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 12/19] tree_entry_interesting(): support wildcard matching","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T18:10:12Z","receivedAt":"2010-12-13T18:10: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> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  tree-walk.c |   33 ++++++++++++++++++++++++++++++---\n>  tree-walk.h |    2 +-\n>  2 files changed, 31 insertions(+), 4 deletions(-)\n\n;-)  Looks almost too easy.\n\n> diff --git a/tree-walk.c b/tree-walk.c\n> index d28de30..b5ad42b 100644\n> --- a/tree-walk.c\n> +++ b/tree-walk.c\n> @@ -596,6 +598,31 @@ int tree_entry_interesting(const struct name_entry *entry,\n>  \t\t\t\t\t&never_interesting))\n>  \t\t\t\treturn 1;\n>  \t\t}\n> +\n> +match_wildcards:\n> +\t\t/*\n> +\t\t * Concatenate base and entry->path into one and do\n> +\t\t * fnmatch() on it.\n> +\t\t */\n> +\n> +\t\tif (!item->has_wildcard)\n> +\t\t\tcontinue;\n\nI think the comment comes after this if--continue.\n\n> +\t\tnever_interesting = 0;\n\nWhen we have wildcard we would want to disable the never-interesting\noptimization, but I wonder if doing so only when we do not have exact hit\nis what we want.  If a sick person had a path \"a?b\" tracked, and asked to\nmatch a pathspec \"a?b\", don't we still want to say \"'a?b' of course\nmatches, but 'a1b' ('1' comes earlier than '?' in the sort order) and\n'aAb' ('A' comes later) also match\"?\n"},{"id":"157979","messageId":"7v4oah35o6.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"1292233616-27692-14-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 13/19] tree_entry_interesting(): optimize fnmatch when base is matched","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T18:10:17Z","receivedAt":"2010-12-13T18:10:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Looks good.\n"},{"id":"157982","messageId":"7vwrnd1r2s.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"1292233616-27692-12-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 11/19] tree_entry_interesting: support depth limit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T18:10:51Z","receivedAt":"2010-12-13T18:10:51Z","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> diff --git a/dir.c b/dir.c\n> index 0987d0c..bb5076c 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -71,6 +71,21 @@ int fill_directory(struct dir_struct *dir, const char **pathspec)\n>  \treturn len;\n>  }\n>  \n> +int within_depth(const char *name, int namelen,\n> +\t\t\tint depth, int max_depth)\n> +{\n> +\tconst char *cp = name, *cpe = name + namelen;\n> +\n> +\twhile (cp < cpe) {\n> +\t\tif (*cp++ != '/')\n> +\t\t\tcontinue;\n> +\t\tdepth++;\n> +\t\tif (depth > max_depth)\n> +\t\t\treturn 0;\n> +\t}\n> +\treturn 1;\n> +}\n\nMakes me almost suspect that it may make more sense to keep track of the\n\"depth\" in a similar way as \"base\" and \"baselen\" as \"traversal state\" on\nthe side of the caller so that you do not have to scan the string for\nslashes over and over again.  But given the codeflow, doing so might make\nthe result look too ugly, so I won't recommend that without thinking,\nthough.\n\n> diff --git a/tree-walk.c b/tree-walk.c\n> index 40a4657..d28de30 100644\n> --- a/tree-walk.c\n> +++ b/tree-walk.c\n> @@ -558,9 +559,17 @@ int tree_entry_interesting(const struct name_entry *entry,\n>  \tint pathlen;\n>  \tint never_interesting = -1;\n>  \n> -\tif (!ps || !ps->nr)\n> +\tif (!ps)\n>  \t\treturn 1;\n>  \n> +\tif (!ps->nr) {\n> +\t\tif (!ps->recursive || ps->max_depth == -1)\n> +\t\t\treturn 1;\n> +\t\treturn !!within_depth(base, baselen,\n> +\t\t\t\t      !!S_ISDIR(entry->mode),\n> +\t\t\t\t      ps->max_depth);\n> +\t}\n\nThis gives different behaviour to between callers that give you NULL as\npathspec and callers that give you a pathspec with zero elements.  Is this\nintended?  What is the use case (iow, what does an end user give from the\ncommand line to experience this difference)?\n\n> @@ -571,7 +580,13 @@ int tree_entry_interesting(const struct name_entry *entry,\n>  \t\t\tif (!match_dir_prefix(base, baselen, match, matchlen))\n>  \t\t\t\t/* Just a random prefix match */\n>  \t\t\t\tcontinue;\n> -\t\t\treturn 2;\n> +\n> +\t\t\tif (!ps->recursive || ps->max_depth == -1)\n> +\t\t\t\treturn 2;\n> +\n> +\t\t\treturn !!within_depth(base+matchlen+1, baselen-matchlen-1,\n> +\t\t\t\t\t      !!S_ISDIR(entry->mode),\n> +\t\t\t\t\t      ps->max_depth);\n>  \t\t}\n\nIf two pathspecs that overlap with each other (e.g. \"Documentation/\" and\n\"Documentation/technical\") are given, and if the shorter one comes before\nthe longer one in ps[], wouldn't this give you an unexpected result?  When\ninspecting \"Documentation/technical/api/foo.txt\" with depth limit of 2, if\nyou didn't have \"Documentation/\" pathspec, you count \"api/foo.txt\"\nrelative to \"Documentation/technical\", declare that the path is within\nlimit, and show it.  But if you have \"Documentation/\" in ps[], you look at\nit, decide \"technical/api/foo.txt\" is too deep and return false without\neven looking at \"Documentation/technical\" that may appear later in ps[],\nno?\n"},{"id":"157985","messageId":"7vhbeh1oru.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"1292233616-27692-5-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 04/19] Convert struct diff_options to use struct pathspec","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T19:00:37Z","receivedAt":"2010-12-13T19:00:37Z","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> diff --git a/diff-lib.c b/diff-lib.c\n> index 392ce2b..3b809f2 100644\n> --- a/diff-lib.c\n> +++ b/diff-lib.c\n> @@ -501,7 +501,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)\n>  \tactive_nr = dst - active_cache;\n>  \n>  \tinit_revisions(&revs, NULL);\n> -\trevs.prune_data = opt->paths;\n> +\trevs.prune_data = opt->pathspec.raw;\n>  \ttree = parse_tree_indirect(tree_sha1);\n>  \tif (!tree)\n>  \t\tdie(\"bad tree object %s\", sha1_to_hex(tree_sha1));\n\nHopefully the prune_data will become opt->pathspec not \"raw\" and use your\ngeneralied/unified pathspec matching code in later patches in the series,\nyes?\n\nOther than that (no, \"including that\", really), looks nicely done.\n"},{"id":"157986","messageId":"7vbp4p1o9l.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"1292233616-27692-6-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 05/19] tree_entry_interesting(): remove dependency on struct diff_options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T19:11:34Z","receivedAt":"2010-12-13T19:11:34Z","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> This function can be potentially used in more places than just\n> tree-diff.c. \"struct diff_options\" does not make much sense outside\n> diff_tree_sha1().\n>\n> While removing the use of diff_options, it also removes\n> tree_entry_extract() call, which means S_ISDIR() uses the entry->mode\n> directly, without being filtered by canon_mode() (called internally\n> inside tree_entry_extract)\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nThe patch looks good, but the second paragraph above sounded unnecessarily\nalarming and I had to read it three times to make sure nothing fishy is\ngoing on ;-).\n\nIt bypasses tree-entry-extract call and uses fields of \"entry\" directly.\nWith the change, entry->mode is used without first getting normalized with\ncanon_mode(), but the only use of the mode information in this function is\nto check the type of the entry by giving it to S_ISDIR() macro, and the\nresult does not change with or without canon_mode(), so it is Ok.\n"},{"id":"157989","messageId":"7vzks9zd3f.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"1292233616-27692-16-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 15/19] pathspec: add match_pathspec_depth()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T19:28:52Z","receivedAt":"2010-12-13T19:28:52Z","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> match_pathspec_depth() is similar to match_pathspec() except that it\n> can take depth limit.\n>\n> In long term, match_pathspec() should be removed in favor of this\n> function.\n\nHmm, this strongly suggests that match_pathspec() should take \"const\nstruct pathspec *\" which already contains the necessary information and\nmore, including the depth limit, no?\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  dir.c |   45 +++++++++++++++++++++++++++++++++++++++++++++\n>  dir.h |    3 +++\n>  2 files changed, 48 insertions(+), 0 deletions(-)\n>\n> diff --git a/dir.c b/dir.c\n> index bb5076c..e12dbdd 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -169,6 +169,51 @@ int match_pathspec(const char **pathspec, const char *name, int namelen,\n>  \treturn retval;\n>  }\n>  \n> +int match_pathspec_depth(const char **pathspec, int max_depth,\n> +\t\t\t const char *name, int namelen,\n> +\t\t\t int prefix, char *seen)\n> +{\n> +\tint i, retval = 0;\n> +\n> +\tif (!pathspec) {\n> +\t\tif (max_depth == -1)\n> +\t\t\treturn MATCHED_RECURSIVELY;\n> +\n> +\t\tif (within_depth(name, namelen, 0, max_depth))\n> +\t\t\treturn MATCHED_EXACTLY;\n\nWhy the difference between _RECURSIVELY and _EXACTLY here?  If you have a\nfive-level deep project and give max-depth of 1000, shouldn't you get the\nsame result as you run the same command with unlimited depth?\n"},{"id":"157990","messageId":"7vsjy1zcyg.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"1292233616-27692-15-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 14/19] Convert ce_path_match() use to match_pathspec()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-13T19:31:51Z","receivedAt":"2010-12-13T19:31:51Z","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> Previously ce_path_match() is used together with tree_entry_interesting().\n> Both do not support wildcards. tree_entry_interesting() understands\n> wildcards now, so it's time to teach ce_path_match() to do the same.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nThis function is called from both Porcelains (e.g. \"git diff\" via\ndiff-lib.c and \"git status\" via wt-status.c) and plumbing commands\n(e.g. \"git update-index\"), and we are changing the semantics in a big way,\neven though it is a huge improvement.\n\nI am fairly excited with the progress of this series.  When it gets merged\nto 'master', we will be declaring 1.8.0 ;-)\n\nI imagine that eventually ce_path_match() will also take \"struct pathspec *\"\nnot \"const char **\" when the series is completed, yes?  Or there is no\nreal need for that?\n\n>  read-cache.c |   20 +-------------------\n>  1 files changed, 1 insertions(+), 19 deletions(-)\n>\n> diff --git a/read-cache.c b/read-cache.c\n> index 1f42473..cbabd8b 100644\n> --- a/read-cache.c\n> +++ b/read-cache.c\n> @@ -685,28 +685,10 @@ int ce_same_name(struct cache_entry *a, struct cache_entry *b)\n>  \n>  int ce_path_match(const struct cache_entry *ce, const char **pathspec)\n>  {\n> -\tconst char *match, *name;\n> -\tint len;\n> -\n>  \tif (!pathspec)\n>  \t\treturn 1;\n>  \n> -\tlen = ce_namelen(ce);\n> -\tname = ce->name;\n> -\twhile ((match = *pathspec++) != NULL) {\n> -\t\tint matchlen = strlen(match);\n> -\t\tif (matchlen > len)\n> -\t\t\tcontinue;\n> -\t\tif (memcmp(name, match, matchlen))\n> -\t\t\tcontinue;\n> -\t\tif (matchlen && name[matchlen-1] == '/')\n> -\t\t\treturn 1;\n> -\t\tif (name[matchlen] == '/' || !name[matchlen])\n> -\t\t\treturn 1;\n> -\t\tif (!matchlen)\n> -\t\t\treturn 1;\n> -\t}\n> -\treturn 0;\n> +\treturn match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL);\n>  }\n>  \n>  /*\n> -- \n> 1.7.3.3.476.g10a82\n"},{"id":"158028","messageId":"AANLkTi=5QgkBd434=Z7MCA_ZNZCXVyXJYh_fd1A+B2ue@mail.gmail.com","threadId":"26036","inReplyTo":"7vlj3t35ol.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 09/19] tree-diff.c: reserve space in \"base\" for pathname concatenation","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-14T05:00:11Z","receivedAt":"2010-12-14T05:00:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/12/14 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> This patch make sure that \"base\" parameter is writable. The callees\n>> are free to modify it as long as base remains the same before\n>> entering and after leaving the callee.\n>>\n>> This avoids quite a bit of malloc and memcpy().\n>>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>\n> I like what I see in this patch in general, but there is nothing that\n> guarantees that you are \"reserving\" enough space.  Doesn't the buffer\n> overflow with deep enough trees?\n\nAll paths should not exceed PATH_MAX, right? That's my assumption. If\nit's wrong, then, well.. we need another way.\n-- \nDuy\n"},{"id":"158029","messageId":"AANLkTim_SfWQ4OTsj0Are8kJGbiOLoj1wTa92qhb0KJA@mail.gmail.com","threadId":"26036","inReplyTo":"7vhbeh1oru.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 04/19] Convert struct diff_options to use struct pathspec","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-14T05:02:39Z","receivedAt":"2010-12-14T05:02:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/12/14 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n>\n>> diff --git a/diff-lib.c b/diff-lib.c\n>> index 392ce2b..3b809f2 100644\n>> --- a/diff-lib.c\n>> +++ b/diff-lib.c\n>> @@ -501,7 +501,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)\n>>       active_nr = dst - active_cache;\n>>\n>>       init_revisions(&revs, NULL);\n>> -     revs.prune_data = opt->paths;\n>> +     revs.prune_data = opt->pathspec.raw;\n>>       tree = parse_tree_indirect(tree_sha1);\n>>       if (!tree)\n>>               die(\"bad tree object %s\", sha1_to_hex(tree_sha1));\n>\n> Hopefully the prune_data will become opt->pathspec not \"raw\" and use your\n> generalied/unified pathspec matching code in later patches in the series,\n> yes?\n\nYes. But that needs closer look. \"prune_data\" is void* and casted in\nmany places. Compiler won't catch mistyping for us.\n-- \nDuy\n"},{"id":"158030","messageId":"AANLkTikmkr8aSH_tmRJJajHSvw81YHQtJsZ4RqNd+Hx=@mail.gmail.com","threadId":"26036","inReplyTo":"7vzks9zd3f.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 15/19] pathspec: add match_pathspec_depth()","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-14T05:07:12Z","receivedAt":"2010-12-14T05:07:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/12/14 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n>\n>> match_pathspec_depth() is similar to match_pathspec() except that it\n>> can take depth limit.\n>>\n>> In long term, match_pathspec() should be removed in favor of this\n>> function.\n>\n> Hmm, this strongly suggests that match_pathspec() should take \"const\n> struct pathspec *\" which already contains the necessary information and\n> more, including the depth limit, no?\n\nGood idea. Thanks!\n\n>> +int match_pathspec_depth(const char **pathspec, int max_depth,\n>> +                      const char *name, int namelen,\n>> +                      int prefix, char *seen)\n>> +{\n>> +     int i, retval = 0;\n>> +\n>> +     if (!pathspec) {\n>> +             if (max_depth == -1)\n>> +                     return MATCHED_RECURSIVELY;\n>> +\n>> +             if (within_depth(name, namelen, 0, max_depth))\n>> +                     return MATCHED_EXACTLY;\n>\n> Why the difference between _RECURSIVELY and _EXACTLY here?  If you have a\n> five-level deep project and give max-depth of 1000, shouldn't you get the\n> same result as you run the same command with unlimited depth?\n\nBut if max-depth is 5 and the project is 1000-level deep, it should\nreturn _EXACTLY, not _RECURSIVELY, right?\n-- \nDuy\n"},{"id":"158031","messageId":"7vvd2wvs0m.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"AANLkTi=5QgkBd434=Z7MCA_ZNZCXVyXJYh_fd1A+B2ue@mail.gmail.com","subject":"Re: [PATCH 09/19] tree-diff.c: reserve space in \"base\" for pathname concatenation","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-14T05:32:25Z","receivedAt":"2010-12-14T05:32:25Z","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> All paths should not exceed PATH_MAX, right?\n\nYour PATH_MAX may be a lot shorter than the PATH_MAX on the system I\ncreated my trees on that you are reading.  Besides, you can create\narbitrarily deep tree structure without busting the limit on the\nfilesystem by writing tree objects by hand.\n\nIn other words, all paths shouldn't exceed PATH_MAX, but the responsiblity\nto verify that is _yours_.\n"},{"id":"158034","messageId":"AANLkTintP4dMbJnterPBpWUJY77_JdmHJd+PWmKzzfuJ@mail.gmail.com","threadId":"26036","inReplyTo":"7vvd2wvs0m.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 09/19] tree-diff.c: reserve space in \"base\" for pathname concatenation","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-14T07:10:59Z","receivedAt":"2010-12-14T07:10:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 14, 2010 at 12:32 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> All paths should not exceed PATH_MAX, right?\n>\n> Your PATH_MAX may be a lot shorter than the PATH_MAX on the system I\n> created my trees on that you are reading.  Besides, you can create\n> arbitrarily deep tree structure without busting the limit on the\n> filesystem by writing tree objects by hand.\n>\n> In other words, all paths shouldn't exceed PATH_MAX, but the responsiblity\n> to verify that is _yours_.\n\nOK. I can check and die if any path exceeds PATH_MAX.\n-- \nDuy\n"},{"id":"158036","messageId":"4D071DAA.3070400@viscovery.net","threadId":"26036","inReplyTo":"7vvd2wvs0m.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 09/19] tree-diff.c: reserve space in \"base\" for pathname concatenation","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2010-12-14T07:32:58Z","receivedAt":"2010-12-14T07:32:58Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 12/14/2010 6:32, schrieb Junio C Hamano:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n> \n>> All paths should not exceed PATH_MAX, right?\n> \n> Your PATH_MAX may be a lot shorter than the PATH_MAX on the system I\n> created my trees on that you are reading.\n\nAnd that is not just gray theory: On Windows, PATH_MAX is whopping 260\ncharacters!\n\n-- Hannes\n"},{"id":"158037","messageId":"AANLkTimzmq8gUpXUZ801Q+pua_mZMHTJ6wYt_CwcTr3v@mail.gmail.com","threadId":"26036","inReplyTo":"4D071DAA.3070400@viscovery.net","subject":"Re: [PATCH 09/19] tree-diff.c: reserve space in \"base\" for pathname concatenation","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-14T07:43:48Z","receivedAt":"2010-12-14T07:43:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 14, 2010 at 2:32 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:\n> Am 12/14/2010 6:32, schrieb Junio C Hamano:\n>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>\n>>> All paths should not exceed PATH_MAX, right?\n>>\n>> Your PATH_MAX may be a lot shorter than the PATH_MAX on the system I\n>> created my trees on that you are reading.\n>\n> And that is not just gray theory: On Windows, PATH_MAX is whopping 260\n> characters!\n\nDoes that mean Windows won't accept any path longer than that, or\nPATH_MAX is defined just for fun?\n-- \nDuy\n"},{"id":"158038","messageId":"4D0728F0.9020807@viscovery.net","threadId":"26036","inReplyTo":"AANLkTimzmq8gUpXUZ801Q+pua_mZMHTJ6wYt_CwcTr3v@mail.gmail.com","subject":"Re: [PATCH 09/19] tree-diff.c: reserve space in \"base\" for pathname concatenation","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2010-12-14T08:21:04Z","receivedAt":"2010-12-14T08:21:04Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 12/14/2010 8:43, schrieb Nguyen Thai Ngoc Duy:\n> On Tue, Dec 14, 2010 at 2:32 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:\n>> Am 12/14/2010 6:32, schrieb Junio C Hamano:\n>>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>>\n>>>> All paths should not exceed PATH_MAX, right?\n>>>\n>>> Your PATH_MAX may be a lot shorter than the PATH_MAX on the system I\n>>> created my trees on that you are reading.\n>>\n>> And that is not just gray theory: On Windows, PATH_MAX is whopping 260\n>> characters!\n> \n> Does that mean Windows won't accept any path longer than that, or\n> PATH_MAX is defined just for fun?\n\nIt is the limit for path names that we can have without MAJOR extra\nefforts. It is not a limit implied by the file system.\n\n-- Hannes\n"},{"id":"158048","messageId":"AANLkTikR_AGCH-UaTo9pNtKQxq=Nc4=p8xaMkj22503u@mail.gmail.com","threadId":"26036","inReplyTo":"AANLkTim+6d+XZ7j4pxE+WwBCsAvcZfN2qYu4=1rs6AAn@mail.gmail.com","subject":"Re: [PATCH 01/19] Add struct pathspec","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-14T12:50:53Z","receivedAt":"2010-12-14T12:50:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/12/14 Thiago Farina <tfransosi@gmail.com>:\n>> +void free_pathspec(struct pathspec *pathspec)\n>> +{\n>> +       ; /* do nothing */\n>\n> Of curse this ; here is not necessary :)\n>\n\nWell, can't please everyone [1]. It's for clarity only.\nfree_pathspec() becomes real in the next patches.\n\n[1] http://mid.gmane.org/7vbp7ix20u.fsf@alter.siamese.dyndns.org\n-- \nDuy\n"},{"id":"158049","messageId":"AANLkTikXvx7-Q8B_dqG5mMHGK_Rw-dFaeQdXi0zW98SD@mail.gmail.com","threadId":"26036","inReplyTo":"4D0728F0.9020807@viscovery.net","subject":"Re: [PATCH 09/19] tree-diff.c: reserve space in \"base\" for pathname concatenation","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-14T13:01:21Z","receivedAt":"2010-12-14T13:01:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 14, 2010 at 3:21 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:\n> Am 12/14/2010 8:43, schrieb Nguyen Thai Ngoc Duy:\n>> On Tue, Dec 14, 2010 at 2:32 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:\n>>> Am 12/14/2010 6:32, schrieb Junio C Hamano:\n>>>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>>>\n>>>>> All paths should not exceed PATH_MAX, right?\n>>>>\n>>>> Your PATH_MAX may be a lot shorter than the PATH_MAX on the system I\n>>>> created my trees on that you are reading.\n>>>\n>>> And that is not just gray theory: On Windows, PATH_MAX is whopping 260\n>>> characters!\n>>\n>> Does that mean Windows won't accept any path longer than that, or\n>> PATH_MAX is defined just for fun?\n>\n> It is the limit for path names that we can have without MAJOR extra\n> efforts. It is not a limit implied by the file system.\n\nJust googled around and found this [1]. I'm going with strbuf so it\ncan be reallocated if necessary. A bit tricky because \"base\" can be\nmoved, but I think it's worth it.\n\n[1] http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html\n-- \nDuy\n"},{"id":"158054","messageId":"AANLkTikkU1h3vYDGpf4h8-9-sSWeYqtnZh1ZfB3NU3Db@mail.gmail.com","threadId":"26036","inReplyTo":"7vwrnd1r2s.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 11/19] tree_entry_interesting: support depth limit","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-14T14:44:48Z","receivedAt":"2010-12-14T14:44:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/12/14 Junio C Hamano <gitster@pobox.com>:\n>> +int within_depth(const char *name, int namelen,\n>> +                     int depth, int max_depth)\n>> +{\n>> +     const char *cp = name, *cpe = name + namelen;\n>> +\n>> +     while (cp < cpe) {\n>> +             if (*cp++ != '/')\n>> +                     continue;\n>> +             depth++;\n>> +             if (depth > max_depth)\n>> +                     return 0;\n>> +     }\n>> +     return 1;\n>> +}\n>\n> Makes me almost suspect that it may make more sense to keep track of the\n> \"depth\" in a similar way as \"base\" and \"baselen\" as \"traversal state\" on\n> the side of the caller so that you do not have to scan the string for\n> slashes over and over again.  But given the codeflow, doing so might make\n> the result look too ugly, so I won't recommend that without thinking,\n> though.\n\nMoreover, this function is also used by match_pathspec_depth().\n\n>> -     if (!ps || !ps->nr)\n>> +     if (!ps)\n>>               return 1;\n>>\n>> +     if (!ps->nr) {\n>> +             if (!ps->recursive || ps->max_depth == -1)\n>> +                     return 1;\n>> +             return !!within_depth(base, baselen,\n>> +                                   !!S_ISDIR(entry->mode),\n>> +                                   ps->max_depth);\n>> +     }\n>\n> This gives different behaviour to between callers that give you NULL as\n> pathspec and callers that give you a pathspec with zero elements.  Is this\n> intended?  What is the use case (iow, what does an end user give from the\n> command line to experience this difference)?\n\nOld pathspec type's legacy. When pathspec is of \"const char **\", it\ncan be NULL. When struct pathspec is used, I don't think we need to\nsupport NULL pathspec. Will remove that \"if (!ps)\" clause.\n\n>> @@ -571,7 +580,13 @@ int tree_entry_interesting(const struct name_entry *entry,\n>>                       if (!match_dir_prefix(base, baselen, match, matchlen))\n>>                               /* Just a random prefix match */\n>>                               continue;\n>> -                     return 2;\n>> +\n>> +                     if (!ps->recursive || ps->max_depth == -1)\n>> +                             return 2;\n>> +\n>> +                     return !!within_depth(base+matchlen+1, baselen-matchlen-1,\n>> +                                           !!S_ISDIR(entry->mode),\n>> +                                           ps->max_depth);\n>>               }\n>\n> If two pathspecs that overlap with each other (e.g. \"Documentation/\" and\n> \"Documentation/technical\") are given, and if the shorter one comes before\n> the longer one in ps[], wouldn't this give you an unexpected result?  When\n> inspecting \"Documentation/technical/api/foo.txt\" with depth limit of 2, if\n> you didn't have \"Documentation/\" pathspec, you count \"api/foo.txt\"\n> relative to \"Documentation/technical\", declare that the path is within\n> limit, and show it.  But if you have \"Documentation/\" in ps[], you look at\n> it, decide \"technical/api/foo.txt\" is too deep and return false without\n> even looking at \"Documentation/technical\" that may appear later in ps[],\n> no?\n\nRight. Hmm.. grep's pathspec_matches() probably has this problem too.\n-- \nDuy\n"},{"id":"158055","messageId":"AANLkTimwFw+ZjqKzbL0yNEC3Pw8RMW9Yr1KKpPFOOa4u@mail.gmail.com","threadId":"26036","inReplyTo":"7vaak935ob.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 12/19] tree_entry_interesting(): support wildcard matching","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-14T15:04:36Z","receivedAt":"2010-12-14T15:04:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/12/14 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  tree-walk.c |   33 ++++++++++++++++++++++++++++++---\n>>  tree-walk.h |    2 +-\n>>  2 files changed, 31 insertions(+), 4 deletions(-)\n>\n> ;-)  Looks almost too easy.\n\nMuch better, yes. I went the wrong way and allowed wildcards in\ndirectory matching too. It turned tree_entry_interesting() into hell.\n\n>> +             never_interesting = 0;\n>\n> When we have wildcard we would want to disable the never-interesting\n> optimization, but I wonder if doing so only when we do not have exact hit\n> is what we want.  If a sick person had a path \"a?b\" tracked, and asked to\n> match a pathspec \"a?b\", don't we still want to say \"'a?b' of course\n> matches, but 'a1b' ('1' comes earlier than '?' in the sort order) and\n> 'aAb' ('A' comes later) also match\"?\n\nHeck, yeah. Easier for me though, just put this at top :-)\n\nint never_interesting = ps->has_wildcard ? 0 : -1;\n-- \nDuy\n"},{"id":"158056","messageId":"AANLkTikPKEPX48S=j7SU+T+6XkP1qKFgotvw42af3jRY@mail.gmail.com","threadId":"26036","inReplyTo":"7vsjy1zcyg.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 14/19] Convert ce_path_match() use to match_pathspec()","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-14T15:14:55Z","receivedAt":"2010-12-14T15:14:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/12/14 Junio C Hamano <gitster@pobox.com>:\n> I imagine that eventually ce_path_match() will also take \"struct pathspec *\"\n> not \"const char **\" when the series is completed, yes?  Or there is no\n> real need for that?\n\nBut I do have the patches to convert ce_path_match to struct\npathspec*. Hmm.. I did. Need to look into reflogs..\n-- \nDuy\n"},{"id":"158062","messageId":"7vk4jcuvnw.fsf@alter.siamese.dyndns.org","threadId":"26036","inReplyTo":"AANLkTikXvx7-Q8B_dqG5mMHGK_Rw-dFaeQdXi0zW98SD@mail.gmail.com","subject":"Re: [PATCH 09/19] tree-diff.c: reserve space in \"base\" for pathname concatenation","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-14T17:11:15Z","receivedAt":"2010-12-14T17:11:15Z","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> Just googled around and found this [1]. I'm going with strbuf so it\n> can be reallocated if necessary. A bit tricky because \"base\" can be\n> moved, but I think it's worth it.\n\nThanks---I was about to suggest using strbuf but you figured it out\nalready, which is good ;-).\n"}]}