Merge branch 'sg/line-log-merge-optim'

"git log -L..." compared trees of multiple parents with the tree of the
merge result in an unnecessarily inefficient way.

* sg/line-log-merge-optim:
  line-log: simplify condition checking for merge commits
  line-log: initialize diff queue in process_ranges_ordinary_commit()
  line-log: get rid of the parents array in process_ranges_merge_commit()
  line-log: avoid unnecessary tree diffs when processing merge commits
main
Junio C Hamano 2025-08-28 11:28:57 -07:00
commit c039684293
1 changed files with 20 additions and 30 deletions

View File

@ -1087,13 +1087,6 @@ static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair)
return new_filepair;
}

static void free_diffqueues(int n, struct diff_queue_struct *dq)
{
for (int i = 0; i < n; i++)
diff_queue_clear(&dq[i]);
free(dq);
}

static int process_all_files(struct line_log_data **range_out,
struct rev_info *rev,
struct diff_queue_struct *queue,
@ -1189,7 +1182,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c
struct line_log_data *range)
{
struct commit *parent = NULL;
struct diff_queue_struct queue;
struct diff_queue_struct queue = DIFF_QUEUE_INIT;
struct line_log_data *parent_range;
int changed;

@ -1209,9 +1202,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c
static int process_ranges_merge_commit(struct rev_info *rev, struct commit *commit,
struct line_log_data *range)
{
struct diff_queue_struct *diffqueues;
struct line_log_data **cand;
struct commit **parents;
struct commit_list *p;
int i;
int nparents = commit_list_count(commit->parents);
@ -1220,28 +1211,27 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
if (nparents > 1 && rev->first_parent_only)
nparents = 1;

ALLOC_ARRAY(diffqueues, nparents);
CALLOC_ARRAY(cand, nparents);
ALLOC_ARRAY(parents, nparents);

p = commit->parents;
for (i = 0; i < nparents; i++) {
parents[i] = p->item;
p = p->next;
queue_diffs(range, &rev->diffopt, &diffqueues[i], commit, parents[i]);
}

for (i = 0; i < nparents; i++) {
for (p = commit->parents, i = 0;
p && i < nparents;
p = p->next, i++) {
struct commit *parent = p->item;
struct diff_queue_struct diffqueue = DIFF_QUEUE_INIT;
int changed;
changed = process_all_files(&cand[i], rev, &diffqueues[i], range);

queue_diffs(range, &rev->diffopt, &diffqueue, commit, parent);

changed = process_all_files(&cand[i], rev, &diffqueue, range);
diff_queue_clear(&diffqueue);
if (!changed) {
/*
* This parent can take all the blame, so we
* don't follow any other path in history
*/
add_line_range(rev, parents[i], cand[i]);
add_line_range(rev, parent, cand[i]);
free_commit_list(commit->parents);
commit_list_append(parents[i], &commit->parents);
commit_list_append(parent, &commit->parents);

ret = 0;
goto out;
@ -1252,14 +1242,15 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
* No single parent took the blame. We add the candidates
* from the above loop to the parents.
*/
for (i = 0; i < nparents; i++)
add_line_range(rev, parents[i], cand[i]);
for (p = commit->parents, i = 0;
p && i < nparents;
p = p->next, i++)
add_line_range(rev, p->item, cand[i]);

ret = 1;

out:
clear_commit_line_range(rev, commit);
free(parents);
for (i = 0; i < nparents; i++) {
if (!cand[i])
continue;
@ -1267,7 +1258,6 @@ out:
free(cand[i]);
}
free(cand);
free_diffqueues(nparents, diffqueues);
return ret;

/* NEEDSWORK evil merge detection stuff */
@ -1283,10 +1273,10 @@ int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit
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
} else if (commit->parents && commit->parents->next)
changed = process_ranges_merge_commit(rev, commit, range);
else
changed = process_ranges_ordinary_commit(rev, commit, range);
}

if (!changed)