##// END OF EJS Templates
hgweb: pre-init mimetypes module (fixes ugly bug in python-2.6.2 mimetypes)...
hgweb: pre-init mimetypes module (fixes ugly bug in python-2.6.2 mimetypes) The ugly initialization hack in mimetypes' initialization procedure can make hg serve hit the maximum recursion depth when in threaded mode.

File last commit:

r7436:07faba78 default
r8224:1075f5c1 default
Show More
mdiff.py
269 lines | 8.2 KiB | text/x-python | PythonLexer
mpm@selenic.com
mdiff.py: kill #! line, add copyright notice...
r239 # mdiff.py - diff and patch routines for mercurial
#
Vadim Gelfer
update copyrights.
r2859 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
mpm@selenic.com
mdiff.py: kill #! line, add copyright notice...
r239 #
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
Patrick Mezard
Let --unified default to diff.unified (issue 1076)
r6467 from i18n import _
Dirkjan Ochtman
python 2.6 compatibility: compatibility wrappers for hash functions
r6470 import bdiff, mpatch, re, struct, util
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
Vadim Gelfer
fix speed regression in mdiff caused by line split bugfix.
r2251 def splitnewlines(text):
Vadim Gelfer
fix diffs containing embedded "\r"....
r2248 '''like str.splitlines, but only split on newlines.'''
Vadim Gelfer
fix speed regression in mdiff caused by line split bugfix.
r2251 lines = [l + '\n' for l in text.split('\n')]
if lines:
if lines[-1] == '\n':
lines.pop()
else:
lines[-1] = lines[-1][:-1]
return lines
Vadim Gelfer
fix diffs containing embedded "\r"....
r2248
Vadim Gelfer
refactor text diff/patch code....
r2874 class diffopts(object):
'''context is the number of context lines
text treats all files as text
showfunc enables diff -p output
Brendan Cully
Add diff --git option
r2907 git enables the git extended patch format
Stephen Darnell
Add -D/--nodates options to hg diff/export that removes dates from diff headers...
r3199 nodates removes dates from diff headers
Vadim Gelfer
refactor text diff/patch code....
r2874 ignorews ignores all whitespace changes in the diff
ignorewsamount ignores changes in the amount of whitespace
ignoreblanklines ignores changes whose lines are all blank'''
Thomas Arendsen Hein
Show revisions in diffs like CVS, based on a patch from Goffredo Baroncelli....
r396
Vadim Gelfer
refactor text diff/patch code....
r2874 defaults = {
'context': 3,
'text': False,
Matt Mackall
diff: don't show function name by default...
r5863 'showfunc': False,
Brendan Cully
Add diff --git option
r2907 'git': False,
Stephen Darnell
Add -D/--nodates options to hg diff/export that removes dates from diff headers...
r3199 'nodates': False,
Vadim Gelfer
refactor text diff/patch code....
r2874 'ignorews': False,
'ignorewsamount': False,
'ignoreblanklines': False,
}
__slots__ = defaults.keys()
def __init__(self, **opts):
for k in self.__slots__:
v = opts.get(k)
if v is None:
v = self.defaults[k]
setattr(self, k, v)
Patrick Mezard
Let --unified default to diff.unified (issue 1076)
r6467 try:
self.context = int(self.context)
except ValueError:
raise util.Abort(_('diff context lines count must be '
'an integer, not %r') % self.context)
Vadim Gelfer
refactor text diff/patch code....
r2874 defaultopts = diffopts()
Matt Mackall
diff: correctly handle combinations of whitespace options
r4878 def wsclean(opts, text):
if opts.ignorews:
text = re.sub('[ \t]+', '', text)
elif opts.ignorewsamount:
text = re.sub('[ \t]+', ' ', text)
text = re.sub('[ \t]+\n', '\n', text)
if opts.ignoreblanklines:
text = re.sub('\n+', '', text)
return text
Dirkjan Ochtman
patch/diff: use a separate function to write the first line of a file diff
r7200 def diffline(revs, a, b, opts):
parts = ['diff']
if opts.git:
parts.append('--git')
if revs and not opts.git:
parts.append(' '.join(["-r %s" % rev for rev in revs]))
if opts.git:
parts.append('a/%s' % a)
parts.append('b/%s' % b)
else:
parts.append(a)
return ' '.join(parts) + '\n'
Thomas Arendsen Hein
Remove trailing space
r7204
Dustin Sallings
Use both the from and to name in mdiff.unidiff....
r5482 def unidiff(a, ad, b, bd, fn1, fn2, r=None, opts=defaultopts):
Alexis S. L. Carvalho
git patches: correct handling of filenames with spaces...
r4679 def datetag(date, addtab=True):
if not opts.git and not opts.nodates:
return '\t%s\n' % date
Dustin Sallings
Use both the from and to name in mdiff.unidiff....
r5482 if addtab and ' ' in fn1:
Alexis S. L. Carvalho
git patches: correct handling of filenames with spaces...
r4679 return '\t\n'
return '\n'
Brendan Cully
Remove dates from git export file lines - they confuse git-apply
r3026
mpm@selenic.com
unidiff: punt on comparing empty files
r35 if not a and not b: return ""
Matt Mackall
Clean up mdiff imports
r1379 epoch = util.datestr((0, 0))
mpm@selenic.com
Attempt to make diff deal with null sources properly...
r264
Vadim Gelfer
refactor text diff/patch code....
r2874 if not opts.text and (util.binary(a) or util.binary(b)):
Martin Geisler
mdiff: compare content of binary files directly...
r6871 if a and b and len(a) == len(b) and a == b:
tailgunner@smtp.ru
Don't lie that "binary file has changed"...
r4103 return ""
Dustin Sallings
Use both the from and to name in mdiff.unidiff....
r5482 l = ['Binary file %s has changed\n' % fn1]
Thomas Arendsen Hein
Fix diff against an empty file (issue124) and add a test for this.
r1723 elif not a:
Vadim Gelfer
fix speed regression in mdiff caused by line split bugfix.
r2251 b = splitnewlines(b)
Thomas Arendsen Hein
Fix diff against an empty file (issue124) and add a test for this.
r1723 if a is None:
Alexis S. L. Carvalho
git patches: correct handling of filenames with spaces...
r4679 l1 = '--- /dev/null%s' % datetag(epoch, False)
Thomas Arendsen Hein
Fix diff against an empty file (issue124) and add a test for this.
r1723 else:
Dustin Sallings
Use both the from and to name in mdiff.unidiff....
r5482 l1 = "--- %s%s" % ("a/" + fn1, datetag(ad))
l2 = "+++ %s%s" % ("b/" + fn2, datetag(bd))
mpm@selenic.com
Attempt to make diff deal with null sources properly...
r264 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
l = [l1, l2, l3] + ["+" + e for e in b]
Thomas Arendsen Hein
Fix diff against an empty file (issue124) and add a test for this.
r1723 elif not b:
Vadim Gelfer
fix speed regression in mdiff caused by line split bugfix.
r2251 a = splitnewlines(a)
Dustin Sallings
Use both the from and to name in mdiff.unidiff....
r5482 l1 = "--- %s%s" % ("a/" + fn1, datetag(ad))
Thomas Arendsen Hein
Fix diff against an empty file (issue124) and add a test for this.
r1723 if b is None:
Alexis S. L. Carvalho
git patches: correct handling of filenames with spaces...
r4679 l2 = '+++ /dev/null%s' % datetag(epoch, False)
Thomas Arendsen Hein
Fix diff against an empty file (issue124) and add a test for this.
r1723 else:
Dustin Sallings
Use both the from and to name in mdiff.unidiff....
r5482 l2 = "+++ %s%s" % ("b/" + fn2, datetag(bd))
mpm@selenic.com
Attempt to make diff deal with null sources properly...
r264 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
l = [l1, l2, l3] + ["-" + e for e in a]
else:
Vadim Gelfer
fix speed regression in mdiff caused by line split bugfix.
r2251 al = splitnewlines(a)
bl = splitnewlines(b)
Dustin Sallings
Use both the from and to name in mdiff.unidiff....
r5482 l = list(bunidiff(a, b, al, bl, "a/" + fn1, "b/" + fn2, opts=opts))
mpm@selenic.com
unidiff: handle empty diffs more gracefully...
r278 if not l: return ""
mpm@selenic.com
diff: use tab to separate date from filename...
r272 # difflib uses a space, rather than a tab
Brendan Cully
Remove dates from git export file lines - they confuse git-apply
r3026 l[0] = "%s%s" % (l[0][:-2], datetag(ad))
l[1] = "%s%s" % (l[1][:-2], datetag(bd))
mpm@selenic.com
hg diff: fix missing final newline bug
r170
for ln in xrange(len(l)):
if l[ln][-1] != '\n':
l[ln] += "\n\ No newline at end of file\n"
Thomas Arendsen Hein
Show revisions in diffs like CVS, based on a patch from Goffredo Baroncelli....
r396 if r:
Dirkjan Ochtman
patch/diff: use a separate function to write the first line of a file diff
r7200 l.insert(0, diffline(r, fn1, fn2, opts))
Thomas Arendsen Hein
Show revisions in diffs like CVS, based on a patch from Goffredo Baroncelli....
r396
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 return "".join(l)
mason@suse.com
Add new bdiff based unidiff generation.
r1637 # somewhat self contained replacement for difflib.unified_diff
# t1 and t2 are the text to be diffed
# l1 and l2 are the text broken up into lines
# header1 and header2 are the filenames for the diff output
Vadim Gelfer
refactor text diff/patch code....
r2874 def bunidiff(t1, t2, l1, l2, header1, header2, opts=defaultopts):
mason@suse.com
Add new bdiff based unidiff generation.
r1637 def contextend(l, len):
Vadim Gelfer
refactor text diff/patch code....
r2874 ret = l + opts.context
mason@suse.com
Add new bdiff based unidiff generation.
r1637 if ret > len:
ret = len
return ret
def contextstart(l):
Vadim Gelfer
refactor text diff/patch code....
r2874 ret = l - opts.context
mason@suse.com
Add new bdiff based unidiff generation.
r1637 if ret < 0:
return 0
return ret
def yieldhunk(hunk, header):
if header:
for x in header:
yield x
(astart, a2, bstart, b2, delta) = hunk
aend = contextend(a2, len(l1))
alen = aend - astart
blen = b2 - bstart + aend - a2
func = ""
Vadim Gelfer
refactor text diff/patch code....
r2874 if opts.showfunc:
mason@suse.com
Add new bdiff based unidiff generation.
r1637 # walk backwards from the start of the context
# to find a line starting with an alphanumeric char.
Dirkjan Ochtman
diff: fix obscure off-by-one error in diff -p
r7436 for x in xrange(astart - 1, -1, -1):
mason@suse.com
Add new bdiff based unidiff generation.
r1637 t = l1[x].rstrip()
if funcre.match(t):
func = ' ' + t[:40]
break
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]
header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ]
Vadim Gelfer
refactor text diff/patch code....
r2874 if opts.showfunc:
mason@suse.com
Add new bdiff based unidiff generation.
r1637 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.
#
diff = bdiff.blocks(t1, t2)
hunk = None
for i in xrange(len(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 = []
s1 = diff[i]
a1 = s[1]
a2 = s1[0]
b1 = s[3]
b2 = s1[2]
old = l1[a1:a2]
new = l2[b1:b2]
# 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
Matt Mackall
diff: correctly handle combinations of whitespace options
r4878 if opts.ignorews or opts.ignorewsamount or opts.ignoreblanklines:
if wsclean(opts, "".join(old)) == wsclean(opts, "".join(new)):
mason@suse.com
Add new bdiff based unidiff generation.
r1637 continue
astart = contextstart(a1)
bstart = contextstart(b1)
prev = None
if hunk:
# join with the previous hunk if it falls inside the context
Vadim Gelfer
refactor text diff/patch code....
r2874 if astart < hunk[1] + opts.context + 1:
mason@suse.com
Add new bdiff based unidiff generation.
r1637 prev = hunk
astart = hunk[1]
bstart = hunk[3]
else:
for x in yieldhunk(hunk, header):
yield x
# we only want to yield the header if the files differ, and
# we only want to yield it once.
header = None
if prev:
# we've joined the previous hunk, record the new ending points.
hunk[1] = a2
hunk[3] = b2
delta = hunk[4]
else:
# create a new hunk
hunk = [ astart, a2, bstart, b2, delta ]
delta[len(delta):] = [ ' ' + x for x in l1[astart:a1] ]
delta[len(delta):] = [ '-' + x for x in old ]
delta[len(delta):] = [ '+' + x for x in new ]
if hunk:
for x in yieldhunk(hunk, header):
yield x
mpm@selenic.com
Add a function to return the new text from a binary diff
r120 def patchtext(bin):
pos = 0
t = []
while pos < len(bin):
p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
pos += 12
t.append(bin[pos:pos + l])
pos += l
return "".join(t)
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 def patch(a, bin):
Matt Mackall
Clean up mdiff imports
r1379 return mpatch.patches(a, [bin])
mpm@selenic.com
Start using bdiff for generating deltas...
r432
Alexis S. L. Carvalho
add mdiff.get_matching_blocks
r4361 # similar to difflib.SequenceMatcher.get_matching_blocks
def get_matching_blocks(a, b):
return [(d[0], d[2], d[1] - d[0]) for d in bdiff.blocks(a, b)]
Matt Mackall
revlog: generate trivial deltas against null revision...
r5367 def trivialdiffheader(length):
return struct.pack(">lll", 0, 0, length)
Matt Mackall
Clean up mdiff imports
r1379 patches = mpatch.patches
mason@suse.com
Fill in the uncompressed size during revlog.addgroup...
r2078 patchedsize = mpatch.patchedsize
mpm@selenic.com
Start using bdiff for generating deltas...
r432 textdiff = bdiff.bdiff