##// END OF EJS Templates
diff: do not split function name if character encoding is unknown...
Yuya Nishihara -
r36432:44c4a38b default
parent child Browse files
Show More
@@ -1,512 +1,517 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 __future__ import absolute_import
9 9
10 10 import re
11 11 import struct
12 12 import zlib
13 13
14 14 from .i18n import _
15 15 from . import (
16 encoding,
16 17 error,
17 18 policy,
18 19 pycompat,
19 20 util,
20 21 )
21 22
22 23 _missing_newline_marker = "\\ No newline at end of file\n"
23 24
24 25 bdiff = policy.importmod(r'bdiff')
25 26 mpatch = policy.importmod(r'mpatch')
26 27
27 28 blocks = bdiff.blocks
28 29 fixws = bdiff.fixws
29 30 patches = mpatch.patches
30 31 patchedsize = mpatch.patchedsize
31 32 textdiff = bdiff.bdiff
32 33 splitnewlines = bdiff.splitnewlines
33 34
34 35 class diffopts(object):
35 36 '''context is the number of context lines
36 37 text treats all files as text
37 38 showfunc enables diff -p output
38 39 git enables the git extended patch format
39 40 nodates removes dates from diff headers
40 41 nobinary ignores binary files
41 42 noprefix disables the 'a/' and 'b/' prefixes (ignored in plain mode)
42 43 ignorews ignores all whitespace changes in the diff
43 44 ignorewsamount ignores changes in the amount of whitespace
44 45 ignoreblanklines ignores changes whose lines are all blank
45 46 upgrade generates git diffs to avoid data loss
46 47 '''
47 48
48 49 defaults = {
49 50 'context': 3,
50 51 'text': False,
51 52 'showfunc': False,
52 53 'git': False,
53 54 'nodates': False,
54 55 'nobinary': False,
55 56 'noprefix': False,
56 57 'index': 0,
57 58 'ignorews': False,
58 59 'ignorewsamount': False,
59 60 'ignorewseol': False,
60 61 'ignoreblanklines': False,
61 62 'upgrade': False,
62 63 'showsimilarity': False,
63 64 'worddiff': False,
64 65 }
65 66
66 67 def __init__(self, **opts):
67 68 opts = pycompat.byteskwargs(opts)
68 69 for k in self.defaults.keys():
69 70 v = opts.get(k)
70 71 if v is None:
71 72 v = self.defaults[k]
72 73 setattr(self, k, v)
73 74
74 75 try:
75 76 self.context = int(self.context)
76 77 except ValueError:
77 78 raise error.Abort(_('diff context lines count must be '
78 79 'an integer, not %r') % self.context)
79 80
80 81 def copy(self, **kwargs):
81 82 opts = dict((k, getattr(self, k)) for k in self.defaults)
82 83 opts = pycompat.strkwargs(opts)
83 84 opts.update(kwargs)
84 85 return diffopts(**opts)
85 86
86 87 defaultopts = diffopts()
87 88
88 89 def wsclean(opts, text, blank=True):
89 90 if opts.ignorews:
90 91 text = bdiff.fixws(text, 1)
91 92 elif opts.ignorewsamount:
92 93 text = bdiff.fixws(text, 0)
93 94 if blank and opts.ignoreblanklines:
94 95 text = re.sub('\n+', '\n', text).strip('\n')
95 96 if opts.ignorewseol:
96 97 text = re.sub(br'[ \t\r\f]+\n', r'\n', text)
97 98 return text
98 99
99 100 def splitblock(base1, lines1, base2, lines2, opts):
100 101 # The input lines matches except for interwoven blank lines. We
101 102 # transform it into a sequence of matching blocks and blank blocks.
102 103 lines1 = [(wsclean(opts, l) and 1 or 0) for l in lines1]
103 104 lines2 = [(wsclean(opts, l) and 1 or 0) for l in lines2]
104 105 s1, e1 = 0, len(lines1)
105 106 s2, e2 = 0, len(lines2)
106 107 while s1 < e1 or s2 < e2:
107 108 i1, i2, btype = s1, s2, '='
108 109 if (i1 >= e1 or lines1[i1] == 0
109 110 or i2 >= e2 or lines2[i2] == 0):
110 111 # Consume the block of blank lines
111 112 btype = '~'
112 113 while i1 < e1 and lines1[i1] == 0:
113 114 i1 += 1
114 115 while i2 < e2 and lines2[i2] == 0:
115 116 i2 += 1
116 117 else:
117 118 # Consume the matching lines
118 119 while i1 < e1 and lines1[i1] == 1 and lines2[i2] == 1:
119 120 i1 += 1
120 121 i2 += 1
121 122 yield [base1 + s1, base1 + i1, base2 + s2, base2 + i2], btype
122 123 s1 = i1
123 124 s2 = i2
124 125
125 126 def hunkinrange(hunk, linerange):
126 127 """Return True if `hunk` defined as (start, length) is in `linerange`
127 128 defined as (lowerbound, upperbound).
128 129
129 130 >>> hunkinrange((5, 10), (2, 7))
130 131 True
131 132 >>> hunkinrange((5, 10), (6, 12))
132 133 True
133 134 >>> hunkinrange((5, 10), (13, 17))
134 135 True
135 136 >>> hunkinrange((5, 10), (3, 17))
136 137 True
137 138 >>> hunkinrange((5, 10), (1, 3))
138 139 False
139 140 >>> hunkinrange((5, 10), (18, 20))
140 141 False
141 142 >>> hunkinrange((5, 10), (1, 5))
142 143 False
143 144 >>> hunkinrange((5, 10), (15, 27))
144 145 False
145 146 """
146 147 start, length = hunk
147 148 lowerbound, upperbound = linerange
148 149 return lowerbound < start + length and start < upperbound
149 150
150 151 def blocksinrange(blocks, rangeb):
151 152 """filter `blocks` like (a1, a2, b1, b2) from items outside line range
152 153 `rangeb` from ``(b1, b2)`` point of view.
153 154
154 155 Return `filteredblocks, rangea` where:
155 156
156 157 * `filteredblocks` is list of ``block = (a1, a2, b1, b2), stype`` items of
157 158 `blocks` that are inside `rangeb` from ``(b1, b2)`` point of view; a
158 159 block ``(b1, b2)`` being inside `rangeb` if
159 160 ``rangeb[0] < b2 and b1 < rangeb[1]``;
160 161 * `rangea` is the line range w.r.t. to ``(a1, a2)`` parts of `blocks`.
161 162 """
162 163 lbb, ubb = rangeb
163 164 lba, uba = None, None
164 165 filteredblocks = []
165 166 for block in blocks:
166 167 (a1, a2, b1, b2), stype = block
167 168 if lbb >= b1 and ubb <= b2 and stype == '=':
168 169 # rangeb is within a single "=" hunk, restrict back linerange1
169 170 # by offsetting rangeb
170 171 lba = lbb - b1 + a1
171 172 uba = ubb - b1 + a1
172 173 else:
173 174 if b1 <= lbb < b2:
174 175 if stype == '=':
175 176 lba = a2 - (b2 - lbb)
176 177 else:
177 178 lba = a1
178 179 if b1 < ubb <= b2:
179 180 if stype == '=':
180 181 uba = a1 + (ubb - b1)
181 182 else:
182 183 uba = a2
183 184 if hunkinrange((b1, (b2 - b1)), rangeb):
184 185 filteredblocks.append(block)
185 186 if lba is None or uba is None or uba < lba:
186 187 raise error.Abort(_('line range exceeds file size'))
187 188 return filteredblocks, (lba, uba)
188 189
189 190 def allblocks(text1, text2, opts=None, lines1=None, lines2=None):
190 191 """Return (block, type) tuples, where block is an mdiff.blocks
191 192 line entry. type is '=' for blocks matching exactly one another
192 193 (bdiff blocks), '!' for non-matching blocks and '~' for blocks
193 194 matching only after having filtered blank lines.
194 195 line1 and line2 are text1 and text2 split with splitnewlines() if
195 196 they are already available.
196 197 """
197 198 if opts is None:
198 199 opts = defaultopts
199 200 if opts.ignorews or opts.ignorewsamount or opts.ignorewseol:
200 201 text1 = wsclean(opts, text1, False)
201 202 text2 = wsclean(opts, text2, False)
202 203 diff = bdiff.blocks(text1, text2)
203 204 for i, s1 in enumerate(diff):
204 205 # The first match is special.
205 206 # we've either found a match starting at line 0 or a match later
206 207 # in the file. If it starts later, old and new below will both be
207 208 # empty and we'll continue to the next match.
208 209 if i > 0:
209 210 s = diff[i - 1]
210 211 else:
211 212 s = [0, 0, 0, 0]
212 213 s = [s[1], s1[0], s[3], s1[2]]
213 214
214 215 # bdiff sometimes gives huge matches past eof, this check eats them,
215 216 # and deals with the special first match case described above
216 217 if s[0] != s[1] or s[2] != s[3]:
217 218 type = '!'
218 219 if opts.ignoreblanklines:
219 220 if lines1 is None:
220 221 lines1 = splitnewlines(text1)
221 222 if lines2 is None:
222 223 lines2 = splitnewlines(text2)
223 224 old = wsclean(opts, "".join(lines1[s[0]:s[1]]))
224 225 new = wsclean(opts, "".join(lines2[s[2]:s[3]]))
225 226 if old == new:
226 227 type = '~'
227 228 yield s, type
228 229 yield s1, '='
229 230
230 231 def unidiff(a, ad, b, bd, fn1, fn2, binary, opts=defaultopts):
231 232 """Return a unified diff as a (headers, hunks) tuple.
232 233
233 234 If the diff is not null, `headers` is a list with unified diff header
234 235 lines "--- <original>" and "+++ <new>" and `hunks` is a generator yielding
235 236 (hunkrange, hunklines) coming from _unidiff().
236 237 Otherwise, `headers` and `hunks` are empty.
237 238
238 239 Set binary=True if either a or b should be taken as a binary file.
239 240 """
240 241 def datetag(date, fn=None):
241 242 if not opts.git and not opts.nodates:
242 243 return '\t%s' % date
243 244 if fn and ' ' in fn:
244 245 return '\t'
245 246 return ''
246 247
247 248 sentinel = [], ()
248 249 if not a and not b:
249 250 return sentinel
250 251
251 252 if opts.noprefix:
252 253 aprefix = bprefix = ''
253 254 else:
254 255 aprefix = 'a/'
255 256 bprefix = 'b/'
256 257
257 258 epoch = util.datestr((0, 0))
258 259
259 260 fn1 = util.pconvert(fn1)
260 261 fn2 = util.pconvert(fn2)
261 262
262 263 if binary:
263 264 if a and b and len(a) == len(b) and a == b:
264 265 return sentinel
265 266 headerlines = []
266 267 hunks = (None, ['Binary file %s has changed\n' % fn1]),
267 268 elif not a:
268 269 without_newline = not b.endswith('\n')
269 270 b = splitnewlines(b)
270 271 if a is None:
271 272 l1 = '--- /dev/null%s' % datetag(epoch)
272 273 else:
273 274 l1 = "--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1))
274 275 l2 = "+++ %s%s" % (bprefix + fn2, datetag(bd, fn2))
275 276 headerlines = [l1, l2]
276 277 size = len(b)
277 278 hunkrange = (0, 0, 1, size)
278 279 hunklines = ["@@ -0,0 +1,%d @@\n" % size] + ["+" + e for e in b]
279 280 if without_newline:
280 281 hunklines[-1] += '\n'
281 282 hunklines.append(_missing_newline_marker)
282 283 hunks = (hunkrange, hunklines),
283 284 elif not b:
284 285 without_newline = not a.endswith('\n')
285 286 a = splitnewlines(a)
286 287 l1 = "--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1))
287 288 if b is None:
288 289 l2 = '+++ /dev/null%s' % datetag(epoch)
289 290 else:
290 291 l2 = "+++ %s%s%s" % (bprefix, fn2, datetag(bd, fn2))
291 292 headerlines = [l1, l2]
292 293 size = len(a)
293 294 hunkrange = (1, size, 0, 0)
294 295 hunklines = ["@@ -1,%d +0,0 @@\n" % size] + ["-" + e for e in a]
295 296 if without_newline:
296 297 hunklines[-1] += '\n'
297 298 hunklines.append(_missing_newline_marker)
298 299 hunks = (hunkrange, hunklines),
299 300 else:
300 301 hunks = _unidiff(a, b, opts=opts)
301 302 if not next(hunks):
302 303 return sentinel
303 304
304 305 headerlines = [
305 306 "--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1)),
306 307 "+++ %s%s%s" % (bprefix, fn2, datetag(bd, fn2)),
307 308 ]
308 309
309 310 return headerlines, hunks
310 311
311 312 def _unidiff(t1, t2, opts=defaultopts):
312 313 """Yield hunks of a headerless unified diff from t1 and t2 texts.
313 314
314 315 Each hunk consists of a (hunkrange, hunklines) tuple where `hunkrange` is a
315 316 tuple (s1, l1, s2, l2) representing the range information of the hunk to
316 317 form the '@@ -s1,l1 +s2,l2 @@' header and `hunklines` is a list of lines
317 318 of the hunk combining said header followed by line additions and
318 319 deletions.
319 320
320 321 The hunks are prefixed with a bool.
321 322 """
322 323 l1 = splitnewlines(t1)
323 324 l2 = splitnewlines(t2)
324 325 def contextend(l, len):
325 326 ret = l + opts.context
326 327 if ret > len:
327 328 ret = len
328 329 return ret
329 330
330 331 def contextstart(l):
331 332 ret = l - opts.context
332 333 if ret < 0:
333 334 return 0
334 335 return ret
335 336
336 337 lastfunc = [0, '']
337 338 def yieldhunk(hunk):
338 339 (astart, a2, bstart, b2, delta) = hunk
339 340 aend = contextend(a2, len(l1))
340 341 alen = aend - astart
341 342 blen = b2 - bstart + aend - a2
342 343
343 344 func = ""
344 345 if opts.showfunc:
345 346 lastpos, func = lastfunc
346 347 # walk backwards from the start of the context up to the start of
347 348 # the previous hunk context until we find a line starting with an
348 349 # alphanumeric char.
349 350 for i in xrange(astart - 1, lastpos - 1, -1):
350 351 if l1[i][0:1].isalnum():
351 func = ' ' + l1[i].rstrip()[:40]
352 func = b' ' + l1[i].rstrip()
353 # split long function name if ASCII. otherwise we have no
354 # idea where the multi-byte boundary is, so just leave it.
355 if encoding.isasciistr(func):
356 func = func[:41]
352 357 lastfunc[1] = func
353 358 break
354 359 # by recording this hunk's starting point as the next place to
355 360 # start looking for function lines, we avoid reading any line in
356 361 # the file more than once.
357 362 lastfunc[0] = astart
358 363
359 364 # zero-length hunk ranges report their start line as one less
360 365 if alen:
361 366 astart += 1
362 367 if blen:
363 368 bstart += 1
364 369
365 370 hunkrange = astart, alen, bstart, blen
366 371 hunklines = (
367 372 ["@@ -%d,%d +%d,%d @@%s\n" % (hunkrange + (func,))]
368 373 + delta
369 374 + [' ' + l1[x] for x in xrange(a2, aend)]
370 375 )
371 376 # If either file ends without a newline and the last line of
372 377 # that file is part of a hunk, a marker is printed. If the
373 378 # last line of both files is identical and neither ends in
374 379 # a newline, print only one marker. That's the only case in
375 380 # which the hunk can end in a shared line without a newline.
376 381 skip = False
377 382 if not t1.endswith('\n') and astart + alen == len(l1) + 1:
378 383 for i in xrange(len(hunklines) - 1, -1, -1):
379 384 if hunklines[i].startswith(('-', ' ')):
380 385 if hunklines[i].startswith(' '):
381 386 skip = True
382 387 hunklines[i] += '\n'
383 388 hunklines.insert(i + 1, _missing_newline_marker)
384 389 break
385 390 if not skip and not t2.endswith('\n') and bstart + blen == len(l2) + 1:
386 391 for i in xrange(len(hunklines) - 1, -1, -1):
387 392 if hunklines[i].startswith('+'):
388 393 hunklines[i] += '\n'
389 394 hunklines.insert(i + 1, _missing_newline_marker)
390 395 break
391 396 yield hunkrange, hunklines
392 397
393 398 # bdiff.blocks gives us the matching sequences in the files. The loop
394 399 # below finds the spaces between those matching sequences and translates
395 400 # them into diff output.
396 401 #
397 402 hunk = None
398 403 ignoredlines = 0
399 404 has_hunks = False
400 405 for s, stype in allblocks(t1, t2, opts, l1, l2):
401 406 a1, a2, b1, b2 = s
402 407 if stype != '!':
403 408 if stype == '~':
404 409 # The diff context lines are based on t1 content. When
405 410 # blank lines are ignored, the new lines offsets must
406 411 # be adjusted as if equivalent blocks ('~') had the
407 412 # same sizes on both sides.
408 413 ignoredlines += (b2 - b1) - (a2 - a1)
409 414 continue
410 415 delta = []
411 416 old = l1[a1:a2]
412 417 new = l2[b1:b2]
413 418
414 419 b1 -= ignoredlines
415 420 b2 -= ignoredlines
416 421 astart = contextstart(a1)
417 422 bstart = contextstart(b1)
418 423 prev = None
419 424 if hunk:
420 425 # join with the previous hunk if it falls inside the context
421 426 if astart < hunk[1] + opts.context + 1:
422 427 prev = hunk
423 428 astart = hunk[1]
424 429 bstart = hunk[3]
425 430 else:
426 431 if not has_hunks:
427 432 has_hunks = True
428 433 yield True
429 434 for x in yieldhunk(hunk):
430 435 yield x
431 436 if prev:
432 437 # we've joined the previous hunk, record the new ending points.
433 438 hunk[1] = a2
434 439 hunk[3] = b2
435 440 delta = hunk[4]
436 441 else:
437 442 # create a new hunk
438 443 hunk = [astart, a2, bstart, b2, delta]
439 444
440 445 delta[len(delta):] = [' ' + x for x in l1[astart:a1]]
441 446 delta[len(delta):] = ['-' + x for x in old]
442 447 delta[len(delta):] = ['+' + x for x in new]
443 448
444 449 if hunk:
445 450 if not has_hunks:
446 451 has_hunks = True
447 452 yield True
448 453 for x in yieldhunk(hunk):
449 454 yield x
450 455 elif not has_hunks:
451 456 yield False
452 457
453 458 def b85diff(to, tn):
454 459 '''print base85-encoded binary diff'''
455 460 def fmtline(line):
456 461 l = len(line)
457 462 if l <= 26:
458 463 l = pycompat.bytechr(ord('A') + l - 1)
459 464 else:
460 465 l = pycompat.bytechr(l - 26 + ord('a') - 1)
461 466 return '%c%s\n' % (l, util.b85encode(line, True))
462 467
463 468 def chunk(text, csize=52):
464 469 l = len(text)
465 470 i = 0
466 471 while i < l:
467 472 yield text[i:i + csize]
468 473 i += csize
469 474
470 475 if to is None:
471 476 to = ''
472 477 if tn is None:
473 478 tn = ''
474 479
475 480 if to == tn:
476 481 return ''
477 482
478 483 # TODO: deltas
479 484 ret = []
480 485 ret.append('GIT binary patch\n')
481 486 ret.append('literal %d\n' % len(tn))
482 487 for l in chunk(zlib.compress(tn)):
483 488 ret.append(fmtline(l))
484 489 ret.append('\n')
485 490
486 491 return ''.join(ret)
487 492
488 493 def patchtext(bin):
489 494 pos = 0
490 495 t = []
491 496 while pos < len(bin):
492 497 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
493 498 pos += 12
494 499 t.append(bin[pos:pos + l])
495 500 pos += l
496 501 return "".join(t)
497 502
498 503 def patch(a, bin):
499 504 if len(a) == 0:
500 505 # skip over trivial delta header
501 506 return util.buffer(bin, 12)
502 507 return mpatch.patches(a, [bin])
503 508
504 509 # similar to difflib.SequenceMatcher.get_matching_blocks
505 510 def get_matching_blocks(a, b):
506 511 return [(d[0], d[2], d[1] - d[0]) for d in bdiff.blocks(a, b)]
507 512
508 513 def trivialdiffheader(length):
509 514 return struct.pack(">lll", 0, 0, length) if length else ''
510 515
511 516 def replacediffheader(oldlen, newlen):
512 517 return struct.pack(">lll", 0, oldlen, newlen)
@@ -1,388 +1,458 b''
1 1 $ hg init repo
2 2 $ cd repo
3 3 $ cat > a <<EOF
4 4 > c
5 5 > c
6 6 > a
7 7 > a
8 8 > b
9 9 > a
10 10 > a
11 11 > c
12 12 > c
13 13 > EOF
14 14 $ hg ci -Am adda
15 15 adding a
16 16
17 17 $ cat > a <<EOF
18 18 > c
19 19 > c
20 20 > a
21 21 > a
22 22 > dd
23 23 > a
24 24 > a
25 25 > c
26 26 > c
27 27 > EOF
28 28
29 29 default context
30 30
31 31 $ hg diff --nodates
32 32 diff -r cf9f4ba66af2 a
33 33 --- a/a
34 34 +++ b/a
35 35 @@ -2,7 +2,7 @@
36 36 c
37 37 a
38 38 a
39 39 -b
40 40 +dd
41 41 a
42 42 a
43 43 c
44 44
45 45 invalid --unified
46 46
47 47 $ hg diff --nodates -U foo
48 48 abort: diff context lines count must be an integer, not 'foo'
49 49 [255]
50 50
51 51
52 52 $ hg diff --nodates -U 2
53 53 diff -r cf9f4ba66af2 a
54 54 --- a/a
55 55 +++ b/a
56 56 @@ -3,5 +3,5 @@
57 57 a
58 58 a
59 59 -b
60 60 +dd
61 61 a
62 62 a
63 63
64 64 $ hg --config diff.unified=2 diff --nodates
65 65 diff -r cf9f4ba66af2 a
66 66 --- a/a
67 67 +++ b/a
68 68 @@ -3,5 +3,5 @@
69 69 a
70 70 a
71 71 -b
72 72 +dd
73 73 a
74 74 a
75 75
76 76 $ hg diff --nodates -U 1
77 77 diff -r cf9f4ba66af2 a
78 78 --- a/a
79 79 +++ b/a
80 80 @@ -4,3 +4,3 @@
81 81 a
82 82 -b
83 83 +dd
84 84 a
85 85
86 86 invalid diff.unified
87 87
88 88 $ hg --config diff.unified=foo diff --nodates
89 89 abort: diff context lines count must be an integer, not 'foo'
90 90 [255]
91 91
92 92 noprefix config and option
93 93
94 94 $ hg --config diff.noprefix=True diff --nodates
95 95 diff -r cf9f4ba66af2 a
96 96 --- a
97 97 +++ a
98 98 @@ -2,7 +2,7 @@
99 99 c
100 100 a
101 101 a
102 102 -b
103 103 +dd
104 104 a
105 105 a
106 106 c
107 107 $ hg diff --noprefix --nodates
108 108 diff -r cf9f4ba66af2 a
109 109 --- a
110 110 +++ a
111 111 @@ -2,7 +2,7 @@
112 112 c
113 113 a
114 114 a
115 115 -b
116 116 +dd
117 117 a
118 118 a
119 119 c
120 120
121 121 noprefix config disabled in plain mode, but option still enabled
122 122
123 123 $ HGPLAIN=1 hg --config diff.noprefix=True diff --nodates
124 124 diff -r cf9f4ba66af2 a
125 125 --- a/a
126 126 +++ b/a
127 127 @@ -2,7 +2,7 @@
128 128 c
129 129 a
130 130 a
131 131 -b
132 132 +dd
133 133 a
134 134 a
135 135 c
136 136 $ HGPLAIN=1 hg diff --noprefix --nodates
137 137 diff -r cf9f4ba66af2 a
138 138 --- a
139 139 +++ a
140 140 @@ -2,7 +2,7 @@
141 141 c
142 142 a
143 143 a
144 144 -b
145 145 +dd
146 146 a
147 147 a
148 148 c
149 149
150 150 $ cd ..
151 151
152 152
153 153 0 lines of context hunk header matches gnu diff hunk header
154 154
155 155 $ hg init diffzero
156 156 $ cd diffzero
157 157 $ cat > f1 << EOF
158 158 > c2
159 159 > c4
160 160 > c5
161 161 > EOF
162 162 $ hg commit -Am0
163 163 adding f1
164 164
165 165 $ cat > f2 << EOF
166 166 > c1
167 167 > c2
168 168 > c3
169 169 > c4
170 170 > EOF
171 171 $ mv f2 f1
172 172 $ hg diff -U0 --nodates
173 173 diff -r 55d8ff78db23 f1
174 174 --- a/f1
175 175 +++ b/f1
176 176 @@ -0,0 +1,1 @@
177 177 +c1
178 178 @@ -1,0 +3,1 @@
179 179 +c3
180 180 @@ -3,1 +4,0 @@
181 181 -c5
182 182
183 183 $ hg diff -U0 --nodates --git
184 184 diff --git a/f1 b/f1
185 185 --- a/f1
186 186 +++ b/f1
187 187 @@ -0,0 +1,1 @@
188 188 +c1
189 189 @@ -1,0 +3,1 @@
190 190 +c3
191 191 @@ -3,1 +4,0 @@
192 192 -c5
193 193
194 194 $ hg diff -U0 --nodates -p
195 195 diff -r 55d8ff78db23 f1
196 196 --- a/f1
197 197 +++ b/f1
198 198 @@ -0,0 +1,1 @@
199 199 +c1
200 200 @@ -1,0 +3,1 @@ c2
201 201 +c3
202 202 @@ -3,1 +4,0 @@ c4
203 203 -c5
204 204
205 205 $ echo a > f1
206 206 $ hg ci -m movef2
207 207
208 208 Test diff headers terminating with TAB when necessary (issue3357)
209 209 Regular diff --nodates, file creation
210 210
211 211 $ hg mv f1 'f 1'
212 212 $ echo b > 'f 1'
213 213 $ hg diff --nodates 'f 1'
214 214 diff -r 7574207d0d15 f 1
215 215 --- /dev/null
216 216 +++ b/f 1
217 217 @@ -0,0 +1,1 @@
218 218 +b
219 219
220 220 Git diff, adding space
221 221
222 222 $ hg diff --git
223 223 diff --git a/f1 b/f 1
224 224 rename from f1
225 225 rename to f 1
226 226 --- a/f1
227 227 +++ b/f 1
228 228 @@ -1,1 +1,1 @@
229 229 -a
230 230 +b
231 231
232 232 Git diff, adding extended headers
233 233
234 234 $ hg diff --git --config experimental.extendedheader.index=7 --config experimental.extendedheader.similarity=True
235 235 diff --git a/f1 b/f 1
236 236 similarity index 0%
237 237 rename from f1
238 238 rename to f 1
239 239 index 7898192..6178079 100644
240 240 --- a/f1
241 241 +++ b/f 1
242 242 @@ -1,1 +1,1 @@
243 243 -a
244 244 +b
245 245
246 246 $ hg diff --git --config experimental.extendedheader.index=-1
247 247 invalid length for extendedheader.index: '-1'
248 248 diff --git a/f1 b/f 1
249 249 rename from f1
250 250 rename to f 1
251 251 --- a/f1
252 252 +++ b/f 1
253 253 @@ -1,1 +1,1 @@
254 254 -a
255 255 +b
256 256
257 257 $ hg diff --git --config experimental.extendedheader.index=whatever
258 258 invalid value for extendedheader.index: 'whatever'
259 259 diff --git a/f1 b/f 1
260 260 rename from f1
261 261 rename to f 1
262 262 --- a/f1
263 263 +++ b/f 1
264 264 @@ -1,1 +1,1 @@
265 265 -a
266 266 +b
267 267
268 268 Git diff with noprefix
269 269
270 270 $ hg --config diff.noprefix=True diff --git --nodates
271 271 diff --git f1 f 1
272 272 rename from f1
273 273 rename to f 1
274 274 --- f1
275 275 +++ f 1
276 276 @@ -1,1 +1,1 @@
277 277 -a
278 278 +b
279 279
280 280 noprefix config disabled in plain mode, but option still enabled
281 281
282 282 $ HGPLAIN=1 hg --config diff.noprefix=True diff --git --nodates
283 283 diff --git a/f1 b/f 1
284 284 rename from f1
285 285 rename to f 1
286 286 --- a/f1
287 287 +++ b/f 1
288 288 @@ -1,1 +1,1 @@
289 289 -a
290 290 +b
291 291 $ HGPLAIN=1 hg diff --git --noprefix --nodates
292 292 diff --git f1 f 1
293 293 rename from f1
294 294 rename to f 1
295 295 --- f1
296 296 +++ f 1
297 297 @@ -1,1 +1,1 @@
298 298 -a
299 299 +b
300 300
301 301 Regular diff --nodates, file deletion
302 302
303 303 $ hg ci -m addspace
304 304 $ hg mv 'f 1' f1
305 305 $ echo a > f1
306 306 $ hg diff --nodates 'f 1'
307 307 diff -r ca50fe67c9c7 f 1
308 308 --- a/f 1
309 309 +++ /dev/null
310 310 @@ -1,1 +0,0 @@
311 311 -b
312 312
313 313 Git diff, removing space
314 314
315 315 $ hg diff --git
316 316 diff --git a/f 1 b/f1
317 317 rename from f 1
318 318 rename to f1
319 319 --- a/f 1
320 320 +++ b/f1
321 321 @@ -1,1 +1,1 @@
322 322 -b
323 323 +a
324 324
325 325 showfunc diff
326 326 $ cat > f1 << EOF
327 327 > int main() {
328 328 > int a = 0;
329 329 > int b = 1;
330 330 > int c = 2;
331 331 > int d = 3;
332 332 > return a + b + c + d;
333 333 > }
334 334 > EOF
335 335 $ hg commit -m addfunction
336 336 $ cat > f1 << EOF
337 337 > int main() {
338 338 > int a = 0;
339 339 > int b = 1;
340 340 > int c = 2;
341 341 > int e = 3;
342 342 > return a + b + c + e;
343 343 > }
344 344 > EOF
345 345 $ hg diff --git
346 346 diff --git a/f1 b/f1
347 347 --- a/f1
348 348 +++ b/f1
349 349 @@ -2,6 +2,6 @@
350 350 int a = 0;
351 351 int b = 1;
352 352 int c = 2;
353 353 - int d = 3;
354 354 - return a + b + c + d;
355 355 + int e = 3;
356 356 + return a + b + c + e;
357 357 }
358 358 $ hg diff --config diff.showfunc=True --git
359 359 diff --git a/f1 b/f1
360 360 --- a/f1
361 361 +++ b/f1
362 362 @@ -2,6 +2,6 @@ int main() {
363 363 int a = 0;
364 364 int b = 1;
365 365 int c = 2;
366 366 - int d = 3;
367 367 - return a + b + c + d;
368 368 + int e = 3;
369 369 + return a + b + c + e;
370 370 }
371 371
372 372 If [diff] git is set to true, but the user says --no-git, we should
373 373 *not* get git diffs
374 374 $ hg diff --nodates --config diff.git=1 --no-git
375 375 diff -r f2c7c817fa55 f1
376 376 --- a/f1
377 377 +++ b/f1
378 378 @@ -2,6 +2,6 @@
379 379 int a = 0;
380 380 int b = 1;
381 381 int c = 2;
382 382 - int d = 3;
383 383 - return a + b + c + d;
384 384 + int e = 3;
385 385 + return a + b + c + e;
386 386 }
387 387
388 388 $ cd ..
389
390 Long function names should be abbreviated, but multi-byte character shouldn't
391 be broken up
392
393 $ hg init longfunc
394 $ cd longfunc
395
396 >>> with open('a', 'wb') as f:
397 ... f.write(b'a' * 39 + b'bb' + b'\n')
398 ... f.write(b' .\n' * 3)
399 ... f.write(b' 0 b\n')
400 ... f.write(b' .\n' * 3)
401 ... f.write(b'a' * 39 + b'\xc3\xa0' + b'\n')
402 ... f.write(b' .\n' * 3)
403 ... f.write(b' 0 a with grave (single code point)\n')
404 ... f.write(b' .\n' * 3)
405 ... f.write(b'a' * 39 + b'a\xcc\x80' + b'\n')
406 ... f.write(b' .\n' * 3)
407 ... f.write(b' 0 a with grave (composition)\n')
408 ... f.write(b' .\n' * 3)
409 $ hg ci -qAm0
410
411 >>> with open('a', 'wb') as f:
412 ... f.write(b'a' * 39 + b'bb' + b'\n')
413 ... f.write(b' .\n' * 3)
414 ... f.write(b' 1 b\n')
415 ... f.write(b' .\n' * 3)
416 ... f.write(b'a' * 39 + b'\xc3\xa0' + b'\n')
417 ... f.write(b' .\n' * 3)
418 ... f.write(b' 1 a with grave (single code point)\n')
419 ... f.write(b' .\n' * 3)
420 ... f.write(b'a' * 39 + b'a\xcc\x80' + b'\n')
421 ... f.write(b' .\n' * 3)
422 ... f.write(b' 1 a with grave (composition)\n')
423 ... f.write(b' .\n' * 3)
424 $ hg ci -m1
425
426 $ hg diff -c1 --nodates --show-function
427 diff -r 3e92dd6fa812 -r a256341606cb a
428 --- a/a
429 +++ b/a
430 @@ -2,7 +2,7 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab
431 .
432 .
433 .
434 - 0 b
435 + 1 b
436 .
437 .
438 .
439 @@ -10,7 +10,7 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xc3\xa0 (esc)
440 .
441 .
442 .
443 - 0 a with grave (single code point)
444 + 1 a with grave (single code point)
445 .
446 .
447 .
448 @@ -18,7 +18,7 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xcc\x80 (esc)
449 .
450 .
451 .
452 - 0 a with grave (composition)
453 + 1 a with grave (composition)
454 .
455 .
456 .
457
458 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now