diff options
Diffstat (limited to 'third_party/git/line-log.c')
-rw-r--r-- | third_party/git/line-log.c | 122 |
1 files changed, 96 insertions, 26 deletions
diff --git a/third_party/git/line-log.c b/third_party/git/line-log.c index 3aff1849e7d5..68eeb425f8ad 100644 --- a/third_party/git/line-log.c +++ b/third_party/git/line-log.c @@ -14,7 +14,8 @@ #include "graph.h" #include "userdiff.h" #include "line-log.h" -#include "argv-array.h" +#include "strvec.h" +#include "bloom.h" static void range_set_grow(struct range_set *rs, size_t extra) { @@ -519,7 +520,7 @@ static void fill_line_ends(struct repository *r, unsigned long *ends = NULL; char *data = NULL; - if (diff_populate_filespec(r, spec, 0)) + if (diff_populate_filespec(r, spec, NULL)) die("Cannot read blob %s", oid_to_hex(&spec->oid)); ALLOC_ARRAY(ends, size); @@ -737,6 +738,38 @@ static struct line_log_data *lookup_line_range(struct rev_info *revs, return ret; } +static int same_paths_in_pathspec_and_range(struct pathspec *pathspec, + struct line_log_data *range) +{ + int i; + struct line_log_data *r; + + for (i = 0, r = range; i < pathspec->nr && r; i++, r = r->next) + if (strcmp(pathspec->items[i].match, r->path)) + return 0; + if (i < pathspec->nr || r) + /* different number of pathspec items and ranges */ + return 0; + + return 1; +} + +static void parse_pathspec_from_ranges(struct pathspec *pathspec, + struct line_log_data *range) +{ + struct line_log_data *r; + struct strvec array = STRVEC_INIT; + const char **paths; + + for (r = range; r; r = r->next) + strvec_push(&array, r->path); + paths = strvec_detach(&array); + + parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, "", paths); + /* strings are now owned by pathspec */ + free(paths); +} + void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args) { struct commit *commit = NULL; @@ -746,20 +779,7 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list range = parse_lines(rev->diffopt.repo, commit, prefix, args); add_line_range(rev, commit, range); - if (!rev->diffopt.detect_rename) { - struct line_log_data *r; - struct argv_array array = ARGV_ARRAY_INIT; - const char **paths; - - for (r = range; r; r = r->next) - argv_array_push(&array, r->path); - paths = argv_array_detach(&array); - - parse_pathspec(&rev->diffopt.pathspec, 0, - PATHSPEC_PREFER_FULL, "", paths); - /* strings are now owned by pathspec */ - free(paths); - } + parse_pathspec_from_ranges(&rev->diffopt.pathspec, range); } static void move_diff_queue(struct diff_queue_struct *dst, @@ -817,15 +837,29 @@ static void queue_diffs(struct line_log_data *range, struct diff_queue_struct *queue, struct commit *commit, struct commit *parent) { + struct object_id *tree_oid, *parent_tree_oid; + assert(commit); + tree_oid = get_commit_tree_oid(commit); + parent_tree_oid = parent ? get_commit_tree_oid(parent) : NULL; + + if (opt->detect_rename && + !same_paths_in_pathspec_and_range(&opt->pathspec, range)) { + clear_pathspec(&opt->pathspec); + parse_pathspec_from_ranges(&opt->pathspec, range); + } DIFF_QUEUE_CLEAR(&diff_queued_diff); - diff_tree_oid(parent ? get_commit_tree_oid(parent) : NULL, - get_commit_tree_oid(commit), "", opt); - if (opt->detect_rename) { + diff_tree_oid(parent_tree_oid, tree_oid, "", opt); + if (opt->detect_rename && diff_might_be_rename()) { + /* must look at the full tree diff to detect renames */ + clear_pathspec(&opt->pathspec); + DIFF_QUEUE_CLEAR(&diff_queued_diff); + + diff_tree_oid(parent_tree_oid, tree_oid, "", opt); + filter_diffs_for_paths(range, 1); - if (diff_might_be_rename()) - diffcore_std(opt); + diffcore_std(opt); filter_diffs_for_paths(range, 0); } move_diff_queue(queue, &diff_queued_diff); @@ -1012,12 +1046,12 @@ static int process_diff_filepair(struct rev_info *rev, return 0; assert(pair->two->oid_valid); - diff_populate_filespec(rev->diffopt.repo, pair->two, 0); + diff_populate_filespec(rev->diffopt.repo, pair->two, NULL); file_target.ptr = pair->two->data; file_target.size = pair->two->size; if (pair->one->oid_valid) { - diff_populate_filespec(rev->diffopt.repo, pair->one, 0); + diff_populate_filespec(rev->diffopt.repo, pair->one, NULL); file_parent.ptr = pair->one->data; file_parent.size = pair->one->size; } else { @@ -1113,6 +1147,37 @@ int line_log_print(struct rev_info *rev, struct commit *commit) return 1; } +static int bloom_filter_check(struct rev_info *rev, + struct commit *commit, + struct line_log_data *range) +{ + struct bloom_filter *filter; + struct bloom_key key; + int result = 0; + + if (!commit->parents) + return 1; + + if (!rev->bloom_filter_settings || + !(filter = get_bloom_filter(rev->repo, commit))) + return 1; + + if (!range) + return 0; + + while (!result && range) { + fill_bloom_key(range->path, strlen(range->path), &key, rev->bloom_filter_settings); + + if (bloom_filter_contains(filter, &key, rev->bloom_filter_settings)) + result = 1; + + clear_bloom_key(&key); + range = range->next; + } + + return result; +} + static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *commit, struct line_log_data *range) { @@ -1126,6 +1191,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c queue_diffs(range, &rev->diffopt, &queue, commit, parent); changed = process_all_files(&parent_range, rev, &queue, range); + if (parent) add_line_range(rev, parent, parent_range); free_line_log_data(parent_range); @@ -1194,13 +1260,17 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm /* NEEDSWORK leaking like a sieve */ } -static int process_ranges_arbitrary_commit(struct rev_info *rev, struct commit *commit) +int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit *commit) { struct line_log_data *range = lookup_line_range(rev, commit); int changed = 0; if (range) { - if (!commit->parents || !commit->parents->next) + if (commit->parents && !bloom_filter_check(rev, commit, range)) { + struct line_log_data *prange = line_log_data_copy(range); + add_line_range(rev, commit->parents->item, prange); + clear_commit_line_range(rev, commit); + } else if (!commit->parents || !commit->parents->next) changed = process_ranges_ordinary_commit(rev, commit, range); else changed = process_ranges_merge_commit(rev, commit, range); @@ -1237,7 +1307,7 @@ int line_log_filter(struct rev_info *rev) while (list) { struct commit_list *to_free = NULL; commit = list->item; - if (process_ranges_arbitrary_commit(rev, commit)) { + if (line_log_process_ranges_arbitrary_commit(rev, commit)) { *pp = list; pp = &list->next; } else |