diff options
Diffstat (limited to 'mercurial/mdiff.py')
-rw-r--r-- | mercurial/mdiff.py | 175 |
1 files changed, 55 insertions, 120 deletions
diff --git a/mercurial/mdiff.py b/mercurial/mdiff.py index f93a8a6..4d1e760 100644 --- a/mercurial/mdiff.py +++ b/mercurial/mdiff.py @@ -67,81 +67,14 @@ defaultopts = diffopts() def wsclean(opts, text, blank=True): if opts.ignorews: - text = bdiff.fixws(text, 1) + text = re.sub('[ \t\r]+', '', text) elif opts.ignorewsamount: - text = bdiff.fixws(text, 0) + text = re.sub('[ \t\r]+', ' ', text) + text = text.replace(' \n', '\n') if blank and opts.ignoreblanklines: - text = re.sub('\n+', '\n', text).strip('\n') + text = re.sub('\n+', '', text) return text -def splitblock(base1, lines1, base2, lines2, opts): - # The input lines matches except for interwoven blank lines. We - # transform it into a sequence of matching blocks and blank blocks. - lines1 = [(wsclean(opts, l) and 1 or 0) for l in lines1] - lines2 = [(wsclean(opts, l) and 1 or 0) for l in lines2] - s1, e1 = 0, len(lines1) - s2, e2 = 0, len(lines2) - while s1 < e1 or s2 < e2: - i1, i2, btype = s1, s2, '=' - if (i1 >= e1 or lines1[i1] == 0 - or i2 >= e2 or lines2[i2] == 0): - # Consume the block of blank lines - btype = '~' - while i1 < e1 and lines1[i1] == 0: - i1 += 1 - while i2 < e2 and lines2[i2] == 0: - i2 += 1 - else: - # Consume the matching lines - while i1 < e1 and lines1[i1] == 1 and lines2[i2] == 1: - i1 += 1 - i2 += 1 - yield [base1 + s1, base1 + i1, base2 + s2, base2 + i2], btype - s1 = i1 - s2 = i2 - -def allblocks(text1, text2, opts=None, lines1=None, lines2=None, refine=False): - """Return (block, type) tuples, where block is an mdiff.blocks - line entry. type is '=' for blocks matching exactly one another - (bdiff blocks), '!' for non-matching blocks and '~' for blocks - matching only after having filtered blank lines. If refine is True, - then '~' blocks are refined and are only made of blank lines. - line1 and line2 are text1 and text2 split with splitnewlines() if - they are already available. - """ - if opts is None: - opts = defaultopts - if opts.ignorews or opts.ignorewsamount: - text1 = wsclean(opts, text1, False) - text2 = wsclean(opts, text2, False) - diff = bdiff.blocks(text1, text2) - for i, s1 in enumerate(diff): - # The first match is special. - # we've either found a match starting at line 0 or a match later - # in the file. If it starts later, old and new below will both be - # empty and we'll continue to the next match. - if i > 0: - s = diff[i - 1] - else: - s = [0, 0, 0, 0] - s = [s[1], s1[0], s[3], s1[2]] - - # bdiff sometimes gives huge matches past eof, this check eats them, - # and deals with the special first match case described above - if s[0] != s[1] or s[2] != s[3]: - type = '!' - if opts.ignoreblanklines: - if lines1 is None: - lines1 = splitnewlines(text1) - if lines2 is None: - lines2 = splitnewlines(text2) - old = wsclean(opts, "".join(lines1[s[0]:s[1]])) - new = wsclean(opts, "".join(lines2[s[2]:s[3]])) - if old == new: - type = '~' - yield s, type - yield s1, '=' - def diffline(revs, a, b, opts): parts = ['diff'] if opts.git: @@ -156,10 +89,10 @@ def diffline(revs, a, b, opts): return ' '.join(parts) + '\n' def unidiff(a, ad, b, bd, fn1, fn2, r=None, opts=defaultopts): - def datetag(date, fn=None): + def datetag(date, addtab=True): if not opts.git and not opts.nodates: return '\t%s\n' % date - if fn and ' ' in fn: + if addtab and ' ' in fn1: return '\t\n' return '\n' @@ -167,9 +100,6 @@ def unidiff(a, ad, b, bd, fn1, fn2, r=None, opts=defaultopts): return "" epoch = util.datestr((0, 0)) - fn1 = util.pconvert(fn1) - fn2 = util.pconvert(fn2) - if not opts.text and (util.binary(a) or util.binary(b)): if a and b and len(a) == len(b) and a == b: return "" @@ -177,19 +107,19 @@ def unidiff(a, ad, b, bd, fn1, fn2, r=None, opts=defaultopts): elif not a: b = splitnewlines(b) if a is None: - l1 = '--- /dev/null%s' % datetag(epoch) + l1 = '--- /dev/null%s' % datetag(epoch, False) else: - l1 = "--- %s%s" % ("a/" + fn1, datetag(ad, fn1)) - l2 = "+++ %s%s" % ("b/" + fn2, datetag(bd, fn2)) + l1 = "--- %s%s" % ("a/" + fn1, datetag(ad)) + l2 = "+++ %s%s" % ("b/" + fn2, datetag(bd)) l3 = "@@ -0,0 +1,%d @@\n" % len(b) l = [l1, l2, l3] + ["+" + e for e in b] elif not b: a = splitnewlines(a) - l1 = "--- %s%s" % ("a/" + fn1, datetag(ad, fn1)) + l1 = "--- %s%s" % ("a/" + fn1, datetag(ad)) if b is None: - l2 = '+++ /dev/null%s' % datetag(epoch) + l2 = '+++ /dev/null%s' % datetag(epoch, False) else: - l2 = "+++ %s%s" % ("b/" + fn2, datetag(bd, fn2)) + l2 = "+++ %s%s" % ("b/" + fn2, datetag(bd)) l3 = "@@ -1,%d +0,0 @@\n" % len(a) l = [l1, l2, l3] + ["-" + e for e in a] else: @@ -199,8 +129,8 @@ def unidiff(a, ad, b, bd, fn1, fn2, r=None, opts=defaultopts): if not l: return "" - l.insert(0, "--- a/%s%s" % (fn1, datetag(ad, fn1))) - l.insert(1, "+++ b/%s%s" % (fn2, datetag(bd, fn2))) + l.insert(0, "--- a/%s%s" % (fn1, datetag(ad))) + l.insert(1, "+++ b/%s%s" % (fn2, datetag(bd))) for ln in xrange(len(l)): if l[ln][-1] != '\n': @@ -227,7 +157,6 @@ def _unidiff(t1, t2, l1, l2, opts=defaultopts): return 0 return ret - lastfunc = [0, ''] def yieldhunk(hunk): (astart, a2, bstart, b2, delta) = hunk aend = contextend(a2, len(l1)) @@ -236,55 +165,61 @@ def _unidiff(t1, t2, l1, l2, opts=defaultopts): func = "" if opts.showfunc: - lastpos, func = lastfunc - # walk backwards from the start of the context up to the start of - # the previous hunk context until we find a line starting with an - # alphanumeric char. - for i in xrange(astart - 1, lastpos - 1, -1): - if l1[i][0].isalnum(): - func = ' ' + l1[i].rstrip()[:40] - lastfunc[1] = func + # walk backwards from the start of the context + # to find a line starting with an alphanumeric char. + for x in xrange(astart - 1, -1, -1): + t = l1[x].rstrip() + if funcre.match(t): + func = ' ' + t[:40] break - # by recording this hunk's starting point as the next place to - # start looking for function lines, we avoid reading any line in - # the file more than once. - lastfunc[0] = astart - - # zero-length hunk ranges report their start line as one less - if alen: - astart += 1 - if blen: - bstart += 1 - - yield "@@ -%d,%d +%d,%d @@%s\n" % (astart, alen, - bstart, blen, func) + + yield "@@ -%d,%d +%d,%d @@%s\n" % (astart + 1, alen, + bstart + 1, blen, func) for x in delta: yield x for x in xrange(a2, aend): yield ' ' + l1[x] + if opts.showfunc: + funcre = re.compile('\w') + # bdiff.blocks gives us the matching sequences in the files. The loop # below finds the spaces between those matching sequences and translates # them into diff output. # + if opts.ignorews or opts.ignorewsamount: + t1 = wsclean(opts, t1, False) + t2 = wsclean(opts, t2, False) + + diff = bdiff.blocks(t1, t2) hunk = None - ignoredlines = 0 - for s, stype in allblocks(t1, t2, opts, l1, l2): - a1, a2, b1, b2 = s - if stype != '!': - if stype == '~': - # The diff context lines are based on t1 content. When - # blank lines are ignored, the new lines offsets must - # be adjusted as if equivalent blocks ('~') had the - # same sizes on both sides. - ignoredlines += (b2 - b1) - (a2 - a1) - continue + for i, s1 in enumerate(diff): + # The first match is special. + # we've either found a match starting at line 0 or a match later + # in the file. If it starts later, old and new below will both be + # empty and we'll continue to the next match. + if i > 0: + s = diff[i - 1] + else: + s = [0, 0, 0, 0] delta = [] + a1 = s[1] + a2 = s1[0] + b1 = s[3] + b2 = s1[2] + old = l1[a1:a2] new = l2[b1:b2] - b1 -= ignoredlines - b2 -= ignoredlines + # bdiff sometimes gives huge matches past eof, this check eats them, + # and deals with the special first match case described above + if not old and not new: + continue + + if opts.ignoreblanklines: + if wsclean(opts, "".join(old)) == wsclean(opts, "".join(new)): + continue + astart = contextstart(a1) bstart = contextstart(b1) prev = None @@ -327,7 +262,7 @@ def patchtext(bin): def patch(a, bin): if len(a) == 0: # skip over trivial delta header - return util.buffer(bin, 12) + return buffer(bin, 12) return mpatch.patches(a, [bin]) # similar to difflib.SequenceMatcher.get_matching_blocks |