comparison mercurial/mdiff.py @ 3044:fcadf7a32425

Merge with mpm
author Josef "Jeff" Sipek <jeffpc@josefsipek.net>
date Sun, 03 Sep 2006 06:06:02 -0400
parents 8b02af865990
children d838bfac668d
comparison
equal deleted inserted replaced
3043:2a4d4aecb2b4 3044:fcadf7a32425
1 # mdiff.py - diff and patch routines for mercurial 1 # mdiff.py - diff and patch routines for mercurial
2 # 2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com> 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # 4 #
5 # This software may be used and distributed according to the terms 5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference. 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 from demandload import demandload 8 from demandload import demandload
17 lines.pop() 17 lines.pop()
18 else: 18 else:
19 lines[-1] = lines[-1][:-1] 19 lines[-1] = lines[-1][:-1]
20 return lines 20 return lines
21 21
22 def unidiff(a, ad, b, bd, fn, r=None, text=False, 22 class diffopts(object):
23 showfunc=False, ignorews=False, ignorewsamount=False, 23 '''context is the number of context lines
24 ignoreblanklines=False): 24 text treats all files as text
25 25 showfunc enables diff -p output
26 git enables the git extended patch format
27 ignorews ignores all whitespace changes in the diff
28 ignorewsamount ignores changes in the amount of whitespace
29 ignoreblanklines ignores changes whose lines are all blank'''
30
31 defaults = {
32 'context': 3,
33 'text': False,
34 'showfunc': True,
35 'git': False,
36 'ignorews': False,
37 'ignorewsamount': False,
38 'ignoreblanklines': False,
39 }
40
41 __slots__ = defaults.keys()
42
43 def __init__(self, **opts):
44 for k in self.__slots__:
45 v = opts.get(k)
46 if v is None:
47 v = self.defaults[k]
48 setattr(self, k, v)
49
50 defaultopts = diffopts()
51
52 def unidiff(a, ad, b, bd, fn, r=None, opts=defaultopts):
26 if not a and not b: return "" 53 if not a and not b: return ""
27 epoch = util.datestr((0, 0)) 54 epoch = util.datestr((0, 0))
28 55
29 if not text and (util.binary(a) or util.binary(b)): 56 if not opts.text and (util.binary(a) or util.binary(b)):
30 l = ['Binary file %s has changed\n' % fn] 57 l = ['Binary file %s has changed\n' % fn]
31 elif not a: 58 elif not a:
32 b = splitnewlines(b) 59 b = splitnewlines(b)
33 if a is None: 60 if a is None:
34 l1 = "--- %s\t%s\n" % ("/dev/null", epoch) 61 l1 = "--- %s\t%s\n" % ("/dev/null", epoch)
47 l3 = "@@ -1,%d +0,0 @@\n" % len(a) 74 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
48 l = [l1, l2, l3] + ["-" + e for e in a] 75 l = [l1, l2, l3] + ["-" + e for e in a]
49 else: 76 else:
50 al = splitnewlines(a) 77 al = splitnewlines(a)
51 bl = splitnewlines(b) 78 bl = splitnewlines(b)
52 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn, 79 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn, opts=opts))
53 showfunc=showfunc, ignorews=ignorews,
54 ignorewsamount=ignorewsamount,
55 ignoreblanklines=ignoreblanklines))
56 if not l: return "" 80 if not l: return ""
57 # difflib uses a space, rather than a tab 81 # difflib uses a space, rather than a tab
58 l[0] = "%s\t%s\n" % (l[0][:-2], ad) 82 l[0] = "%s\t%s\n" % (l[0][:-2], ad)
59 l[1] = "%s\t%s\n" % (l[1][:-2], bd) 83 l[1] = "%s\t%s\n" % (l[1][:-2], bd)
60 84
70 94
71 # somewhat self contained replacement for difflib.unified_diff 95 # somewhat self contained replacement for difflib.unified_diff
72 # t1 and t2 are the text to be diffed 96 # t1 and t2 are the text to be diffed
73 # l1 and l2 are the text broken up into lines 97 # l1 and l2 are the text broken up into lines
74 # header1 and header2 are the filenames for the diff output 98 # header1 and header2 are the filenames for the diff output
75 # context is the number of context lines 99 def bunidiff(t1, t2, l1, l2, header1, header2, opts=defaultopts):
76 # showfunc enables diff -p output
77 # ignorews ignores all whitespace changes in the diff
78 # ignorewsamount ignores changes in the amount of whitespace
79 # ignoreblanklines ignores changes whose lines are all blank
80 def bunidiff(t1, t2, l1, l2, header1, header2, context=3, showfunc=False,
81 ignorews=False, ignorewsamount=False, ignoreblanklines=False):
82 def contextend(l, len): 100 def contextend(l, len):
83 ret = l + context 101 ret = l + opts.context
84 if ret > len: 102 if ret > len:
85 ret = len 103 ret = len
86 return ret 104 return ret
87 105
88 def contextstart(l): 106 def contextstart(l):
89 ret = l - context 107 ret = l - opts.context
90 if ret < 0: 108 if ret < 0:
91 return 0 109 return 0
92 return ret 110 return ret
93 111
94 def yieldhunk(hunk, header): 112 def yieldhunk(hunk, header):
99 aend = contextend(a2, len(l1)) 117 aend = contextend(a2, len(l1))
100 alen = aend - astart 118 alen = aend - astart
101 blen = b2 - bstart + aend - a2 119 blen = b2 - bstart + aend - a2
102 120
103 func = "" 121 func = ""
104 if showfunc: 122 if opts.showfunc:
105 # walk backwards from the start of the context 123 # walk backwards from the start of the context
106 # to find a line starting with an alphanumeric char. 124 # to find a line starting with an alphanumeric char.
107 for x in xrange(astart, -1, -1): 125 for x in xrange(astart, -1, -1):
108 t = l1[x].rstrip() 126 t = l1[x].rstrip()
109 if funcre.match(t): 127 if funcre.match(t):
117 for x in xrange(a2, aend): 135 for x in xrange(a2, aend):
118 yield ' ' + l1[x] 136 yield ' ' + l1[x]
119 137
120 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ] 138 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ]
121 139
122 if showfunc: 140 if opts.showfunc:
123 funcre = re.compile('\w') 141 funcre = re.compile('\w')
124 if ignorewsamount: 142 if opts.ignorewsamount:
125 wsamountre = re.compile('[ \t]+') 143 wsamountre = re.compile('[ \t]+')
126 wsappendedre = re.compile(' \n') 144 wsappendedre = re.compile(' \n')
127 if ignoreblanklines: 145 if opts.ignoreblanklines:
128 wsblanklinesre = re.compile('\n') 146 wsblanklinesre = re.compile('\n')
129 if ignorews: 147 if opts.ignorews:
130 wsre = re.compile('[ \t]') 148 wsre = re.compile('[ \t]')
131 149
132 # bdiff.blocks gives us the matching sequences in the files. The loop 150 # bdiff.blocks gives us the matching sequences in the files. The loop
133 # below finds the spaces between those matching sequences and translates 151 # below finds the spaces between those matching sequences and translates
134 # them into diff output. 152 # them into diff output.
157 # bdiff sometimes gives huge matches past eof, this check eats them, 175 # bdiff sometimes gives huge matches past eof, this check eats them,
158 # and deals with the special first match case described above 176 # and deals with the special first match case described above
159 if not old and not new: 177 if not old and not new:
160 continue 178 continue
161 179
162 if ignoreblanklines: 180 if opts.ignoreblanklines:
163 wsold = wsblanklinesre.sub('', "".join(old)) 181 wsold = wsblanklinesre.sub('', "".join(old))
164 wsnew = wsblanklinesre.sub('', "".join(new)) 182 wsnew = wsblanklinesre.sub('', "".join(new))
165 if wsold == wsnew: 183 if wsold == wsnew:
166 continue 184 continue
167 185
168 if ignorewsamount: 186 if opts.ignorewsamount:
169 wsold = wsamountre.sub(' ', "".join(old)) 187 wsold = wsamountre.sub(' ', "".join(old))
170 wsold = wsappendedre.sub('\n', wsold) 188 wsold = wsappendedre.sub('\n', wsold)
171 wsnew = wsamountre.sub(' ', "".join(new)) 189 wsnew = wsamountre.sub(' ', "".join(new))
172 wsnew = wsappendedre.sub('\n', wsnew) 190 wsnew = wsappendedre.sub('\n', wsnew)
173 if wsold == wsnew: 191 if wsold == wsnew:
174 continue 192 continue
175 193
176 if ignorews: 194 if opts.ignorews:
177 wsold = wsre.sub('', "".join(old)) 195 wsold = wsre.sub('', "".join(old))
178 wsnew = wsre.sub('', "".join(new)) 196 wsnew = wsre.sub('', "".join(new))
179 if wsold == wsnew: 197 if wsold == wsnew:
180 continue 198 continue
181 199
182 astart = contextstart(a1) 200 astart = contextstart(a1)
183 bstart = contextstart(b1) 201 bstart = contextstart(b1)
184 prev = None 202 prev = None
185 if hunk: 203 if hunk:
186 # join with the previous hunk if it falls inside the context 204 # join with the previous hunk if it falls inside the context
187 if astart < hunk[1] + context + 1: 205 if astart < hunk[1] + opts.context + 1:
188 prev = hunk 206 prev = hunk
189 astart = hunk[1] 207 astart = hunk[1]
190 bstart = hunk[3] 208 bstart = hunk[3]
191 else: 209 else:
192 for x in yieldhunk(hunk, header): 210 for x in yieldhunk(hunk, header):