|
13 | 13 | #include "tree-walk.h"
|
14 | 14 | #include "environment.h"
|
15 | 15 | #include "repository.h"
|
| 16 | +#include "dir.h" |
16 | 17 |
|
17 | 18 | /*
|
18 | 19 | * Some mode bits are also used internally for computations.
|
|
48 | 49 | free((x)); \
|
49 | 50 | } while(0)
|
50 | 51 |
|
| 52 | +/* Returns true if and only if "dir" is a leading directory of "path" */ |
| 53 | +static int is_dir_prefix(const char *path, const char *dir, int dirlen) |
| 54 | +{ |
| 55 | + return !strncmp(path, dir, dirlen) && |
| 56 | + (!path[dirlen] || path[dirlen] == '/'); |
| 57 | +} |
| 58 | + |
| 59 | +static int check_recursion_depth(const struct strbuf *name, |
| 60 | + const struct pathspec *ps, |
| 61 | + int max_depth) |
| 62 | +{ |
| 63 | + int i; |
| 64 | + |
| 65 | + if (!ps->nr) |
| 66 | + return within_depth(name->buf, name->len, 1, max_depth); |
| 67 | + |
| 68 | + /* |
| 69 | + * We look through the pathspecs in reverse-sorted order, because we |
| 70 | + * want to find the longest match first (e.g., "a/b" is better for |
| 71 | + * checking depth than "a/b/c"). |
| 72 | + */ |
| 73 | + for (i = ps->nr - 1; i >= 0; i--) { |
| 74 | + const struct pathspec_item *item = ps->items+i; |
| 75 | + |
| 76 | + /* |
| 77 | + * If the name to match is longer than the pathspec, then we |
| 78 | + * are only interested if the pathspec matches and we are |
| 79 | + * within the allowed depth. |
| 80 | + */ |
| 81 | + if (name->len >= item->len) { |
| 82 | + if (!is_dir_prefix(name->buf, item->match, item->len)) |
| 83 | + continue; |
| 84 | + return within_depth(name->buf + item->len, |
| 85 | + name->len - item->len, |
| 86 | + 1, max_depth); |
| 87 | + } |
| 88 | + |
| 89 | + /* |
| 90 | + * Otherwise, our name is shorter than the pathspec. We need to |
| 91 | + * check if it is a prefix of the pathspec; if so, we must |
| 92 | + * always recurse in order to process further (the resulting |
| 93 | + * paths we find might or might not match our pathspec, but we |
| 94 | + * cannot know until we recurse). |
| 95 | + */ |
| 96 | + if (is_dir_prefix(item->match, name->buf, name->len)) |
| 97 | + return 1; |
| 98 | + } |
| 99 | + return 0; |
| 100 | +} |
| 101 | + |
| 102 | +static int should_recurse(const struct strbuf *name, struct diff_options *opt) |
| 103 | +{ |
| 104 | + if (!opt->flags.recursive) |
| 105 | + return 0; |
| 106 | + if (!opt->max_depth_valid) |
| 107 | + return 1; |
| 108 | + |
| 109 | + /* |
| 110 | + * We catch this during diff_setup_done, but let's double-check |
| 111 | + * against any internal munging. |
| 112 | + */ |
| 113 | + if (opt->pathspec.has_wildcard) |
| 114 | + BUG("wildcard pathspecs are incompatible with max-depth"); |
| 115 | + |
| 116 | + return check_recursion_depth(name, &opt->pathspec, opt->max_depth); |
| 117 | +} |
| 118 | + |
51 | 119 | static void ll_diff_tree_paths(
|
52 | 120 | struct combine_diff_path ***tail, const struct object_id *oid,
|
53 | 121 | const struct object_id **parents_oid, int nparent,
|
@@ -170,9 +238,13 @@ static void emit_path(struct combine_diff_path ***tail,
|
170 | 238 | mode = 0;
|
171 | 239 | }
|
172 | 240 |
|
173 |
| - if (opt->flags.recursive && isdir) { |
174 |
| - recurse = 1; |
175 |
| - emitthis = opt->flags.tree_in_recursive; |
| 241 | + if (isdir) { |
| 242 | + strbuf_add(base, path, pathlen); |
| 243 | + if (should_recurse(base, opt)) { |
| 244 | + recurse = 1; |
| 245 | + emitthis = opt->flags.tree_in_recursive; |
| 246 | + } |
| 247 | + strbuf_setlen(base, old_baselen); |
176 | 248 | }
|
177 | 249 |
|
178 | 250 | if (emitthis) {
|
|
0 commit comments