Merge branch 'nd/struct-pathspec'
* nd/struct-pathspec: (22 commits) t6004: add pathspec globbing test for log family t7810: overlapping pathspecs and depth limit grep: drop pathspec_matches() in favor of tree_entry_interesting() grep: use writable strbuf from caller for grep_tree() grep: use match_pathspec_depth() for cache/worktree grepping grep: convert to use struct pathspec Convert ce_path_match() to use match_pathspec_depth() Convert ce_path_match() to use struct pathspec struct rev_info: convert prune_data to struct pathspec pathspec: add match_pathspec_depth() tree_entry_interesting(): optimize wildcard matching when base is matched tree_entry_interesting(): support wildcard matching tree_entry_interesting(): fix depth limit with overlapping pathspecs tree_entry_interesting(): support depth limit tree_entry_interesting(): refactor into separate smaller functions diff-tree: convert base+baselen to writable strbuf glossary: define pathspec Move tree_entry_interesting() to tree-walk.c and export it tree_entry_interesting(): remove dependency on struct diff_options Convert struct diff_options to use struct pathspec ...
This commit is contained in:
commit
d5c87a802d
@ -273,6 +273,29 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||
<<def_pack,pack>>, to assist in efficiently accessing the contents of a
|
||||
pack.
|
||||
|
||||
[[def_pathspec]]pathspec::
|
||||
Pattern used to specify paths.
|
||||
+
|
||||
Pathspecs are used on the command line of "git ls-files", "git
|
||||
ls-tree", "git grep", "git checkout", and many other commands to
|
||||
limit the scope of operations to some subset of the tree or
|
||||
worktree. See the documentation of each command for whether
|
||||
paths are relative to the current directory or toplevel. The
|
||||
pathspec syntax is as follows:
|
||||
|
||||
* any path matches itself
|
||||
* the pathspec up to the last slash represents a
|
||||
directory prefix. The scope of that pathspec is
|
||||
limited to that subtree.
|
||||
* the rest of the pathspec is a pattern for the remainder
|
||||
of the pathname. Paths relative to the directory
|
||||
prefix will be matched against that pattern using fnmatch(3);
|
||||
in particular, '*' and '?' _can_ match directory separators.
|
||||
+
|
||||
For example, Documentation/*.jpg will match all .jpg files
|
||||
in the Documentation subtree,
|
||||
including Documentation/chapter_1/figure_1.jpg.
|
||||
|
||||
[[def_parent]]parent::
|
||||
A <<def_commit_object,commit object>> contains a (possibly empty) list
|
||||
of the logical predecessor(s) in the line of development, i.e. its
|
||||
|
@ -86,7 +86,7 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
|
||||
struct rev_info rev;
|
||||
init_revisions(&rev, prefix);
|
||||
setup_revisions(0, NULL, &rev, NULL);
|
||||
rev.prune_data = pathspec;
|
||||
init_pathspec(&rev.prune_data, pathspec);
|
||||
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
rev.diffopt.format_callback = update_callback;
|
||||
data.flags = flags;
|
||||
|
@ -61,7 +61,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
|
||||
(rev.diffopt.output_format & DIFF_FORMAT_PATCH))
|
||||
rev.combine_merges = rev.dense_combined_merges = 1;
|
||||
|
||||
if (read_cache_preload(rev.diffopt.paths) < 0) {
|
||||
if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ static int builtin_diff_index(struct rev_info *revs,
|
||||
revs->max_count != -1 || revs->min_age != -1 ||
|
||||
revs->max_age != -1)
|
||||
usage(builtin_diff_usage);
|
||||
if (read_cache_preload(revs->diffopt.paths) < 0) {
|
||||
if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
@ -237,7 +237,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
|
||||
revs->combine_merges = revs->dense_combined_merges = 1;
|
||||
|
||||
setup_work_tree();
|
||||
if (read_cache_preload(revs->diffopt.paths) < 0) {
|
||||
if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
@ -374,14 +374,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
die("unhandled object '%s' given.", name);
|
||||
}
|
||||
if (rev.prune_data) {
|
||||
const char **pathspec = rev.prune_data;
|
||||
while (*pathspec) {
|
||||
if (!path)
|
||||
path = *pathspec;
|
||||
paths++;
|
||||
pathspec++;
|
||||
}
|
||||
if (rev.prune_data.nr) {
|
||||
if (!path)
|
||||
path = rev.prune_data.items[0].match;
|
||||
paths += rev.prune_data.nr;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -651,7 +651,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
||||
if (import_filename)
|
||||
import_marks(import_filename);
|
||||
|
||||
if (import_filename && revs.prune_data)
|
||||
if (import_filename && revs.prune_data.nr)
|
||||
full_tree = 1;
|
||||
|
||||
get_tags_and_duplicates(&revs.pending, &extra_refs);
|
||||
|
192
builtin/grep.c
192
builtin/grep.c
@ -329,106 +329,6 @@ static int grep_config(const char *var, const char *value, void *cb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return non-zero if max_depth is negative or path has no more then max_depth
|
||||
* slashes.
|
||||
*/
|
||||
static int accept_subdir(const char *path, int max_depth)
|
||||
{
|
||||
if (max_depth < 0)
|
||||
return 1;
|
||||
|
||||
while ((path = strchr(path, '/')) != NULL) {
|
||||
max_depth--;
|
||||
if (max_depth < 0)
|
||||
return 0;
|
||||
path++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return non-zero if name is a subdirectory of match and is not too deep.
|
||||
*/
|
||||
static int is_subdir(const char *name, int namelen,
|
||||
const char *match, int matchlen, int max_depth)
|
||||
{
|
||||
if (matchlen > namelen || strncmp(name, match, matchlen))
|
||||
return 0;
|
||||
|
||||
if (name[matchlen] == '\0') /* exact match */
|
||||
return 1;
|
||||
|
||||
if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/')
|
||||
return accept_subdir(name + matchlen + 1, max_depth);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* git grep pathspecs are somewhat different from diff-tree pathspecs;
|
||||
* pathname wildcards are allowed.
|
||||
*/
|
||||
static int pathspec_matches(const char **paths, const char *name, int max_depth)
|
||||
{
|
||||
int namelen, i;
|
||||
if (!paths || !*paths)
|
||||
return accept_subdir(name, max_depth);
|
||||
namelen = strlen(name);
|
||||
for (i = 0; paths[i]; i++) {
|
||||
const char *match = paths[i];
|
||||
int matchlen = strlen(match);
|
||||
const char *cp, *meta;
|
||||
|
||||
if (is_subdir(name, namelen, match, matchlen, max_depth))
|
||||
return 1;
|
||||
if (!fnmatch(match, name, 0))
|
||||
return 1;
|
||||
if (name[namelen-1] != '/')
|
||||
continue;
|
||||
|
||||
/* We are being asked if the directory ("name") is worth
|
||||
* descending into.
|
||||
*
|
||||
* Find the longest leading directory name that does
|
||||
* not have metacharacter in the pathspec; the name
|
||||
* we are looking at must overlap with that directory.
|
||||
*/
|
||||
for (cp = match, meta = NULL; cp - match < matchlen; cp++) {
|
||||
char ch = *cp;
|
||||
if (ch == '*' || ch == '[' || ch == '?') {
|
||||
meta = cp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!meta)
|
||||
meta = cp; /* fully literal */
|
||||
|
||||
if (namelen <= meta - match) {
|
||||
/* Looking at "Documentation/" and
|
||||
* the pattern says "Documentation/howto/", or
|
||||
* "Documentation/diff*.txt". The name we
|
||||
* have should match prefix.
|
||||
*/
|
||||
if (!memcmp(match, name, namelen))
|
||||
return 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (meta - match < namelen) {
|
||||
/* Looking at "Documentation/howto/" and
|
||||
* the pattern says "Documentation/h*";
|
||||
* match up to "Do.../h"; this avoids descending
|
||||
* into "Documentation/technical/".
|
||||
*/
|
||||
if (!memcmp(match, name, meta - match))
|
||||
return 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
|
||||
{
|
||||
void *data;
|
||||
@ -581,7 +481,7 @@ static void run_pager(struct grep_opt *opt, const char *prefix)
|
||||
free(argv);
|
||||
}
|
||||
|
||||
static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
|
||||
static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached)
|
||||
{
|
||||
int hit = 0;
|
||||
int nr;
|
||||
@ -591,7 +491,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
|
||||
struct cache_entry *ce = active_cache[nr];
|
||||
if (!S_ISREG(ce->ce_mode))
|
||||
continue;
|
||||
if (!pathspec_matches(paths, ce->name, opt->max_depth))
|
||||
if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
|
||||
continue;
|
||||
/*
|
||||
* If CE_VALID is on, we assume worktree file and its cache entry
|
||||
@ -618,44 +518,29 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
|
||||
return hit;
|
||||
}
|
||||
|
||||
static int grep_tree(struct grep_opt *opt, const char **paths,
|
||||
struct tree_desc *tree,
|
||||
const char *tree_name, const char *base)
|
||||
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
||||
struct tree_desc *tree, struct strbuf *base, int tn_len)
|
||||
{
|
||||
int len;
|
||||
int hit = 0;
|
||||
int hit = 0, matched = 0;
|
||||
struct name_entry entry;
|
||||
char *down;
|
||||
int tn_len = strlen(tree_name);
|
||||
struct strbuf pathbuf;
|
||||
|
||||
strbuf_init(&pathbuf, PATH_MAX + tn_len);
|
||||
|
||||
if (tn_len) {
|
||||
strbuf_add(&pathbuf, tree_name, tn_len);
|
||||
strbuf_addch(&pathbuf, ':');
|
||||
tn_len = pathbuf.len;
|
||||
}
|
||||
strbuf_addstr(&pathbuf, base);
|
||||
len = pathbuf.len;
|
||||
int old_baselen = base->len;
|
||||
|
||||
while (tree_entry(tree, &entry)) {
|
||||
int te_len = tree_entry_len(entry.path, entry.sha1);
|
||||
pathbuf.len = len;
|
||||
strbuf_add(&pathbuf, entry.path, te_len);
|
||||
|
||||
if (S_ISDIR(entry.mode))
|
||||
/* Match "abc/" against pathspec to
|
||||
* decide if we want to descend into "abc"
|
||||
* directory.
|
||||
*/
|
||||
strbuf_addch(&pathbuf, '/');
|
||||
if (matched != 2) {
|
||||
matched = tree_entry_interesting(&entry, base, tn_len, pathspec);
|
||||
if (matched == -1)
|
||||
break; /* no more matches */
|
||||
if (!matched)
|
||||
continue;
|
||||
}
|
||||
|
||||
down = pathbuf.buf + tn_len;
|
||||
if (!pathspec_matches(paths, down, opt->max_depth))
|
||||
;
|
||||
else if (S_ISREG(entry.mode))
|
||||
hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
|
||||
strbuf_add(base, entry.path, te_len);
|
||||
|
||||
if (S_ISREG(entry.mode)) {
|
||||
hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len);
|
||||
}
|
||||
else if (S_ISDIR(entry.mode)) {
|
||||
enum object_type type;
|
||||
struct tree_desc sub;
|
||||
@ -666,18 +551,21 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
|
||||
if (!data)
|
||||
die("unable to read tree (%s)",
|
||||
sha1_to_hex(entry.sha1));
|
||||
|
||||
strbuf_addch(base, '/');
|
||||
init_tree_desc(&sub, data, size);
|
||||
hit |= grep_tree(opt, paths, &sub, tree_name, down);
|
||||
hit |= grep_tree(opt, pathspec, &sub, base, tn_len);
|
||||
free(data);
|
||||
}
|
||||
strbuf_setlen(base, old_baselen);
|
||||
|
||||
if (hit && opt->status_only)
|
||||
break;
|
||||
}
|
||||
strbuf_release(&pathbuf);
|
||||
return hit;
|
||||
}
|
||||
|
||||
static int grep_object(struct grep_opt *opt, const char **paths,
|
||||
static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
|
||||
struct object *obj, const char *name)
|
||||
{
|
||||
if (obj->type == OBJ_BLOB)
|
||||
@ -686,20 +574,30 @@ static int grep_object(struct grep_opt *opt, const char **paths,
|
||||
struct tree_desc tree;
|
||||
void *data;
|
||||
unsigned long size;
|
||||
int hit;
|
||||
struct strbuf base;
|
||||
int hit, len;
|
||||
|
||||
data = read_object_with_reference(obj->sha1, tree_type,
|
||||
&size, NULL);
|
||||
if (!data)
|
||||
die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
|
||||
|
||||
len = name ? strlen(name) : 0;
|
||||
strbuf_init(&base, PATH_MAX + len + 1);
|
||||
if (len) {
|
||||
strbuf_add(&base, name, len);
|
||||
strbuf_addch(&base, ':');
|
||||
}
|
||||
init_tree_desc(&tree, data, size);
|
||||
hit = grep_tree(opt, paths, &tree, name, "");
|
||||
hit = grep_tree(opt, pathspec, &tree, &base, base.len);
|
||||
strbuf_release(&base);
|
||||
free(data);
|
||||
return hit;
|
||||
}
|
||||
die("unable to grep from object of type %s", typename(obj->type));
|
||||
}
|
||||
|
||||
static int grep_objects(struct grep_opt *opt, const char **paths,
|
||||
static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
|
||||
const struct object_array *list)
|
||||
{
|
||||
unsigned int i;
|
||||
@ -709,7 +607,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths,
|
||||
for (i = 0; i < nr; i++) {
|
||||
struct object *real_obj;
|
||||
real_obj = deref_tag(list->objects[i].item, NULL, 0);
|
||||
if (grep_object(opt, paths, real_obj, list->objects[i].name)) {
|
||||
if (grep_object(opt, pathspec, real_obj, list->objects[i].name)) {
|
||||
hit = 1;
|
||||
if (opt->status_only)
|
||||
break;
|
||||
@ -718,7 +616,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths,
|
||||
return hit;
|
||||
}
|
||||
|
||||
static int grep_directory(struct grep_opt *opt, const char **paths)
|
||||
static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec)
|
||||
{
|
||||
struct dir_struct dir;
|
||||
int i, hit = 0;
|
||||
@ -726,7 +624,7 @@ static int grep_directory(struct grep_opt *opt, const char **paths)
|
||||
memset(&dir, 0, sizeof(dir));
|
||||
setup_standard_excludes(&dir);
|
||||
|
||||
fill_directory(&dir, paths);
|
||||
fill_directory(&dir, pathspec->raw);
|
||||
for (i = 0; i < dir.nr; i++) {
|
||||
hit |= grep_file(opt, dir.entries[i]->name);
|
||||
if (hit && opt->status_only)
|
||||
@ -832,6 +730,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||
struct grep_opt opt;
|
||||
struct object_array list = OBJECT_ARRAY_INIT;
|
||||
const char **paths = NULL;
|
||||
struct pathspec pathspec;
|
||||
struct string_list path_list = STRING_LIST_INIT_NODUP;
|
||||
int i;
|
||||
int dummy;
|
||||
@ -1059,6 +958,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||
paths[0] = prefix;
|
||||
paths[1] = NULL;
|
||||
}
|
||||
init_pathspec(&pathspec, paths);
|
||||
pathspec.max_depth = opt.max_depth;
|
||||
pathspec.recursive = 1;
|
||||
|
||||
if (show_in_pager && (cached || list.nr))
|
||||
die("--open-files-in-pager only works on the worktree");
|
||||
@ -1089,16 +991,16 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||
die("--cached cannot be used with --no-index.");
|
||||
if (list.nr)
|
||||
die("--no-index cannot be used with revs.");
|
||||
hit = grep_directory(&opt, paths);
|
||||
hit = grep_directory(&opt, &pathspec);
|
||||
} else if (!list.nr) {
|
||||
if (!cached)
|
||||
setup_work_tree();
|
||||
|
||||
hit = grep_cache(&opt, paths, cached);
|
||||
hit = grep_cache(&opt, &pathspec, cached);
|
||||
} else {
|
||||
if (cached)
|
||||
die("both --cached and trees are given.");
|
||||
hit = grep_objects(&opt, paths, &list);
|
||||
hit = grep_objects(&opt, &pathspec, &list);
|
||||
}
|
||||
|
||||
if (use_threads)
|
||||
|
@ -89,7 +89,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
|
||||
rev->always_show_header = 0;
|
||||
if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
|
||||
rev->always_show_header = 0;
|
||||
if (rev->diffopt.nr_paths != 1)
|
||||
if (rev->diffopt.pathspec.nr != 1)
|
||||
usage("git logs can only follow renames on one pathname at a time");
|
||||
}
|
||||
for (i = 1; i < argc; i++) {
|
||||
|
@ -546,7 +546,10 @@ static int do_reupdate(int ac, const char **av,
|
||||
*/
|
||||
int pos;
|
||||
int has_head = 1;
|
||||
const char **pathspec = get_pathspec(prefix, av + 1);
|
||||
const char **paths = get_pathspec(prefix, av + 1);
|
||||
struct pathspec pathspec;
|
||||
|
||||
init_pathspec(&pathspec, paths);
|
||||
|
||||
if (read_ref("HEAD", head_sha1))
|
||||
/* If there is no HEAD, that means it is an initial
|
||||
@ -559,7 +562,7 @@ static int do_reupdate(int ac, const char **av,
|
||||
struct cache_entry *old = NULL;
|
||||
int save_nr;
|
||||
|
||||
if (ce_stage(ce) || !ce_path_match(ce, pathspec))
|
||||
if (ce_stage(ce) || !ce_path_match(ce, &pathspec))
|
||||
continue;
|
||||
if (has_head)
|
||||
old = read_one_ent(NULL, head_sha1,
|
||||
@ -578,6 +581,7 @@ static int do_reupdate(int ac, const char **av,
|
||||
if (save_nr != active_nr)
|
||||
goto redo;
|
||||
}
|
||||
free_pathspec(&pathspec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
17
cache.h
17
cache.h
@ -500,7 +500,22 @@ extern int index_name_is_other(const struct index_state *, const char *, int);
|
||||
extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
|
||||
extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
|
||||
|
||||
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
|
||||
struct pathspec {
|
||||
const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
|
||||
int nr;
|
||||
int has_wildcard:1;
|
||||
int recursive:1;
|
||||
int max_depth;
|
||||
struct pathspec_item {
|
||||
const char *match;
|
||||
int len;
|
||||
int has_wildcard:1;
|
||||
} *items;
|
||||
};
|
||||
|
||||
extern int init_pathspec(struct pathspec *, const char **);
|
||||
extern void free_pathspec(struct pathspec *);
|
||||
extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
|
||||
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
|
||||
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
|
||||
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
|
||||
|
@ -106,7 +106,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
|
||||
DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES))
|
||||
break;
|
||||
|
||||
if (!ce_path_match(ce, revs->prune_data))
|
||||
if (!ce_path_match(ce, &revs->prune_data))
|
||||
continue;
|
||||
|
||||
if (ce_stage(ce)) {
|
||||
@ -427,7 +427,7 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
|
||||
if (tree == o->df_conflict_entry)
|
||||
tree = NULL;
|
||||
|
||||
if (ce_path_match(idx ? idx : tree, revs->prune_data))
|
||||
if (ce_path_match(idx ? idx : tree, &revs->prune_data))
|
||||
do_oneway_diff(o, idx, tree);
|
||||
|
||||
return 0;
|
||||
@ -501,7 +501,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
|
||||
active_nr = dst - active_cache;
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
revs.prune_data = opt->paths;
|
||||
init_pathspec(&revs.prune_data, opt->pathspec.raw);
|
||||
tree = parse_tree_indirect(tree_sha1);
|
||||
if (!tree)
|
||||
die("bad tree object %s", sha1_to_hex(tree_sha1));
|
||||
|
@ -231,8 +231,9 @@ void diff_no_index(struct rev_info *revs,
|
||||
|
||||
if (prefix) {
|
||||
int len = strlen(prefix);
|
||||
const char *paths[3];
|
||||
memset(paths, 0, sizeof(paths));
|
||||
|
||||
revs->diffopt.paths = xcalloc(2, sizeof(char *));
|
||||
for (i = 0; i < 2; i++) {
|
||||
const char *p = argv[argc - 2 + i];
|
||||
/*
|
||||
@ -242,12 +243,12 @@ void diff_no_index(struct rev_info *revs,
|
||||
p = (strcmp(p, "-")
|
||||
? xstrdup(prefix_filename(prefix, len, p))
|
||||
: p);
|
||||
revs->diffopt.paths[i] = p;
|
||||
paths[i] = p;
|
||||
}
|
||||
diff_tree_setup_paths(paths, &revs->diffopt);
|
||||
}
|
||||
else
|
||||
revs->diffopt.paths = argv + argc - 2;
|
||||
revs->diffopt.nr_paths = 2;
|
||||
diff_tree_setup_paths(argv + argc - 2, &revs->diffopt);
|
||||
revs->diffopt.skip_stat_unmatch = 1;
|
||||
if (!revs->diffopt.output_format)
|
||||
revs->diffopt.output_format = DIFF_FORMAT_PATCH;
|
||||
@ -259,8 +260,8 @@ void diff_no_index(struct rev_info *revs,
|
||||
if (diff_setup_done(&revs->diffopt) < 0)
|
||||
die("diff_setup_done failed");
|
||||
|
||||
if (queue_diff(&revs->diffopt, revs->diffopt.paths[0],
|
||||
revs->diffopt.paths[1]))
|
||||
if (queue_diff(&revs->diffopt, revs->diffopt.pathspec.raw[0],
|
||||
revs->diffopt.pathspec.raw[1]))
|
||||
exit(1);
|
||||
diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
|
||||
diffcore_std(&revs->diffopt);
|
||||
|
4
diff.h
4
diff.h
@ -133,9 +133,7 @@ struct diff_options {
|
||||
FILE *file;
|
||||
int close_file;
|
||||
|
||||
int nr_paths;
|
||||
const char **paths;
|
||||
int *pathlens;
|
||||
struct pathspec pathspec;
|
||||
change_fn_t change;
|
||||
add_remove_fn_t add_remove;
|
||||
diff_format_fn_t format_callback;
|
||||
|
151
dir.c
151
dir.c
@ -87,6 +87,21 @@ int fill_directory(struct dir_struct *dir, const char **pathspec)
|
||||
return len;
|
||||
}
|
||||
|
||||
int within_depth(const char *name, int namelen,
|
||||
int depth, int max_depth)
|
||||
{
|
||||
const char *cp = name, *cpe = name + namelen;
|
||||
|
||||
while (cp < cpe) {
|
||||
if (*cp++ != '/')
|
||||
continue;
|
||||
depth++;
|
||||
if (depth > max_depth)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does 'match' match the given name?
|
||||
* A match is found if
|
||||
@ -184,6 +199,95 @@ int match_pathspec(const char **pathspec, const char *name, int namelen,
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does 'match' match the given name?
|
||||
* A match is found if
|
||||
*
|
||||
* (1) the 'match' string is leading directory of 'name', or
|
||||
* (2) the 'match' string is a wildcard and matches 'name', or
|
||||
* (3) the 'match' string is exactly the same as 'name'.
|
||||
*
|
||||
* and the return value tells which case it was.
|
||||
*
|
||||
* It returns 0 when there is no match.
|
||||
*/
|
||||
static int match_pathspec_item(const struct pathspec_item *item, int prefix,
|
||||
const char *name, int namelen)
|
||||
{
|
||||
/* name/namelen has prefix cut off by caller */
|
||||
const char *match = item->match + prefix;
|
||||
int matchlen = item->len - prefix;
|
||||
|
||||
/* If the match was just the prefix, we matched */
|
||||
if (!*match)
|
||||
return MATCHED_RECURSIVELY;
|
||||
|
||||
if (matchlen <= namelen && !strncmp(match, name, matchlen)) {
|
||||
if (matchlen == namelen)
|
||||
return MATCHED_EXACTLY;
|
||||
|
||||
if (match[matchlen-1] == '/' || name[matchlen] == '/')
|
||||
return MATCHED_RECURSIVELY;
|
||||
}
|
||||
|
||||
if (item->has_wildcard && !fnmatch(match, name, 0))
|
||||
return MATCHED_FNMATCH;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a name and a list of pathspecs, see if the name matches
|
||||
* any of the pathspecs. The caller is also interested in seeing
|
||||
* all pathspec matches some names it calls this function with
|
||||
* (otherwise the user could have mistyped the unmatched pathspec),
|
||||
* and a mark is left in seen[] array for pathspec element that
|
||||
* actually matched anything.
|
||||
*/
|
||||
int match_pathspec_depth(const struct pathspec *ps,
|
||||
const char *name, int namelen,
|
||||
int prefix, char *seen)
|
||||
{
|
||||
int i, retval = 0;
|
||||
|
||||
if (!ps->nr) {
|
||||
if (!ps->recursive || ps->max_depth == -1)
|
||||
return MATCHED_RECURSIVELY;
|
||||
|
||||
if (within_depth(name, namelen, 0, ps->max_depth))
|
||||
return MATCHED_EXACTLY;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
name += prefix;
|
||||
namelen -= prefix;
|
||||
|
||||
for (i = ps->nr - 1; i >= 0; i--) {
|
||||
int how;
|
||||
if (seen && seen[i] == MATCHED_EXACTLY)
|
||||
continue;
|
||||
how = match_pathspec_item(ps->items+i, prefix, name, namelen);
|
||||
if (ps->recursive && ps->max_depth != -1 &&
|
||||
how && how != MATCHED_FNMATCH) {
|
||||
int len = ps->items[i].len;
|
||||
if (name[len] == '/')
|
||||
len++;
|
||||
if (within_depth(name+len, namelen-len, 0, ps->max_depth))
|
||||
how = MATCHED_EXACTLY;
|
||||
else
|
||||
how = 0;
|
||||
}
|
||||
if (how) {
|
||||
if (retval < how)
|
||||
retval = how;
|
||||
if (seen && seen[i] < how)
|
||||
seen[i] = how;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int no_wildcard(const char *string)
|
||||
{
|
||||
return string[strcspn(string, "*?[{\\")] == '\0';
|
||||
@ -1151,3 +1255,50 @@ int remove_path(const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pathspec_item_cmp(const void *a_, const void *b_)
|
||||
{
|
||||
struct pathspec_item *a, *b;
|
||||
|
||||
a = (struct pathspec_item *)a_;
|
||||
b = (struct pathspec_item *)b_;
|
||||
return strcmp(a->match, b->match);
|
||||
}
|
||||
|
||||
int init_pathspec(struct pathspec *pathspec, const char **paths)
|
||||
{
|
||||
const char **p = paths;
|
||||
int i;
|
||||
|
||||
memset(pathspec, 0, sizeof(*pathspec));
|
||||
if (!p)
|
||||
return 0;
|
||||
while (*p)
|
||||
p++;
|
||||
pathspec->raw = paths;
|
||||
pathspec->nr = p - paths;
|
||||
if (!pathspec->nr)
|
||||
return 0;
|
||||
|
||||
pathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr);
|
||||
for (i = 0; i < pathspec->nr; i++) {
|
||||
struct pathspec_item *item = pathspec->items+i;
|
||||
const char *path = paths[i];
|
||||
|
||||
item->match = path;
|
||||
item->len = strlen(path);
|
||||
item->has_wildcard = !no_wildcard(path);
|
||||
if (item->has_wildcard)
|
||||
pathspec->has_wildcard = 1;
|
||||
}
|
||||
|
||||
qsort(pathspec->items, pathspec->nr,
|
||||
sizeof(struct pathspec_item), pathspec_item_cmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_pathspec(struct pathspec *pathspec)
|
||||
{
|
||||
free(pathspec->items);
|
||||
pathspec->items = NULL;
|
||||
}
|
||||
|
4
dir.h
4
dir.h
@ -65,6 +65,10 @@ struct dir_struct {
|
||||
#define MATCHED_FNMATCH 2
|
||||
#define MATCHED_EXACTLY 3
|
||||
extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
|
||||
extern int match_pathspec_depth(const struct pathspec *pathspec,
|
||||
const char *name, int namelen,
|
||||
int prefix, char *seen);
|
||||
extern int within_depth(const char *name, int namelen, int depth, int max_depth);
|
||||
|
||||
extern int fill_directory(struct dir_struct *dir, const char **pathspec);
|
||||
extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec);
|
||||
|
@ -35,7 +35,9 @@ static void *preload_thread(void *_data)
|
||||
struct index_state *index = p->index;
|
||||
struct cache_entry **cep = index->cache + p->offset;
|
||||
struct cache_def cache;
|
||||
struct pathspec pathspec;
|
||||
|
||||
init_pathspec(&pathspec, p->pathspec);
|
||||
memset(&cache, 0, sizeof(cache));
|
||||
nr = p->nr;
|
||||
if (nr + p->offset > index->cache_nr)
|
||||
@ -51,7 +53,7 @@ static void *preload_thread(void *_data)
|
||||
continue;
|
||||
if (ce_uptodate(ce))
|
||||
continue;
|
||||
if (!ce_path_match(ce, p->pathspec))
|
||||
if (!ce_path_match(ce, &pathspec))
|
||||
continue;
|
||||
if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
|
||||
continue;
|
||||
@ -61,6 +63,7 @@ static void *preload_thread(void *_data)
|
||||
continue;
|
||||
ce_mark_uptodate(ce);
|
||||
} while (--nr > 0);
|
||||
free_pathspec(&pathspec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
25
read-cache.c
25
read-cache.c
@ -706,30 +706,9 @@ int ce_same_name(struct cache_entry *a, struct cache_entry *b)
|
||||
return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
|
||||
}
|
||||
|
||||
int ce_path_match(const struct cache_entry *ce, const char **pathspec)
|
||||
int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec)
|
||||
{
|
||||
const char *match, *name;
|
||||
int len;
|
||||
|
||||
if (!pathspec)
|
||||
return 1;
|
||||
|
||||
len = ce_namelen(ce);
|
||||
name = ce->name;
|
||||
while ((match = *pathspec++) != NULL) {
|
||||
int matchlen = strlen(match);
|
||||
if (matchlen > len)
|
||||
continue;
|
||||
if (memcmp(name, match, matchlen))
|
||||
continue;
|
||||
if (matchlen && name[matchlen-1] == '/')
|
||||
return 1;
|
||||
if (name[matchlen] == '/' || !name[matchlen])
|
||||
return 1;
|
||||
if (!matchlen)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
21
revision.c
21
revision.c
@ -323,7 +323,7 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct
|
||||
* tagged commit by specifying both --simplify-by-decoration
|
||||
* and pathspec.
|
||||
*/
|
||||
if (!revs->prune_data)
|
||||
if (!revs->prune_data.nr)
|
||||
return REV_TREE_SAME;
|
||||
}
|
||||
|
||||
@ -553,11 +553,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
|
||||
|
||||
left_first = left_count < right_count;
|
||||
init_patch_ids(&ids);
|
||||
if (revs->diffopt.nr_paths) {
|
||||
ids.diffopts.nr_paths = revs->diffopt.nr_paths;
|
||||
ids.diffopts.paths = revs->diffopt.paths;
|
||||
ids.diffopts.pathlens = revs->diffopt.pathlens;
|
||||
}
|
||||
ids.diffopts.pathspec = revs->diffopt.pathspec;
|
||||
|
||||
/* Compute patch-ids for one side */
|
||||
for (p = list; p; p = p->next) {
|
||||
@ -973,7 +969,7 @@ static void prepare_show_merge(struct rev_info *revs)
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (!ce_stage(ce))
|
||||
continue;
|
||||
if (ce_path_match(ce, revs->prune_data)) {
|
||||
if (ce_path_match(ce, &revs->prune_data)) {
|
||||
prune_num++;
|
||||
prune = xrealloc(prune, sizeof(*prune) * prune_num);
|
||||
prune[prune_num-2] = ce->name;
|
||||
@ -983,7 +979,8 @@ static void prepare_show_merge(struct rev_info *revs)
|
||||
ce_same_name(ce, active_cache[i+1]))
|
||||
i++;
|
||||
}
|
||||
revs->prune_data = prune;
|
||||
free_pathspec(&revs->prune_data);
|
||||
init_pathspec(&revs->prune_data, prune);
|
||||
revs->limited = 1;
|
||||
}
|
||||
|
||||
@ -1620,7 +1617,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
||||
}
|
||||
|
||||
if (prune_data)
|
||||
revs->prune_data = get_pathspec(revs->prefix, prune_data);
|
||||
init_pathspec(&revs->prune_data, get_pathspec(revs->prefix, prune_data));
|
||||
|
||||
if (revs->def == NULL)
|
||||
revs->def = opt ? opt->def : NULL;
|
||||
@ -1651,13 +1648,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
||||
if (revs->topo_order)
|
||||
revs->limited = 1;
|
||||
|
||||
if (revs->prune_data) {
|
||||
diff_tree_setup_paths(revs->prune_data, &revs->pruning);
|
||||
if (revs->prune_data.nr) {
|
||||
diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning);
|
||||
/* Can't prune commits with rename following: the paths change.. */
|
||||
if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
|
||||
revs->prune = 1;
|
||||
if (!revs->full_diff)
|
||||
diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
|
||||
diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt);
|
||||
}
|
||||
if (revs->combine_merges)
|
||||
revs->ignore_merges = 0;
|
||||
|
@ -34,7 +34,7 @@ struct rev_info {
|
||||
/* Basic information */
|
||||
const char *prefix;
|
||||
const char *def;
|
||||
void *prune_data;
|
||||
struct pathspec prune_data;
|
||||
unsigned int early_output;
|
||||
|
||||
/* Traversal flags */
|
||||
|
@ -70,4 +70,36 @@ test_expect_success 'diff-tree pathspec' '
|
||||
test_cmp expected current
|
||||
'
|
||||
|
||||
EMPTY_TREE=4b825dc642cb6eb9a060e54bf8d69288fbee4904
|
||||
|
||||
test_expect_success 'diff-tree with wildcard shows dir also matches' '
|
||||
git diff-tree --name-only $EMPTY_TREE $tree -- "f*" >result &&
|
||||
echo file0 >expected &&
|
||||
test_cmp expected result
|
||||
'
|
||||
|
||||
test_expect_success 'diff-tree -r with wildcard' '
|
||||
git diff-tree -r --name-only $EMPTY_TREE $tree -- "*file1" >result &&
|
||||
echo path1/file1 >expected &&
|
||||
test_cmp expected result
|
||||
'
|
||||
|
||||
test_expect_success 'diff-tree with wildcard shows dir also matches' '
|
||||
git diff-tree --name-only $tree $tree2 -- "path1/f*" >result &&
|
||||
echo path1 >expected &&
|
||||
test_cmp expected result
|
||||
'
|
||||
|
||||
test_expect_success 'diff-tree -r with wildcard from beginning' '
|
||||
git diff-tree -r --name-only $tree $tree2 -- "path1/*file1" >result &&
|
||||
echo path1/file1 >expected &&
|
||||
test_cmp expected result
|
||||
'
|
||||
|
||||
test_expect_success 'diff-tree -r with wildcard' '
|
||||
git diff-tree -r --name-only $tree $tree2 -- "path1/f*" >result &&
|
||||
echo path1/file1 >expected &&
|
||||
test_cmp expected result
|
||||
'
|
||||
|
||||
test_done
|
||||
|
@ -1,51 +1,96 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git rev-list trivial path optimization test'
|
||||
test_description='git rev-list trivial path optimization test
|
||||
|
||||
d/z1
|
||||
b0 b1
|
||||
o------------------------*----o master
|
||||
/ /
|
||||
o---------o----o----o----o side
|
||||
a0 c0 c1 a1 c2
|
||||
d/f0 d/f1
|
||||
d/z0
|
||||
|
||||
'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
echo Hello > a &&
|
||||
git add a &&
|
||||
git commit -m "Initial commit" a &&
|
||||
initial=$(git rev-parse --verify HEAD)
|
||||
echo Hello >a &&
|
||||
mkdir d &&
|
||||
echo World >d/f &&
|
||||
echo World >d/z &&
|
||||
git add a d &&
|
||||
test_tick &&
|
||||
git commit -m "Initial commit" &&
|
||||
git rev-parse --verify HEAD &&
|
||||
git tag initial
|
||||
'
|
||||
|
||||
test_expect_success path-optimization '
|
||||
commit=$(echo "Unchanged tree" | git commit-tree "HEAD^{tree}" -p HEAD) &&
|
||||
test $(git rev-list $commit | wc -l) = 2 &&
|
||||
test $(git rev-list $commit -- . | wc -l) = 1
|
||||
test_tick &&
|
||||
commit=$(echo "Unchanged tree" | git commit-tree "HEAD^{tree}" -p HEAD) &&
|
||||
test $(git rev-list $commit | wc -l) = 2 &&
|
||||
test $(git rev-list $commit -- . | wc -l) = 1
|
||||
'
|
||||
|
||||
test_expect_success 'further setup' '
|
||||
git checkout -b side &&
|
||||
echo Irrelevant >c &&
|
||||
git add c &&
|
||||
echo Irrelevant >d/f &&
|
||||
git add c d/f &&
|
||||
test_tick &&
|
||||
git commit -m "Side makes an irrelevant commit" &&
|
||||
git tag side_c0 &&
|
||||
echo "More Irrelevancy" >c &&
|
||||
git add c &&
|
||||
test_tick &&
|
||||
git commit -m "Side makes another irrelevant commit" &&
|
||||
echo Bye >a &&
|
||||
git add a &&
|
||||
test_tick &&
|
||||
git commit -m "Side touches a" &&
|
||||
side=$(git rev-parse --verify HEAD) &&
|
||||
git tag side_a1 &&
|
||||
echo "Yet more Irrelevancy" >c &&
|
||||
git add c &&
|
||||
test_tick &&
|
||||
git commit -m "Side makes yet another irrelevant commit" &&
|
||||
git checkout master &&
|
||||
echo Another >b &&
|
||||
git add b &&
|
||||
echo Munged >d/z &&
|
||||
git add b d/z &&
|
||||
test_tick &&
|
||||
git commit -m "Master touches b" &&
|
||||
git tag master_b0 &&
|
||||
git merge side &&
|
||||
echo Touched >b &&
|
||||
git add b &&
|
||||
test_tick &&
|
||||
git commit -m "Master touches b again"
|
||||
'
|
||||
|
||||
test_expect_success 'path optimization 2' '
|
||||
( echo "$side"; echo "$initial" ) >expected &&
|
||||
git rev-parse side_a1 initial >expected &&
|
||||
git rev-list HEAD -- a >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'pathspec with leading path' '
|
||||
git rev-parse master^ master_b0 side_c0 initial >expected &&
|
||||
git rev-list HEAD -- d >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'pathspec with glob (1)' '
|
||||
git rev-parse master^ master_b0 side_c0 initial >expected &&
|
||||
git rev-list HEAD -- "d/*" >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'pathspec with glob (2)' '
|
||||
git rev-parse side_c0 initial >expected &&
|
||||
git rev-list HEAD -- "d/[a-m]*" >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
@ -182,6 +182,24 @@ do
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success "grep --max-depth 0 -- . t $L" '
|
||||
{
|
||||
echo ${HC}t/v:1:vvv
|
||||
echo ${HC}v:1:vvv
|
||||
} >expected &&
|
||||
git grep --max-depth 0 -n -e vvv $H -- . t >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success "grep --max-depth 0 -- t . $L" '
|
||||
{
|
||||
echo ${HC}t/v:1:vvv
|
||||
echo ${HC}v:1:vvv
|
||||
} >expected &&
|
||||
git grep --max-depth 0 -n -e vvv $H -- t . >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
done
|
||||
|
||||
cat >expected <<EOF
|
||||
|
280
tree-diff.c
280
tree-diff.c
@ -6,34 +6,18 @@
|
||||
#include "diffcore.h"
|
||||
#include "tree.h"
|
||||
|
||||
static char *malloc_base(const char *base, int baselen, const char *path, int pathlen)
|
||||
{
|
||||
char *newbase = xmalloc(baselen + pathlen + 2);
|
||||
memcpy(newbase, base, baselen);
|
||||
memcpy(newbase + baselen, path, pathlen);
|
||||
memcpy(newbase + baselen + pathlen, "/", 2);
|
||||
return newbase;
|
||||
}
|
||||
static void show_entry(struct diff_options *opt, const char *prefix,
|
||||
struct tree_desc *desc, struct strbuf *base);
|
||||
|
||||
static char *malloc_fullname(const char *base, int baselen, const char *path, int pathlen)
|
||||
{
|
||||
char *fullname = xmalloc(baselen + pathlen + 1);
|
||||
memcpy(fullname, base, baselen);
|
||||
memcpy(fullname + baselen, path, pathlen);
|
||||
fullname[baselen + pathlen] = 0;
|
||||
return fullname;
|
||||
}
|
||||
|
||||
static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
|
||||
const char *base, int baselen);
|
||||
|
||||
static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, int baselen, struct diff_options *opt)
|
||||
static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,
|
||||
struct strbuf *base, struct diff_options *opt)
|
||||
{
|
||||
unsigned mode1, mode2;
|
||||
const char *path1, *path2;
|
||||
const unsigned char *sha1, *sha2;
|
||||
int cmp, pathlen1, pathlen2;
|
||||
char *fullname;
|
||||
int old_baselen = base->len;
|
||||
int retval = 0;
|
||||
|
||||
sha1 = tree_entry_extract(t1, &path1, &mode1);
|
||||
sha2 = tree_entry_extract(t2, &path2, &mode2);
|
||||
@ -42,11 +26,11 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
|
||||
pathlen2 = tree_entry_len(path2, sha2);
|
||||
cmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2);
|
||||
if (cmp < 0) {
|
||||
show_entry(opt, "-", t1, base, baselen);
|
||||
show_entry(opt, "-", t1, base);
|
||||
return -1;
|
||||
}
|
||||
if (cmp > 0) {
|
||||
show_entry(opt, "+", t2, base, baselen);
|
||||
show_entry(opt, "+", t2, base);
|
||||
return 1;
|
||||
}
|
||||
if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER) && !hashcmp(sha1, sha2) && mode1 == mode2)
|
||||
@ -57,149 +41,29 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
|
||||
* file, we need to consider it a remove and an add.
|
||||
*/
|
||||
if (S_ISDIR(mode1) != S_ISDIR(mode2)) {
|
||||
show_entry(opt, "-", t1, base, baselen);
|
||||
show_entry(opt, "+", t2, base, baselen);
|
||||
show_entry(opt, "-", t1, base);
|
||||
show_entry(opt, "+", t2, base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
strbuf_add(base, path1, pathlen1);
|
||||
if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode1)) {
|
||||
int retval;
|
||||
char *newbase = malloc_base(base, baselen, path1, pathlen1);
|
||||
if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {
|
||||
newbase[baselen + pathlen1] = 0;
|
||||
opt->change(opt, mode1, mode2,
|
||||
sha1, sha2, newbase, 0, 0);
|
||||
newbase[baselen + pathlen1] = '/';
|
||||
sha1, sha2, base->buf, 0, 0);
|
||||
}
|
||||
retval = diff_tree_sha1(sha1, sha2, newbase, opt);
|
||||
free(newbase);
|
||||
return retval;
|
||||
strbuf_addch(base, '/');
|
||||
retval = diff_tree_sha1(sha1, sha2, base->buf, opt);
|
||||
} else {
|
||||
opt->change(opt, mode1, mode2, sha1, sha2, base->buf, 0, 0);
|
||||
}
|
||||
|
||||
fullname = malloc_fullname(base, baselen, path1, pathlen1);
|
||||
opt->change(opt, mode1, mode2, sha1, sha2, fullname, 0, 0);
|
||||
free(fullname);
|
||||
strbuf_setlen(base, old_baselen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is a tree entry interesting given the pathspec we have?
|
||||
*
|
||||
* Pre-condition: baselen == 0 || base[baselen-1] == '/'
|
||||
*
|
||||
* Return:
|
||||
* - 2 for "yes, and all subsequent entries will be"
|
||||
* - 1 for yes
|
||||
* - zero for no
|
||||
* - negative for "no, and no subsequent entries will be either"
|
||||
*/
|
||||
static int tree_entry_interesting(struct tree_desc *desc, const char *base, int baselen, struct diff_options *opt)
|
||||
{
|
||||
const char *path;
|
||||
const unsigned char *sha1;
|
||||
unsigned mode;
|
||||
int i;
|
||||
int pathlen;
|
||||
int never_interesting = -1;
|
||||
|
||||
if (!opt->nr_paths)
|
||||
return 2;
|
||||
|
||||
sha1 = tree_entry_extract(desc, &path, &mode);
|
||||
|
||||
pathlen = tree_entry_len(path, sha1);
|
||||
|
||||
for (i = 0; i < opt->nr_paths; i++) {
|
||||
const char *match = opt->paths[i];
|
||||
int matchlen = opt->pathlens[i];
|
||||
int m = -1; /* signals that we haven't called strncmp() */
|
||||
|
||||
if (baselen >= matchlen) {
|
||||
/* If it doesn't match, move along... */
|
||||
if (strncmp(base, match, matchlen))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If the base is a subdirectory of a path which
|
||||
* was specified, all of them are interesting.
|
||||
*/
|
||||
if (!matchlen ||
|
||||
base[matchlen] == '/' ||
|
||||
match[matchlen - 1] == '/')
|
||||
return 2;
|
||||
|
||||
/* Just a random prefix match */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Does the base match? */
|
||||
if (strncmp(base, match, baselen))
|
||||
continue;
|
||||
|
||||
match += baselen;
|
||||
matchlen -= baselen;
|
||||
|
||||
if (never_interesting) {
|
||||
/*
|
||||
* We have not seen any match that sorts later
|
||||
* than the current path.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Does match sort strictly earlier than path
|
||||
* with their common parts?
|
||||
*/
|
||||
m = strncmp(match, path,
|
||||
(matchlen < pathlen) ? matchlen : pathlen);
|
||||
if (m < 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If we come here even once, that means there is at
|
||||
* least one pathspec that would sort equal to or
|
||||
* later than the path we are currently looking at.
|
||||
* In other words, if we have never reached this point
|
||||
* after iterating all pathspecs, it means all
|
||||
* pathspecs are either outside of base, or inside the
|
||||
* base but sorts strictly earlier than the current
|
||||
* one. In either case, they will never match the
|
||||
* subsequent entries. In such a case, we initialized
|
||||
* the variable to -1 and that is what will be
|
||||
* returned, allowing the caller to terminate early.
|
||||
*/
|
||||
never_interesting = 0;
|
||||
}
|
||||
|
||||
if (pathlen > matchlen)
|
||||
continue;
|
||||
|
||||
if (matchlen > pathlen) {
|
||||
if (match[pathlen] != '/')
|
||||
continue;
|
||||
if (!S_ISDIR(mode))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m == -1)
|
||||
/*
|
||||
* we cheated and did not do strncmp(), so we do
|
||||
* that here.
|
||||
*/
|
||||
m = strncmp(match, path, pathlen);
|
||||
|
||||
/*
|
||||
* If common part matched earlier then it is a hit,
|
||||
* because we rejected the case where path is not a
|
||||
* leading directory and is shorter than match.
|
||||
*/
|
||||
if (!m)
|
||||
return 1;
|
||||
}
|
||||
return never_interesting; /* No matches */
|
||||
}
|
||||
|
||||
/* A whole sub-tree went away or appeared */
|
||||
static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen)
|
||||
static void show_tree(struct diff_options *opt, const char *prefix,
|
||||
struct tree_desc *desc, struct strbuf *base)
|
||||
{
|
||||
int all_interesting = 0;
|
||||
while (desc->size) {
|
||||
@ -208,31 +72,32 @@ static void show_tree(struct diff_options *opt, const char *prefix, struct tree_
|
||||
if (all_interesting)
|
||||
show = 1;
|
||||
else {
|
||||
show = tree_entry_interesting(desc, base, baselen,
|
||||
opt);
|
||||
show = tree_entry_interesting(&desc->entry, base, 0,
|
||||
&opt->pathspec);
|
||||
if (show == 2)
|
||||
all_interesting = 1;
|
||||
}
|
||||
if (show < 0)
|
||||
break;
|
||||
if (show)
|
||||
show_entry(opt, prefix, desc, base, baselen);
|
||||
show_entry(opt, prefix, desc, base);
|
||||
update_tree_entry(desc);
|
||||
}
|
||||
}
|
||||
|
||||
/* A file entry went away or appeared */
|
||||
static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
|
||||
const char *base, int baselen)
|
||||
static void show_entry(struct diff_options *opt, const char *prefix,
|
||||
struct tree_desc *desc, struct strbuf *base)
|
||||
{
|
||||
unsigned mode;
|
||||
const char *path;
|
||||
const unsigned char *sha1 = tree_entry_extract(desc, &path, &mode);
|
||||
int pathlen = tree_entry_len(path, sha1);
|
||||
int old_baselen = base->len;
|
||||
|
||||
strbuf_add(base, path, pathlen);
|
||||
if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode)) {
|
||||
enum object_type type;
|
||||
char *newbase = malloc_base(base, baselen, path, pathlen);
|
||||
struct tree_desc inner;
|
||||
void *tree;
|
||||
unsigned long size;
|
||||
@ -241,28 +106,25 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
|
||||
if (!tree || type != OBJ_TREE)
|
||||
die("corrupt tree sha %s", sha1_to_hex(sha1));
|
||||
|
||||
if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {
|
||||
newbase[baselen + pathlen] = 0;
|
||||
opt->add_remove(opt, *prefix, mode, sha1, newbase, 0);
|
||||
newbase[baselen + pathlen] = '/';
|
||||
}
|
||||
if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE))
|
||||
opt->add_remove(opt, *prefix, mode, sha1, base->buf, 0);
|
||||
|
||||
strbuf_addch(base, '/');
|
||||
|
||||
init_tree_desc(&inner, tree, size);
|
||||
show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);
|
||||
|
||||
show_tree(opt, prefix, &inner, base);
|
||||
free(tree);
|
||||
free(newbase);
|
||||
} else {
|
||||
char *fullname = malloc_fullname(base, baselen, path, pathlen);
|
||||
opt->add_remove(opt, prefix[0], mode, sha1, fullname, 0);
|
||||
free(fullname);
|
||||
}
|
||||
} else
|
||||
opt->add_remove(opt, prefix[0], mode, sha1, base->buf, 0);
|
||||
|
||||
strbuf_setlen(base, old_baselen);
|
||||
}
|
||||
|
||||
static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt, int *all_interesting)
|
||||
static void skip_uninteresting(struct tree_desc *t, struct strbuf *base,
|
||||
struct diff_options *opt, int *all_interesting)
|
||||
{
|
||||
while (t->size) {
|
||||
int show = tree_entry_interesting(t, base, baselen, opt);
|
||||
int show = tree_entry_interesting(&t->entry, base, 0, &opt->pathspec);
|
||||
if (show == 2)
|
||||
*all_interesting = 1;
|
||||
if (!show) {
|
||||
@ -276,37 +138,44 @@ static void skip_uninteresting(struct tree_desc *t, const char *base, int basele
|
||||
}
|
||||
}
|
||||
|
||||
int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
|
||||
int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
|
||||
const char *base_str, struct diff_options *opt)
|
||||
{
|
||||
int baselen = strlen(base);
|
||||
struct strbuf base;
|
||||
int baselen = strlen(base_str);
|
||||
int all_t1_interesting = 0;
|
||||
int all_t2_interesting = 0;
|
||||
|
||||
/* Enable recursion indefinitely */
|
||||
opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
|
||||
opt->pathspec.max_depth = -1;
|
||||
|
||||
strbuf_init(&base, PATH_MAX);
|
||||
strbuf_add(&base, base_str, baselen);
|
||||
|
||||
for (;;) {
|
||||
if (DIFF_OPT_TST(opt, QUICK) &&
|
||||
DIFF_OPT_TST(opt, HAS_CHANGES))
|
||||
break;
|
||||
if (opt->nr_paths) {
|
||||
if (opt->pathspec.nr) {
|
||||
if (!all_t1_interesting)
|
||||
skip_uninteresting(t1, base, baselen, opt,
|
||||
&all_t1_interesting);
|
||||
skip_uninteresting(t1, &base, opt, &all_t1_interesting);
|
||||
if (!all_t2_interesting)
|
||||
skip_uninteresting(t2, base, baselen, opt,
|
||||
&all_t2_interesting);
|
||||
skip_uninteresting(t2, &base, opt, &all_t2_interesting);
|
||||
}
|
||||
if (!t1->size) {
|
||||
if (!t2->size)
|
||||
break;
|
||||
show_entry(opt, "+", t2, base, baselen);
|
||||
show_entry(opt, "+", t2, &base);
|
||||
update_tree_entry(t2);
|
||||
continue;
|
||||
}
|
||||
if (!t2->size) {
|
||||
show_entry(opt, "-", t1, base, baselen);
|
||||
show_entry(opt, "-", t1, &base);
|
||||
update_tree_entry(t1);
|
||||
continue;
|
||||
}
|
||||
switch (compare_tree_entry(t1, t2, base, baselen, opt)) {
|
||||
switch (compare_tree_entry(t1, t2, &base, opt)) {
|
||||
case -1:
|
||||
update_tree_entry(t1);
|
||||
continue;
|
||||
@ -319,6 +188,8 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, stru
|
||||
}
|
||||
die("git diff-tree: internal error");
|
||||
}
|
||||
|
||||
strbuf_release(&base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -349,7 +220,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
|
||||
DIFF_OPT_SET(&diff_opts, RECURSIVE);
|
||||
DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
|
||||
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
||||
diff_opts.single_follow = opt->paths[0];
|
||||
diff_opts.single_follow = opt->pathspec.raw[0];
|
||||
diff_opts.break_opt = opt->break_opt;
|
||||
paths[0] = NULL;
|
||||
diff_tree_setup_paths(paths, &diff_opts);
|
||||
@ -369,15 +240,16 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
|
||||
* diff_queued_diff, we will also use that as the path in
|
||||
* the future!
|
||||
*/
|
||||
if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, opt->paths[0])) {
|
||||
if ((p->status == 'R' || p->status == 'C') &&
|
||||
!strcmp(p->two->path, opt->pathspec.raw[0])) {
|
||||
/* Switch the file-pairs around */
|
||||
q->queue[i] = choice;
|
||||
choice = p;
|
||||
|
||||
/* Update the path we use from now on.. */
|
||||
diff_tree_release_paths(opt);
|
||||
opt->paths[0] = xstrdup(p->one->path);
|
||||
diff_tree_setup_paths(opt->paths, opt);
|
||||
opt->pathspec.raw[0] = xstrdup(p->one->path);
|
||||
diff_tree_setup_paths(opt->pathspec.raw, opt);
|
||||
|
||||
/*
|
||||
* The caller expects us to return a set of vanilla
|
||||
@ -452,36 +324,12 @@ int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int count_paths(const char **paths)
|
||||
{
|
||||
int i = 0;
|
||||
while (*paths++)
|
||||
i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
void diff_tree_release_paths(struct diff_options *opt)
|
||||
{
|
||||
free(opt->pathlens);
|
||||
free_pathspec(&opt->pathspec);
|
||||
}
|
||||
|
||||
void diff_tree_setup_paths(const char **p, struct diff_options *opt)
|
||||
{
|
||||
opt->nr_paths = 0;
|
||||
opt->pathlens = NULL;
|
||||
opt->paths = NULL;
|
||||
|
||||
if (p) {
|
||||
int i;
|
||||
|
||||
opt->paths = p;
|
||||
opt->nr_paths = count_paths(p);
|
||||
if (opt->nr_paths == 0) {
|
||||
opt->pathlens = NULL;
|
||||
return;
|
||||
}
|
||||
opt->pathlens = xmalloc(opt->nr_paths * sizeof(int));
|
||||
for (i=0; i < opt->nr_paths; i++)
|
||||
opt->pathlens[i] = strlen(p[i]);
|
||||
}
|
||||
init_pathspec(&opt->pathspec, p);
|
||||
}
|
||||
|
184
tree-walk.c
184
tree-walk.c
@ -1,6 +1,7 @@
|
||||
#include "cache.h"
|
||||
#include "tree-walk.h"
|
||||
#include "unpack-trees.h"
|
||||
#include "dir.h"
|
||||
#include "tree.h"
|
||||
|
||||
static const char *get_mode(const char *str, unsigned int *modep)
|
||||
@ -455,3 +456,186 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
|
||||
free(tree);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int match_entry(const struct name_entry *entry, int pathlen,
|
||||
const char *match, int matchlen,
|
||||
int *never_interesting)
|
||||
{
|
||||
int m = -1; /* signals that we haven't called strncmp() */
|
||||
|
||||
if (*never_interesting) {
|
||||
/*
|
||||
* We have not seen any match that sorts later
|
||||
* than the current path.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Does match sort strictly earlier than path
|
||||
* with their common parts?
|
||||
*/
|
||||
m = strncmp(match, entry->path,
|
||||
(matchlen < pathlen) ? matchlen : pathlen);
|
||||
if (m < 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If we come here even once, that means there is at
|
||||
* least one pathspec that would sort equal to or
|
||||
* later than the path we are currently looking at.
|
||||
* In other words, if we have never reached this point
|
||||
* after iterating all pathspecs, it means all
|
||||
* pathspecs are either outside of base, or inside the
|
||||
* base but sorts strictly earlier than the current
|
||||
* one. In either case, they will never match the
|
||||
* subsequent entries. In such a case, we initialized
|
||||
* the variable to -1 and that is what will be
|
||||
* returned, allowing the caller to terminate early.
|
||||
*/
|
||||
*never_interesting = 0;
|
||||
}
|
||||
|
||||
if (pathlen > matchlen)
|
||||
return 0;
|
||||
|
||||
if (matchlen > pathlen) {
|
||||
if (match[pathlen] != '/')
|
||||
return 0;
|
||||
if (!S_ISDIR(entry->mode))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m == -1)
|
||||
/*
|
||||
* we cheated and did not do strncmp(), so we do
|
||||
* that here.
|
||||
*/
|
||||
m = strncmp(match, entry->path, pathlen);
|
||||
|
||||
/*
|
||||
* If common part matched earlier then it is a hit,
|
||||
* because we rejected the case where path is not a
|
||||
* leading directory and is shorter than match.
|
||||
*/
|
||||
if (!m)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_dir_prefix(const char *base, int baselen,
|
||||
const char *match, int matchlen)
|
||||
{
|
||||
if (strncmp(base, match, matchlen))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If the base is a subdirectory of a path which
|
||||
* was specified, all of them are interesting.
|
||||
*/
|
||||
if (!matchlen ||
|
||||
base[matchlen] == '/' ||
|
||||
match[matchlen - 1] == '/')
|
||||
return 1;
|
||||
|
||||
/* Just a random prefix match */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is a tree entry interesting given the pathspec we have?
|
||||
*
|
||||
* Pre-condition: either baselen == base_offset (i.e. empty path)
|
||||
* or base[baselen-1] == '/' (i.e. with trailing slash).
|
||||
*
|
||||
* Return:
|
||||
* - 2 for "yes, and all subsequent entries will be"
|
||||
* - 1 for yes
|
||||
* - zero for no
|
||||
* - negative for "no, and no subsequent entries will be either"
|
||||
*/
|
||||
int tree_entry_interesting(const struct name_entry *entry,
|
||||
struct strbuf *base, int base_offset,
|
||||
const struct pathspec *ps)
|
||||
{
|
||||
int i;
|
||||
int pathlen, baselen = base->len - base_offset;
|
||||
int never_interesting = ps->has_wildcard ? 0 : -1;
|
||||
|
||||
if (!ps->nr) {
|
||||
if (!ps->recursive || ps->max_depth == -1)
|
||||
return 2;
|
||||
return !!within_depth(base->buf + base_offset, baselen,
|
||||
!!S_ISDIR(entry->mode),
|
||||
ps->max_depth);
|
||||
}
|
||||
|
||||
pathlen = tree_entry_len(entry->path, entry->sha1);
|
||||
|
||||
for (i = ps->nr - 1; i >= 0; i--) {
|
||||
const struct pathspec_item *item = ps->items+i;
|
||||
const char *match = item->match;
|
||||
const char *base_str = base->buf + base_offset;
|
||||
int matchlen = item->len;
|
||||
|
||||
if (baselen >= matchlen) {
|
||||
/* If it doesn't match, move along... */
|
||||
if (!match_dir_prefix(base_str, baselen, match, matchlen))
|
||||
goto match_wildcards;
|
||||
|
||||
if (!ps->recursive || ps->max_depth == -1)
|
||||
return 2;
|
||||
|
||||
return !!within_depth(base_str + matchlen + 1,
|
||||
baselen - matchlen - 1,
|
||||
!!S_ISDIR(entry->mode),
|
||||
ps->max_depth);
|
||||
}
|
||||
|
||||
/* Does the base match? */
|
||||
if (!strncmp(base_str, match, baselen)) {
|
||||
if (match_entry(entry, pathlen,
|
||||
match + baselen, matchlen - baselen,
|
||||
&never_interesting))
|
||||
return 1;
|
||||
|
||||
if (ps->items[i].has_wildcard) {
|
||||
if (!fnmatch(match + baselen, entry->path, 0))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Match all directories. We'll try to
|
||||
* match files later on.
|
||||
*/
|
||||
if (ps->recursive && S_ISDIR(entry->mode))
|
||||
return 1;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
match_wildcards:
|
||||
if (!ps->items[i].has_wildcard)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Concatenate base and entry->path into one and do
|
||||
* fnmatch() on it.
|
||||
*/
|
||||
|
||||
strbuf_add(base, entry->path, pathlen);
|
||||
|
||||
if (!fnmatch(match, base->buf + base_offset, 0)) {
|
||||
strbuf_setlen(base, base_offset + baselen);
|
||||
return 1;
|
||||
}
|
||||
strbuf_setlen(base, base_offset + baselen);
|
||||
|
||||
/*
|
||||
* Match all directories. We'll try to match files
|
||||
* later on.
|
||||
*/
|
||||
if (ps->recursive && S_ISDIR(entry->mode))
|
||||
return 1;
|
||||
}
|
||||
return never_interesting; /* No matches */
|
||||
}
|
||||
|
@ -60,4 +60,6 @@ static inline int traverse_path_len(const struct traverse_info *info, const stru
|
||||
return info->pathlen + tree_entry_len(n->path, n->sha1);
|
||||
}
|
||||
|
||||
extern int tree_entry_interesting(const struct name_entry *, struct strbuf *, int, const struct pathspec *ps);
|
||||
|
||||
#endif
|
||||
|
@ -323,7 +323,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
|
||||
}
|
||||
rev.diffopt.format_callback = wt_status_collect_changed_cb;
|
||||
rev.diffopt.format_callback_data = s;
|
||||
rev.prune_data = s->pathspec;
|
||||
init_pathspec(&rev.prune_data, s->pathspec);
|
||||
run_diff_files(&rev, 0);
|
||||
}
|
||||
|
||||
@ -348,20 +348,22 @@ static void wt_status_collect_changes_index(struct wt_status *s)
|
||||
rev.diffopt.detect_rename = 1;
|
||||
rev.diffopt.rename_limit = 200;
|
||||
rev.diffopt.break_opt = 0;
|
||||
rev.prune_data = s->pathspec;
|
||||
init_pathspec(&rev.prune_data, s->pathspec);
|
||||
run_diff_index(&rev, 1);
|
||||
}
|
||||
|
||||
static void wt_status_collect_changes_initial(struct wt_status *s)
|
||||
{
|
||||
struct pathspec pathspec;
|
||||
int i;
|
||||
|
||||
init_pathspec(&pathspec, s->pathspec);
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct string_list_item *it;
|
||||
struct wt_status_change_data *d;
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
|
||||
if (!ce_path_match(ce, s->pathspec))
|
||||
if (!ce_path_match(ce, &pathspec))
|
||||
continue;
|
||||
it = string_list_insert(&s->change, ce->name);
|
||||
d = it->util;
|
||||
@ -376,6 +378,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
|
||||
else
|
||||
d->index_status = DIFF_STATUS_ADDED;
|
||||
}
|
||||
free_pathspec(&pathspec);
|
||||
}
|
||||
|
||||
static void wt_status_collect_untracked(struct wt_status *s)
|
||||
|
Loading…
Reference in New Issue
Block a user