##// END OF EJS Templates
diff: always use / in paths in diff...
Mads Kiilerich -
r15437:8f08b635 default
parent child Browse files
Show More
@@ -1,281 +1,284 b''
1 1 # mdiff.py - diff and patch routines for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import bdiff, mpatch, util
10 10 import re, struct
11 11
12 12 def splitnewlines(text):
13 13 '''like str.splitlines, but only split on newlines.'''
14 14 lines = [l + '\n' for l in text.split('\n')]
15 15 if lines:
16 16 if lines[-1] == '\n':
17 17 lines.pop()
18 18 else:
19 19 lines[-1] = lines[-1][:-1]
20 20 return lines
21 21
22 22 class diffopts(object):
23 23 '''context is the number of context lines
24 24 text treats all files as text
25 25 showfunc enables diff -p output
26 26 git enables the git extended patch format
27 27 nodates removes dates from diff headers
28 28 ignorews ignores all whitespace changes in the diff
29 29 ignorewsamount ignores changes in the amount of whitespace
30 30 ignoreblanklines ignores changes whose lines are all blank
31 31 upgrade generates git diffs to avoid data loss
32 32 '''
33 33
34 34 defaults = {
35 35 'context': 3,
36 36 'text': False,
37 37 'showfunc': False,
38 38 'git': False,
39 39 'nodates': False,
40 40 'ignorews': False,
41 41 'ignorewsamount': False,
42 42 'ignoreblanklines': False,
43 43 'upgrade': False,
44 44 }
45 45
46 46 __slots__ = defaults.keys()
47 47
48 48 def __init__(self, **opts):
49 49 for k in self.__slots__:
50 50 v = opts.get(k)
51 51 if v is None:
52 52 v = self.defaults[k]
53 53 setattr(self, k, v)
54 54
55 55 try:
56 56 self.context = int(self.context)
57 57 except ValueError:
58 58 raise util.Abort(_('diff context lines count must be '
59 59 'an integer, not %r') % self.context)
60 60
61 61 def copy(self, **kwargs):
62 62 opts = dict((k, getattr(self, k)) for k in self.defaults)
63 63 opts.update(kwargs)
64 64 return diffopts(**opts)
65 65
66 66 defaultopts = diffopts()
67 67
68 68 def wsclean(opts, text, blank=True):
69 69 if opts.ignorews:
70 70 text = re.sub('[ \t\r]+', '', text)
71 71 elif opts.ignorewsamount:
72 72 text = re.sub('[ \t\r]+', ' ', text)
73 73 text = text.replace(' \n', '\n')
74 74 if blank and opts.ignoreblanklines:
75 75 text = re.sub('\n+', '', text)
76 76 return text
77 77
78 78 def diffline(revs, a, b, opts):
79 79 parts = ['diff']
80 80 if opts.git:
81 81 parts.append('--git')
82 82 if revs and not opts.git:
83 83 parts.append(' '.join(["-r %s" % rev for rev in revs]))
84 84 if opts.git:
85 85 parts.append('a/%s' % a)
86 86 parts.append('b/%s' % b)
87 87 else:
88 88 parts.append(a)
89 89 return ' '.join(parts) + '\n'
90 90
91 91 def unidiff(a, ad, b, bd, fn1, fn2, r=None, opts=defaultopts):
92 92 def datetag(date, addtab=True):
93 93 if not opts.git and not opts.nodates:
94 94 return '\t%s\n' % date
95 95 if addtab and ' ' in fn1:
96 96 return '\t\n'
97 97 return '\n'
98 98
99 99 if not a and not b:
100 100 return ""
101 101 epoch = util.datestr((0, 0))
102 102
103 fn1 = util.pconvert(fn1)
104 fn2 = util.pconvert(fn2)
105
103 106 if not opts.text and (util.binary(a) or util.binary(b)):
104 107 if a and b and len(a) == len(b) and a == b:
105 108 return ""
106 109 l = ['Binary file %s has changed\n' % fn1]
107 110 elif not a:
108 111 b = splitnewlines(b)
109 112 if a is None:
110 113 l1 = '--- /dev/null%s' % datetag(epoch, False)
111 114 else:
112 115 l1 = "--- %s%s" % ("a/" + fn1, datetag(ad))
113 116 l2 = "+++ %s%s" % ("b/" + fn2, datetag(bd))
114 117 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
115 118 l = [l1, l2, l3] + ["+" + e for e in b]
116 119 elif not b:
117 120 a = splitnewlines(a)
118 121 l1 = "--- %s%s" % ("a/" + fn1, datetag(ad))
119 122 if b is None:
120 123 l2 = '+++ /dev/null%s' % datetag(epoch, False)
121 124 else:
122 125 l2 = "+++ %s%s" % ("b/" + fn2, datetag(bd))
123 126 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
124 127 l = [l1, l2, l3] + ["-" + e for e in a]
125 128 else:
126 129 al = splitnewlines(a)
127 130 bl = splitnewlines(b)
128 131 l = list(_unidiff(a, b, al, bl, opts=opts))
129 132 if not l:
130 133 return ""
131 134
132 135 l.insert(0, "--- a/%s%s" % (fn1, datetag(ad)))
133 136 l.insert(1, "+++ b/%s%s" % (fn2, datetag(bd)))
134 137
135 138 for ln in xrange(len(l)):
136 139 if l[ln][-1] != '\n':
137 140 l[ln] += "\n\ No newline at end of file\n"
138 141
139 142 if r:
140 143 l.insert(0, diffline(r, fn1, fn2, opts))
141 144
142 145 return "".join(l)
143 146
144 147 # creates a headerless unified diff
145 148 # t1 and t2 are the text to be diffed
146 149 # l1 and l2 are the text broken up into lines
147 150 def _unidiff(t1, t2, l1, l2, opts=defaultopts):
148 151 def contextend(l, len):
149 152 ret = l + opts.context
150 153 if ret > len:
151 154 ret = len
152 155 return ret
153 156
154 157 def contextstart(l):
155 158 ret = l - opts.context
156 159 if ret < 0:
157 160 return 0
158 161 return ret
159 162
160 163 lastfunc = [0, '']
161 164 def yieldhunk(hunk):
162 165 (astart, a2, bstart, b2, delta) = hunk
163 166 aend = contextend(a2, len(l1))
164 167 alen = aend - astart
165 168 blen = b2 - bstart + aend - a2
166 169
167 170 func = ""
168 171 if opts.showfunc:
169 172 lastpos, func = lastfunc
170 173 # walk backwards from the start of the context up to the start of
171 174 # the previous hunk context until we find a line starting with an
172 175 # alphanumeric char.
173 176 for i in xrange(astart - 1, lastpos - 1, -1):
174 177 if l1[i][0].isalnum():
175 178 func = ' ' + l1[i].rstrip()[:40]
176 179 lastfunc[1] = func
177 180 break
178 181 # by recording this hunk's starting point as the next place to
179 182 # start looking for function lines, we avoid reading any line in
180 183 # the file more than once.
181 184 lastfunc[0] = astart
182 185
183 186 yield "@@ -%d,%d +%d,%d @@%s\n" % (astart + 1, alen,
184 187 bstart + 1, blen, func)
185 188 for x in delta:
186 189 yield x
187 190 for x in xrange(a2, aend):
188 191 yield ' ' + l1[x]
189 192
190 193 # bdiff.blocks gives us the matching sequences in the files. The loop
191 194 # below finds the spaces between those matching sequences and translates
192 195 # them into diff output.
193 196 #
194 197 if opts.ignorews or opts.ignorewsamount:
195 198 t1 = wsclean(opts, t1, False)
196 199 t2 = wsclean(opts, t2, False)
197 200
198 201 diff = bdiff.blocks(t1, t2)
199 202 hunk = None
200 203 for i, s1 in enumerate(diff):
201 204 # The first match is special.
202 205 # we've either found a match starting at line 0 or a match later
203 206 # in the file. If it starts later, old and new below will both be
204 207 # empty and we'll continue to the next match.
205 208 if i > 0:
206 209 s = diff[i - 1]
207 210 else:
208 211 s = [0, 0, 0, 0]
209 212 delta = []
210 213 a1 = s[1]
211 214 a2 = s1[0]
212 215 b1 = s[3]
213 216 b2 = s1[2]
214 217
215 218 old = l1[a1:a2]
216 219 new = l2[b1:b2]
217 220
218 221 # bdiff sometimes gives huge matches past eof, this check eats them,
219 222 # and deals with the special first match case described above
220 223 if not old and not new:
221 224 continue
222 225
223 226 if opts.ignoreblanklines:
224 227 if wsclean(opts, "".join(old)) == wsclean(opts, "".join(new)):
225 228 continue
226 229
227 230 astart = contextstart(a1)
228 231 bstart = contextstart(b1)
229 232 prev = None
230 233 if hunk:
231 234 # join with the previous hunk if it falls inside the context
232 235 if astart < hunk[1] + opts.context + 1:
233 236 prev = hunk
234 237 astart = hunk[1]
235 238 bstart = hunk[3]
236 239 else:
237 240 for x in yieldhunk(hunk):
238 241 yield x
239 242 if prev:
240 243 # we've joined the previous hunk, record the new ending points.
241 244 hunk[1] = a2
242 245 hunk[3] = b2
243 246 delta = hunk[4]
244 247 else:
245 248 # create a new hunk
246 249 hunk = [astart, a2, bstart, b2, delta]
247 250
248 251 delta[len(delta):] = [' ' + x for x in l1[astart:a1]]
249 252 delta[len(delta):] = ['-' + x for x in old]
250 253 delta[len(delta):] = ['+' + x for x in new]
251 254
252 255 if hunk:
253 256 for x in yieldhunk(hunk):
254 257 yield x
255 258
256 259 def patchtext(bin):
257 260 pos = 0
258 261 t = []
259 262 while pos < len(bin):
260 263 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
261 264 pos += 12
262 265 t.append(bin[pos:pos + l])
263 266 pos += l
264 267 return "".join(t)
265 268
266 269 def patch(a, bin):
267 270 if len(a) == 0:
268 271 # skip over trivial delta header
269 272 return buffer(bin, 12)
270 273 return mpatch.patches(a, [bin])
271 274
272 275 # similar to difflib.SequenceMatcher.get_matching_blocks
273 276 def get_matching_blocks(a, b):
274 277 return [(d[0], d[2], d[1] - d[0]) for d in bdiff.blocks(a, b)]
275 278
276 279 def trivialdiffheader(length):
277 280 return struct.pack(">lll", 0, 0, length)
278 281
279 282 patches = mpatch.patches
280 283 patchedsize = mpatch.patchedsize
281 284 textdiff = bdiff.bdiff
General Comments 0
You need to be logged in to leave comments. Login now