@@ -79,6 +79,18 @@ void blame_tree_init(struct repository *r, struct blame_tree *bt,
if (add_from_revs(bt) < 0)
die(_("unable to setup blame-tree"));
+
+ pathspec_setup(&bt->rev.prune_data, &bt->paths);
+ copy_pathspec(&bt->rev.pruning.pathspec, &bt->rev.prune_data);
+ copy_pathspec(&bt->rev.diffopt.pathspec, &bt->rev.prune_data);
+ bt->rev.prune = 1;
+
+ /*
+ * Have the diff engine tell us about everything, including trees.
+ * We may have used --max-depth to get our list of paths to blame,
+ * in which case we would mention trees explicitly.
+ */
+ bt->rev.diffopt.flags.tree_in_recursive = 1;
}
void blame_tree_release(struct blame_tree *bt)
@@ -91,6 +103,7 @@ struct blame_tree_callback_data {
struct commit *commit;
struct string_list *paths;
size_t num_interesting;
+ struct rev_info *rev;
blame_tree_fn callback;
void *callback_data;
@@ -122,6 +135,10 @@ static void mark_path(const char *path, const struct object_id *oid,
data->num_interesting--;
if (data->callback)
data->callback(path, data->commit, data->callback_data);
+
+ pathspec_drop(&data->rev->pruning.pathspec, path);
+ clear_pathspec(&data->rev->diffopt.pathspec);
+ copy_pathspec(&data->rev->diffopt.pathspec, &data->rev->pruning.pathspec);
}
static void blame_diff(struct diff_queue_struct *q,
@@ -172,6 +189,7 @@ int blame_tree_run(struct blame_tree *bt, blame_tree_fn cb, void *cbdata)
data.num_interesting = bt->paths.nr;
data.callback = cb;
data.callback_data = cbdata;
+ data.rev = &bt->rev;
bt->rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
bt->rev.diffopt.format_callback = blame_diff;
@@ -117,6 +117,50 @@ struct pathspec_trie *pathspec_trie_build(const struct pathspec *pathspec)
return ret;
}
+static void pathspec_drop_from_trie(struct pathspec *ps,
+ const char *path)
+{
+ struct pathspec_trie *prev = NULL, *cur = ps->trie;
+ int pos = -1;
+
+ if (!cur)
+ return;
+
+ while (*path) {
+ const char *end = strchrnul(path, '/');
+ size_t len = end - path;
+ pos = pathspec_trie_lookup(cur, path, len);
+
+ if (pos < 0)
+ die("BUG: didn't find the pathspec trie we matched");
+
+ prev = cur;
+ cur = cur->entries[pos];
+ path = end;
+ while (*path == '/')
+ path++;
+ }
+
+ if (!cur->terminal)
+ die("BUG: pathspec trie we found isn't terminal?");
+
+ if (cur->nr) {
+ cur->terminal = 0;
+ cur->must_be_dir = 0;
+ return;
+ }
+
+ free(cur);
+ if (pos < 0)
+ ps->trie = NULL;
+ else if (prev) {
+ prev->nr--;
+ memmove(prev->entries + pos,
+ prev->entries + pos + 1,
+ sizeof(*prev->entries) * (prev->nr - pos));
+ }
+}
+
static void pathspec_trie_clear(struct pathspec_trie *t)
{
if (t) {
@@ -878,6 +922,40 @@ void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
dst->trie = pathspec_trie_build(dst);
}
+void pathspec_setup(struct pathspec *ps, const struct string_list *paths)
+{
+ struct strvec argv = STRVEC_INIT;
+ size_t i;
+
+ for (i = 0; i < paths->nr; i++)
+ strvec_push(&argv, paths->items[i].string);
+
+ clear_pathspec(ps);
+ parse_pathspec(ps, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
+ PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH, "",
+ argv.v);
+ strvec_clear(&argv);
+}
+
+void pathspec_drop(struct pathspec *ps, const char *path)
+{
+ int i;
+
+ /* We know these are literals, so we can just strcmp */
+ for (i = 0; i < ps->nr; i++)
+ if (!strcmp(ps->items[i].match, path))
+ break;
+
+ if (i == ps->nr)
+ die("BUG: didn't find the pathspec we just matched");
+
+ memmove(ps->items + i, ps->items + i + 1,
+ sizeof(*ps->items) * (ps->nr - i - 1));
+ ps->nr--;
+
+ pathspec_drop_from_trie(ps, path);
+}
+
void clear_pathspec(struct pathspec *pathspec)
{
int i, j;
@@ -3,6 +3,7 @@
struct index_state;
struct pathspec;
+struct string_list;
/* Pathspec magic */
#define PATHSPEC_FROMTOP (1<<0)
@@ -152,6 +153,8 @@ void parse_pathspec_file(struct pathspec *pathspec,
int nul_term_line);
void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
+void pathspec_setup(struct pathspec *ps, const struct string_list *paths);
+void pathspec_drop(struct pathspec *ps, const char *path);
void clear_pathspec(struct pathspec *);
/*