So far log -L only used the implicit diff filtering by pathspec.  If
the user specifies -M, we cannot do that, and so we simply handed the
whole diff queue (which is approximately 'git show --raw') to
diffcore_std().

Unfortunately this is very slow.  We can optimize a lot if we throw
out files that we know cannot possibly be interesting, in the same
spirit that the pathspec filtering reduces the number of files.

However, in this case, we have to be more careful.  Because we want to
look out for renames, we need to keep all filepairs where something
was deleted.

This is a bit hacky and should really be replaced by equivalent
support in --follow, and just using that.  However, in the meantime it
speeds up 'log -M -L' by an order of magnitude.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Thomas Rast 2013-03-28 17:47:34 +01:00 коммит произвёл Junio C Hamano
Родитель 13b8f68c1f
Коммит 39410bf0bf
1 изменённых файлов: 52 добавлений и 4 удалений

Просмотреть файл

@ -750,7 +750,50 @@ static void move_diff_queue(struct diff_queue_struct *dst,
DIFF_QUEUE_CLEAR(src);
}
static void queue_diffs(struct diff_options *opt,
static void filter_diffs_for_paths(struct line_log_data *range, int keep_deletions)
{
int i;
struct diff_queue_struct outq;
DIFF_QUEUE_CLEAR(&outq);
for (i = 0; i < diff_queued_diff.nr; i++) {
struct diff_filepair *p = diff_queued_diff.queue[i];
struct line_log_data *rg = NULL;
if (!DIFF_FILE_VALID(p->two)) {
if (keep_deletions)
diff_q(&outq, p);
else
diff_free_filepair(p);
continue;
}
for (rg = range; rg; rg = rg->next) {
if (!strcmp(rg->spec->path, p->two->path))
break;
}
if (rg)
diff_q(&outq, p);
else
diff_free_filepair(p);
}
free(diff_queued_diff.queue);
diff_queued_diff = outq;
}
static inline int diff_might_be_rename(void)
{
int i;
for (i = 0; i < diff_queued_diff.nr; i++)
if (!DIFF_FILE_VALID(diff_queued_diff.queue[i]->one)) {
/* fprintf(stderr, "diff_might_be_rename found creation of: %s\n", */
/* diff_queued_diff.queue[i]->two->path); */
return 1;
}
return 0;
}
static void queue_diffs(struct line_log_data *range,
struct diff_options *opt,
struct diff_queue_struct *queue,
struct commit *commit, struct commit *parent)
{
@ -766,7 +809,12 @@ static void queue_diffs(struct diff_options *opt,
DIFF_QUEUE_CLEAR(&diff_queued_diff);
diff_tree(&desc1, &desc2, "", opt);
diffcore_std(opt);
if (opt->detect_rename) {
filter_diffs_for_paths(range, 1);
if (diff_might_be_rename())
diffcore_std(opt);
filter_diffs_for_paths(range, 0);
}
move_diff_queue(queue, &diff_queued_diff);
if (tree1)
@ -1050,7 +1098,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c
if (commit->parents)
parent = commit->parents->item;
queue_diffs(&rev->diffopt, &queue, commit, parent);
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);
@ -1075,7 +1123,7 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
for (i = 0; i < nparents; i++) {
parents[i] = p->item;
p = p->next;
queue_diffs(&rev->diffopt, &diffqueues[i], commit, parents[i]);
queue_diffs(range, &rev->diffopt, &diffqueues[i], commit, parents[i]);
}
for (i = 0; i < nparents; i++) {