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