Skip to content

Commit 39410bf

Browse files
trastgitster
authored andcommitted
Speed up log -L... -M
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 13b8f68 commit 39410bf

File tree

1 file changed

+52
-4
lines changed

1 file changed

+52
-4
lines changed

line-log.c

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,50 @@ static void move_diff_queue(struct diff_queue_struct *dst,
750750
DIFF_QUEUE_CLEAR(src);
751751
}
752752

753-
static void queue_diffs(struct diff_options *opt,
753+
static void filter_diffs_for_paths(struct line_log_data *range, int keep_deletions)
754+
{
755+
int i;
756+
struct diff_queue_struct outq;
757+
DIFF_QUEUE_CLEAR(&outq);
758+
759+
for (i = 0; i < diff_queued_diff.nr; i++) {
760+
struct diff_filepair *p = diff_queued_diff.queue[i];
761+
struct line_log_data *rg = NULL;
762+
763+
if (!DIFF_FILE_VALID(p->two)) {
764+
if (keep_deletions)
765+
diff_q(&outq, p);
766+
else
767+
diff_free_filepair(p);
768+
continue;
769+
}
770+
for (rg = range; rg; rg = rg->next) {
771+
if (!strcmp(rg->spec->path, p->two->path))
772+
break;
773+
}
774+
if (rg)
775+
diff_q(&outq, p);
776+
else
777+
diff_free_filepair(p);
778+
}
779+
free(diff_queued_diff.queue);
780+
diff_queued_diff = outq;
781+
}
782+
783+
static inline int diff_might_be_rename(void)
784+
{
785+
int i;
786+
for (i = 0; i < diff_queued_diff.nr; i++)
787+
if (!DIFF_FILE_VALID(diff_queued_diff.queue[i]->one)) {
788+
/* fprintf(stderr, "diff_might_be_rename found creation of: %s\n", */
789+
/* diff_queued_diff.queue[i]->two->path); */
790+
return 1;
791+
}
792+
return 0;
793+
}
794+
795+
static void queue_diffs(struct line_log_data *range,
796+
struct diff_options *opt,
754797
struct diff_queue_struct *queue,
755798
struct commit *commit, struct commit *parent)
756799
{
@@ -766,7 +809,12 @@ static void queue_diffs(struct diff_options *opt,
766809

767810
DIFF_QUEUE_CLEAR(&diff_queued_diff);
768811
diff_tree(&desc1, &desc2, "", opt);
769-
diffcore_std(opt);
812+
if (opt->detect_rename) {
813+
filter_diffs_for_paths(range, 1);
814+
if (diff_might_be_rename())
815+
diffcore_std(opt);
816+
filter_diffs_for_paths(range, 0);
817+
}
770818
move_diff_queue(queue, &diff_queued_diff);
771819

772820
if (tree1)
@@ -1050,7 +1098,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c
10501098
if (commit->parents)
10511099
parent = commit->parents->item;
10521100

1053-
queue_diffs(&rev->diffopt, &queue, commit, parent);
1101+
queue_diffs(range, &rev->diffopt, &queue, commit, parent);
10541102
changed = process_all_files(&parent_range, rev, &queue, range);
10551103
if (parent)
10561104
add_line_range(rev, parent, parent_range);
@@ -1075,7 +1123,7 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
10751123
for (i = 0; i < nparents; i++) {
10761124
parents[i] = p->item;
10771125
p = p->next;
1078-
queue_diffs(&rev->diffopt, &diffqueues[i], commit, parents[i]);
1126+
queue_diffs(range, &rev->diffopt, &diffqueues[i], commit, parents[i]);
10791127
}
10801128

10811129
for (i = 0; i < nparents; i++) {

0 commit comments

Comments
 (0)