diff --git a/builtin/last-modified.c b/builtin/last-modified.c index fe012b0c2e..f4ccef6ac1 100644 --- a/builtin/last-modified.c +++ b/builtin/last-modified.c @@ -272,6 +272,9 @@ static bool maybe_changed_path(struct last_modified *lm, if (!filter) return true; + if (revs_maybe_changed_in_bloom(&lm->rev, filter) == 0) + return false; + hashmap_for_each_entry(&lm->paths, &iter, ent, hashent) { if (active && !bitmap_get(active, ent->diff_idx)) continue; @@ -358,6 +361,14 @@ static int last_modified_run(struct last_modified *lm) prepare_revision_walk(&lm->rev); + /* + * prepare_revision_walk() clears bloom_filter_settings for pathspecs + * without a Bloom key. Restore it so the per-path check keeps working. + */ + if (!lm->rev.bloom_filter_settings) + lm->rev.bloom_filter_settings = + get_bloom_filter_settings(lm->rev.repo); + max_count = lm->rev.max_count; init_active_paths_for_commit(&lm->active_paths); diff --git a/revision.c b/revision.c index 6bd3e3b7ec..8750ab210a 100644 --- a/revision.c +++ b/revision.c @@ -750,29 +750,43 @@ static int check_maybe_different_in_bloom_filter(struct rev_info *revs, struct commit *commit) { struct bloom_filter *filter; - int result = 0; + int result; if (commit_graph_generation(commit) == GENERATION_NUMBER_INFINITY) return -1; filter = get_bloom_filter(revs->repo, commit); - if (!filter) { count_bloom_filter_not_present++; return -1; } + result = revs_maybe_changed_in_bloom(revs, filter); + if (result < 0) + return result; + + if (result) + count_bloom_filter_maybe++; + else + count_bloom_filter_definitely_not++; + + return result; +} + +int revs_maybe_changed_in_bloom(struct rev_info *revs, + struct bloom_filter *filter) +{ + int result = 0; + + if (!revs->bloom_keyvecs_nr) + return -1; + for (size_t nr = 0; !result && nr < revs->bloom_keyvecs_nr; nr++) { result = bloom_filter_contains_vec(filter, revs->bloom_keyvecs[nr], revs->bloom_filter_settings); } - if (result) - count_bloom_filter_maybe++; - else - count_bloom_filter_definitely_not++; - return result; } @@ -806,7 +820,7 @@ static int rev_compare_tree(struct rev_info *revs, return REV_TREE_SAME; } - if (revs->bloom_keyvecs_nr && !nth_parent) { + if (!nth_parent) { bloom_ret = check_maybe_different_in_bloom_filter(revs, commit); if (bloom_ret == 0) @@ -833,7 +847,7 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit, if (!t1) return 0; - if (!nth_parent && revs->bloom_keyvecs_nr) { + if (!nth_parent) { bloom_ret = check_maybe_different_in_bloom_filter(revs, commit); if (!bloom_ret) return 1; diff --git a/revision.h b/revision.h index acf6d06b24..4fe83c7510 100644 --- a/revision.h +++ b/revision.h @@ -68,6 +68,7 @@ struct string_list; struct saved_parents; struct follow_pathspec_slab; struct bloom_keyvec; +struct bloom_filter; struct bloom_filter_settings; struct option; struct parse_opt_ctx_t; @@ -495,6 +496,22 @@ void reset_revision_walk(void); */ int prepare_revision_walk(struct rev_info *revs); +/** + * Take in a changed-path Bloom filter that belongs to a commit, and consult it + * to see if it might have modified any of the paths in the `revs`. + * The caller should look up `filter`, probably with get_bloom_filter(). + * prepare_revision_walk() needs to be called in advance to ensure + * pathspec key vectors are set up. + * + * Returns -1 if no sensible answer could be given because of missing + * preconditions (no pathspec key vectors). + * Returns 0 if the commit definitely did not change any of the paths and 1 if + * the commit maybe has changed one of them, although that might be a + * false-positive. + */ +int revs_maybe_changed_in_bloom(struct rev_info *revs, + struct bloom_filter *filter); + /* Drain the commits linked list into the priority queue. */ void rev_info_commit_list_to_queue(struct rev_info *revs); /**