Mercurial > hg
comparison mercurial/bdiff.c @ 29322:66dbdd3cc2b9 stable
bdiff: extend matches across popular lines
For very large diffs that have large numbers of identical lines (JSON
dumps) that also have large blocks of identical text, bdiff could become
confused about which block matches which because it can only match
very limited regions. The result is very large diffs for small sets of edits.
The earlier recursion rebalancing fix made this behavior more frequent because
it's now more prone to match block 1 to block 2. One frequent user of
large JSON files reported being unable to pass the resulting diffs
through their code review system.
Prior to this change, bdiff would calculate the length of a match at
(i, j) as 1 + length found at (i-1, j-1). With large number of popular
(ignored) lines, this often meant matches couldn't be extended
backwards at all and thus all matching regions were very small.
Disabling the popularity threshold is not an option because it brings
back quadratic behavior.
Instead, we extend a match backwards until we either found a previously
discovered match or we find a mismatching line. This thus successfully
bridges over any popular lines inside and before a matching region.
The larger regions then significant reduce the probability of confusion.
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Thu, 02 Jun 2016 17:09:06 -0500 |
parents | 87d4a6c5567e |
children | d29cb5e735e9 |
comparison
equal
deleted
inserted
replaced
29295:9b4f0ad02f51 | 29322:66dbdd3cc2b9 |
---|---|
164 ; | 164 ; |
165 | 165 |
166 /* loop through all lines match a[i] in b */ | 166 /* loop through all lines match a[i] in b */ |
167 for (; j >= b1; j = b[j].n) { | 167 for (; j >= b1; j = b[j].n) { |
168 /* does this extend an earlier match? */ | 168 /* does this extend an earlier match? */ |
169 if (i > a1 && j > b1 && pos[j - 1].pos == i - 1) | 169 for (k = 1; j - k >= b1 && i - k >= a1; k++) { |
170 k = pos[j - 1].len + 1; | 170 /* reached an earlier match? */ |
171 else | 171 if (pos[j - k].pos == i - k) { |
172 k = 1; | 172 k += pos[j - k].len; |
173 break; | |
174 } | |
175 /* previous line mismatch? */ | |
176 if (a[i - k].e != b[j - k].e) | |
177 break; | |
178 } | |
179 | |
173 pos[j].pos = i; | 180 pos[j].pos = i; |
174 pos[j].len = k; | 181 pos[j].len = k; |
175 | 182 |
176 /* best match so far? we prefer matches closer | 183 /* best match so far? we prefer matches closer |
177 to the middle to balance recursion */ | 184 to the middle to balance recursion */ |