File ticket13861.patch of Package trac
diff -ur Trac-1.6/doc/api/trac_versioncontrol_diff.rst Trac-1.6_fix/doc/api/trac_versioncontrol_diff.rst
--- doc/api/trac_versioncontrol_diff.rst 2023-09-23 01:00:51.000000000 +0200
+++ doc/api/trac_versioncontrol_diff.rst 2025-02-20 09:21:59.390945253 +0100
@@ -10,7 +10,7 @@
`difflib.SequenceMatcher`, and they generate groups of opcodes
corresponding to diff "hunks".
-`get_change_extent` is a low-level utility used when marking
+`get_change_extents` is a low-level utility used when marking
intra-lines differences.
`diff_blocks` is used at a higher-level to fill the template data
@@ -27,7 +27,7 @@
Function Reference
------------------
-.. autofunction :: get_change_extent
+.. autofunction :: get_change_extents
.. autofunction :: get_filtered_hunks
.. autofunction :: get_hunks
.. autofunction :: diff_blocks
diff -ur Trac-1.6/trac/mimeview/patch.py Trac-1.6_fix/trac/mimeview/patch.py
--- trac/mimeview/patch.py 2023-09-23 01:00:49.000000000 +0200
+++ trac/mimeview/patch.py 2025-02-20 09:20:07.829126484 +0100
@@ -77,15 +77,20 @@
def _markup_intraline_change(fromlines, tolines):
from trac.versioncontrol.diff import get_change_extent
for i in range(len(fromlines)):
- fr, to = fromlines[i], tolines[i]
- (start, end) = get_change_extent(fr, to)
- if start != 0 or end != 0:
- last = end+len(fr)
- fromlines[i] = fr[:start] + '\0' + fr[start:last] + \
- '\1' + fr[last:]
- last = end+len(to)
- tolines[i] = to[:start] + '\0' + to[start:last] + \
- '\1' + to[last:]
+ fromline, toline = fromlines[i], tolines[i]
+ fromlen = len(fromline)
+ tolen = len(toline)
+ for fromstart, fromend, tostart, toend in get_change_extents(fromline, toline):
+ if fromstart != fromend and (fromstart != 0 or fromend != fromlen):
+ fromline = (
+ fromline[:fromstart] + '\0' + fromline[fromstart:fromend] +
+ '\1' + fromline[fromend:])
+ if tostart != toend and (tostart != 0 or toend != tolen):
+ toline = (
+ toline[:tostart] + '\0' + toline[tostart:toend] +
+ '\1' + toline[toend:])
+ fromlines[i] = fromline
+ tolines[i] = toline
space_re = re.compile(' ( +)|^ ')
def htmlify(match):
diff -ur Trac-1.6/trac/versioncontrol/diff.py Trac-1.6_fix/trac/versioncontrol/diff.py
--- trac/versioncontrol/diff.py 2023-09-23 01:00:28.000000000 +0200
+++ trac/versioncontrol/diff.py 2025-02-20 09:16:31.485599568 +0100
@@ -30,24 +30,42 @@
return ' '.join(_whitespace_split(text))
-def get_change_extent(str1, str2):
- """Determines the extent of differences between two strings.
+def get_change_extents(str1, str2):
+ """Determines the extents of differences between two strings.
- Returns a pair containing the offset at which the changes start,
- and the negative offset at which the changes end.
-
- If the two strings have neither a common prefix nor a common
- suffix, ``(0, 0)`` is returned.
+ Returns a list of typles containing the offsets at which the changes start,
+ and the offsets at which the changes end:
+ [(startstr1,endstr1,startstr2,endstr2),(...)].
+ The list is sorted from the last to the first.
"""
+ done = []
start = 0
- limit = min(len(str1), len(str2))
+ minlen = 7
+ l1 = len(str1)
+ l2 = len(str2)
+ limit = min(l1, l2)
while start < limit and str1[start] == str2[start]:
start += 1
end = -1
limit = limit - start
while -end <= limit and str1[end] == str2[end]:
end -= 1
- return start, end + 1
+ new = [(start, l1 + end + 1, start, l2 + end + 1)]
+ matcher = difflib.SequenceMatcher(None, str1, str2)
+ while new:
+ n = []
+ for e in new:
+ if min(e[1]-e[0],e[3]-e[2]) < minlen+2:
+ done.append(e)
+ else:
+ (s1,s2,s) = matcher.find_longest_match(e[0], e[1], e[2], e[3])
+ if s < minlen:
+ done.append(e)
+ else:
+ n.append((e[0],s1,e[2],s2))
+ n.append((s1+s,e[1],s2+s,e[3]))
+ new = n
+ return sorted(done, reverse=True)
def get_filtered_hunks(fromlines, tolines, context=None,
@@ -207,16 +225,19 @@
if tag == 'replace' and i2 - i1 == j2 - j1:
for i in range(i2 - i1):
fromline, toline = fromlines[i1 + i], tolines[j1 + i]
- (start, end) = get_change_extent(fromline, toline)
- if start != 0 or end != 0:
- last = end + len(fromline)
- fromlines[i1 + i] = (
- fromline[:start] + '\0' + fromline[start:last] +
- '\1' + fromline[last:])
- last = end+len(toline)
- tolines[j1 + i] = (
- toline[:start] + '\0' + toline[start:last] +
- '\1' + toline[last:])
+ fromlen = len(fromline)
+ tolen = len(toline)
+ for fromstart, fromend, tostart, toend in get_change_extents(fromline, toline):
+ if fromstart != fromend and (fromstart != 0 or fromend != fromlen):
+ fromline = (
+ fromline[:fromstart] + '\0' + fromline[fromstart:fromend] +
+ '\1' + fromline[fromend:])
+ if tostart != toend and (tostart != 0 or toend != tolen):
+ toline = (
+ toline[:tostart] + '\0' + toline[tostart:toend] +
+ '\1' + toline[toend:])
+ fromlines[i1 + i] = fromline
+ tolines[j1 + i] = toline
yield tag, i1, i2, j1, j2
changes = []
diff -ur Trac-1.6/trac/versioncontrol/tests/diff.py Trac-1.6_fix/trac/versioncontrol/tests/diff.py
--- trac/versioncontrol/tests/diff.py 2023-09-23 01:00:29.000000000 +0200
+++ trac/versioncontrol/tests/diff.py 2025-02-20 09:31:47.556991308 +0100
@@ -25,15 +25,15 @@
class DiffTestCase(unittest.TestCase):
def testget_change_extent(self):
- self.assertEqual((3, 0), diff.get_change_extent('xxx', 'xxx'))
- self.assertEqual((0, 0), diff.get_change_extent('', 'xxx'))
- self.assertEqual((0, 0), diff.get_change_extent('xxx', ''))
- self.assertEqual((0, 0), diff.get_change_extent('xxx', 'yyy'))
- self.assertEqual((1, -1), diff.get_change_extent('xxx', 'xyx'))
- self.assertEqual((1, -1), diff.get_change_extent('xxx', 'xyyyx'))
- self.assertEqual((1, 0), diff.get_change_extent('xy', 'xzz'))
- self.assertEqual((1, -1), diff.get_change_extent('xyx', 'xzzx'))
- self.assertEqual((1, -1), diff.get_change_extent('xzzx', 'xyx'))
+ self.assertEqual([(3, 3, 3, 3)], diff.get_change_extents('xxx', 'xxx'))
+ self.assertEqual([(0, 0, 0, 3)], diff.get_change_extents('', 'xxx'))
+ self.assertEqual([(0, 3, 0, 0)], diff.get_change_extents('xxx', ''))
+ self.assertEqual([(0, 3, 0, 3)], diff.get_change_extents('xxx', 'yyy'))
+ self.assertEqual([(1, 2, 1, 2)], diff.get_change_extents('xxx', 'xyx'))
+ self.assertEqual([(1, 2, 1, 4)], diff.get_change_extents('xxx', 'xyyyx'))
+ self.assertEqual([(1, 2, 1, 3)], diff.get_change_extents('xy', 'xzz'))
+ self.assertEqual([(1, 2, 1, 3)], diff.get_change_extents('xyx', 'xzzx'))
+ self.assertEqual([(1, 3, 1, 2)], diff.get_change_extents('xzzx', 'xyx'))
def test_insert_blank_line(self):
opcodes = get_opcodes(['A', 'B'], ['A', 'B', ''], ignore_blank_lines=0)