diff --git a/builtin-add.c b/builtin-add.c index bf13aa3..02c6751 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -123,6 +123,7 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags) init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); rev.prune_data = pathspec; + rev.glob_paths = 0; /* git-add has its own filename matching machinery */ rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; data.flags = flags; diff --git a/builtin-blame.c b/builtin-blame.c index 9bced3b..237d1fe 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -343,7 +343,7 @@ static struct origin *find_origin(struct scoreboard *sb, paths[0] = origin->path; paths[1] = NULL; - diff_tree_setup_paths(paths, &diff_opts); + diff_tree_setup_paths(paths, &diff_opts, 0); if (diff_setup_done(&diff_opts) < 0) die("diff-setup"); @@ -417,7 +417,7 @@ static struct origin *find_rename(struct scoreboard *sb, diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_opts.single_follow = origin->path; paths[0] = NULL; - diff_tree_setup_paths(paths, &diff_opts); + diff_tree_setup_paths(paths, &diff_opts, 0); if (diff_setup_done(&diff_opts) < 0) die("diff-setup"); @@ -1099,7 +1099,7 @@ static int find_copy_in_parent(struct scoreboard *sb, diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; paths[0] = NULL; - diff_tree_setup_paths(paths, &diff_opts); + diff_tree_setup_paths(paths, &diff_opts, 0); if (diff_setup_done(&diff_opts) < 0) die("diff-setup"); @@ -2346,6 +2346,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix) parse_done: argc = parse_options_end(&ctx); + if (revs.glob_paths) { + error("git blame does not support `--glob-paths'"); + usage_with_options(blame_opt_usage, options); + } + if (!blame_move_score) blame_move_score = BLAME_DEFAULT_MOVE_SCORE; if (!blame_copy_score) diff --git a/builtin-reset.c b/builtin-reset.c index 2e5a886..6026b34 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -128,14 +128,15 @@ static void update_index_from_diff(struct diff_queue_struct *q, } static int read_from_tree(const char *prefix, const char **argv, - unsigned char *tree_sha1) + unsigned char *tree_sha1, int glob_paths) { struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); int index_fd, index_was_discarded = 0; struct diff_options opt; memset(&opt, 0, sizeof(opt)); - diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt); + diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), + &opt, glob_paths); opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; opt.format_callback_data = &index_was_discarded; @@ -171,6 +172,7 @@ static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL }; int cmd_reset(int argc, const char **argv, const char *prefix) { int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0; + int glob_paths = 0; const char *rev = "HEAD"; unsigned char sha1[20], *orig = NULL, sha1_orig[20], *old_orig = NULL, sha1_old_orig[20]; @@ -182,6 +184,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT), OPT_SET_INT(0, "hard", &reset_type, "reset HEAD, index and working tree", HARD), + OPT_BOOLEAN(0, "glob-paths", &glob_paths, + "match paths with fnmatch"), OPT_BOOLEAN('q', NULL, &quiet, "disable showing new HEAD in hard reset and progress message"), OPT_END() @@ -246,7 +250,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) else if (reset_type != NONE) die("Cannot do %s reset with paths.", reset_type_names[reset_type]); - return read_from_tree(prefix, argv + i, sha1); + return read_from_tree(prefix, argv + i, sha1, glob_paths); } if (reset_type == NONE) reset_type = MIXED; /* by default */ diff --git a/builtin-update-index.c b/builtin-update-index.c index 38eb53c..28b09a3 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -23,6 +23,7 @@ static int allow_replace; static int info_only; static int force_remove; static int verbose; +static int glob_paths; static int mark_valid_only; #define MARK_VALID 1 #define UNMARK_VALID 2 @@ -534,7 +535,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, glob_paths)) continue; if (has_head) old = read_one_ent(NULL, head_sha1, @@ -659,6 +660,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) force_remove = 1; continue; } + if (!strcmp(path, "--glob-paths")) { + glob_paths = 1; + continue; + } if (!strcmp(path, "-z")) { line_termination = 0; continue; @@ -702,6 +707,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) usage(update_index_usage); die("unknown option %s", path); } + if (glob_paths) + die("--glob-paths without -g"); p = prefix_path(prefix, prefix_length, path); update_one(p, NULL, 0); if (set_executable_bit) @@ -712,6 +719,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) if (read_from_stdin) { struct strbuf buf, nbuf; + if (glob_paths) + die("--glob-paths without -g"); strbuf_init(&buf, 0); strbuf_init(&nbuf, 0); while (strbuf_getline(&buf, stdin, line_termination) != EOF) { diff --git a/cache.h b/cache.h index a779d92..5560195 100644 --- a/cache.h +++ b/cache.h @@ -387,7 +387,8 @@ extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); 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); +extern int ce_path_match(const struct cache_entry *ce, const char **pathspec, + int glob_paths); 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_pipe(unsigned char *sha1, int fd, const char *type, int write_object); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); diff --git a/diff-lib.c b/diff-lib.c index e7eaff9..87925a2 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -77,7 +77,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, revs->glob_paths)) continue; if (ce_stage(ce)) { @@ -431,7 +431,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, revs->glob_paths)) do_oneway_diff(o, idx, tree); return 0; @@ -508,6 +508,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) init_revisions(&revs, NULL); revs.prune_data = opt->paths; + revs.glob_paths = opt->glob_paths; tree = parse_tree_indirect(tree_sha1); if (!tree) die("bad tree object %s", sha1_to_hex(tree_sha1)); diff --git a/diff-no-index.c b/diff-no-index.c index f6994cf..ec549a7 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -240,6 +240,7 @@ void diff_no_index(struct rev_info *revs, } else revs->diffopt.paths = argv + argc - 2; + revs->diffopt.glob_paths = 0; revs->diffopt.nr_paths = 2; DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); diff --git a/diff.h b/diff.h index 50fb5dd..56f0857 100644 --- a/diff.h +++ b/diff.h @@ -102,6 +102,7 @@ struct diff_options { FILE *file; int close_file; + int glob_paths; int nr_paths; const char **paths; int *pathlens; @@ -128,7 +129,8 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix); extern const char mime_boundary_leader[]; -extern void diff_tree_setup_paths(const char **paths, struct diff_options *); +extern void diff_tree_setup_paths(const char **paths, struct diff_options *opt, + int glob_paths); extern void diff_tree_release_paths(struct diff_options *); extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt); diff --git a/gitk-git/gitk b/gitk-git/gitk index fddcb45..18c5cbc 100644 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -1866,6 +1866,7 @@ proc makewindow {} { set gm [tk_optionMenu .tf.lbar.gdttype gdttype \ [mc "containing:"] \ [mc "touching paths:"] \ + [mc "touching paths (glob):"] \ [mc "adding/removing string:"]] trace add variable gdttype write gdttype_change pack .tf.lbar.gdttype -side left -fill y @@ -3588,6 +3589,11 @@ proc do_file_hl {serial} { set highlight_paths [makepatterns $paths] highlight_filelist set gdtargs [concat -- $paths] + } elseif {$gdttype eq [mc "touching paths (glob):"]} { + if {[catch {set paths [shellsplit $highlight_files]}]} return + set highlight_paths $paths + highlight_filelist + set gdtargs [concat --glob-paths -- $paths] } elseif {$gdttype eq [mc "adding/removing string:"]} { set gdtargs [list "-S$highlight_files"] } else { diff --git a/read-cache.c b/read-cache.c index 1648428..c11ded9 100644 --- a/read-cache.c +++ b/read-cache.c @@ -582,7 +582,8 @@ 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) +static int ce_path_match_standard(const struct cache_entry *ce, + const char **pathspec) { const char *match, *name; int len; @@ -608,6 +609,31 @@ int ce_path_match(const struct cache_entry *ce, const char **pathspec) return 0; } +static int ce_path_match_globbed(const struct cache_entry *ce, + const char **pathspec) +{ + const char *match, *name; + + if (!pathspec) + return 1; + + name = ce->name; + while ((match = *pathspec++) != NULL) { + if (!fnmatch(match, name, 0)) + return 1; + } + return 0; +} + +int ce_path_match(const struct cache_entry *ce, + const char **pathspec, int glob_paths) +{ + if (glob_paths) + return ce_path_match_globbed(ce, pathspec); + else + return ce_path_match_standard(ce, pathspec); +} + /* * We fundamentally don't like some paths: we don't want * dot or dot-dot anywhere, and for obvious reasons don't diff --git a/revision.c b/revision.c index 3897fec..0dd1091 100644 --- a/revision.c +++ b/revision.c @@ -519,6 +519,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs) if (revs->diffopt.nr_paths) { ids.diffopts.nr_paths = revs->diffopt.nr_paths; ids.diffopts.paths = revs->diffopt.paths; + ids.diffopts.glob_paths = revs->diffopt.glob_paths; /* CHECKME */ ids.diffopts.pathlens = revs->diffopt.pathlens; } @@ -826,7 +827,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, revs->glob_paths)) { prune_num++; prune = xrealloc(prune, sizeof(*prune) * prune_num); prune[prune_num-2] = ce->name; @@ -837,6 +838,7 @@ static void prepare_show_merge(struct rev_info *revs) i++; } revs->prune_data = prune; + revs->glob_paths = 0; revs->limited = 1; } @@ -1033,6 +1035,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->min_age = approxidate(arg + 8); } else if (!strcmp(arg, "--first-parent")) { revs->first_parent_only = 1; + } else if (!strcmp(arg, "--glob-paths")) { + revs->glob_paths = 1; } else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) { init_reflog_walk(&revs->reflog_info); } else if (!strcmp(arg, "--default")) { @@ -1220,6 +1224,7 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx, int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def) { int i, flags, left, seen_dashdash; + const char **paths = NULL; /* First, search for "--" */ seen_dashdash = 0; @@ -1230,7 +1235,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch argv[i] = NULL; argc = i; if (argv[i + 1]) - revs->prune_data = get_pathspec(revs->prefix, argv + i + 1); + paths = argv + i + 1; seen_dashdash = 1; break; } @@ -1290,6 +1295,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (seen_dashdash || *arg == '^') die("bad revision '%s'", arg); + if (revs->glob_paths) + die("--glob-paths without --"); + /* If we didn't have a "--": * (1) all filenames must exist; * (2) all rev-args must not be interpretable @@ -1301,10 +1309,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->prune_data = get_pathspec(revs->prefix, argv + i); + revs->glob_paths = 0; break; } } + /* Third, handle paths listed after -- */ + if (paths != NULL) { + if (revs->glob_paths) + revs->prune_data = paths; + else + revs->prune_data = get_pathspec(revs->prefix, paths); + } + if (revs->def == NULL) revs->def = def; if (revs->show_merge) @@ -1333,12 +1350,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->limited = 1; if (revs->prune_data) { - diff_tree_setup_paths(revs->prune_data, &revs->pruning); + diff_tree_setup_paths(revs->prune_data, &revs->pruning, + revs->glob_paths); /* 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, &revs->diffopt, + revs->glob_paths); } if (revs->combine_merges) { revs->ignore_merges = 0; diff --git a/revision.h b/revision.h index fa68c65..a68cdb8 100644 --- a/revision.h +++ b/revision.h @@ -32,6 +32,9 @@ struct rev_info { void *prune_data; unsigned int early_output; + /* whether prune_data contains fnmatch() patterns */ + unsigned int glob_paths:1; + /* Traversal flags */ unsigned int dense:1, prune:1, diff --git a/tree-diff.c b/tree-diff.c index bbb126f..0aa1e9b 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -82,6 +82,11 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const return 0; } +static int tree_entry_interesting_globbed(struct tree_desc *, const char *, + int, struct diff_options *); +static int tree_entry_interesting_standard(struct tree_desc *, const char *, + int, struct diff_options *); + /* * Is a tree entry interesting given the pathspec we have? * @@ -91,7 +96,19 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const * - 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) +static int tree_entry_interesting(struct tree_desc *desc, + const char *base, int baselen, struct diff_options *opt) +{ + if (opt->glob_paths) + return tree_entry_interesting_globbed(desc, base, + baselen, opt); + else + return tree_entry_interesting_standard(desc, base, + baselen, opt); +} + +static int tree_entry_interesting_standard(struct tree_desc *desc, + const char *base, int baselen, struct diff_options *opt) { const char *path; const unsigned char *sha1; @@ -190,6 +207,41 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int return never_interesting; /* No matches */ } +static int tree_entry_interesting_globbed(struct tree_desc *desc, + const char *base, int baselen, struct diff_options *opt) +{ + const char *path; + char *fullpath; + const unsigned char *sha1; + unsigned mode; + int i; + int pathlen; + int result; + + if (!opt->nr_paths) + return 1; + sha1 = tree_entry_extract(desc, &path, &mode); + if (S_ISDIR(mode)) + return 1; + pathlen = tree_entry_len(path, sha1); + + fullpath = xmalloc(pathlen + baselen + 1); + memcpy(fullpath, base, baselen); + memcpy(fullpath + baselen, path, pathlen + 1); + + result = 0; + for (i = 0; i < opt->nr_paths; i++) { + const char *match = opt->paths[i]; + if (!fnmatch(match, fullpath, 0)) { + result = 1; + break; + } + } + + free(fullpath); + return result; +} + /* 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) { @@ -338,7 +390,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co diff_opts.single_follow = opt->paths[0]; diff_opts.break_opt = opt->break_opt; paths[0] = NULL; - diff_tree_setup_paths(paths, &diff_opts); + diff_tree_setup_paths(paths, &diff_opts, 0); if (diff_setup_done(&diff_opts) < 0) die("unable to set up diff options to follow renames"); diff_tree(t1, t2, base, &diff_opts); @@ -362,7 +414,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co /* 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); + diff_tree_setup_paths(opt->paths, opt, 0); break; } } @@ -440,11 +492,13 @@ void diff_tree_release_paths(struct diff_options *opt) free(opt->pathlens); } -void diff_tree_setup_paths(const char **p, struct diff_options *opt) +void diff_tree_setup_paths(const char **p, struct diff_options *opt, + int glob_paths) { opt->nr_paths = 0; opt->pathlens = NULL; opt->paths = NULL; + opt->glob_paths = glob_paths; if (p) { int i;