##// END OF EJS Templates
errors: raise InputError when line range to followlines() is out of bounds...
Martin von Zweigbergk -
r46485:c8860a21 default
parent child Browse files
Show More
@@ -1,556 +1,556 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 .pycompat import (
15 from .pycompat import (
16 getattr,
16 getattr,
17 setattr,
17 setattr,
18 )
18 )
19 from . import (
19 from . import (
20 diffhelper,
20 diffhelper,
21 encoding,
21 encoding,
22 error,
22 error,
23 policy,
23 policy,
24 pycompat,
24 pycompat,
25 util,
25 util,
26 )
26 )
27 from .utils import dateutil
27 from .utils import dateutil
28
28
29 bdiff = policy.importmod('bdiff')
29 bdiff = policy.importmod('bdiff')
30 mpatch = policy.importmod('mpatch')
30 mpatch = policy.importmod('mpatch')
31
31
32 blocks = bdiff.blocks
32 blocks = bdiff.blocks
33 fixws = bdiff.fixws
33 fixws = bdiff.fixws
34 patches = mpatch.patches
34 patches = mpatch.patches
35 patchedsize = mpatch.patchedsize
35 patchedsize = mpatch.patchedsize
36 textdiff = bdiff.bdiff
36 textdiff = bdiff.bdiff
37 splitnewlines = bdiff.splitnewlines
37 splitnewlines = bdiff.splitnewlines
38
38
39
39
40 # TODO: this looks like it could be an attrs, which might help pytype
40 # TODO: this looks like it could be an attrs, which might help pytype
41 class diffopts(object):
41 class diffopts(object):
42 '''context is the number of context lines
42 '''context is the number of context lines
43 text treats all files as text
43 text treats all files as text
44 showfunc enables diff -p output
44 showfunc enables diff -p output
45 git enables the git extended patch format
45 git enables the git extended patch format
46 nodates removes dates from diff headers
46 nodates removes dates from diff headers
47 nobinary ignores binary files
47 nobinary ignores binary files
48 noprefix disables the 'a/' and 'b/' prefixes (ignored in plain mode)
48 noprefix disables the 'a/' and 'b/' prefixes (ignored in plain mode)
49 ignorews ignores all whitespace changes in the diff
49 ignorews ignores all whitespace changes in the diff
50 ignorewsamount ignores changes in the amount of whitespace
50 ignorewsamount ignores changes in the amount of whitespace
51 ignoreblanklines ignores changes whose lines are all blank
51 ignoreblanklines ignores changes whose lines are all blank
52 upgrade generates git diffs to avoid data loss
52 upgrade generates git diffs to avoid data loss
53 '''
53 '''
54
54
55 _HAS_DYNAMIC_ATTRIBUTES = True
55 _HAS_DYNAMIC_ATTRIBUTES = True
56
56
57 defaults = {
57 defaults = {
58 b'context': 3,
58 b'context': 3,
59 b'text': False,
59 b'text': False,
60 b'showfunc': False,
60 b'showfunc': False,
61 b'git': False,
61 b'git': False,
62 b'nodates': False,
62 b'nodates': False,
63 b'nobinary': False,
63 b'nobinary': False,
64 b'noprefix': False,
64 b'noprefix': False,
65 b'index': 0,
65 b'index': 0,
66 b'ignorews': False,
66 b'ignorews': False,
67 b'ignorewsamount': False,
67 b'ignorewsamount': False,
68 b'ignorewseol': False,
68 b'ignorewseol': False,
69 b'ignoreblanklines': False,
69 b'ignoreblanklines': False,
70 b'upgrade': False,
70 b'upgrade': False,
71 b'showsimilarity': False,
71 b'showsimilarity': False,
72 b'worddiff': False,
72 b'worddiff': False,
73 b'xdiff': False,
73 b'xdiff': False,
74 }
74 }
75
75
76 def __init__(self, **opts):
76 def __init__(self, **opts):
77 opts = pycompat.byteskwargs(opts)
77 opts = pycompat.byteskwargs(opts)
78 for k in self.defaults.keys():
78 for k in self.defaults.keys():
79 v = opts.get(k)
79 v = opts.get(k)
80 if v is None:
80 if v is None:
81 v = self.defaults[k]
81 v = self.defaults[k]
82 setattr(self, k, v)
82 setattr(self, k, v)
83
83
84 try:
84 try:
85 self.context = int(self.context)
85 self.context = int(self.context)
86 except ValueError:
86 except ValueError:
87 raise error.Abort(
87 raise error.Abort(
88 _(b'diff context lines count must be an integer, not %r')
88 _(b'diff context lines count must be an integer, not %r')
89 % pycompat.bytestr(self.context)
89 % pycompat.bytestr(self.context)
90 )
90 )
91
91
92 def copy(self, **kwargs):
92 def copy(self, **kwargs):
93 opts = {k: getattr(self, k) for k in self.defaults}
93 opts = {k: getattr(self, k) for k in self.defaults}
94 opts = pycompat.strkwargs(opts)
94 opts = pycompat.strkwargs(opts)
95 opts.update(kwargs)
95 opts.update(kwargs)
96 return diffopts(**opts)
96 return diffopts(**opts)
97
97
98
98
99 defaultopts = diffopts()
99 defaultopts = diffopts()
100
100
101
101
102 def wsclean(opts, text, blank=True):
102 def wsclean(opts, text, blank=True):
103 if opts.ignorews:
103 if opts.ignorews:
104 text = bdiff.fixws(text, 1)
104 text = bdiff.fixws(text, 1)
105 elif opts.ignorewsamount:
105 elif opts.ignorewsamount:
106 text = bdiff.fixws(text, 0)
106 text = bdiff.fixws(text, 0)
107 if blank and opts.ignoreblanklines:
107 if blank and opts.ignoreblanklines:
108 text = re.sub(b'\n+', b'\n', text).strip(b'\n')
108 text = re.sub(b'\n+', b'\n', text).strip(b'\n')
109 if opts.ignorewseol:
109 if opts.ignorewseol:
110 text = re.sub(br'[ \t\r\f]+\n', br'\n', text)
110 text = re.sub(br'[ \t\r\f]+\n', br'\n', text)
111 return text
111 return text
112
112
113
113
114 def splitblock(base1, lines1, base2, lines2, opts):
114 def splitblock(base1, lines1, base2, lines2, opts):
115 # The input lines matches except for interwoven blank lines. We
115 # The input lines matches except for interwoven blank lines. We
116 # transform it into a sequence of matching blocks and blank blocks.
116 # transform it into a sequence of matching blocks and blank blocks.
117 lines1 = [(wsclean(opts, l) and 1 or 0) for l in lines1]
117 lines1 = [(wsclean(opts, l) and 1 or 0) for l in lines1]
118 lines2 = [(wsclean(opts, l) and 1 or 0) for l in lines2]
118 lines2 = [(wsclean(opts, l) and 1 or 0) for l in lines2]
119 s1, e1 = 0, len(lines1)
119 s1, e1 = 0, len(lines1)
120 s2, e2 = 0, len(lines2)
120 s2, e2 = 0, len(lines2)
121 while s1 < e1 or s2 < e2:
121 while s1 < e1 or s2 < e2:
122 i1, i2, btype = s1, s2, b'='
122 i1, i2, btype = s1, s2, b'='
123 if i1 >= e1 or lines1[i1] == 0 or i2 >= e2 or lines2[i2] == 0:
123 if i1 >= e1 or lines1[i1] == 0 or i2 >= e2 or lines2[i2] == 0:
124 # Consume the block of blank lines
124 # Consume the block of blank lines
125 btype = b'~'
125 btype = b'~'
126 while i1 < e1 and lines1[i1] == 0:
126 while i1 < e1 and lines1[i1] == 0:
127 i1 += 1
127 i1 += 1
128 while i2 < e2 and lines2[i2] == 0:
128 while i2 < e2 and lines2[i2] == 0:
129 i2 += 1
129 i2 += 1
130 else:
130 else:
131 # Consume the matching lines
131 # Consume the matching lines
132 while i1 < e1 and lines1[i1] == 1 and lines2[i2] == 1:
132 while i1 < e1 and lines1[i1] == 1 and lines2[i2] == 1:
133 i1 += 1
133 i1 += 1
134 i2 += 1
134 i2 += 1
135 yield [base1 + s1, base1 + i1, base2 + s2, base2 + i2], btype
135 yield [base1 + s1, base1 + i1, base2 + s2, base2 + i2], btype
136 s1 = i1
136 s1 = i1
137 s2 = i2
137 s2 = i2
138
138
139
139
140 def hunkinrange(hunk, linerange):
140 def hunkinrange(hunk, linerange):
141 """Return True if `hunk` defined as (start, length) is in `linerange`
141 """Return True if `hunk` defined as (start, length) is in `linerange`
142 defined as (lowerbound, upperbound).
142 defined as (lowerbound, upperbound).
143
143
144 >>> hunkinrange((5, 10), (2, 7))
144 >>> hunkinrange((5, 10), (2, 7))
145 True
145 True
146 >>> hunkinrange((5, 10), (6, 12))
146 >>> hunkinrange((5, 10), (6, 12))
147 True
147 True
148 >>> hunkinrange((5, 10), (13, 17))
148 >>> hunkinrange((5, 10), (13, 17))
149 True
149 True
150 >>> hunkinrange((5, 10), (3, 17))
150 >>> hunkinrange((5, 10), (3, 17))
151 True
151 True
152 >>> hunkinrange((5, 10), (1, 3))
152 >>> hunkinrange((5, 10), (1, 3))
153 False
153 False
154 >>> hunkinrange((5, 10), (18, 20))
154 >>> hunkinrange((5, 10), (18, 20))
155 False
155 False
156 >>> hunkinrange((5, 10), (1, 5))
156 >>> hunkinrange((5, 10), (1, 5))
157 False
157 False
158 >>> hunkinrange((5, 10), (15, 27))
158 >>> hunkinrange((5, 10), (15, 27))
159 False
159 False
160 """
160 """
161 start, length = hunk
161 start, length = hunk
162 lowerbound, upperbound = linerange
162 lowerbound, upperbound = linerange
163 return lowerbound < start + length and start < upperbound
163 return lowerbound < start + length and start < upperbound
164
164
165
165
166 def blocksinrange(blocks, rangeb):
166 def blocksinrange(blocks, rangeb):
167 """filter `blocks` like (a1, a2, b1, b2) from items outside line range
167 """filter `blocks` like (a1, a2, b1, b2) from items outside line range
168 `rangeb` from ``(b1, b2)`` point of view.
168 `rangeb` from ``(b1, b2)`` point of view.
169
169
170 Return `filteredblocks, rangea` where:
170 Return `filteredblocks, rangea` where:
171
171
172 * `filteredblocks` is list of ``block = (a1, a2, b1, b2), stype`` items of
172 * `filteredblocks` is list of ``block = (a1, a2, b1, b2), stype`` items of
173 `blocks` that are inside `rangeb` from ``(b1, b2)`` point of view; a
173 `blocks` that are inside `rangeb` from ``(b1, b2)`` point of view; a
174 block ``(b1, b2)`` being inside `rangeb` if
174 block ``(b1, b2)`` being inside `rangeb` if
175 ``rangeb[0] < b2 and b1 < rangeb[1]``;
175 ``rangeb[0] < b2 and b1 < rangeb[1]``;
176 * `rangea` is the line range w.r.t. to ``(a1, a2)`` parts of `blocks`.
176 * `rangea` is the line range w.r.t. to ``(a1, a2)`` parts of `blocks`.
177 """
177 """
178 lbb, ubb = rangeb
178 lbb, ubb = rangeb
179 lba, uba = None, None
179 lba, uba = None, None
180 filteredblocks = []
180 filteredblocks = []
181 for block in blocks:
181 for block in blocks:
182 (a1, a2, b1, b2), stype = block
182 (a1, a2, b1, b2), stype = block
183 if lbb >= b1 and ubb <= b2 and stype == b'=':
183 if lbb >= b1 and ubb <= b2 and stype == b'=':
184 # rangeb is within a single "=" hunk, restrict back linerange1
184 # rangeb is within a single "=" hunk, restrict back linerange1
185 # by offsetting rangeb
185 # by offsetting rangeb
186 lba = lbb - b1 + a1
186 lba = lbb - b1 + a1
187 uba = ubb - b1 + a1
187 uba = ubb - b1 + a1
188 else:
188 else:
189 if b1 <= lbb < b2:
189 if b1 <= lbb < b2:
190 if stype == b'=':
190 if stype == b'=':
191 lba = a2 - (b2 - lbb)
191 lba = a2 - (b2 - lbb)
192 else:
192 else:
193 lba = a1
193 lba = a1
194 if b1 < ubb <= b2:
194 if b1 < ubb <= b2:
195 if stype == b'=':
195 if stype == b'=':
196 uba = a1 + (ubb - b1)
196 uba = a1 + (ubb - b1)
197 else:
197 else:
198 uba = a2
198 uba = a2
199 if hunkinrange((b1, (b2 - b1)), rangeb):
199 if hunkinrange((b1, (b2 - b1)), rangeb):
200 filteredblocks.append(block)
200 filteredblocks.append(block)
201 if lba is None or uba is None or uba < lba:
201 if lba is None or uba is None or uba < lba:
202 raise error.Abort(_(b'line range exceeds file size'))
202 raise error.InputError(_(b'line range exceeds file size'))
203 return filteredblocks, (lba, uba)
203 return filteredblocks, (lba, uba)
204
204
205
205
206 def chooseblocksfunc(opts=None):
206 def chooseblocksfunc(opts=None):
207 if (
207 if (
208 opts is None
208 opts is None
209 or not opts.xdiff
209 or not opts.xdiff
210 or not util.safehasattr(bdiff, b'xdiffblocks')
210 or not util.safehasattr(bdiff, b'xdiffblocks')
211 ):
211 ):
212 return bdiff.blocks
212 return bdiff.blocks
213 else:
213 else:
214 return bdiff.xdiffblocks
214 return bdiff.xdiffblocks
215
215
216
216
217 def allblocks(text1, text2, opts=None, lines1=None, lines2=None):
217 def allblocks(text1, text2, opts=None, lines1=None, lines2=None):
218 """Return (block, type) tuples, where block is an mdiff.blocks
218 """Return (block, type) tuples, where block is an mdiff.blocks
219 line entry. type is '=' for blocks matching exactly one another
219 line entry. type is '=' for blocks matching exactly one another
220 (bdiff blocks), '!' for non-matching blocks and '~' for blocks
220 (bdiff blocks), '!' for non-matching blocks and '~' for blocks
221 matching only after having filtered blank lines.
221 matching only after having filtered blank lines.
222 line1 and line2 are text1 and text2 split with splitnewlines() if
222 line1 and line2 are text1 and text2 split with splitnewlines() if
223 they are already available.
223 they are already available.
224 """
224 """
225 if opts is None:
225 if opts is None:
226 opts = defaultopts
226 opts = defaultopts
227 if opts.ignorews or opts.ignorewsamount or opts.ignorewseol:
227 if opts.ignorews or opts.ignorewsamount or opts.ignorewseol:
228 text1 = wsclean(opts, text1, False)
228 text1 = wsclean(opts, text1, False)
229 text2 = wsclean(opts, text2, False)
229 text2 = wsclean(opts, text2, False)
230 diff = chooseblocksfunc(opts)(text1, text2)
230 diff = chooseblocksfunc(opts)(text1, text2)
231 for i, s1 in enumerate(diff):
231 for i, s1 in enumerate(diff):
232 # The first match is special.
232 # The first match is special.
233 # we've either found a match starting at line 0 or a match later
233 # we've either found a match starting at line 0 or a match later
234 # in the file. If it starts later, old and new below will both be
234 # in the file. If it starts later, old and new below will both be
235 # empty and we'll continue to the next match.
235 # empty and we'll continue to the next match.
236 if i > 0:
236 if i > 0:
237 s = diff[i - 1]
237 s = diff[i - 1]
238 else:
238 else:
239 s = [0, 0, 0, 0]
239 s = [0, 0, 0, 0]
240 s = [s[1], s1[0], s[3], s1[2]]
240 s = [s[1], s1[0], s[3], s1[2]]
241
241
242 # bdiff sometimes gives huge matches past eof, this check eats them,
242 # bdiff sometimes gives huge matches past eof, this check eats them,
243 # and deals with the special first match case described above
243 # and deals with the special first match case described above
244 if s[0] != s[1] or s[2] != s[3]:
244 if s[0] != s[1] or s[2] != s[3]:
245 type = b'!'
245 type = b'!'
246 if opts.ignoreblanklines:
246 if opts.ignoreblanklines:
247 if lines1 is None:
247 if lines1 is None:
248 lines1 = splitnewlines(text1)
248 lines1 = splitnewlines(text1)
249 if lines2 is None:
249 if lines2 is None:
250 lines2 = splitnewlines(text2)
250 lines2 = splitnewlines(text2)
251 old = wsclean(opts, b"".join(lines1[s[0] : s[1]]))
251 old = wsclean(opts, b"".join(lines1[s[0] : s[1]]))
252 new = wsclean(opts, b"".join(lines2[s[2] : s[3]]))
252 new = wsclean(opts, b"".join(lines2[s[2] : s[3]]))
253 if old == new:
253 if old == new:
254 type = b'~'
254 type = b'~'
255 yield s, type
255 yield s, type
256 yield s1, b'='
256 yield s1, b'='
257
257
258
258
259 def unidiff(a, ad, b, bd, fn1, fn2, binary, opts=defaultopts):
259 def unidiff(a, ad, b, bd, fn1, fn2, binary, opts=defaultopts):
260 """Return a unified diff as a (headers, hunks) tuple.
260 """Return a unified diff as a (headers, hunks) tuple.
261
261
262 If the diff is not null, `headers` is a list with unified diff header
262 If the diff is not null, `headers` is a list with unified diff header
263 lines "--- <original>" and "+++ <new>" and `hunks` is a generator yielding
263 lines "--- <original>" and "+++ <new>" and `hunks` is a generator yielding
264 (hunkrange, hunklines) coming from _unidiff().
264 (hunkrange, hunklines) coming from _unidiff().
265 Otherwise, `headers` and `hunks` are empty.
265 Otherwise, `headers` and `hunks` are empty.
266
266
267 Set binary=True if either a or b should be taken as a binary file.
267 Set binary=True if either a or b should be taken as a binary file.
268 """
268 """
269
269
270 def datetag(date, fn=None):
270 def datetag(date, fn=None):
271 if not opts.git and not opts.nodates:
271 if not opts.git and not opts.nodates:
272 return b'\t%s' % date
272 return b'\t%s' % date
273 if fn and b' ' in fn:
273 if fn and b' ' in fn:
274 return b'\t'
274 return b'\t'
275 return b''
275 return b''
276
276
277 sentinel = [], ()
277 sentinel = [], ()
278 if not a and not b:
278 if not a and not b:
279 return sentinel
279 return sentinel
280
280
281 if opts.noprefix:
281 if opts.noprefix:
282 aprefix = bprefix = b''
282 aprefix = bprefix = b''
283 else:
283 else:
284 aprefix = b'a/'
284 aprefix = b'a/'
285 bprefix = b'b/'
285 bprefix = b'b/'
286
286
287 epoch = dateutil.datestr((0, 0))
287 epoch = dateutil.datestr((0, 0))
288
288
289 fn1 = util.pconvert(fn1)
289 fn1 = util.pconvert(fn1)
290 fn2 = util.pconvert(fn2)
290 fn2 = util.pconvert(fn2)
291
291
292 if binary:
292 if binary:
293 if a and b and len(a) == len(b) and a == b:
293 if a and b and len(a) == len(b) and a == b:
294 return sentinel
294 return sentinel
295 headerlines = []
295 headerlines = []
296 hunks = ((None, [b'Binary file %s has changed\n' % fn1]),)
296 hunks = ((None, [b'Binary file %s has changed\n' % fn1]),)
297 elif not a:
297 elif not a:
298 without_newline = not b.endswith(b'\n')
298 without_newline = not b.endswith(b'\n')
299 b = splitnewlines(b)
299 b = splitnewlines(b)
300 if a is None:
300 if a is None:
301 l1 = b'--- /dev/null%s' % datetag(epoch)
301 l1 = b'--- /dev/null%s' % datetag(epoch)
302 else:
302 else:
303 l1 = b"--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1))
303 l1 = b"--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1))
304 l2 = b"+++ %s%s" % (bprefix + fn2, datetag(bd, fn2))
304 l2 = b"+++ %s%s" % (bprefix + fn2, datetag(bd, fn2))
305 headerlines = [l1, l2]
305 headerlines = [l1, l2]
306 size = len(b)
306 size = len(b)
307 hunkrange = (0, 0, 1, size)
307 hunkrange = (0, 0, 1, size)
308 hunklines = [b"@@ -0,0 +1,%d @@\n" % size] + [b"+" + e for e in b]
308 hunklines = [b"@@ -0,0 +1,%d @@\n" % size] + [b"+" + e for e in b]
309 if without_newline:
309 if without_newline:
310 hunklines[-1] += b'\n'
310 hunklines[-1] += b'\n'
311 hunklines.append(diffhelper.MISSING_NEWLINE_MARKER)
311 hunklines.append(diffhelper.MISSING_NEWLINE_MARKER)
312 hunks = ((hunkrange, hunklines),)
312 hunks = ((hunkrange, hunklines),)
313 elif not b:
313 elif not b:
314 without_newline = not a.endswith(b'\n')
314 without_newline = not a.endswith(b'\n')
315 a = splitnewlines(a)
315 a = splitnewlines(a)
316 l1 = b"--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1))
316 l1 = b"--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1))
317 if b is None:
317 if b is None:
318 l2 = b'+++ /dev/null%s' % datetag(epoch)
318 l2 = b'+++ /dev/null%s' % datetag(epoch)
319 else:
319 else:
320 l2 = b"+++ %s%s%s" % (bprefix, fn2, datetag(bd, fn2))
320 l2 = b"+++ %s%s%s" % (bprefix, fn2, datetag(bd, fn2))
321 headerlines = [l1, l2]
321 headerlines = [l1, l2]
322 size = len(a)
322 size = len(a)
323 hunkrange = (1, size, 0, 0)
323 hunkrange = (1, size, 0, 0)
324 hunklines = [b"@@ -1,%d +0,0 @@\n" % size] + [b"-" + e for e in a]
324 hunklines = [b"@@ -1,%d +0,0 @@\n" % size] + [b"-" + e for e in a]
325 if without_newline:
325 if without_newline:
326 hunklines[-1] += b'\n'
326 hunklines[-1] += b'\n'
327 hunklines.append(diffhelper.MISSING_NEWLINE_MARKER)
327 hunklines.append(diffhelper.MISSING_NEWLINE_MARKER)
328 hunks = ((hunkrange, hunklines),)
328 hunks = ((hunkrange, hunklines),)
329 else:
329 else:
330 hunks = _unidiff(a, b, opts=opts)
330 hunks = _unidiff(a, b, opts=opts)
331 if not next(hunks):
331 if not next(hunks):
332 return sentinel
332 return sentinel
333
333
334 headerlines = [
334 headerlines = [
335 b"--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1)),
335 b"--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1)),
336 b"+++ %s%s%s" % (bprefix, fn2, datetag(bd, fn2)),
336 b"+++ %s%s%s" % (bprefix, fn2, datetag(bd, fn2)),
337 ]
337 ]
338
338
339 return headerlines, hunks
339 return headerlines, hunks
340
340
341
341
342 def _unidiff(t1, t2, opts=defaultopts):
342 def _unidiff(t1, t2, opts=defaultopts):
343 """Yield hunks of a headerless unified diff from t1 and t2 texts.
343 """Yield hunks of a headerless unified diff from t1 and t2 texts.
344
344
345 Each hunk consists of a (hunkrange, hunklines) tuple where `hunkrange` is a
345 Each hunk consists of a (hunkrange, hunklines) tuple where `hunkrange` is a
346 tuple (s1, l1, s2, l2) representing the range information of the hunk to
346 tuple (s1, l1, s2, l2) representing the range information of the hunk to
347 form the '@@ -s1,l1 +s2,l2 @@' header and `hunklines` is a list of lines
347 form the '@@ -s1,l1 +s2,l2 @@' header and `hunklines` is a list of lines
348 of the hunk combining said header followed by line additions and
348 of the hunk combining said header followed by line additions and
349 deletions.
349 deletions.
350
350
351 The hunks are prefixed with a bool.
351 The hunks are prefixed with a bool.
352 """
352 """
353 l1 = splitnewlines(t1)
353 l1 = splitnewlines(t1)
354 l2 = splitnewlines(t2)
354 l2 = splitnewlines(t2)
355
355
356 def contextend(l, len):
356 def contextend(l, len):
357 ret = l + opts.context
357 ret = l + opts.context
358 if ret > len:
358 if ret > len:
359 ret = len
359 ret = len
360 return ret
360 return ret
361
361
362 def contextstart(l):
362 def contextstart(l):
363 ret = l - opts.context
363 ret = l - opts.context
364 if ret < 0:
364 if ret < 0:
365 return 0
365 return 0
366 return ret
366 return ret
367
367
368 lastfunc = [0, b'']
368 lastfunc = [0, b'']
369
369
370 def yieldhunk(hunk):
370 def yieldhunk(hunk):
371 (astart, a2, bstart, b2, delta) = hunk
371 (astart, a2, bstart, b2, delta) = hunk
372 aend = contextend(a2, len(l1))
372 aend = contextend(a2, len(l1))
373 alen = aend - astart
373 alen = aend - astart
374 blen = b2 - bstart + aend - a2
374 blen = b2 - bstart + aend - a2
375
375
376 func = b""
376 func = b""
377 if opts.showfunc:
377 if opts.showfunc:
378 lastpos, func = lastfunc
378 lastpos, func = lastfunc
379 # walk backwards from the start of the context up to the start of
379 # walk backwards from the start of the context up to the start of
380 # the previous hunk context until we find a line starting with an
380 # the previous hunk context until we find a line starting with an
381 # alphanumeric char.
381 # alphanumeric char.
382 for i in pycompat.xrange(astart - 1, lastpos - 1, -1):
382 for i in pycompat.xrange(astart - 1, lastpos - 1, -1):
383 if l1[i][0:1].isalnum():
383 if l1[i][0:1].isalnum():
384 func = b' ' + l1[i].rstrip()
384 func = b' ' + l1[i].rstrip()
385 # split long function name if ASCII. otherwise we have no
385 # split long function name if ASCII. otherwise we have no
386 # idea where the multi-byte boundary is, so just leave it.
386 # idea where the multi-byte boundary is, so just leave it.
387 if encoding.isasciistr(func):
387 if encoding.isasciistr(func):
388 func = func[:41]
388 func = func[:41]
389 lastfunc[1] = func
389 lastfunc[1] = func
390 break
390 break
391 # by recording this hunk's starting point as the next place to
391 # by recording this hunk's starting point as the next place to
392 # start looking for function lines, we avoid reading any line in
392 # start looking for function lines, we avoid reading any line in
393 # the file more than once.
393 # the file more than once.
394 lastfunc[0] = astart
394 lastfunc[0] = astart
395
395
396 # zero-length hunk ranges report their start line as one less
396 # zero-length hunk ranges report their start line as one less
397 if alen:
397 if alen:
398 astart += 1
398 astart += 1
399 if blen:
399 if blen:
400 bstart += 1
400 bstart += 1
401
401
402 hunkrange = astart, alen, bstart, blen
402 hunkrange = astart, alen, bstart, blen
403 hunklines = (
403 hunklines = (
404 [b"@@ -%d,%d +%d,%d @@%s\n" % (hunkrange + (func,))]
404 [b"@@ -%d,%d +%d,%d @@%s\n" % (hunkrange + (func,))]
405 + delta
405 + delta
406 + [b' ' + l1[x] for x in pycompat.xrange(a2, aend)]
406 + [b' ' + l1[x] for x in pycompat.xrange(a2, aend)]
407 )
407 )
408 # If either file ends without a newline and the last line of
408 # If either file ends without a newline and the last line of
409 # that file is part of a hunk, a marker is printed. If the
409 # that file is part of a hunk, a marker is printed. If the
410 # last line of both files is identical and neither ends in
410 # last line of both files is identical and neither ends in
411 # a newline, print only one marker. That's the only case in
411 # a newline, print only one marker. That's the only case in
412 # which the hunk can end in a shared line without a newline.
412 # which the hunk can end in a shared line without a newline.
413 skip = False
413 skip = False
414 if not t1.endswith(b'\n') and astart + alen == len(l1) + 1:
414 if not t1.endswith(b'\n') and astart + alen == len(l1) + 1:
415 for i in pycompat.xrange(len(hunklines) - 1, -1, -1):
415 for i in pycompat.xrange(len(hunklines) - 1, -1, -1):
416 if hunklines[i].startswith((b'-', b' ')):
416 if hunklines[i].startswith((b'-', b' ')):
417 if hunklines[i].startswith(b' '):
417 if hunklines[i].startswith(b' '):
418 skip = True
418 skip = True
419 hunklines[i] += b'\n'
419 hunklines[i] += b'\n'
420 hunklines.insert(i + 1, diffhelper.MISSING_NEWLINE_MARKER)
420 hunklines.insert(i + 1, diffhelper.MISSING_NEWLINE_MARKER)
421 break
421 break
422 if not skip and not t2.endswith(b'\n') and bstart + blen == len(l2) + 1:
422 if not skip and not t2.endswith(b'\n') and bstart + blen == len(l2) + 1:
423 for i in pycompat.xrange(len(hunklines) - 1, -1, -1):
423 for i in pycompat.xrange(len(hunklines) - 1, -1, -1):
424 if hunklines[i].startswith(b'+'):
424 if hunklines[i].startswith(b'+'):
425 hunklines[i] += b'\n'
425 hunklines[i] += b'\n'
426 hunklines.insert(i + 1, diffhelper.MISSING_NEWLINE_MARKER)
426 hunklines.insert(i + 1, diffhelper.MISSING_NEWLINE_MARKER)
427 break
427 break
428 yield hunkrange, hunklines
428 yield hunkrange, hunklines
429
429
430 # bdiff.blocks gives us the matching sequences in the files. The loop
430 # bdiff.blocks gives us the matching sequences in the files. The loop
431 # below finds the spaces between those matching sequences and translates
431 # below finds the spaces between those matching sequences and translates
432 # them into diff output.
432 # them into diff output.
433 #
433 #
434 hunk = None
434 hunk = None
435 ignoredlines = 0
435 ignoredlines = 0
436 has_hunks = False
436 has_hunks = False
437 for s, stype in allblocks(t1, t2, opts, l1, l2):
437 for s, stype in allblocks(t1, t2, opts, l1, l2):
438 a1, a2, b1, b2 = s
438 a1, a2, b1, b2 = s
439 if stype != b'!':
439 if stype != b'!':
440 if stype == b'~':
440 if stype == b'~':
441 # The diff context lines are based on t1 content. When
441 # The diff context lines are based on t1 content. When
442 # blank lines are ignored, the new lines offsets must
442 # blank lines are ignored, the new lines offsets must
443 # be adjusted as if equivalent blocks ('~') had the
443 # be adjusted as if equivalent blocks ('~') had the
444 # same sizes on both sides.
444 # same sizes on both sides.
445 ignoredlines += (b2 - b1) - (a2 - a1)
445 ignoredlines += (b2 - b1) - (a2 - a1)
446 continue
446 continue
447 delta = []
447 delta = []
448 old = l1[a1:a2]
448 old = l1[a1:a2]
449 new = l2[b1:b2]
449 new = l2[b1:b2]
450
450
451 b1 -= ignoredlines
451 b1 -= ignoredlines
452 b2 -= ignoredlines
452 b2 -= ignoredlines
453 astart = contextstart(a1)
453 astart = contextstart(a1)
454 bstart = contextstart(b1)
454 bstart = contextstart(b1)
455 prev = None
455 prev = None
456 if hunk:
456 if hunk:
457 # join with the previous hunk if it falls inside the context
457 # join with the previous hunk if it falls inside the context
458 if astart < hunk[1] + opts.context + 1:
458 if astart < hunk[1] + opts.context + 1:
459 prev = hunk
459 prev = hunk
460 astart = hunk[1]
460 astart = hunk[1]
461 bstart = hunk[3]
461 bstart = hunk[3]
462 else:
462 else:
463 if not has_hunks:
463 if not has_hunks:
464 has_hunks = True
464 has_hunks = True
465 yield True
465 yield True
466 for x in yieldhunk(hunk):
466 for x in yieldhunk(hunk):
467 yield x
467 yield x
468 if prev:
468 if prev:
469 # we've joined the previous hunk, record the new ending points.
469 # we've joined the previous hunk, record the new ending points.
470 hunk[1] = a2
470 hunk[1] = a2
471 hunk[3] = b2
471 hunk[3] = b2
472 delta = hunk[4]
472 delta = hunk[4]
473 else:
473 else:
474 # create a new hunk
474 # create a new hunk
475 hunk = [astart, a2, bstart, b2, delta]
475 hunk = [astart, a2, bstart, b2, delta]
476
476
477 delta[len(delta) :] = [b' ' + x for x in l1[astart:a1]]
477 delta[len(delta) :] = [b' ' + x for x in l1[astart:a1]]
478 delta[len(delta) :] = [b'-' + x for x in old]
478 delta[len(delta) :] = [b'-' + x for x in old]
479 delta[len(delta) :] = [b'+' + x for x in new]
479 delta[len(delta) :] = [b'+' + x for x in new]
480
480
481 if hunk:
481 if hunk:
482 if not has_hunks:
482 if not has_hunks:
483 has_hunks = True
483 has_hunks = True
484 yield True
484 yield True
485 for x in yieldhunk(hunk):
485 for x in yieldhunk(hunk):
486 yield x
486 yield x
487 elif not has_hunks:
487 elif not has_hunks:
488 yield False
488 yield False
489
489
490
490
491 def b85diff(to, tn):
491 def b85diff(to, tn):
492 '''print base85-encoded binary diff'''
492 '''print base85-encoded binary diff'''
493
493
494 def fmtline(line):
494 def fmtline(line):
495 l = len(line)
495 l = len(line)
496 if l <= 26:
496 if l <= 26:
497 l = pycompat.bytechr(ord(b'A') + l - 1)
497 l = pycompat.bytechr(ord(b'A') + l - 1)
498 else:
498 else:
499 l = pycompat.bytechr(l - 26 + ord(b'a') - 1)
499 l = pycompat.bytechr(l - 26 + ord(b'a') - 1)
500 return b'%c%s\n' % (l, util.b85encode(line, True))
500 return b'%c%s\n' % (l, util.b85encode(line, True))
501
501
502 def chunk(text, csize=52):
502 def chunk(text, csize=52):
503 l = len(text)
503 l = len(text)
504 i = 0
504 i = 0
505 while i < l:
505 while i < l:
506 yield text[i : i + csize]
506 yield text[i : i + csize]
507 i += csize
507 i += csize
508
508
509 if to is None:
509 if to is None:
510 to = b''
510 to = b''
511 if tn is None:
511 if tn is None:
512 tn = b''
512 tn = b''
513
513
514 if to == tn:
514 if to == tn:
515 return b''
515 return b''
516
516
517 # TODO: deltas
517 # TODO: deltas
518 ret = []
518 ret = []
519 ret.append(b'GIT binary patch\n')
519 ret.append(b'GIT binary patch\n')
520 ret.append(b'literal %d\n' % len(tn))
520 ret.append(b'literal %d\n' % len(tn))
521 for l in chunk(zlib.compress(tn)):
521 for l in chunk(zlib.compress(tn)):
522 ret.append(fmtline(l))
522 ret.append(fmtline(l))
523 ret.append(b'\n')
523 ret.append(b'\n')
524
524
525 return b''.join(ret)
525 return b''.join(ret)
526
526
527
527
528 def patchtext(bin):
528 def patchtext(bin):
529 pos = 0
529 pos = 0
530 t = []
530 t = []
531 while pos < len(bin):
531 while pos < len(bin):
532 p1, p2, l = struct.unpack(b">lll", bin[pos : pos + 12])
532 p1, p2, l = struct.unpack(b">lll", bin[pos : pos + 12])
533 pos += 12
533 pos += 12
534 t.append(bin[pos : pos + l])
534 t.append(bin[pos : pos + l])
535 pos += l
535 pos += l
536 return b"".join(t)
536 return b"".join(t)
537
537
538
538
539 def patch(a, bin):
539 def patch(a, bin):
540 if len(a) == 0:
540 if len(a) == 0:
541 # skip over trivial delta header
541 # skip over trivial delta header
542 return util.buffer(bin, 12)
542 return util.buffer(bin, 12)
543 return mpatch.patches(a, [bin])
543 return mpatch.patches(a, [bin])
544
544
545
545
546 # similar to difflib.SequenceMatcher.get_matching_blocks
546 # similar to difflib.SequenceMatcher.get_matching_blocks
547 def get_matching_blocks(a, b):
547 def get_matching_blocks(a, b):
548 return [(d[0], d[2], d[1] - d[0]) for d in bdiff.blocks(a, b)]
548 return [(d[0], d[2], d[1] - d[0]) for d in bdiff.blocks(a, b)]
549
549
550
550
551 def trivialdiffheader(length):
551 def trivialdiffheader(length):
552 return struct.pack(b">lll", 0, 0, length) if length else b''
552 return struct.pack(b">lll", 0, 0, length) if length else b''
553
553
554
554
555 def replacediffheader(oldlen, newlen):
555 def replacediffheader(oldlen, newlen):
556 return struct.pack(b">lll", 0, oldlen, newlen)
556 return struct.pack(b">lll", 0, oldlen, newlen)
@@ -1,1202 +1,1202 b''
1 $ cat >> "$HGRCPATH" << EOF
1 $ cat >> "$HGRCPATH" << EOF
2 > [ui]
2 > [ui]
3 > merge = :merge3
3 > merge = :merge3
4 > EOF
4 > EOF
5
5
6 init
6 init
7
7
8 $ hg init repo
8 $ hg init repo
9 $ cd repo
9 $ cd repo
10
10
11 commit
11 commit
12
12
13 $ echo 'a' > a
13 $ echo 'a' > a
14 $ hg ci -A -m test -u nobody -d '1 0'
14 $ hg ci -A -m test -u nobody -d '1 0'
15 adding a
15 adding a
16
16
17 annotate -c
17 annotate -c
18
18
19 $ hg annotate -c a
19 $ hg annotate -c a
20 8435f90966e4: a
20 8435f90966e4: a
21
21
22 annotate -cl
22 annotate -cl
23
23
24 $ hg annotate -cl a
24 $ hg annotate -cl a
25 8435f90966e4:1: a
25 8435f90966e4:1: a
26
26
27 annotate -d
27 annotate -d
28
28
29 $ hg annotate -d a
29 $ hg annotate -d a
30 Thu Jan 01 00:00:01 1970 +0000: a
30 Thu Jan 01 00:00:01 1970 +0000: a
31
31
32 annotate -n
32 annotate -n
33
33
34 $ hg annotate -n a
34 $ hg annotate -n a
35 0: a
35 0: a
36
36
37 annotate -nl
37 annotate -nl
38
38
39 $ hg annotate -nl a
39 $ hg annotate -nl a
40 0:1: a
40 0:1: a
41
41
42 annotate -u
42 annotate -u
43
43
44 $ hg annotate -u a
44 $ hg annotate -u a
45 nobody: a
45 nobody: a
46
46
47 annotate -cdnu
47 annotate -cdnu
48
48
49 $ hg annotate -cdnu a
49 $ hg annotate -cdnu a
50 nobody 0 8435f90966e4 Thu Jan 01 00:00:01 1970 +0000: a
50 nobody 0 8435f90966e4 Thu Jan 01 00:00:01 1970 +0000: a
51
51
52 annotate -cdnul
52 annotate -cdnul
53
53
54 $ hg annotate -cdnul a
54 $ hg annotate -cdnul a
55 nobody 0 8435f90966e4 Thu Jan 01 00:00:01 1970 +0000:1: a
55 nobody 0 8435f90966e4 Thu Jan 01 00:00:01 1970 +0000:1: a
56
56
57 annotate (JSON)
57 annotate (JSON)
58
58
59 $ hg annotate -Tjson a
59 $ hg annotate -Tjson a
60 [
60 [
61 {
61 {
62 "lines": [{"line": "a\n", "rev": 0}],
62 "lines": [{"line": "a\n", "rev": 0}],
63 "path": "a"
63 "path": "a"
64 }
64 }
65 ]
65 ]
66
66
67 $ hg annotate -Tjson -cdfnul a
67 $ hg annotate -Tjson -cdfnul a
68 [
68 [
69 {
69 {
70 "lines": [{"date": [1.0, 0], "line": "a\n", "lineno": 1, "node": "8435f90966e442695d2ded29fdade2bac5ad8065", "path": "a", "rev": 0, "user": "nobody"}],
70 "lines": [{"date": [1.0, 0], "line": "a\n", "lineno": 1, "node": "8435f90966e442695d2ded29fdade2bac5ad8065", "path": "a", "rev": 0, "user": "nobody"}],
71 "path": "a"
71 "path": "a"
72 }
72 }
73 ]
73 ]
74
74
75 log-like templating
75 log-like templating
76
76
77 $ hg annotate -T'{lines % "{rev} {node|shortest}: {line}"}' a
77 $ hg annotate -T'{lines % "{rev} {node|shortest}: {line}"}' a
78 0 8435: a
78 0 8435: a
79
79
80 '{lineno}' field should be populated as necessary
80 '{lineno}' field should be populated as necessary
81
81
82 $ hg annotate -T'{lines % "{rev}:{lineno}: {line}"}' a
82 $ hg annotate -T'{lines % "{rev}:{lineno}: {line}"}' a
83 0:1: a
83 0:1: a
84 $ hg annotate -Ta a \
84 $ hg annotate -Ta a \
85 > --config templates.a='"{lines % "{rev}:{lineno}: {line}"}"'
85 > --config templates.a='"{lines % "{rev}:{lineno}: {line}"}"'
86 0:1: a
86 0:1: a
87
87
88 $ cat <<EOF >>a
88 $ cat <<EOF >>a
89 > a
89 > a
90 > a
90 > a
91 > EOF
91 > EOF
92 $ hg ci -ma1 -d '1 0'
92 $ hg ci -ma1 -d '1 0'
93 $ hg cp a b
93 $ hg cp a b
94 $ hg ci -mb -d '1 0'
94 $ hg ci -mb -d '1 0'
95 $ cat <<EOF >> b
95 $ cat <<EOF >> b
96 > b4
96 > b4
97 > b5
97 > b5
98 > b6
98 > b6
99 > EOF
99 > EOF
100 $ hg ci -mb2 -d '2 0'
100 $ hg ci -mb2 -d '2 0'
101
101
102 default output of '{lines}' should be readable
102 default output of '{lines}' should be readable
103
103
104 $ hg annotate -T'{lines}' a
104 $ hg annotate -T'{lines}' a
105 0: a
105 0: a
106 1: a
106 1: a
107 1: a
107 1: a
108 $ hg annotate -T'{join(lines, "\n")}' a
108 $ hg annotate -T'{join(lines, "\n")}' a
109 0: a
109 0: a
110
110
111 1: a
111 1: a
112
112
113 1: a
113 1: a
114
114
115 several filters can be applied to '{lines}'
115 several filters can be applied to '{lines}'
116
116
117 $ hg annotate -T'{lines|json}\n' a
117 $ hg annotate -T'{lines|json}\n' a
118 [{"line": "a\n", "rev": 0}, {"line": "a\n", "rev": 1}, {"line": "a\n", "rev": 1}]
118 [{"line": "a\n", "rev": 0}, {"line": "a\n", "rev": 1}, {"line": "a\n", "rev": 1}]
119 $ hg annotate -T'{lines|stringify}' a
119 $ hg annotate -T'{lines|stringify}' a
120 0: a
120 0: a
121 1: a
121 1: a
122 1: a
122 1: a
123 $ hg annotate -T'{lines|count}\n' a
123 $ hg annotate -T'{lines|count}\n' a
124 3
124 3
125
125
126 annotate multiple files (JSON)
126 annotate multiple files (JSON)
127
127
128 $ hg annotate -Tjson a b
128 $ hg annotate -Tjson a b
129 [
129 [
130 {
130 {
131 "lines": [{"line": "a\n", "rev": 0}, {"line": "a\n", "rev": 1}, {"line": "a\n", "rev": 1}],
131 "lines": [{"line": "a\n", "rev": 0}, {"line": "a\n", "rev": 1}, {"line": "a\n", "rev": 1}],
132 "path": "a"
132 "path": "a"
133 },
133 },
134 {
134 {
135 "lines": [{"line": "a\n", "rev": 0}, {"line": "a\n", "rev": 1}, {"line": "a\n", "rev": 1}, {"line": "b4\n", "rev": 3}, {"line": "b5\n", "rev": 3}, {"line": "b6\n", "rev": 3}],
135 "lines": [{"line": "a\n", "rev": 0}, {"line": "a\n", "rev": 1}, {"line": "a\n", "rev": 1}, {"line": "b4\n", "rev": 3}, {"line": "b5\n", "rev": 3}, {"line": "b6\n", "rev": 3}],
136 "path": "b"
136 "path": "b"
137 }
137 }
138 ]
138 ]
139
139
140 annotate multiple files (template)
140 annotate multiple files (template)
141
141
142 $ hg annotate -T'== {path} ==\n{lines % "{rev}: {line}"}' a b
142 $ hg annotate -T'== {path} ==\n{lines % "{rev}: {line}"}' a b
143 == a ==
143 == a ==
144 0: a
144 0: a
145 1: a
145 1: a
146 1: a
146 1: a
147 == b ==
147 == b ==
148 0: a
148 0: a
149 1: a
149 1: a
150 1: a
150 1: a
151 3: b4
151 3: b4
152 3: b5
152 3: b5
153 3: b6
153 3: b6
154
154
155 annotate -n b
155 annotate -n b
156
156
157 $ hg annotate -n b
157 $ hg annotate -n b
158 0: a
158 0: a
159 1: a
159 1: a
160 1: a
160 1: a
161 3: b4
161 3: b4
162 3: b5
162 3: b5
163 3: b6
163 3: b6
164
164
165 annotate --no-follow b
165 annotate --no-follow b
166
166
167 $ hg annotate --no-follow b
167 $ hg annotate --no-follow b
168 2: a
168 2: a
169 2: a
169 2: a
170 2: a
170 2: a
171 3: b4
171 3: b4
172 3: b5
172 3: b5
173 3: b6
173 3: b6
174
174
175 annotate -nl b
175 annotate -nl b
176
176
177 $ hg annotate -nl b
177 $ hg annotate -nl b
178 0:1: a
178 0:1: a
179 1:2: a
179 1:2: a
180 1:3: a
180 1:3: a
181 3:4: b4
181 3:4: b4
182 3:5: b5
182 3:5: b5
183 3:6: b6
183 3:6: b6
184
184
185 annotate -nf b
185 annotate -nf b
186
186
187 $ hg annotate -nf b
187 $ hg annotate -nf b
188 0 a: a
188 0 a: a
189 1 a: a
189 1 a: a
190 1 a: a
190 1 a: a
191 3 b: b4
191 3 b: b4
192 3 b: b5
192 3 b: b5
193 3 b: b6
193 3 b: b6
194
194
195 annotate -nlf b
195 annotate -nlf b
196
196
197 $ hg annotate -nlf b
197 $ hg annotate -nlf b
198 0 a:1: a
198 0 a:1: a
199 1 a:2: a
199 1 a:2: a
200 1 a:3: a
200 1 a:3: a
201 3 b:4: b4
201 3 b:4: b4
202 3 b:5: b5
202 3 b:5: b5
203 3 b:6: b6
203 3 b:6: b6
204
204
205 $ hg up -C 2
205 $ hg up -C 2
206 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
206 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
207 $ cat <<EOF >> b
207 $ cat <<EOF >> b
208 > b4
208 > b4
209 > c
209 > c
210 > b5
210 > b5
211 > EOF
211 > EOF
212 $ hg ci -mb2.1 -d '2 0'
212 $ hg ci -mb2.1 -d '2 0'
213 created new head
213 created new head
214 $ hg merge
214 $ hg merge
215 merging b
215 merging b
216 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
216 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
217 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
217 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
218 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
218 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
219 [1]
219 [1]
220 $ cat b
220 $ cat b
221 a
221 a
222 a
222 a
223 a
223 a
224 <<<<<<< working copy: 5fbdc1152d97 - test: b2.1
224 <<<<<<< working copy: 5fbdc1152d97 - test: b2.1
225 b4
225 b4
226 c
226 c
227 b5
227 b5
228 ||||||| base
228 ||||||| base
229 =======
229 =======
230 b4
230 b4
231 b5
231 b5
232 b6
232 b6
233 >>>>>>> merge rev: 37ec9f5c3d1f - test: b2
233 >>>>>>> merge rev: 37ec9f5c3d1f - test: b2
234 $ cat <<EOF > b
234 $ cat <<EOF > b
235 > a
235 > a
236 > a
236 > a
237 > a
237 > a
238 > b4
238 > b4
239 > c
239 > c
240 > b5
240 > b5
241 > EOF
241 > EOF
242 $ hg resolve --mark -q
242 $ hg resolve --mark -q
243 $ rm b.orig
243 $ rm b.orig
244 $ hg ci -mmergeb -d '3 0'
244 $ hg ci -mmergeb -d '3 0'
245
245
246 annotate after merge
246 annotate after merge
247
247
248 $ hg annotate -nf b
248 $ hg annotate -nf b
249 0 a: a
249 0 a: a
250 1 a: a
250 1 a: a
251 1 a: a
251 1 a: a
252 3 b: b4
252 3 b: b4
253 4 b: c
253 4 b: c
254 3 b: b5
254 3 b: b5
255
255
256 annotate after merge with -l
256 annotate after merge with -l
257
257
258 $ hg annotate -nlf b
258 $ hg annotate -nlf b
259 0 a:1: a
259 0 a:1: a
260 1 a:2: a
260 1 a:2: a
261 1 a:3: a
261 1 a:3: a
262 3 b:4: b4
262 3 b:4: b4
263 4 b:5: c
263 4 b:5: c
264 3 b:5: b5
264 3 b:5: b5
265
265
266 $ hg up -C 1
266 $ hg up -C 1
267 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
267 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
268 $ hg cp a b
268 $ hg cp a b
269 $ cat <<EOF > b
269 $ cat <<EOF > b
270 > a
270 > a
271 > z
271 > z
272 > a
272 > a
273 > EOF
273 > EOF
274 $ hg ci -mc -d '3 0'
274 $ hg ci -mc -d '3 0'
275 created new head
275 created new head
276 Work around the pure version not resolving the conflict like native code
276 Work around the pure version not resolving the conflict like native code
277 #if pure
277 #if pure
278 $ hg merge
278 $ hg merge
279 merging b
279 merging b
280 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
280 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
281 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
281 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
282 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
282 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
283 [1]
283 [1]
284 $ cat <<EOF > b
284 $ cat <<EOF > b
285 > a
285 > a
286 > z
286 > z
287 > a
287 > a
288 > b4
288 > b4
289 > c
289 > c
290 > b5
290 > b5
291 > EOF
291 > EOF
292 $ hg resolve -m b
292 $ hg resolve -m b
293 (no more unresolved files)
293 (no more unresolved files)
294 $ rm b.orig
294 $ rm b.orig
295 #else
295 #else
296 $ hg merge
296 $ hg merge
297 merging b
297 merging b
298 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
298 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
299 (branch merge, don't forget to commit)
299 (branch merge, don't forget to commit)
300 #endif
300 #endif
301 $ echo d >> b
301 $ echo d >> b
302 $ hg ci -mmerge2 -d '4 0'
302 $ hg ci -mmerge2 -d '4 0'
303
303
304 annotate after rename merge
304 annotate after rename merge
305
305
306 $ hg annotate -nf b
306 $ hg annotate -nf b
307 0 a: a
307 0 a: a
308 6 b: z
308 6 b: z
309 1 a: a
309 1 a: a
310 3 b: b4
310 3 b: b4
311 4 b: c
311 4 b: c
312 3 b: b5
312 3 b: b5
313 7 b: d
313 7 b: d
314
314
315 annotate after rename merge with -l
315 annotate after rename merge with -l
316
316
317 $ hg annotate -nlf b
317 $ hg annotate -nlf b
318 0 a:1: a
318 0 a:1: a
319 6 b:2: z
319 6 b:2: z
320 1 a:3: a
320 1 a:3: a
321 3 b:4: b4
321 3 b:4: b4
322 4 b:5: c
322 4 b:5: c
323 3 b:5: b5
323 3 b:5: b5
324 7 b:7: d
324 7 b:7: d
325
325
326 --skip nothing (should be the same as no --skip at all)
326 --skip nothing (should be the same as no --skip at all)
327
327
328 $ hg annotate -nlf b --skip '1::0'
328 $ hg annotate -nlf b --skip '1::0'
329 0 a:1: a
329 0 a:1: a
330 6 b:2: z
330 6 b:2: z
331 1 a:3: a
331 1 a:3: a
332 3 b:4: b4
332 3 b:4: b4
333 4 b:5: c
333 4 b:5: c
334 3 b:5: b5
334 3 b:5: b5
335 7 b:7: d
335 7 b:7: d
336
336
337 --skip a modified line. Note a slight behavior difference in pure - this is
337 --skip a modified line. Note a slight behavior difference in pure - this is
338 because the pure code comes up with slightly different deltas internally.
338 because the pure code comes up with slightly different deltas internally.
339
339
340 $ hg annotate -nlf b --skip 6
340 $ hg annotate -nlf b --skip 6
341 0 a:1: a
341 0 a:1: a
342 1 a:2* z (no-pure !)
342 1 a:2* z (no-pure !)
343 0 a:1* z (pure !)
343 0 a:1* z (pure !)
344 1 a:3: a
344 1 a:3: a
345 3 b:4: b4
345 3 b:4: b4
346 4 b:5: c
346 4 b:5: c
347 3 b:5: b5
347 3 b:5: b5
348 7 b:7: d
348 7 b:7: d
349
349
350 --skip added lines (and test multiple skip)
350 --skip added lines (and test multiple skip)
351
351
352 $ hg annotate -nlf b --skip 3
352 $ hg annotate -nlf b --skip 3
353 0 a:1: a
353 0 a:1: a
354 6 b:2: z
354 6 b:2: z
355 1 a:3: a
355 1 a:3: a
356 1 a:3* b4
356 1 a:3* b4
357 4 b:5: c
357 4 b:5: c
358 1 a:3* b5
358 1 a:3* b5
359 7 b:7: d
359 7 b:7: d
360
360
361 $ hg annotate -nlf b --skip 4
361 $ hg annotate -nlf b --skip 4
362 0 a:1: a
362 0 a:1: a
363 6 b:2: z
363 6 b:2: z
364 1 a:3: a
364 1 a:3: a
365 3 b:4: b4
365 3 b:4: b4
366 1 a:3* c
366 1 a:3* c
367 3 b:5: b5
367 3 b:5: b5
368 7 b:7: d
368 7 b:7: d
369
369
370 $ hg annotate -nlf b --skip 3 --skip 4
370 $ hg annotate -nlf b --skip 3 --skip 4
371 0 a:1: a
371 0 a:1: a
372 6 b:2: z
372 6 b:2: z
373 1 a:3: a
373 1 a:3: a
374 1 a:3* b4
374 1 a:3* b4
375 1 a:3* c
375 1 a:3* c
376 1 a:3* b5
376 1 a:3* b5
377 7 b:7: d
377 7 b:7: d
378
378
379 $ hg annotate -nlf b --skip 'merge()'
379 $ hg annotate -nlf b --skip 'merge()'
380 0 a:1: a
380 0 a:1: a
381 6 b:2: z
381 6 b:2: z
382 1 a:3: a
382 1 a:3: a
383 3 b:4: b4
383 3 b:4: b4
384 4 b:5: c
384 4 b:5: c
385 3 b:5: b5
385 3 b:5: b5
386 3 b:5* d
386 3 b:5* d
387
387
388 --skip everything -- use the revision the file was introduced in
388 --skip everything -- use the revision the file was introduced in
389
389
390 $ hg annotate -nlf b --skip 'all()'
390 $ hg annotate -nlf b --skip 'all()'
391 0 a:1: a
391 0 a:1: a
392 0 a:1* z
392 0 a:1* z
393 0 a:1* a
393 0 a:1* a
394 0 a:1* b4
394 0 a:1* b4
395 0 a:1* c
395 0 a:1* c
396 0 a:1* b5
396 0 a:1* b5
397 0 a:1* d
397 0 a:1* d
398
398
399 Issue2807: alignment of line numbers with -l
399 Issue2807: alignment of line numbers with -l
400
400
401 $ echo more >> b
401 $ echo more >> b
402 $ hg ci -mmore -d '5 0'
402 $ hg ci -mmore -d '5 0'
403 $ echo more >> b
403 $ echo more >> b
404 $ hg ci -mmore -d '6 0'
404 $ hg ci -mmore -d '6 0'
405 $ echo more >> b
405 $ echo more >> b
406 $ hg ci -mmore -d '7 0'
406 $ hg ci -mmore -d '7 0'
407 $ hg annotate -nlf b
407 $ hg annotate -nlf b
408 0 a: 1: a
408 0 a: 1: a
409 6 b: 2: z
409 6 b: 2: z
410 1 a: 3: a
410 1 a: 3: a
411 3 b: 4: b4
411 3 b: 4: b4
412 4 b: 5: c
412 4 b: 5: c
413 3 b: 5: b5
413 3 b: 5: b5
414 7 b: 7: d
414 7 b: 7: d
415 8 b: 8: more
415 8 b: 8: more
416 9 b: 9: more
416 9 b: 9: more
417 10 b:10: more
417 10 b:10: more
418
418
419 linkrev vs rev
419 linkrev vs rev
420
420
421 $ hg annotate -r tip -n a
421 $ hg annotate -r tip -n a
422 0: a
422 0: a
423 1: a
423 1: a
424 1: a
424 1: a
425
425
426 linkrev vs rev with -l
426 linkrev vs rev with -l
427
427
428 $ hg annotate -r tip -nl a
428 $ hg annotate -r tip -nl a
429 0:1: a
429 0:1: a
430 1:2: a
430 1:2: a
431 1:3: a
431 1:3: a
432
432
433 Issue589: "undelete" sequence leads to crash
433 Issue589: "undelete" sequence leads to crash
434
434
435 annotate was crashing when trying to --follow something
435 annotate was crashing when trying to --follow something
436
436
437 like A -> B -> A
437 like A -> B -> A
438
438
439 generate ABA rename configuration
439 generate ABA rename configuration
440
440
441 $ echo foo > foo
441 $ echo foo > foo
442 $ hg add foo
442 $ hg add foo
443 $ hg ci -m addfoo
443 $ hg ci -m addfoo
444 $ hg rename foo bar
444 $ hg rename foo bar
445 $ hg ci -m renamefoo
445 $ hg ci -m renamefoo
446 $ hg rename bar foo
446 $ hg rename bar foo
447 $ hg ci -m renamebar
447 $ hg ci -m renamebar
448
448
449 annotate after ABA with follow
449 annotate after ABA with follow
450
450
451 $ hg annotate --follow foo
451 $ hg annotate --follow foo
452 foo: foo
452 foo: foo
453
453
454 missing file
454 missing file
455
455
456 $ hg ann nosuchfile
456 $ hg ann nosuchfile
457 abort: nosuchfile: no such file in rev e9e6b4fa872f
457 abort: nosuchfile: no such file in rev e9e6b4fa872f
458 [255]
458 [255]
459
459
460 annotate file without '\n' on last line
460 annotate file without '\n' on last line
461
461
462 $ printf "" > c
462 $ printf "" > c
463 $ hg ci -A -m test -u nobody -d '1 0'
463 $ hg ci -A -m test -u nobody -d '1 0'
464 adding c
464 adding c
465 $ hg annotate c
465 $ hg annotate c
466 $ printf "a\nb" > c
466 $ printf "a\nb" > c
467 $ hg ci -m test
467 $ hg ci -m test
468 $ hg annotate c
468 $ hg annotate c
469 [0-9]+: a (re)
469 [0-9]+: a (re)
470 [0-9]+: b (re)
470 [0-9]+: b (re)
471
471
472 Issue3841: check annotation of the file of which filelog includes
472 Issue3841: check annotation of the file of which filelog includes
473 merging between the revision and its ancestor
473 merging between the revision and its ancestor
474
474
475 to reproduce the situation with recent Mercurial, this script uses (1)
475 to reproduce the situation with recent Mercurial, this script uses (1)
476 "hg debugsetparents" to merge without ancestor check by "hg merge",
476 "hg debugsetparents" to merge without ancestor check by "hg merge",
477 and (2) the extension to allow filelog merging between the revision
477 and (2) the extension to allow filelog merging between the revision
478 and its ancestor by overriding "repo._filecommit".
478 and its ancestor by overriding "repo._filecommit".
479
479
480 $ cat > ../legacyrepo.py <<EOF
480 $ cat > ../legacyrepo.py <<EOF
481 > from __future__ import absolute_import
481 > from __future__ import absolute_import
482 > from mercurial import commit, error, extensions, node
482 > from mercurial import commit, error, extensions, node
483 > def _filecommit(orig, repo, fctx, manifest1, manifest2,
483 > def _filecommit(orig, repo, fctx, manifest1, manifest2,
484 > linkrev, tr, includecopymeta, ms):
484 > linkrev, tr, includecopymeta, ms):
485 > fname = fctx.path()
485 > fname = fctx.path()
486 > text = fctx.data()
486 > text = fctx.data()
487 > flog = repo.file(fname)
487 > flog = repo.file(fname)
488 > fparent1 = manifest1.get(fname, node.nullid)
488 > fparent1 = manifest1.get(fname, node.nullid)
489 > fparent2 = manifest2.get(fname, node.nullid)
489 > fparent2 = manifest2.get(fname, node.nullid)
490 > meta = {}
490 > meta = {}
491 > copy = fctx.copysource()
491 > copy = fctx.copysource()
492 > if copy and copy != fname:
492 > if copy and copy != fname:
493 > raise error.Abort('copying is not supported')
493 > raise error.Abort('copying is not supported')
494 > if fparent2 != node.nullid:
494 > if fparent2 != node.nullid:
495 > return flog.add(text, meta, tr, linkrev,
495 > return flog.add(text, meta, tr, linkrev,
496 > fparent1, fparent2), 'modified'
496 > fparent1, fparent2), 'modified'
497 > raise error.Abort('only merging is supported')
497 > raise error.Abort('only merging is supported')
498 > def uisetup(ui):
498 > def uisetup(ui):
499 > extensions.wrapfunction(commit, '_filecommit', _filecommit)
499 > extensions.wrapfunction(commit, '_filecommit', _filecommit)
500 > EOF
500 > EOF
501
501
502 $ cat > baz <<EOF
502 $ cat > baz <<EOF
503 > 1
503 > 1
504 > 2
504 > 2
505 > 3
505 > 3
506 > 4
506 > 4
507 > 5
507 > 5
508 > EOF
508 > EOF
509 $ hg add baz
509 $ hg add baz
510 $ hg commit -m "baz:0"
510 $ hg commit -m "baz:0"
511
511
512 $ cat > baz <<EOF
512 $ cat > baz <<EOF
513 > 1 baz:1
513 > 1 baz:1
514 > 2
514 > 2
515 > 3
515 > 3
516 > 4
516 > 4
517 > 5
517 > 5
518 > EOF
518 > EOF
519 $ hg commit -m "baz:1"
519 $ hg commit -m "baz:1"
520
520
521 $ cat > baz <<EOF
521 $ cat > baz <<EOF
522 > 1 baz:1
522 > 1 baz:1
523 > 2 baz:2
523 > 2 baz:2
524 > 3
524 > 3
525 > 4
525 > 4
526 > 5
526 > 5
527 > EOF
527 > EOF
528 $ hg debugsetparents 17 17
528 $ hg debugsetparents 17 17
529 $ hg --config extensions.legacyrepo=../legacyrepo.py commit -m "baz:2"
529 $ hg --config extensions.legacyrepo=../legacyrepo.py commit -m "baz:2"
530 $ hg debugindexdot baz
530 $ hg debugindexdot baz
531 digraph G {
531 digraph G {
532 -1 -> 0
532 -1 -> 0
533 0 -> 1
533 0 -> 1
534 1 -> 2
534 1 -> 2
535 1 -> 2
535 1 -> 2
536 }
536 }
537 $ hg annotate baz
537 $ hg annotate baz
538 17: 1 baz:1
538 17: 1 baz:1
539 18: 2 baz:2
539 18: 2 baz:2
540 16: 3
540 16: 3
541 16: 4
541 16: 4
542 16: 5
542 16: 5
543
543
544 $ cat > baz <<EOF
544 $ cat > baz <<EOF
545 > 1 baz:1
545 > 1 baz:1
546 > 2 baz:2
546 > 2 baz:2
547 > 3 baz:3
547 > 3 baz:3
548 > 4
548 > 4
549 > 5
549 > 5
550 > EOF
550 > EOF
551 $ hg commit -m "baz:3"
551 $ hg commit -m "baz:3"
552
552
553 $ cat > baz <<EOF
553 $ cat > baz <<EOF
554 > 1 baz:1
554 > 1 baz:1
555 > 2 baz:2
555 > 2 baz:2
556 > 3 baz:3
556 > 3 baz:3
557 > 4 baz:4
557 > 4 baz:4
558 > 5
558 > 5
559 > EOF
559 > EOF
560 $ hg debugsetparents 19 18
560 $ hg debugsetparents 19 18
561 $ hg --config extensions.legacyrepo=../legacyrepo.py commit -m "baz:4"
561 $ hg --config extensions.legacyrepo=../legacyrepo.py commit -m "baz:4"
562 $ hg debugindexdot baz
562 $ hg debugindexdot baz
563 digraph G {
563 digraph G {
564 -1 -> 0
564 -1 -> 0
565 0 -> 1
565 0 -> 1
566 1 -> 2
566 1 -> 2
567 1 -> 2
567 1 -> 2
568 2 -> 3
568 2 -> 3
569 3 -> 4
569 3 -> 4
570 2 -> 4
570 2 -> 4
571 }
571 }
572 $ hg annotate baz
572 $ hg annotate baz
573 17: 1 baz:1
573 17: 1 baz:1
574 18: 2 baz:2
574 18: 2 baz:2
575 19: 3 baz:3
575 19: 3 baz:3
576 20: 4 baz:4
576 20: 4 baz:4
577 16: 5
577 16: 5
578
578
579 annotate clean file
579 annotate clean file
580
580
581 $ hg annotate -ncr "wdir()" foo
581 $ hg annotate -ncr "wdir()" foo
582 11 472b18db256d : foo
582 11 472b18db256d : foo
583
583
584 annotate modified file
584 annotate modified file
585
585
586 $ echo foofoo >> foo
586 $ echo foofoo >> foo
587 $ hg annotate -r "wdir()" foo
587 $ hg annotate -r "wdir()" foo
588 11 : foo
588 11 : foo
589 20+: foofoo
589 20+: foofoo
590
590
591 $ hg annotate -cr "wdir()" foo
591 $ hg annotate -cr "wdir()" foo
592 472b18db256d : foo
592 472b18db256d : foo
593 b6bedd5477e7+: foofoo
593 b6bedd5477e7+: foofoo
594
594
595 $ hg annotate -ncr "wdir()" foo
595 $ hg annotate -ncr "wdir()" foo
596 11 472b18db256d : foo
596 11 472b18db256d : foo
597 20 b6bedd5477e7+: foofoo
597 20 b6bedd5477e7+: foofoo
598
598
599 $ hg annotate --debug -ncr "wdir()" foo
599 $ hg annotate --debug -ncr "wdir()" foo
600 11 472b18db256d1e8282064eab4bfdaf48cbfe83cd : foo
600 11 472b18db256d1e8282064eab4bfdaf48cbfe83cd : foo
601 20 b6bedd5477e797f25e568a6402d4697f3f895a72+: foofoo
601 20 b6bedd5477e797f25e568a6402d4697f3f895a72+: foofoo
602
602
603 $ hg annotate -udr "wdir()" foo
603 $ hg annotate -udr "wdir()" foo
604 test Thu Jan 01 00:00:00 1970 +0000: foo
604 test Thu Jan 01 00:00:00 1970 +0000: foo
605 test [A-Za-z0-9:+ ]+: foofoo (re)
605 test [A-Za-z0-9:+ ]+: foofoo (re)
606
606
607 $ hg annotate -ncr "wdir()" -Tjson foo
607 $ hg annotate -ncr "wdir()" -Tjson foo
608 [
608 [
609 {
609 {
610 "lines": [{"line": "foo\n", "node": "472b18db256d1e8282064eab4bfdaf48cbfe83cd", "rev": 11}, {"line": "foofoo\n", "node": "ffffffffffffffffffffffffffffffffffffffff", "rev": 2147483647}],
610 "lines": [{"line": "foo\n", "node": "472b18db256d1e8282064eab4bfdaf48cbfe83cd", "rev": 11}, {"line": "foofoo\n", "node": "ffffffffffffffffffffffffffffffffffffffff", "rev": 2147483647}],
611 "path": "foo"
611 "path": "foo"
612 }
612 }
613 ]
613 ]
614
614
615 annotate added file
615 annotate added file
616
616
617 $ echo bar > bar
617 $ echo bar > bar
618 $ hg add bar
618 $ hg add bar
619 $ hg annotate -ncr "wdir()" bar
619 $ hg annotate -ncr "wdir()" bar
620 20 b6bedd5477e7+: bar
620 20 b6bedd5477e7+: bar
621
621
622 annotate renamed file
622 annotate renamed file
623
623
624 $ hg rename foo renamefoo2
624 $ hg rename foo renamefoo2
625 $ hg annotate -ncr "wdir()" renamefoo2
625 $ hg annotate -ncr "wdir()" renamefoo2
626 11 472b18db256d : foo
626 11 472b18db256d : foo
627 20 b6bedd5477e7+: foofoo
627 20 b6bedd5477e7+: foofoo
628
628
629 annotate missing file
629 annotate missing file
630
630
631 $ rm baz
631 $ rm baz
632
632
633 $ hg annotate -ncr "wdir()" baz
633 $ hg annotate -ncr "wdir()" baz
634 abort: $TESTTMP\repo\baz: $ENOENT$ (windows !)
634 abort: $TESTTMP\repo\baz: $ENOENT$ (windows !)
635 abort: $ENOENT$: '$TESTTMP/repo/baz' (no-windows !)
635 abort: $ENOENT$: '$TESTTMP/repo/baz' (no-windows !)
636 [255]
636 [255]
637
637
638 annotate removed file
638 annotate removed file
639
639
640 $ hg rm baz
640 $ hg rm baz
641
641
642 $ hg annotate -ncr "wdir()" baz
642 $ hg annotate -ncr "wdir()" baz
643 abort: $TESTTMP\repo\baz: $ENOENT$ (windows !)
643 abort: $TESTTMP\repo\baz: $ENOENT$ (windows !)
644 abort: $ENOENT$: '$TESTTMP/repo/baz' (no-windows !)
644 abort: $ENOENT$: '$TESTTMP/repo/baz' (no-windows !)
645 [255]
645 [255]
646
646
647 $ hg revert --all --no-backup --quiet
647 $ hg revert --all --no-backup --quiet
648 $ hg id -n
648 $ hg id -n
649 20
649 20
650
650
651 Test followlines() revset; we usually check both followlines(pat, range) and
651 Test followlines() revset; we usually check both followlines(pat, range) and
652 followlines(pat, range, descend=True) to make sure both give the same result
652 followlines(pat, range, descend=True) to make sure both give the same result
653 when they should.
653 when they should.
654
654
655 $ echo a >> foo
655 $ echo a >> foo
656 $ hg ci -m 'foo: add a'
656 $ hg ci -m 'foo: add a'
657 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5)'
657 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5)'
658 16: baz:0
658 16: baz:0
659 19: baz:3
659 19: baz:3
660 20: baz:4
660 20: baz:4
661 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=20)'
661 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=20)'
662 16: baz:0
662 16: baz:0
663 19: baz:3
663 19: baz:3
664 20: baz:4
664 20: baz:4
665 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19)'
665 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19)'
666 16: baz:0
666 16: baz:0
667 19: baz:3
667 19: baz:3
668 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19, descend=True)'
668 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19, descend=True)'
669 19: baz:3
669 19: baz:3
670 20: baz:4
670 20: baz:4
671 $ printf "0\n0\n" | cat - baz > baz1
671 $ printf "0\n0\n" | cat - baz > baz1
672 $ mv baz1 baz
672 $ mv baz1 baz
673 $ hg ci -m 'added two lines with 0'
673 $ hg ci -m 'added two lines with 0'
674 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7)'
674 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7)'
675 16: baz:0
675 16: baz:0
676 19: baz:3
676 19: baz:3
677 20: baz:4
677 20: baz:4
678 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, descend=true, startrev=19)'
678 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, descend=true, startrev=19)'
679 19: baz:3
679 19: baz:3
680 20: baz:4
680 20: baz:4
681 $ echo 6 >> baz
681 $ echo 6 >> baz
682 $ hg ci -m 'added line 8'
682 $ hg ci -m 'added line 8'
683 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7)'
683 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7)'
684 16: baz:0
684 16: baz:0
685 19: baz:3
685 19: baz:3
686 20: baz:4
686 20: baz:4
687 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19, descend=1)'
687 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19, descend=1)'
688 19: baz:3
688 19: baz:3
689 20: baz:4
689 20: baz:4
690 $ sed 's/3/3+/' baz > baz.new
690 $ sed 's/3/3+/' baz > baz.new
691 $ mv baz.new baz
691 $ mv baz.new baz
692 $ hg ci -m 'baz:3->3+'
692 $ hg ci -m 'baz:3->3+'
693 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7, descend=0)'
693 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7, descend=0)'
694 16: baz:0
694 16: baz:0
695 19: baz:3
695 19: baz:3
696 20: baz:4
696 20: baz:4
697 24: baz:3->3+
697 24: baz:3->3+
698 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=17, descend=True)'
698 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=17, descend=True)'
699 19: baz:3
699 19: baz:3
700 20: baz:4
700 20: baz:4
701 24: baz:3->3+
701 24: baz:3->3+
702 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 1:2, descend=false)'
702 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 1:2, descend=false)'
703 22: added two lines with 0
703 22: added two lines with 0
704
704
705 file patterns are okay
705 file patterns are okay
706 $ hg log -T '{rev}: {desc}\n' -r 'followlines("path:baz", 1:2)'
706 $ hg log -T '{rev}: {desc}\n' -r 'followlines("path:baz", 1:2)'
707 22: added two lines with 0
707 22: added two lines with 0
708
708
709 renames are followed
709 renames are followed
710 $ hg mv baz qux
710 $ hg mv baz qux
711 $ sed 's/4/4+/' qux > qux.new
711 $ sed 's/4/4+/' qux > qux.new
712 $ mv qux.new qux
712 $ mv qux.new qux
713 $ hg ci -m 'qux:4->4+'
713 $ hg ci -m 'qux:4->4+'
714 $ hg log -T '{rev}: {desc}\n' -r 'followlines(qux, 5:7)'
714 $ hg log -T '{rev}: {desc}\n' -r 'followlines(qux, 5:7)'
715 16: baz:0
715 16: baz:0
716 19: baz:3
716 19: baz:3
717 20: baz:4
717 20: baz:4
718 24: baz:3->3+
718 24: baz:3->3+
719 25: qux:4->4+
719 25: qux:4->4+
720
720
721 but are missed when following children
721 but are missed when following children
722 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7, startrev=22, descend=True)'
722 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7, startrev=22, descend=True)'
723 24: baz:3->3+
723 24: baz:3->3+
724
724
725 merge
725 merge
726 $ hg up 24 --quiet
726 $ hg up 24 --quiet
727 $ echo 7 >> baz
727 $ echo 7 >> baz
728 $ hg ci -m 'one more line, out of line range'
728 $ hg ci -m 'one more line, out of line range'
729 created new head
729 created new head
730 $ sed 's/3+/3-/' baz > baz.new
730 $ sed 's/3+/3-/' baz > baz.new
731 $ mv baz.new baz
731 $ mv baz.new baz
732 $ hg ci -m 'baz:3+->3-'
732 $ hg ci -m 'baz:3+->3-'
733 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7)'
733 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7)'
734 16: baz:0
734 16: baz:0
735 19: baz:3
735 19: baz:3
736 20: baz:4
736 20: baz:4
737 24: baz:3->3+
737 24: baz:3->3+
738 27: baz:3+->3-
738 27: baz:3+->3-
739 $ hg merge 25
739 $ hg merge 25
740 merging baz and qux to qux
740 merging baz and qux to qux
741 warning: conflicts while merging qux! (edit, then use 'hg resolve --mark')
741 warning: conflicts while merging qux! (edit, then use 'hg resolve --mark')
742 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
742 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
743 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
743 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
744 [1]
744 [1]
745 $ cat qux
745 $ cat qux
746 0
746 0
747 0
747 0
748 1 baz:1
748 1 baz:1
749 2 baz:2
749 2 baz:2
750 <<<<<<< working copy: 863de62655ef - test: baz:3+->3-
750 <<<<<<< working copy: 863de62655ef - test: baz:3+->3-
751 3- baz:3
751 3- baz:3
752 4 baz:4
752 4 baz:4
753 ||||||| base
753 ||||||| base
754 3+ baz:3
754 3+ baz:3
755 4 baz:4
755 4 baz:4
756 =======
756 =======
757 3+ baz:3
757 3+ baz:3
758 4+ baz:4
758 4+ baz:4
759 >>>>>>> merge rev: cb8df70ae185 - test: qux:4->4+
759 >>>>>>> merge rev: cb8df70ae185 - test: qux:4->4+
760 5
760 5
761 6
761 6
762 7
762 7
763 $ cat > qux <<EOF
763 $ cat > qux <<EOF
764 > 0
764 > 0
765 > 0
765 > 0
766 > 1 baz:1
766 > 1 baz:1
767 > 2 baz:2
767 > 2 baz:2
768 > 3- baz:3
768 > 3- baz:3
769 > 4 baz:4
769 > 4 baz:4
770 > 5
770 > 5
771 > 6
771 > 6
772 > 7
772 > 7
773 > EOF
773 > EOF
774 $ hg resolve --mark -q
774 $ hg resolve --mark -q
775 $ rm qux.orig
775 $ rm qux.orig
776 $ hg ci -m merge
776 $ hg ci -m merge
777 $ hg log -T '{rev}: {desc}\n' -r 'followlines(qux, 5:7)'
777 $ hg log -T '{rev}: {desc}\n' -r 'followlines(qux, 5:7)'
778 16: baz:0
778 16: baz:0
779 19: baz:3
779 19: baz:3
780 20: baz:4
780 20: baz:4
781 24: baz:3->3+
781 24: baz:3->3+
782 25: qux:4->4+
782 25: qux:4->4+
783 27: baz:3+->3-
783 27: baz:3+->3-
784 28: merge
784 28: merge
785 $ hg up 25 --quiet
785 $ hg up 25 --quiet
786 $ hg merge 27
786 $ hg merge 27
787 merging qux and baz to qux
787 merging qux and baz to qux
788 warning: conflicts while merging qux! (edit, then use 'hg resolve --mark')
788 warning: conflicts while merging qux! (edit, then use 'hg resolve --mark')
789 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
789 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
790 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
790 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
791 [1]
791 [1]
792 $ cat qux
792 $ cat qux
793 0
793 0
794 0
794 0
795 1 baz:1
795 1 baz:1
796 2 baz:2
796 2 baz:2
797 <<<<<<< working copy: cb8df70ae185 - test: qux:4->4+
797 <<<<<<< working copy: cb8df70ae185 - test: qux:4->4+
798 3+ baz:3
798 3+ baz:3
799 4+ baz:4
799 4+ baz:4
800 ||||||| base
800 ||||||| base
801 3+ baz:3
801 3+ baz:3
802 4 baz:4
802 4 baz:4
803 =======
803 =======
804 3- baz:3
804 3- baz:3
805 4 baz:4
805 4 baz:4
806 >>>>>>> merge rev: 863de62655ef - test: baz:3+->3-
806 >>>>>>> merge rev: 863de62655ef - test: baz:3+->3-
807 5
807 5
808 6
808 6
809 7
809 7
810 $ cat > qux <<EOF
810 $ cat > qux <<EOF
811 > 0
811 > 0
812 > 0
812 > 0
813 > 1 baz:1
813 > 1 baz:1
814 > 2 baz:2
814 > 2 baz:2
815 > 3+ baz:3
815 > 3+ baz:3
816 > 4+ baz:4
816 > 4+ baz:4
817 > 5
817 > 5
818 > 6
818 > 6
819 > EOF
819 > EOF
820 $ hg resolve --mark -q
820 $ hg resolve --mark -q
821 $ rm qux.orig
821 $ rm qux.orig
822 $ hg ci -m 'merge from other side'
822 $ hg ci -m 'merge from other side'
823 created new head
823 created new head
824 $ hg log -T '{rev}: {desc}\n' -r 'followlines(qux, 5:7)'
824 $ hg log -T '{rev}: {desc}\n' -r 'followlines(qux, 5:7)'
825 16: baz:0
825 16: baz:0
826 19: baz:3
826 19: baz:3
827 20: baz:4
827 20: baz:4
828 24: baz:3->3+
828 24: baz:3->3+
829 25: qux:4->4+
829 25: qux:4->4+
830 27: baz:3+->3-
830 27: baz:3+->3-
831 29: merge from other side
831 29: merge from other side
832 $ hg up 24 --quiet
832 $ hg up 24 --quiet
833
833
834 we are missing the branch with rename when following children
834 we are missing the branch with rename when following children
835 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7, startrev=26, descend=True)'
835 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7, startrev=26, descend=True)'
836 27: baz:3+->3-
836 27: baz:3+->3-
837
837
838 we follow all branches in descending direction
838 we follow all branches in descending direction
839 $ hg up 23 --quiet
839 $ hg up 23 --quiet
840 $ sed 's/3/+3/' baz > baz.new
840 $ sed 's/3/+3/' baz > baz.new
841 $ mv baz.new baz
841 $ mv baz.new baz
842 $ hg ci -m 'baz:3->+3'
842 $ hg ci -m 'baz:3->+3'
843 created new head
843 created new head
844 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 2:5, startrev=16, descend=True)' --graph
844 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 2:5, startrev=16, descend=True)' --graph
845 @ 30: baz:3->+3
845 @ 30: baz:3->+3
846 :
846 :
847 : o 27: baz:3+->3-
847 : o 27: baz:3+->3-
848 : :
848 : :
849 : o 24: baz:3->3+
849 : o 24: baz:3->3+
850 :/
850 :/
851 o 20: baz:4
851 o 20: baz:4
852 |\
852 |\
853 | o 19: baz:3
853 | o 19: baz:3
854 |/
854 |/
855 o 18: baz:2
855 o 18: baz:2
856 :
856 :
857 o 16: baz:0
857 o 16: baz:0
858 |
858 |
859 ~
859 ~
860
860
861 Issue5595: on a merge changeset with different line ranges depending on
861 Issue5595: on a merge changeset with different line ranges depending on
862 parent, be conservative and use the surrounding interval to avoid loosing
862 parent, be conservative and use the surrounding interval to avoid loosing
863 track of possible further descendants in specified range.
863 track of possible further descendants in specified range.
864
864
865 $ hg up 23 --quiet
865 $ hg up 23 --quiet
866 $ hg cat baz -r 24
866 $ hg cat baz -r 24
867 0
867 0
868 0
868 0
869 1 baz:1
869 1 baz:1
870 2 baz:2
870 2 baz:2
871 3+ baz:3
871 3+ baz:3
872 4 baz:4
872 4 baz:4
873 5
873 5
874 6
874 6
875 $ cat > baz << EOF
875 $ cat > baz << EOF
876 > 0
876 > 0
877 > 0
877 > 0
878 > a
878 > a
879 > b
879 > b
880 > 3+ baz:3
880 > 3+ baz:3
881 > 4 baz:4
881 > 4 baz:4
882 > y
882 > y
883 > z
883 > z
884 > EOF
884 > EOF
885 $ hg ci -m 'baz: mostly rewrite with some content from 24'
885 $ hg ci -m 'baz: mostly rewrite with some content from 24'
886 created new head
886 created new head
887 $ hg merge --tool :merge-other 24
887 $ hg merge --tool :merge-other 24
888 merging baz
888 merging baz
889 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
889 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
890 (branch merge, don't forget to commit)
890 (branch merge, don't forget to commit)
891 $ hg ci -m 'merge forgetting about baz rewrite'
891 $ hg ci -m 'merge forgetting about baz rewrite'
892 $ cat > baz << EOF
892 $ cat > baz << EOF
893 > 0
893 > 0
894 > 0
894 > 0
895 > 1 baz:1
895 > 1 baz:1
896 > 2+ baz:2
896 > 2+ baz:2
897 > 3+ baz:3
897 > 3+ baz:3
898 > 4 baz:4
898 > 4 baz:4
899 > 5
899 > 5
900 > 6
900 > 6
901 > EOF
901 > EOF
902 $ hg ci -m 'baz: narrow change (2->2+)'
902 $ hg ci -m 'baz: narrow change (2->2+)'
903 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:4, startrev=20, descend=True)' --graph
903 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:4, startrev=20, descend=True)' --graph
904 @ 33: baz: narrow change (2->2+)
904 @ 33: baz: narrow change (2->2+)
905 |
905 |
906 o 32: merge forgetting about baz rewrite
906 o 32: merge forgetting about baz rewrite
907 |\
907 |\
908 | o 31: baz: mostly rewrite with some content from 24
908 | o 31: baz: mostly rewrite with some content from 24
909 | :
909 | :
910 | : o 30: baz:3->+3
910 | : o 30: baz:3->+3
911 | :/
911 | :/
912 +---o 27: baz:3+->3-
912 +---o 27: baz:3+->3-
913 | :
913 | :
914 o : 24: baz:3->3+
914 o : 24: baz:3->3+
915 :/
915 :/
916 o 20: baz:4
916 o 20: baz:4
917 |\
917 |\
918 ~ ~
918 ~ ~
919
919
920 An integer as a line range, which is parsed as '1:1'
920 An integer as a line range, which is parsed as '1:1'
921
921
922 $ hg log -r 'followlines(baz, 1)'
922 $ hg log -r 'followlines(baz, 1)'
923 changeset: 22:2174d0bf352a
923 changeset: 22:2174d0bf352a
924 user: test
924 user: test
925 date: Thu Jan 01 00:00:00 1970 +0000
925 date: Thu Jan 01 00:00:00 1970 +0000
926 summary: added two lines with 0
926 summary: added two lines with 0
927
927
928
928
929 check error cases
929 check error cases
930 $ hg up 24 --quiet
930 $ hg up 24 --quiet
931 $ hg log -r 'followlines()'
931 $ hg log -r 'followlines()'
932 hg: parse error: followlines takes at least 1 positional arguments
932 hg: parse error: followlines takes at least 1 positional arguments
933 [255]
933 [255]
934 $ hg log -r 'followlines(baz)'
934 $ hg log -r 'followlines(baz)'
935 hg: parse error: followlines requires a line range
935 hg: parse error: followlines requires a line range
936 [255]
936 [255]
937 $ hg log -r 'followlines(baz, x)'
937 $ hg log -r 'followlines(baz, x)'
938 hg: parse error: followlines expects a line number or a range
938 hg: parse error: followlines expects a line number or a range
939 [255]
939 [255]
940 $ hg log -r 'followlines(baz, 1:2, startrev=desc("b"))'
940 $ hg log -r 'followlines(baz, 1:2, startrev=desc("b"))'
941 hg: parse error: followlines expects exactly one revision
941 hg: parse error: followlines expects exactly one revision
942 [255]
942 [255]
943 $ hg log -r 'followlines("glob:*", 1:2)'
943 $ hg log -r 'followlines("glob:*", 1:2)'
944 hg: parse error: followlines expects exactly one file
944 hg: parse error: followlines expects exactly one file
945 [255]
945 [255]
946 $ hg log -r 'followlines(baz, 1:)'
946 $ hg log -r 'followlines(baz, 1:)'
947 hg: parse error: line range bounds must be integers
947 hg: parse error: line range bounds must be integers
948 [255]
948 [255]
949 $ hg log -r 'followlines(baz, :1)'
949 $ hg log -r 'followlines(baz, :1)'
950 hg: parse error: line range bounds must be integers
950 hg: parse error: line range bounds must be integers
951 [255]
951 [255]
952 $ hg log -r 'followlines(baz, x:4)'
952 $ hg log -r 'followlines(baz, x:4)'
953 hg: parse error: line range bounds must be integers
953 hg: parse error: line range bounds must be integers
954 [255]
954 [255]
955 $ hg log -r 'followlines(baz, 5:4)'
955 $ hg log -r 'followlines(baz, 5:4)'
956 hg: parse error: line range must be positive
956 hg: parse error: line range must be positive
957 [255]
957 [255]
958 $ hg log -r 'followlines(baz, 0:4)'
958 $ hg log -r 'followlines(baz, 0:4)'
959 hg: parse error: fromline must be strictly positive
959 hg: parse error: fromline must be strictly positive
960 [255]
960 [255]
961 $ hg log -r 'followlines(baz, 2:40)'
961 $ hg log -r 'followlines(baz, 2:40)'
962 abort: line range exceeds file size
962 abort: line range exceeds file size
963 [255]
963 [10]
964 $ hg log -r 'followlines(baz, 2:4, startrev=20, descend=[1])'
964 $ hg log -r 'followlines(baz, 2:4, startrev=20, descend=[1])'
965 hg: parse error at 43: not a prefix: [
965 hg: parse error at 43: not a prefix: [
966 (followlines(baz, 2:4, startrev=20, descend=[1])
966 (followlines(baz, 2:4, startrev=20, descend=[1])
967 ^ here)
967 ^ here)
968 [255]
968 [255]
969 $ hg log -r 'followlines(baz, 2:4, startrev=20, descend=a)'
969 $ hg log -r 'followlines(baz, 2:4, startrev=20, descend=a)'
970 hg: parse error: descend argument must be a boolean
970 hg: parse error: descend argument must be a boolean
971 [255]
971 [255]
972
972
973 Test empty annotate output
973 Test empty annotate output
974
974
975 $ printf '\0' > binary
975 $ printf '\0' > binary
976 $ touch empty
976 $ touch empty
977 $ hg ci -qAm 'add binary and empty files'
977 $ hg ci -qAm 'add binary and empty files'
978
978
979 $ hg annotate binary empty
979 $ hg annotate binary empty
980 binary: binary file
980 binary: binary file
981
981
982 $ hg annotate -Tjson binary empty
982 $ hg annotate -Tjson binary empty
983 [
983 [
984 {
984 {
985 "path": "binary"
985 "path": "binary"
986 },
986 },
987 {
987 {
988 "lines": [],
988 "lines": [],
989 "path": "empty"
989 "path": "empty"
990 }
990 }
991 ]
991 ]
992
992
993 Test annotate with whitespace options
993 Test annotate with whitespace options
994
994
995 $ cd ..
995 $ cd ..
996 $ hg init repo-ws
996 $ hg init repo-ws
997 $ cd repo-ws
997 $ cd repo-ws
998 $ cat > a <<EOF
998 $ cat > a <<EOF
999 > aa
999 > aa
1000 >
1000 >
1001 > b b
1001 > b b
1002 > EOF
1002 > EOF
1003 $ hg ci -Am "adda"
1003 $ hg ci -Am "adda"
1004 adding a
1004 adding a
1005 $ sed 's/EOL$//g' > a <<EOF
1005 $ sed 's/EOL$//g' > a <<EOF
1006 > a a
1006 > a a
1007 >
1007 >
1008 > EOL
1008 > EOL
1009 > b b
1009 > b b
1010 > EOF
1010 > EOF
1011 $ hg ci -m "changea"
1011 $ hg ci -m "changea"
1012
1012
1013 Annotate with no option
1013 Annotate with no option
1014
1014
1015 $ hg annotate a
1015 $ hg annotate a
1016 1: a a
1016 1: a a
1017 0:
1017 0:
1018 1:
1018 1:
1019 1: b b
1019 1: b b
1020
1020
1021 Annotate with --ignore-space-change
1021 Annotate with --ignore-space-change
1022
1022
1023 $ hg annotate --ignore-space-change a
1023 $ hg annotate --ignore-space-change a
1024 1: a a
1024 1: a a
1025 1:
1025 1:
1026 0:
1026 0:
1027 0: b b
1027 0: b b
1028
1028
1029 Annotate with --ignore-all-space
1029 Annotate with --ignore-all-space
1030
1030
1031 $ hg annotate --ignore-all-space a
1031 $ hg annotate --ignore-all-space a
1032 0: a a
1032 0: a a
1033 0:
1033 0:
1034 1:
1034 1:
1035 0: b b
1035 0: b b
1036
1036
1037 Annotate with --ignore-blank-lines (similar to no options case)
1037 Annotate with --ignore-blank-lines (similar to no options case)
1038
1038
1039 $ hg annotate --ignore-blank-lines a
1039 $ hg annotate --ignore-blank-lines a
1040 1: a a
1040 1: a a
1041 0:
1041 0:
1042 1:
1042 1:
1043 1: b b
1043 1: b b
1044
1044
1045 $ cd ..
1045 $ cd ..
1046
1046
1047 Annotate with orphaned CR (issue5798)
1047 Annotate with orphaned CR (issue5798)
1048 -------------------------------------
1048 -------------------------------------
1049
1049
1050 $ hg init repo-cr
1050 $ hg init repo-cr
1051 $ cd repo-cr
1051 $ cd repo-cr
1052
1052
1053 $ cat <<'EOF' >> "$TESTTMP/substcr.py"
1053 $ cat <<'EOF' >> "$TESTTMP/substcr.py"
1054 > import sys
1054 > import sys
1055 > from mercurial.utils import procutil
1055 > from mercurial.utils import procutil
1056 > procutil.setbinary(sys.stdin)
1056 > procutil.setbinary(sys.stdin)
1057 > procutil.setbinary(sys.stdout)
1057 > procutil.setbinary(sys.stdout)
1058 > stdin = getattr(sys.stdin, 'buffer', sys.stdin)
1058 > stdin = getattr(sys.stdin, 'buffer', sys.stdin)
1059 > stdout = getattr(sys.stdout, 'buffer', sys.stdout)
1059 > stdout = getattr(sys.stdout, 'buffer', sys.stdout)
1060 > stdout.write(stdin.read().replace(b'\r', b'[CR]'))
1060 > stdout.write(stdin.read().replace(b'\r', b'[CR]'))
1061 > EOF
1061 > EOF
1062
1062
1063 >>> with open('a', 'wb') as f:
1063 >>> with open('a', 'wb') as f:
1064 ... f.write(b'0a\r0b\r\n0c\r0d\r\n0e\n0f\n0g') and None
1064 ... f.write(b'0a\r0b\r\n0c\r0d\r\n0e\n0f\n0g') and None
1065 $ hg ci -qAm0
1065 $ hg ci -qAm0
1066 >>> with open('a', 'wb') as f:
1066 >>> with open('a', 'wb') as f:
1067 ... f.write(b'0a\r0b\r\n1c\r1d\r\n0e\n1f\n0g') and None
1067 ... f.write(b'0a\r0b\r\n1c\r1d\r\n0e\n1f\n0g') and None
1068 $ hg ci -m1
1068 $ hg ci -m1
1069
1069
1070 $ hg annotate -r0 a | "$PYTHON" "$TESTTMP/substcr.py"
1070 $ hg annotate -r0 a | "$PYTHON" "$TESTTMP/substcr.py"
1071 0: 0a[CR]0b[CR]
1071 0: 0a[CR]0b[CR]
1072 0: 0c[CR]0d[CR]
1072 0: 0c[CR]0d[CR]
1073 0: 0e
1073 0: 0e
1074 0: 0f
1074 0: 0f
1075 0: 0g
1075 0: 0g
1076 $ hg annotate -r1 a | "$PYTHON" "$TESTTMP/substcr.py"
1076 $ hg annotate -r1 a | "$PYTHON" "$TESTTMP/substcr.py"
1077 0: 0a[CR]0b[CR]
1077 0: 0a[CR]0b[CR]
1078 1: 1c[CR]1d[CR]
1078 1: 1c[CR]1d[CR]
1079 0: 0e
1079 0: 0e
1080 1: 1f
1080 1: 1f
1081 0: 0g
1081 0: 0g
1082
1082
1083 $ cd ..
1083 $ cd ..
1084
1084
1085 Annotate with linkrev pointing to another branch
1085 Annotate with linkrev pointing to another branch
1086 ------------------------------------------------
1086 ------------------------------------------------
1087
1087
1088 create history with a filerev whose linkrev points to another branch
1088 create history with a filerev whose linkrev points to another branch
1089
1089
1090 $ hg init branchedlinkrev
1090 $ hg init branchedlinkrev
1091 $ cd branchedlinkrev
1091 $ cd branchedlinkrev
1092 $ echo A > a
1092 $ echo A > a
1093 $ hg commit -Am 'contentA'
1093 $ hg commit -Am 'contentA'
1094 adding a
1094 adding a
1095 $ echo B >> a
1095 $ echo B >> a
1096 $ hg commit -m 'contentB'
1096 $ hg commit -m 'contentB'
1097 $ hg up --rev 'desc(contentA)'
1097 $ hg up --rev 'desc(contentA)'
1098 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1098 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1099 $ echo unrelated > unrelated
1099 $ echo unrelated > unrelated
1100 $ hg commit -Am 'unrelated'
1100 $ hg commit -Am 'unrelated'
1101 adding unrelated
1101 adding unrelated
1102 created new head
1102 created new head
1103 $ hg graft -r 'desc(contentB)'
1103 $ hg graft -r 'desc(contentB)'
1104 grafting 1:fd27c222e3e6 "contentB"
1104 grafting 1:fd27c222e3e6 "contentB"
1105 $ echo C >> a
1105 $ echo C >> a
1106 $ hg commit -m 'contentC'
1106 $ hg commit -m 'contentC'
1107 $ echo W >> a
1107 $ echo W >> a
1108 $ hg log -G
1108 $ hg log -G
1109 @ changeset: 4:072f1e8df249
1109 @ changeset: 4:072f1e8df249
1110 | tag: tip
1110 | tag: tip
1111 | user: test
1111 | user: test
1112 | date: Thu Jan 01 00:00:00 1970 +0000
1112 | date: Thu Jan 01 00:00:00 1970 +0000
1113 | summary: contentC
1113 | summary: contentC
1114 |
1114 |
1115 o changeset: 3:ff38df03cc4b
1115 o changeset: 3:ff38df03cc4b
1116 | user: test
1116 | user: test
1117 | date: Thu Jan 01 00:00:00 1970 +0000
1117 | date: Thu Jan 01 00:00:00 1970 +0000
1118 | summary: contentB
1118 | summary: contentB
1119 |
1119 |
1120 o changeset: 2:62aaf3f6fc06
1120 o changeset: 2:62aaf3f6fc06
1121 | parent: 0:f0932f74827e
1121 | parent: 0:f0932f74827e
1122 | user: test
1122 | user: test
1123 | date: Thu Jan 01 00:00:00 1970 +0000
1123 | date: Thu Jan 01 00:00:00 1970 +0000
1124 | summary: unrelated
1124 | summary: unrelated
1125 |
1125 |
1126 | o changeset: 1:fd27c222e3e6
1126 | o changeset: 1:fd27c222e3e6
1127 |/ user: test
1127 |/ user: test
1128 | date: Thu Jan 01 00:00:00 1970 +0000
1128 | date: Thu Jan 01 00:00:00 1970 +0000
1129 | summary: contentB
1129 | summary: contentB
1130 |
1130 |
1131 o changeset: 0:f0932f74827e
1131 o changeset: 0:f0932f74827e
1132 user: test
1132 user: test
1133 date: Thu Jan 01 00:00:00 1970 +0000
1133 date: Thu Jan 01 00:00:00 1970 +0000
1134 summary: contentA
1134 summary: contentA
1135
1135
1136
1136
1137 Annotate should list ancestor of starting revision only
1137 Annotate should list ancestor of starting revision only
1138
1138
1139 $ hg annotate a
1139 $ hg annotate a
1140 0: A
1140 0: A
1141 3: B
1141 3: B
1142 4: C
1142 4: C
1143
1143
1144 $ hg annotate a -r 'wdir()'
1144 $ hg annotate a -r 'wdir()'
1145 0 : A
1145 0 : A
1146 3 : B
1146 3 : B
1147 4 : C
1147 4 : C
1148 4+: W
1148 4+: W
1149
1149
1150 Even when the starting revision is the linkrev-shadowed one:
1150 Even when the starting revision is the linkrev-shadowed one:
1151
1151
1152 $ hg annotate a -r 3
1152 $ hg annotate a -r 3
1153 0: A
1153 0: A
1154 3: B
1154 3: B
1155
1155
1156 $ cd ..
1156 $ cd ..
1157
1157
1158 Issue5360: Deleted chunk in p1 of a merge changeset
1158 Issue5360: Deleted chunk in p1 of a merge changeset
1159
1159
1160 $ hg init repo-5360
1160 $ hg init repo-5360
1161 $ cd repo-5360
1161 $ cd repo-5360
1162 $ echo 1 > a
1162 $ echo 1 > a
1163 $ hg commit -A a -m 1
1163 $ hg commit -A a -m 1
1164 $ echo 2 >> a
1164 $ echo 2 >> a
1165 $ hg commit -m 2
1165 $ hg commit -m 2
1166 $ echo a > a
1166 $ echo a > a
1167 $ hg commit -m a
1167 $ hg commit -m a
1168 $ hg update '.^' -q
1168 $ hg update '.^' -q
1169 $ echo 3 >> a
1169 $ echo 3 >> a
1170 $ hg commit -m 3 -q
1170 $ hg commit -m 3 -q
1171 $ hg merge 2 -q
1171 $ hg merge 2 -q
1172 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
1172 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
1173 [1]
1173 [1]
1174 $ cat a
1174 $ cat a
1175 <<<<<<< working copy: 0a068f0261cf - test: 3
1175 <<<<<<< working copy: 0a068f0261cf - test: 3
1176 1
1176 1
1177 2
1177 2
1178 3
1178 3
1179 ||||||| base
1179 ||||||| base
1180 1
1180 1
1181 2
1181 2
1182 =======
1182 =======
1183 a
1183 a
1184 >>>>>>> merge rev: 9409851bc20a - test: a
1184 >>>>>>> merge rev: 9409851bc20a - test: a
1185 $ cat > a << EOF
1185 $ cat > a << EOF
1186 > b
1186 > b
1187 > 1
1187 > 1
1188 > 2
1188 > 2
1189 > 3
1189 > 3
1190 > a
1190 > a
1191 > EOF
1191 > EOF
1192 $ hg resolve --mark -q
1192 $ hg resolve --mark -q
1193 $ rm a.orig
1193 $ rm a.orig
1194 $ hg commit -m m
1194 $ hg commit -m m
1195 $ hg annotate a
1195 $ hg annotate a
1196 4: b
1196 4: b
1197 0: 1
1197 0: 1
1198 1: 2
1198 1: 2
1199 3: 3
1199 3: 3
1200 2: a
1200 2: a
1201
1201
1202 $ cd ..
1202 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now