##// END OF EJS Templates
formatter: load templates section like a map file...
Yuya Nishihara -
r32875:c8f2cf18 default
parent child Browse files
Show More
@@ -1,3591 +1,3594 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 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 errno
10 import errno
11 import itertools
11 import itertools
12 import os
12 import os
13 import re
13 import re
14 import tempfile
14 import tempfile
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 )
22 )
23
23
24 from . import (
24 from . import (
25 bookmarks,
25 bookmarks,
26 changelog,
26 changelog,
27 copies,
27 copies,
28 crecord as crecordmod,
28 crecord as crecordmod,
29 encoding,
29 encoding,
30 error,
30 error,
31 formatter,
31 formatter,
32 graphmod,
32 graphmod,
33 lock as lockmod,
33 lock as lockmod,
34 match as matchmod,
34 match as matchmod,
35 obsolete,
35 obsolete,
36 patch,
36 patch,
37 pathutil,
37 pathutil,
38 phases,
38 phases,
39 pycompat,
39 pycompat,
40 registrar,
40 registrar,
41 repair,
41 repair,
42 revlog,
42 revlog,
43 revset,
43 revset,
44 scmutil,
44 scmutil,
45 smartset,
45 smartset,
46 templatekw,
46 templatekw,
47 templater,
47 templater,
48 util,
48 util,
49 vfs as vfsmod,
49 vfs as vfsmod,
50 )
50 )
51 stringio = util.stringio
51 stringio = util.stringio
52
52
53 # templates of common command options
53 # templates of common command options
54
54
55 dryrunopts = [
55 dryrunopts = [
56 ('n', 'dry-run', None,
56 ('n', 'dry-run', None,
57 _('do not perform actions, just print output')),
57 _('do not perform actions, just print output')),
58 ]
58 ]
59
59
60 remoteopts = [
60 remoteopts = [
61 ('e', 'ssh', '',
61 ('e', 'ssh', '',
62 _('specify ssh command to use'), _('CMD')),
62 _('specify ssh command to use'), _('CMD')),
63 ('', 'remotecmd', '',
63 ('', 'remotecmd', '',
64 _('specify hg command to run on the remote side'), _('CMD')),
64 _('specify hg command to run on the remote side'), _('CMD')),
65 ('', 'insecure', None,
65 ('', 'insecure', None,
66 _('do not verify server certificate (ignoring web.cacerts config)')),
66 _('do not verify server certificate (ignoring web.cacerts config)')),
67 ]
67 ]
68
68
69 walkopts = [
69 walkopts = [
70 ('I', 'include', [],
70 ('I', 'include', [],
71 _('include names matching the given patterns'), _('PATTERN')),
71 _('include names matching the given patterns'), _('PATTERN')),
72 ('X', 'exclude', [],
72 ('X', 'exclude', [],
73 _('exclude names matching the given patterns'), _('PATTERN')),
73 _('exclude names matching the given patterns'), _('PATTERN')),
74 ]
74 ]
75
75
76 commitopts = [
76 commitopts = [
77 ('m', 'message', '',
77 ('m', 'message', '',
78 _('use text as commit message'), _('TEXT')),
78 _('use text as commit message'), _('TEXT')),
79 ('l', 'logfile', '',
79 ('l', 'logfile', '',
80 _('read commit message from file'), _('FILE')),
80 _('read commit message from file'), _('FILE')),
81 ]
81 ]
82
82
83 commitopts2 = [
83 commitopts2 = [
84 ('d', 'date', '',
84 ('d', 'date', '',
85 _('record the specified date as commit date'), _('DATE')),
85 _('record the specified date as commit date'), _('DATE')),
86 ('u', 'user', '',
86 ('u', 'user', '',
87 _('record the specified user as committer'), _('USER')),
87 _('record the specified user as committer'), _('USER')),
88 ]
88 ]
89
89
90 # hidden for now
90 # hidden for now
91 formatteropts = [
91 formatteropts = [
92 ('T', 'template', '',
92 ('T', 'template', '',
93 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
93 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
94 ]
94 ]
95
95
96 templateopts = [
96 templateopts = [
97 ('', 'style', '',
97 ('', 'style', '',
98 _('display using template map file (DEPRECATED)'), _('STYLE')),
98 _('display using template map file (DEPRECATED)'), _('STYLE')),
99 ('T', 'template', '',
99 ('T', 'template', '',
100 _('display with template'), _('TEMPLATE')),
100 _('display with template'), _('TEMPLATE')),
101 ]
101 ]
102
102
103 logopts = [
103 logopts = [
104 ('p', 'patch', None, _('show patch')),
104 ('p', 'patch', None, _('show patch')),
105 ('g', 'git', None, _('use git extended diff format')),
105 ('g', 'git', None, _('use git extended diff format')),
106 ('l', 'limit', '',
106 ('l', 'limit', '',
107 _('limit number of changes displayed'), _('NUM')),
107 _('limit number of changes displayed'), _('NUM')),
108 ('M', 'no-merges', None, _('do not show merges')),
108 ('M', 'no-merges', None, _('do not show merges')),
109 ('', 'stat', None, _('output diffstat-style summary of changes')),
109 ('', 'stat', None, _('output diffstat-style summary of changes')),
110 ('G', 'graph', None, _("show the revision DAG")),
110 ('G', 'graph', None, _("show the revision DAG")),
111 ] + templateopts
111 ] + templateopts
112
112
113 diffopts = [
113 diffopts = [
114 ('a', 'text', None, _('treat all files as text')),
114 ('a', 'text', None, _('treat all files as text')),
115 ('g', 'git', None, _('use git extended diff format')),
115 ('g', 'git', None, _('use git extended diff format')),
116 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
116 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
117 ('', 'nodates', None, _('omit dates from diff headers'))
117 ('', 'nodates', None, _('omit dates from diff headers'))
118 ]
118 ]
119
119
120 diffwsopts = [
120 diffwsopts = [
121 ('w', 'ignore-all-space', None,
121 ('w', 'ignore-all-space', None,
122 _('ignore white space when comparing lines')),
122 _('ignore white space when comparing lines')),
123 ('b', 'ignore-space-change', None,
123 ('b', 'ignore-space-change', None,
124 _('ignore changes in the amount of white space')),
124 _('ignore changes in the amount of white space')),
125 ('B', 'ignore-blank-lines', None,
125 ('B', 'ignore-blank-lines', None,
126 _('ignore changes whose lines are all blank')),
126 _('ignore changes whose lines are all blank')),
127 ]
127 ]
128
128
129 diffopts2 = [
129 diffopts2 = [
130 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
130 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
131 ('p', 'show-function', None, _('show which function each change is in')),
131 ('p', 'show-function', None, _('show which function each change is in')),
132 ('', 'reverse', None, _('produce a diff that undoes the changes')),
132 ('', 'reverse', None, _('produce a diff that undoes the changes')),
133 ] + diffwsopts + [
133 ] + diffwsopts + [
134 ('U', 'unified', '',
134 ('U', 'unified', '',
135 _('number of lines of context to show'), _('NUM')),
135 _('number of lines of context to show'), _('NUM')),
136 ('', 'stat', None, _('output diffstat-style summary of changes')),
136 ('', 'stat', None, _('output diffstat-style summary of changes')),
137 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
137 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
138 ]
138 ]
139
139
140 mergetoolopts = [
140 mergetoolopts = [
141 ('t', 'tool', '', _('specify merge tool')),
141 ('t', 'tool', '', _('specify merge tool')),
142 ]
142 ]
143
143
144 similarityopts = [
144 similarityopts = [
145 ('s', 'similarity', '',
145 ('s', 'similarity', '',
146 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
146 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
147 ]
147 ]
148
148
149 subrepoopts = [
149 subrepoopts = [
150 ('S', 'subrepos', None,
150 ('S', 'subrepos', None,
151 _('recurse into subrepositories'))
151 _('recurse into subrepositories'))
152 ]
152 ]
153
153
154 debugrevlogopts = [
154 debugrevlogopts = [
155 ('c', 'changelog', False, _('open changelog')),
155 ('c', 'changelog', False, _('open changelog')),
156 ('m', 'manifest', False, _('open manifest')),
156 ('m', 'manifest', False, _('open manifest')),
157 ('', 'dir', '', _('open directory manifest')),
157 ('', 'dir', '', _('open directory manifest')),
158 ]
158 ]
159
159
160 # special string such that everything below this line will be ingored in the
160 # special string such that everything below this line will be ingored in the
161 # editor text
161 # editor text
162 _linebelow = "^HG: ------------------------ >8 ------------------------$"
162 _linebelow = "^HG: ------------------------ >8 ------------------------$"
163
163
164 def ishunk(x):
164 def ishunk(x):
165 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
165 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
166 return isinstance(x, hunkclasses)
166 return isinstance(x, hunkclasses)
167
167
168 def newandmodified(chunks, originalchunks):
168 def newandmodified(chunks, originalchunks):
169 newlyaddedandmodifiedfiles = set()
169 newlyaddedandmodifiedfiles = set()
170 for chunk in chunks:
170 for chunk in chunks:
171 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
171 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
172 originalchunks:
172 originalchunks:
173 newlyaddedandmodifiedfiles.add(chunk.header.filename())
173 newlyaddedandmodifiedfiles.add(chunk.header.filename())
174 return newlyaddedandmodifiedfiles
174 return newlyaddedandmodifiedfiles
175
175
176 def parsealiases(cmd):
176 def parsealiases(cmd):
177 return cmd.lstrip("^").split("|")
177 return cmd.lstrip("^").split("|")
178
178
179 def setupwrapcolorwrite(ui):
179 def setupwrapcolorwrite(ui):
180 # wrap ui.write so diff output can be labeled/colorized
180 # wrap ui.write so diff output can be labeled/colorized
181 def wrapwrite(orig, *args, **kw):
181 def wrapwrite(orig, *args, **kw):
182 label = kw.pop('label', '')
182 label = kw.pop('label', '')
183 for chunk, l in patch.difflabel(lambda: args):
183 for chunk, l in patch.difflabel(lambda: args):
184 orig(chunk, label=label + l)
184 orig(chunk, label=label + l)
185
185
186 oldwrite = ui.write
186 oldwrite = ui.write
187 def wrap(*args, **kwargs):
187 def wrap(*args, **kwargs):
188 return wrapwrite(oldwrite, *args, **kwargs)
188 return wrapwrite(oldwrite, *args, **kwargs)
189 setattr(ui, 'write', wrap)
189 setattr(ui, 'write', wrap)
190 return oldwrite
190 return oldwrite
191
191
192 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
192 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
193 if usecurses:
193 if usecurses:
194 if testfile:
194 if testfile:
195 recordfn = crecordmod.testdecorator(testfile,
195 recordfn = crecordmod.testdecorator(testfile,
196 crecordmod.testchunkselector)
196 crecordmod.testchunkselector)
197 else:
197 else:
198 recordfn = crecordmod.chunkselector
198 recordfn = crecordmod.chunkselector
199
199
200 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
200 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
201
201
202 else:
202 else:
203 return patch.filterpatch(ui, originalhunks, operation)
203 return patch.filterpatch(ui, originalhunks, operation)
204
204
205 def recordfilter(ui, originalhunks, operation=None):
205 def recordfilter(ui, originalhunks, operation=None):
206 """ Prompts the user to filter the originalhunks and return a list of
206 """ Prompts the user to filter the originalhunks and return a list of
207 selected hunks.
207 selected hunks.
208 *operation* is used for to build ui messages to indicate the user what
208 *operation* is used for to build ui messages to indicate the user what
209 kind of filtering they are doing: reverting, committing, shelving, etc.
209 kind of filtering they are doing: reverting, committing, shelving, etc.
210 (see patch.filterpatch).
210 (see patch.filterpatch).
211 """
211 """
212 usecurses = crecordmod.checkcurses(ui)
212 usecurses = crecordmod.checkcurses(ui)
213 testfile = ui.config('experimental', 'crecordtest', None)
213 testfile = ui.config('experimental', 'crecordtest', None)
214 oldwrite = setupwrapcolorwrite(ui)
214 oldwrite = setupwrapcolorwrite(ui)
215 try:
215 try:
216 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
216 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
217 testfile, operation)
217 testfile, operation)
218 finally:
218 finally:
219 ui.write = oldwrite
219 ui.write = oldwrite
220 return newchunks, newopts
220 return newchunks, newopts
221
221
222 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
222 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
223 filterfn, *pats, **opts):
223 filterfn, *pats, **opts):
224 from . import merge as mergemod
224 from . import merge as mergemod
225 opts = pycompat.byteskwargs(opts)
225 opts = pycompat.byteskwargs(opts)
226 if not ui.interactive():
226 if not ui.interactive():
227 if cmdsuggest:
227 if cmdsuggest:
228 msg = _('running non-interactively, use %s instead') % cmdsuggest
228 msg = _('running non-interactively, use %s instead') % cmdsuggest
229 else:
229 else:
230 msg = _('running non-interactively')
230 msg = _('running non-interactively')
231 raise error.Abort(msg)
231 raise error.Abort(msg)
232
232
233 # make sure username is set before going interactive
233 # make sure username is set before going interactive
234 if not opts.get('user'):
234 if not opts.get('user'):
235 ui.username() # raise exception, username not provided
235 ui.username() # raise exception, username not provided
236
236
237 def recordfunc(ui, repo, message, match, opts):
237 def recordfunc(ui, repo, message, match, opts):
238 """This is generic record driver.
238 """This is generic record driver.
239
239
240 Its job is to interactively filter local changes, and
240 Its job is to interactively filter local changes, and
241 accordingly prepare working directory into a state in which the
241 accordingly prepare working directory into a state in which the
242 job can be delegated to a non-interactive commit command such as
242 job can be delegated to a non-interactive commit command such as
243 'commit' or 'qrefresh'.
243 'commit' or 'qrefresh'.
244
244
245 After the actual job is done by non-interactive command, the
245 After the actual job is done by non-interactive command, the
246 working directory is restored to its original state.
246 working directory is restored to its original state.
247
247
248 In the end we'll record interesting changes, and everything else
248 In the end we'll record interesting changes, and everything else
249 will be left in place, so the user can continue working.
249 will be left in place, so the user can continue working.
250 """
250 """
251
251
252 checkunfinished(repo, commit=True)
252 checkunfinished(repo, commit=True)
253 wctx = repo[None]
253 wctx = repo[None]
254 merge = len(wctx.parents()) > 1
254 merge = len(wctx.parents()) > 1
255 if merge:
255 if merge:
256 raise error.Abort(_('cannot partially commit a merge '
256 raise error.Abort(_('cannot partially commit a merge '
257 '(use "hg commit" instead)'))
257 '(use "hg commit" instead)'))
258
258
259 def fail(f, msg):
259 def fail(f, msg):
260 raise error.Abort('%s: %s' % (f, msg))
260 raise error.Abort('%s: %s' % (f, msg))
261
261
262 force = opts.get('force')
262 force = opts.get('force')
263 if not force:
263 if not force:
264 vdirs = []
264 vdirs = []
265 match.explicitdir = vdirs.append
265 match.explicitdir = vdirs.append
266 match.bad = fail
266 match.bad = fail
267
267
268 status = repo.status(match=match)
268 status = repo.status(match=match)
269 if not force:
269 if not force:
270 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
270 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
271 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
271 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
272 diffopts.nodates = True
272 diffopts.nodates = True
273 diffopts.git = True
273 diffopts.git = True
274 diffopts.showfunc = True
274 diffopts.showfunc = True
275 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
275 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
276 originalchunks = patch.parsepatch(originaldiff)
276 originalchunks = patch.parsepatch(originaldiff)
277
277
278 # 1. filter patch, since we are intending to apply subset of it
278 # 1. filter patch, since we are intending to apply subset of it
279 try:
279 try:
280 chunks, newopts = filterfn(ui, originalchunks)
280 chunks, newopts = filterfn(ui, originalchunks)
281 except patch.PatchError as err:
281 except patch.PatchError as err:
282 raise error.Abort(_('error parsing patch: %s') % err)
282 raise error.Abort(_('error parsing patch: %s') % err)
283 opts.update(newopts)
283 opts.update(newopts)
284
284
285 # We need to keep a backup of files that have been newly added and
285 # We need to keep a backup of files that have been newly added and
286 # modified during the recording process because there is a previous
286 # modified during the recording process because there is a previous
287 # version without the edit in the workdir
287 # version without the edit in the workdir
288 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
288 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
289 contenders = set()
289 contenders = set()
290 for h in chunks:
290 for h in chunks:
291 try:
291 try:
292 contenders.update(set(h.files()))
292 contenders.update(set(h.files()))
293 except AttributeError:
293 except AttributeError:
294 pass
294 pass
295
295
296 changed = status.modified + status.added + status.removed
296 changed = status.modified + status.added + status.removed
297 newfiles = [f for f in changed if f in contenders]
297 newfiles = [f for f in changed if f in contenders]
298 if not newfiles:
298 if not newfiles:
299 ui.status(_('no changes to record\n'))
299 ui.status(_('no changes to record\n'))
300 return 0
300 return 0
301
301
302 modified = set(status.modified)
302 modified = set(status.modified)
303
303
304 # 2. backup changed files, so we can restore them in the end
304 # 2. backup changed files, so we can restore them in the end
305
305
306 if backupall:
306 if backupall:
307 tobackup = changed
307 tobackup = changed
308 else:
308 else:
309 tobackup = [f for f in newfiles if f in modified or f in \
309 tobackup = [f for f in newfiles if f in modified or f in \
310 newlyaddedandmodifiedfiles]
310 newlyaddedandmodifiedfiles]
311 backups = {}
311 backups = {}
312 if tobackup:
312 if tobackup:
313 backupdir = repo.vfs.join('record-backups')
313 backupdir = repo.vfs.join('record-backups')
314 try:
314 try:
315 os.mkdir(backupdir)
315 os.mkdir(backupdir)
316 except OSError as err:
316 except OSError as err:
317 if err.errno != errno.EEXIST:
317 if err.errno != errno.EEXIST:
318 raise
318 raise
319 try:
319 try:
320 # backup continues
320 # backup continues
321 for f in tobackup:
321 for f in tobackup:
322 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
322 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
323 dir=backupdir)
323 dir=backupdir)
324 os.close(fd)
324 os.close(fd)
325 ui.debug('backup %r as %r\n' % (f, tmpname))
325 ui.debug('backup %r as %r\n' % (f, tmpname))
326 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
326 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
327 backups[f] = tmpname
327 backups[f] = tmpname
328
328
329 fp = stringio()
329 fp = stringio()
330 for c in chunks:
330 for c in chunks:
331 fname = c.filename()
331 fname = c.filename()
332 if fname in backups:
332 if fname in backups:
333 c.write(fp)
333 c.write(fp)
334 dopatch = fp.tell()
334 dopatch = fp.tell()
335 fp.seek(0)
335 fp.seek(0)
336
336
337 # 2.5 optionally review / modify patch in text editor
337 # 2.5 optionally review / modify patch in text editor
338 if opts.get('review', False):
338 if opts.get('review', False):
339 patchtext = (crecordmod.diffhelptext
339 patchtext = (crecordmod.diffhelptext
340 + crecordmod.patchhelptext
340 + crecordmod.patchhelptext
341 + fp.read())
341 + fp.read())
342 reviewedpatch = ui.edit(patchtext, "",
342 reviewedpatch = ui.edit(patchtext, "",
343 extra={"suffix": ".diff"},
343 extra={"suffix": ".diff"},
344 repopath=repo.path)
344 repopath=repo.path)
345 fp.truncate(0)
345 fp.truncate(0)
346 fp.write(reviewedpatch)
346 fp.write(reviewedpatch)
347 fp.seek(0)
347 fp.seek(0)
348
348
349 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
349 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
350 # 3a. apply filtered patch to clean repo (clean)
350 # 3a. apply filtered patch to clean repo (clean)
351 if backups:
351 if backups:
352 # Equivalent to hg.revert
352 # Equivalent to hg.revert
353 m = scmutil.matchfiles(repo, backups.keys())
353 m = scmutil.matchfiles(repo, backups.keys())
354 mergemod.update(repo, repo.dirstate.p1(),
354 mergemod.update(repo, repo.dirstate.p1(),
355 False, True, matcher=m)
355 False, True, matcher=m)
356
356
357 # 3b. (apply)
357 # 3b. (apply)
358 if dopatch:
358 if dopatch:
359 try:
359 try:
360 ui.debug('applying patch\n')
360 ui.debug('applying patch\n')
361 ui.debug(fp.getvalue())
361 ui.debug(fp.getvalue())
362 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
362 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
363 except patch.PatchError as err:
363 except patch.PatchError as err:
364 raise error.Abort(str(err))
364 raise error.Abort(str(err))
365 del fp
365 del fp
366
366
367 # 4. We prepared working directory according to filtered
367 # 4. We prepared working directory according to filtered
368 # patch. Now is the time to delegate the job to
368 # patch. Now is the time to delegate the job to
369 # commit/qrefresh or the like!
369 # commit/qrefresh or the like!
370
370
371 # Make all of the pathnames absolute.
371 # Make all of the pathnames absolute.
372 newfiles = [repo.wjoin(nf) for nf in newfiles]
372 newfiles = [repo.wjoin(nf) for nf in newfiles]
373 return commitfunc(ui, repo, *newfiles, **opts)
373 return commitfunc(ui, repo, *newfiles, **opts)
374 finally:
374 finally:
375 # 5. finally restore backed-up files
375 # 5. finally restore backed-up files
376 try:
376 try:
377 dirstate = repo.dirstate
377 dirstate = repo.dirstate
378 for realname, tmpname in backups.iteritems():
378 for realname, tmpname in backups.iteritems():
379 ui.debug('restoring %r to %r\n' % (tmpname, realname))
379 ui.debug('restoring %r to %r\n' % (tmpname, realname))
380
380
381 if dirstate[realname] == 'n':
381 if dirstate[realname] == 'n':
382 # without normallookup, restoring timestamp
382 # without normallookup, restoring timestamp
383 # may cause partially committed files
383 # may cause partially committed files
384 # to be treated as unmodified
384 # to be treated as unmodified
385 dirstate.normallookup(realname)
385 dirstate.normallookup(realname)
386
386
387 # copystat=True here and above are a hack to trick any
387 # copystat=True here and above are a hack to trick any
388 # editors that have f open that we haven't modified them.
388 # editors that have f open that we haven't modified them.
389 #
389 #
390 # Also note that this racy as an editor could notice the
390 # Also note that this racy as an editor could notice the
391 # file's mtime before we've finished writing it.
391 # file's mtime before we've finished writing it.
392 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
392 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
393 os.unlink(tmpname)
393 os.unlink(tmpname)
394 if tobackup:
394 if tobackup:
395 os.rmdir(backupdir)
395 os.rmdir(backupdir)
396 except OSError:
396 except OSError:
397 pass
397 pass
398
398
399 def recordinwlock(ui, repo, message, match, opts):
399 def recordinwlock(ui, repo, message, match, opts):
400 with repo.wlock():
400 with repo.wlock():
401 return recordfunc(ui, repo, message, match, opts)
401 return recordfunc(ui, repo, message, match, opts)
402
402
403 return commit(ui, repo, recordinwlock, pats, opts)
403 return commit(ui, repo, recordinwlock, pats, opts)
404
404
405 def findpossible(cmd, table, strict=False):
405 def findpossible(cmd, table, strict=False):
406 """
406 """
407 Return cmd -> (aliases, command table entry)
407 Return cmd -> (aliases, command table entry)
408 for each matching command.
408 for each matching command.
409 Return debug commands (or their aliases) only if no normal command matches.
409 Return debug commands (or their aliases) only if no normal command matches.
410 """
410 """
411 choice = {}
411 choice = {}
412 debugchoice = {}
412 debugchoice = {}
413
413
414 if cmd in table:
414 if cmd in table:
415 # short-circuit exact matches, "log" alias beats "^log|history"
415 # short-circuit exact matches, "log" alias beats "^log|history"
416 keys = [cmd]
416 keys = [cmd]
417 else:
417 else:
418 keys = table.keys()
418 keys = table.keys()
419
419
420 allcmds = []
420 allcmds = []
421 for e in keys:
421 for e in keys:
422 aliases = parsealiases(e)
422 aliases = parsealiases(e)
423 allcmds.extend(aliases)
423 allcmds.extend(aliases)
424 found = None
424 found = None
425 if cmd in aliases:
425 if cmd in aliases:
426 found = cmd
426 found = cmd
427 elif not strict:
427 elif not strict:
428 for a in aliases:
428 for a in aliases:
429 if a.startswith(cmd):
429 if a.startswith(cmd):
430 found = a
430 found = a
431 break
431 break
432 if found is not None:
432 if found is not None:
433 if aliases[0].startswith("debug") or found.startswith("debug"):
433 if aliases[0].startswith("debug") or found.startswith("debug"):
434 debugchoice[found] = (aliases, table[e])
434 debugchoice[found] = (aliases, table[e])
435 else:
435 else:
436 choice[found] = (aliases, table[e])
436 choice[found] = (aliases, table[e])
437
437
438 if not choice and debugchoice:
438 if not choice and debugchoice:
439 choice = debugchoice
439 choice = debugchoice
440
440
441 return choice, allcmds
441 return choice, allcmds
442
442
443 def findcmd(cmd, table, strict=True):
443 def findcmd(cmd, table, strict=True):
444 """Return (aliases, command table entry) for command string."""
444 """Return (aliases, command table entry) for command string."""
445 choice, allcmds = findpossible(cmd, table, strict)
445 choice, allcmds = findpossible(cmd, table, strict)
446
446
447 if cmd in choice:
447 if cmd in choice:
448 return choice[cmd]
448 return choice[cmd]
449
449
450 if len(choice) > 1:
450 if len(choice) > 1:
451 clist = sorted(choice)
451 clist = sorted(choice)
452 raise error.AmbiguousCommand(cmd, clist)
452 raise error.AmbiguousCommand(cmd, clist)
453
453
454 if choice:
454 if choice:
455 return list(choice.values())[0]
455 return list(choice.values())[0]
456
456
457 raise error.UnknownCommand(cmd, allcmds)
457 raise error.UnknownCommand(cmd, allcmds)
458
458
459 def findrepo(p):
459 def findrepo(p):
460 while not os.path.isdir(os.path.join(p, ".hg")):
460 while not os.path.isdir(os.path.join(p, ".hg")):
461 oldp, p = p, os.path.dirname(p)
461 oldp, p = p, os.path.dirname(p)
462 if p == oldp:
462 if p == oldp:
463 return None
463 return None
464
464
465 return p
465 return p
466
466
467 def bailifchanged(repo, merge=True, hint=None):
467 def bailifchanged(repo, merge=True, hint=None):
468 """ enforce the precondition that working directory must be clean.
468 """ enforce the precondition that working directory must be clean.
469
469
470 'merge' can be set to false if a pending uncommitted merge should be
470 'merge' can be set to false if a pending uncommitted merge should be
471 ignored (such as when 'update --check' runs).
471 ignored (such as when 'update --check' runs).
472
472
473 'hint' is the usual hint given to Abort exception.
473 'hint' is the usual hint given to Abort exception.
474 """
474 """
475
475
476 if merge and repo.dirstate.p2() != nullid:
476 if merge and repo.dirstate.p2() != nullid:
477 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
477 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
478 modified, added, removed, deleted = repo.status()[:4]
478 modified, added, removed, deleted = repo.status()[:4]
479 if modified or added or removed or deleted:
479 if modified or added or removed or deleted:
480 raise error.Abort(_('uncommitted changes'), hint=hint)
480 raise error.Abort(_('uncommitted changes'), hint=hint)
481 ctx = repo[None]
481 ctx = repo[None]
482 for s in sorted(ctx.substate):
482 for s in sorted(ctx.substate):
483 ctx.sub(s).bailifchanged(hint=hint)
483 ctx.sub(s).bailifchanged(hint=hint)
484
484
485 def logmessage(ui, opts):
485 def logmessage(ui, opts):
486 """ get the log message according to -m and -l option """
486 """ get the log message according to -m and -l option """
487 message = opts.get('message')
487 message = opts.get('message')
488 logfile = opts.get('logfile')
488 logfile = opts.get('logfile')
489
489
490 if message and logfile:
490 if message and logfile:
491 raise error.Abort(_('options --message and --logfile are mutually '
491 raise error.Abort(_('options --message and --logfile are mutually '
492 'exclusive'))
492 'exclusive'))
493 if not message and logfile:
493 if not message and logfile:
494 try:
494 try:
495 if isstdiofilename(logfile):
495 if isstdiofilename(logfile):
496 message = ui.fin.read()
496 message = ui.fin.read()
497 else:
497 else:
498 message = '\n'.join(util.readfile(logfile).splitlines())
498 message = '\n'.join(util.readfile(logfile).splitlines())
499 except IOError as inst:
499 except IOError as inst:
500 raise error.Abort(_("can't read commit message '%s': %s") %
500 raise error.Abort(_("can't read commit message '%s': %s") %
501 (logfile, inst.strerror))
501 (logfile, inst.strerror))
502 return message
502 return message
503
503
504 def mergeeditform(ctxorbool, baseformname):
504 def mergeeditform(ctxorbool, baseformname):
505 """return appropriate editform name (referencing a committemplate)
505 """return appropriate editform name (referencing a committemplate)
506
506
507 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
507 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
508 merging is committed.
508 merging is committed.
509
509
510 This returns baseformname with '.merge' appended if it is a merge,
510 This returns baseformname with '.merge' appended if it is a merge,
511 otherwise '.normal' is appended.
511 otherwise '.normal' is appended.
512 """
512 """
513 if isinstance(ctxorbool, bool):
513 if isinstance(ctxorbool, bool):
514 if ctxorbool:
514 if ctxorbool:
515 return baseformname + ".merge"
515 return baseformname + ".merge"
516 elif 1 < len(ctxorbool.parents()):
516 elif 1 < len(ctxorbool.parents()):
517 return baseformname + ".merge"
517 return baseformname + ".merge"
518
518
519 return baseformname + ".normal"
519 return baseformname + ".normal"
520
520
521 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
521 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
522 editform='', **opts):
522 editform='', **opts):
523 """get appropriate commit message editor according to '--edit' option
523 """get appropriate commit message editor according to '--edit' option
524
524
525 'finishdesc' is a function to be called with edited commit message
525 'finishdesc' is a function to be called with edited commit message
526 (= 'description' of the new changeset) just after editing, but
526 (= 'description' of the new changeset) just after editing, but
527 before checking empty-ness. It should return actual text to be
527 before checking empty-ness. It should return actual text to be
528 stored into history. This allows to change description before
528 stored into history. This allows to change description before
529 storing.
529 storing.
530
530
531 'extramsg' is a extra message to be shown in the editor instead of
531 'extramsg' is a extra message to be shown in the editor instead of
532 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
532 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
533 is automatically added.
533 is automatically added.
534
534
535 'editform' is a dot-separated list of names, to distinguish
535 'editform' is a dot-separated list of names, to distinguish
536 the purpose of commit text editing.
536 the purpose of commit text editing.
537
537
538 'getcommiteditor' returns 'commitforceeditor' regardless of
538 'getcommiteditor' returns 'commitforceeditor' regardless of
539 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
539 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
540 they are specific for usage in MQ.
540 they are specific for usage in MQ.
541 """
541 """
542 if edit or finishdesc or extramsg:
542 if edit or finishdesc or extramsg:
543 return lambda r, c, s: commitforceeditor(r, c, s,
543 return lambda r, c, s: commitforceeditor(r, c, s,
544 finishdesc=finishdesc,
544 finishdesc=finishdesc,
545 extramsg=extramsg,
545 extramsg=extramsg,
546 editform=editform)
546 editform=editform)
547 elif editform:
547 elif editform:
548 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
548 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
549 else:
549 else:
550 return commiteditor
550 return commiteditor
551
551
552 def loglimit(opts):
552 def loglimit(opts):
553 """get the log limit according to option -l/--limit"""
553 """get the log limit according to option -l/--limit"""
554 limit = opts.get('limit')
554 limit = opts.get('limit')
555 if limit:
555 if limit:
556 try:
556 try:
557 limit = int(limit)
557 limit = int(limit)
558 except ValueError:
558 except ValueError:
559 raise error.Abort(_('limit must be a positive integer'))
559 raise error.Abort(_('limit must be a positive integer'))
560 if limit <= 0:
560 if limit <= 0:
561 raise error.Abort(_('limit must be positive'))
561 raise error.Abort(_('limit must be positive'))
562 else:
562 else:
563 limit = None
563 limit = None
564 return limit
564 return limit
565
565
566 def makefilename(repo, pat, node, desc=None,
566 def makefilename(repo, pat, node, desc=None,
567 total=None, seqno=None, revwidth=None, pathname=None):
567 total=None, seqno=None, revwidth=None, pathname=None):
568 node_expander = {
568 node_expander = {
569 'H': lambda: hex(node),
569 'H': lambda: hex(node),
570 'R': lambda: str(repo.changelog.rev(node)),
570 'R': lambda: str(repo.changelog.rev(node)),
571 'h': lambda: short(node),
571 'h': lambda: short(node),
572 'm': lambda: re.sub('[^\w]', '_', str(desc))
572 'm': lambda: re.sub('[^\w]', '_', str(desc))
573 }
573 }
574 expander = {
574 expander = {
575 '%': lambda: '%',
575 '%': lambda: '%',
576 'b': lambda: os.path.basename(repo.root),
576 'b': lambda: os.path.basename(repo.root),
577 }
577 }
578
578
579 try:
579 try:
580 if node:
580 if node:
581 expander.update(node_expander)
581 expander.update(node_expander)
582 if node:
582 if node:
583 expander['r'] = (lambda:
583 expander['r'] = (lambda:
584 str(repo.changelog.rev(node)).zfill(revwidth or 0))
584 str(repo.changelog.rev(node)).zfill(revwidth or 0))
585 if total is not None:
585 if total is not None:
586 expander['N'] = lambda: str(total)
586 expander['N'] = lambda: str(total)
587 if seqno is not None:
587 if seqno is not None:
588 expander['n'] = lambda: str(seqno)
588 expander['n'] = lambda: str(seqno)
589 if total is not None and seqno is not None:
589 if total is not None and seqno is not None:
590 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
590 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
591 if pathname is not None:
591 if pathname is not None:
592 expander['s'] = lambda: os.path.basename(pathname)
592 expander['s'] = lambda: os.path.basename(pathname)
593 expander['d'] = lambda: os.path.dirname(pathname) or '.'
593 expander['d'] = lambda: os.path.dirname(pathname) or '.'
594 expander['p'] = lambda: pathname
594 expander['p'] = lambda: pathname
595
595
596 newname = []
596 newname = []
597 patlen = len(pat)
597 patlen = len(pat)
598 i = 0
598 i = 0
599 while i < patlen:
599 while i < patlen:
600 c = pat[i:i + 1]
600 c = pat[i:i + 1]
601 if c == '%':
601 if c == '%':
602 i += 1
602 i += 1
603 c = pat[i:i + 1]
603 c = pat[i:i + 1]
604 c = expander[c]()
604 c = expander[c]()
605 newname.append(c)
605 newname.append(c)
606 i += 1
606 i += 1
607 return ''.join(newname)
607 return ''.join(newname)
608 except KeyError as inst:
608 except KeyError as inst:
609 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
609 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
610 inst.args[0])
610 inst.args[0])
611
611
612 def isstdiofilename(pat):
612 def isstdiofilename(pat):
613 """True if the given pat looks like a filename denoting stdin/stdout"""
613 """True if the given pat looks like a filename denoting stdin/stdout"""
614 return not pat or pat == '-'
614 return not pat or pat == '-'
615
615
616 class _unclosablefile(object):
616 class _unclosablefile(object):
617 def __init__(self, fp):
617 def __init__(self, fp):
618 self._fp = fp
618 self._fp = fp
619
619
620 def close(self):
620 def close(self):
621 pass
621 pass
622
622
623 def __iter__(self):
623 def __iter__(self):
624 return iter(self._fp)
624 return iter(self._fp)
625
625
626 def __getattr__(self, attr):
626 def __getattr__(self, attr):
627 return getattr(self._fp, attr)
627 return getattr(self._fp, attr)
628
628
629 def __enter__(self):
629 def __enter__(self):
630 return self
630 return self
631
631
632 def __exit__(self, exc_type, exc_value, exc_tb):
632 def __exit__(self, exc_type, exc_value, exc_tb):
633 pass
633 pass
634
634
635 def makefileobj(repo, pat, node=None, desc=None, total=None,
635 def makefileobj(repo, pat, node=None, desc=None, total=None,
636 seqno=None, revwidth=None, mode='wb', modemap=None,
636 seqno=None, revwidth=None, mode='wb', modemap=None,
637 pathname=None):
637 pathname=None):
638
638
639 writable = mode not in ('r', 'rb')
639 writable = mode not in ('r', 'rb')
640
640
641 if isstdiofilename(pat):
641 if isstdiofilename(pat):
642 if writable:
642 if writable:
643 fp = repo.ui.fout
643 fp = repo.ui.fout
644 else:
644 else:
645 fp = repo.ui.fin
645 fp = repo.ui.fin
646 return _unclosablefile(fp)
646 return _unclosablefile(fp)
647 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
647 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
648 if modemap is not None:
648 if modemap is not None:
649 mode = modemap.get(fn, mode)
649 mode = modemap.get(fn, mode)
650 if mode == 'wb':
650 if mode == 'wb':
651 modemap[fn] = 'ab'
651 modemap[fn] = 'ab'
652 return open(fn, mode)
652 return open(fn, mode)
653
653
654 def openrevlog(repo, cmd, file_, opts):
654 def openrevlog(repo, cmd, file_, opts):
655 """opens the changelog, manifest, a filelog or a given revlog"""
655 """opens the changelog, manifest, a filelog or a given revlog"""
656 cl = opts['changelog']
656 cl = opts['changelog']
657 mf = opts['manifest']
657 mf = opts['manifest']
658 dir = opts['dir']
658 dir = opts['dir']
659 msg = None
659 msg = None
660 if cl and mf:
660 if cl and mf:
661 msg = _('cannot specify --changelog and --manifest at the same time')
661 msg = _('cannot specify --changelog and --manifest at the same time')
662 elif cl and dir:
662 elif cl and dir:
663 msg = _('cannot specify --changelog and --dir at the same time')
663 msg = _('cannot specify --changelog and --dir at the same time')
664 elif cl or mf or dir:
664 elif cl or mf or dir:
665 if file_:
665 if file_:
666 msg = _('cannot specify filename with --changelog or --manifest')
666 msg = _('cannot specify filename with --changelog or --manifest')
667 elif not repo:
667 elif not repo:
668 msg = _('cannot specify --changelog or --manifest or --dir '
668 msg = _('cannot specify --changelog or --manifest or --dir '
669 'without a repository')
669 'without a repository')
670 if msg:
670 if msg:
671 raise error.Abort(msg)
671 raise error.Abort(msg)
672
672
673 r = None
673 r = None
674 if repo:
674 if repo:
675 if cl:
675 if cl:
676 r = repo.unfiltered().changelog
676 r = repo.unfiltered().changelog
677 elif dir:
677 elif dir:
678 if 'treemanifest' not in repo.requirements:
678 if 'treemanifest' not in repo.requirements:
679 raise error.Abort(_("--dir can only be used on repos with "
679 raise error.Abort(_("--dir can only be used on repos with "
680 "treemanifest enabled"))
680 "treemanifest enabled"))
681 dirlog = repo.manifestlog._revlog.dirlog(dir)
681 dirlog = repo.manifestlog._revlog.dirlog(dir)
682 if len(dirlog):
682 if len(dirlog):
683 r = dirlog
683 r = dirlog
684 elif mf:
684 elif mf:
685 r = repo.manifestlog._revlog
685 r = repo.manifestlog._revlog
686 elif file_:
686 elif file_:
687 filelog = repo.file(file_)
687 filelog = repo.file(file_)
688 if len(filelog):
688 if len(filelog):
689 r = filelog
689 r = filelog
690 if not r:
690 if not r:
691 if not file_:
691 if not file_:
692 raise error.CommandError(cmd, _('invalid arguments'))
692 raise error.CommandError(cmd, _('invalid arguments'))
693 if not os.path.isfile(file_):
693 if not os.path.isfile(file_):
694 raise error.Abort(_("revlog '%s' not found") % file_)
694 raise error.Abort(_("revlog '%s' not found") % file_)
695 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
695 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
696 file_[:-2] + ".i")
696 file_[:-2] + ".i")
697 return r
697 return r
698
698
699 def copy(ui, repo, pats, opts, rename=False):
699 def copy(ui, repo, pats, opts, rename=False):
700 # called with the repo lock held
700 # called with the repo lock held
701 #
701 #
702 # hgsep => pathname that uses "/" to separate directories
702 # hgsep => pathname that uses "/" to separate directories
703 # ossep => pathname that uses os.sep to separate directories
703 # ossep => pathname that uses os.sep to separate directories
704 cwd = repo.getcwd()
704 cwd = repo.getcwd()
705 targets = {}
705 targets = {}
706 after = opts.get("after")
706 after = opts.get("after")
707 dryrun = opts.get("dry_run")
707 dryrun = opts.get("dry_run")
708 wctx = repo[None]
708 wctx = repo[None]
709
709
710 def walkpat(pat):
710 def walkpat(pat):
711 srcs = []
711 srcs = []
712 if after:
712 if after:
713 badstates = '?'
713 badstates = '?'
714 else:
714 else:
715 badstates = '?r'
715 badstates = '?r'
716 m = scmutil.match(wctx, [pat], opts, globbed=True)
716 m = scmutil.match(wctx, [pat], opts, globbed=True)
717 for abs in wctx.walk(m):
717 for abs in wctx.walk(m):
718 state = repo.dirstate[abs]
718 state = repo.dirstate[abs]
719 rel = m.rel(abs)
719 rel = m.rel(abs)
720 exact = m.exact(abs)
720 exact = m.exact(abs)
721 if state in badstates:
721 if state in badstates:
722 if exact and state == '?':
722 if exact and state == '?':
723 ui.warn(_('%s: not copying - file is not managed\n') % rel)
723 ui.warn(_('%s: not copying - file is not managed\n') % rel)
724 if exact and state == 'r':
724 if exact and state == 'r':
725 ui.warn(_('%s: not copying - file has been marked for'
725 ui.warn(_('%s: not copying - file has been marked for'
726 ' remove\n') % rel)
726 ' remove\n') % rel)
727 continue
727 continue
728 # abs: hgsep
728 # abs: hgsep
729 # rel: ossep
729 # rel: ossep
730 srcs.append((abs, rel, exact))
730 srcs.append((abs, rel, exact))
731 return srcs
731 return srcs
732
732
733 # abssrc: hgsep
733 # abssrc: hgsep
734 # relsrc: ossep
734 # relsrc: ossep
735 # otarget: ossep
735 # otarget: ossep
736 def copyfile(abssrc, relsrc, otarget, exact):
736 def copyfile(abssrc, relsrc, otarget, exact):
737 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
737 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
738 if '/' in abstarget:
738 if '/' in abstarget:
739 # We cannot normalize abstarget itself, this would prevent
739 # We cannot normalize abstarget itself, this would prevent
740 # case only renames, like a => A.
740 # case only renames, like a => A.
741 abspath, absname = abstarget.rsplit('/', 1)
741 abspath, absname = abstarget.rsplit('/', 1)
742 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
742 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
743 reltarget = repo.pathto(abstarget, cwd)
743 reltarget = repo.pathto(abstarget, cwd)
744 target = repo.wjoin(abstarget)
744 target = repo.wjoin(abstarget)
745 src = repo.wjoin(abssrc)
745 src = repo.wjoin(abssrc)
746 state = repo.dirstate[abstarget]
746 state = repo.dirstate[abstarget]
747
747
748 scmutil.checkportable(ui, abstarget)
748 scmutil.checkportable(ui, abstarget)
749
749
750 # check for collisions
750 # check for collisions
751 prevsrc = targets.get(abstarget)
751 prevsrc = targets.get(abstarget)
752 if prevsrc is not None:
752 if prevsrc is not None:
753 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
753 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
754 (reltarget, repo.pathto(abssrc, cwd),
754 (reltarget, repo.pathto(abssrc, cwd),
755 repo.pathto(prevsrc, cwd)))
755 repo.pathto(prevsrc, cwd)))
756 return
756 return
757
757
758 # check for overwrites
758 # check for overwrites
759 exists = os.path.lexists(target)
759 exists = os.path.lexists(target)
760 samefile = False
760 samefile = False
761 if exists and abssrc != abstarget:
761 if exists and abssrc != abstarget:
762 if (repo.dirstate.normalize(abssrc) ==
762 if (repo.dirstate.normalize(abssrc) ==
763 repo.dirstate.normalize(abstarget)):
763 repo.dirstate.normalize(abstarget)):
764 if not rename:
764 if not rename:
765 ui.warn(_("%s: can't copy - same file\n") % reltarget)
765 ui.warn(_("%s: can't copy - same file\n") % reltarget)
766 return
766 return
767 exists = False
767 exists = False
768 samefile = True
768 samefile = True
769
769
770 if not after and exists or after and state in 'mn':
770 if not after and exists or after and state in 'mn':
771 if not opts['force']:
771 if not opts['force']:
772 if state in 'mn':
772 if state in 'mn':
773 msg = _('%s: not overwriting - file already committed\n')
773 msg = _('%s: not overwriting - file already committed\n')
774 if after:
774 if after:
775 flags = '--after --force'
775 flags = '--after --force'
776 else:
776 else:
777 flags = '--force'
777 flags = '--force'
778 if rename:
778 if rename:
779 hint = _('(hg rename %s to replace the file by '
779 hint = _('(hg rename %s to replace the file by '
780 'recording a rename)\n') % flags
780 'recording a rename)\n') % flags
781 else:
781 else:
782 hint = _('(hg copy %s to replace the file by '
782 hint = _('(hg copy %s to replace the file by '
783 'recording a copy)\n') % flags
783 'recording a copy)\n') % flags
784 else:
784 else:
785 msg = _('%s: not overwriting - file exists\n')
785 msg = _('%s: not overwriting - file exists\n')
786 if rename:
786 if rename:
787 hint = _('(hg rename --after to record the rename)\n')
787 hint = _('(hg rename --after to record the rename)\n')
788 else:
788 else:
789 hint = _('(hg copy --after to record the copy)\n')
789 hint = _('(hg copy --after to record the copy)\n')
790 ui.warn(msg % reltarget)
790 ui.warn(msg % reltarget)
791 ui.warn(hint)
791 ui.warn(hint)
792 return
792 return
793
793
794 if after:
794 if after:
795 if not exists:
795 if not exists:
796 if rename:
796 if rename:
797 ui.warn(_('%s: not recording move - %s does not exist\n') %
797 ui.warn(_('%s: not recording move - %s does not exist\n') %
798 (relsrc, reltarget))
798 (relsrc, reltarget))
799 else:
799 else:
800 ui.warn(_('%s: not recording copy - %s does not exist\n') %
800 ui.warn(_('%s: not recording copy - %s does not exist\n') %
801 (relsrc, reltarget))
801 (relsrc, reltarget))
802 return
802 return
803 elif not dryrun:
803 elif not dryrun:
804 try:
804 try:
805 if exists:
805 if exists:
806 os.unlink(target)
806 os.unlink(target)
807 targetdir = os.path.dirname(target) or '.'
807 targetdir = os.path.dirname(target) or '.'
808 if not os.path.isdir(targetdir):
808 if not os.path.isdir(targetdir):
809 os.makedirs(targetdir)
809 os.makedirs(targetdir)
810 if samefile:
810 if samefile:
811 tmp = target + "~hgrename"
811 tmp = target + "~hgrename"
812 os.rename(src, tmp)
812 os.rename(src, tmp)
813 os.rename(tmp, target)
813 os.rename(tmp, target)
814 else:
814 else:
815 util.copyfile(src, target)
815 util.copyfile(src, target)
816 srcexists = True
816 srcexists = True
817 except IOError as inst:
817 except IOError as inst:
818 if inst.errno == errno.ENOENT:
818 if inst.errno == errno.ENOENT:
819 ui.warn(_('%s: deleted in working directory\n') % relsrc)
819 ui.warn(_('%s: deleted in working directory\n') % relsrc)
820 srcexists = False
820 srcexists = False
821 else:
821 else:
822 ui.warn(_('%s: cannot copy - %s\n') %
822 ui.warn(_('%s: cannot copy - %s\n') %
823 (relsrc, inst.strerror))
823 (relsrc, inst.strerror))
824 return True # report a failure
824 return True # report a failure
825
825
826 if ui.verbose or not exact:
826 if ui.verbose or not exact:
827 if rename:
827 if rename:
828 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
828 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
829 else:
829 else:
830 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
830 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
831
831
832 targets[abstarget] = abssrc
832 targets[abstarget] = abssrc
833
833
834 # fix up dirstate
834 # fix up dirstate
835 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
835 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
836 dryrun=dryrun, cwd=cwd)
836 dryrun=dryrun, cwd=cwd)
837 if rename and not dryrun:
837 if rename and not dryrun:
838 if not after and srcexists and not samefile:
838 if not after and srcexists and not samefile:
839 repo.wvfs.unlinkpath(abssrc)
839 repo.wvfs.unlinkpath(abssrc)
840 wctx.forget([abssrc])
840 wctx.forget([abssrc])
841
841
842 # pat: ossep
842 # pat: ossep
843 # dest ossep
843 # dest ossep
844 # srcs: list of (hgsep, hgsep, ossep, bool)
844 # srcs: list of (hgsep, hgsep, ossep, bool)
845 # return: function that takes hgsep and returns ossep
845 # return: function that takes hgsep and returns ossep
846 def targetpathfn(pat, dest, srcs):
846 def targetpathfn(pat, dest, srcs):
847 if os.path.isdir(pat):
847 if os.path.isdir(pat):
848 abspfx = pathutil.canonpath(repo.root, cwd, pat)
848 abspfx = pathutil.canonpath(repo.root, cwd, pat)
849 abspfx = util.localpath(abspfx)
849 abspfx = util.localpath(abspfx)
850 if destdirexists:
850 if destdirexists:
851 striplen = len(os.path.split(abspfx)[0])
851 striplen = len(os.path.split(abspfx)[0])
852 else:
852 else:
853 striplen = len(abspfx)
853 striplen = len(abspfx)
854 if striplen:
854 if striplen:
855 striplen += len(pycompat.ossep)
855 striplen += len(pycompat.ossep)
856 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
856 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
857 elif destdirexists:
857 elif destdirexists:
858 res = lambda p: os.path.join(dest,
858 res = lambda p: os.path.join(dest,
859 os.path.basename(util.localpath(p)))
859 os.path.basename(util.localpath(p)))
860 else:
860 else:
861 res = lambda p: dest
861 res = lambda p: dest
862 return res
862 return res
863
863
864 # pat: ossep
864 # pat: ossep
865 # dest ossep
865 # dest ossep
866 # srcs: list of (hgsep, hgsep, ossep, bool)
866 # srcs: list of (hgsep, hgsep, ossep, bool)
867 # return: function that takes hgsep and returns ossep
867 # return: function that takes hgsep and returns ossep
868 def targetpathafterfn(pat, dest, srcs):
868 def targetpathafterfn(pat, dest, srcs):
869 if matchmod.patkind(pat):
869 if matchmod.patkind(pat):
870 # a mercurial pattern
870 # a mercurial pattern
871 res = lambda p: os.path.join(dest,
871 res = lambda p: os.path.join(dest,
872 os.path.basename(util.localpath(p)))
872 os.path.basename(util.localpath(p)))
873 else:
873 else:
874 abspfx = pathutil.canonpath(repo.root, cwd, pat)
874 abspfx = pathutil.canonpath(repo.root, cwd, pat)
875 if len(abspfx) < len(srcs[0][0]):
875 if len(abspfx) < len(srcs[0][0]):
876 # A directory. Either the target path contains the last
876 # A directory. Either the target path contains the last
877 # component of the source path or it does not.
877 # component of the source path or it does not.
878 def evalpath(striplen):
878 def evalpath(striplen):
879 score = 0
879 score = 0
880 for s in srcs:
880 for s in srcs:
881 t = os.path.join(dest, util.localpath(s[0])[striplen:])
881 t = os.path.join(dest, util.localpath(s[0])[striplen:])
882 if os.path.lexists(t):
882 if os.path.lexists(t):
883 score += 1
883 score += 1
884 return score
884 return score
885
885
886 abspfx = util.localpath(abspfx)
886 abspfx = util.localpath(abspfx)
887 striplen = len(abspfx)
887 striplen = len(abspfx)
888 if striplen:
888 if striplen:
889 striplen += len(pycompat.ossep)
889 striplen += len(pycompat.ossep)
890 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
890 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
891 score = evalpath(striplen)
891 score = evalpath(striplen)
892 striplen1 = len(os.path.split(abspfx)[0])
892 striplen1 = len(os.path.split(abspfx)[0])
893 if striplen1:
893 if striplen1:
894 striplen1 += len(pycompat.ossep)
894 striplen1 += len(pycompat.ossep)
895 if evalpath(striplen1) > score:
895 if evalpath(striplen1) > score:
896 striplen = striplen1
896 striplen = striplen1
897 res = lambda p: os.path.join(dest,
897 res = lambda p: os.path.join(dest,
898 util.localpath(p)[striplen:])
898 util.localpath(p)[striplen:])
899 else:
899 else:
900 # a file
900 # a file
901 if destdirexists:
901 if destdirexists:
902 res = lambda p: os.path.join(dest,
902 res = lambda p: os.path.join(dest,
903 os.path.basename(util.localpath(p)))
903 os.path.basename(util.localpath(p)))
904 else:
904 else:
905 res = lambda p: dest
905 res = lambda p: dest
906 return res
906 return res
907
907
908 pats = scmutil.expandpats(pats)
908 pats = scmutil.expandpats(pats)
909 if not pats:
909 if not pats:
910 raise error.Abort(_('no source or destination specified'))
910 raise error.Abort(_('no source or destination specified'))
911 if len(pats) == 1:
911 if len(pats) == 1:
912 raise error.Abort(_('no destination specified'))
912 raise error.Abort(_('no destination specified'))
913 dest = pats.pop()
913 dest = pats.pop()
914 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
914 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
915 if not destdirexists:
915 if not destdirexists:
916 if len(pats) > 1 or matchmod.patkind(pats[0]):
916 if len(pats) > 1 or matchmod.patkind(pats[0]):
917 raise error.Abort(_('with multiple sources, destination must be an '
917 raise error.Abort(_('with multiple sources, destination must be an '
918 'existing directory'))
918 'existing directory'))
919 if util.endswithsep(dest):
919 if util.endswithsep(dest):
920 raise error.Abort(_('destination %s is not a directory') % dest)
920 raise error.Abort(_('destination %s is not a directory') % dest)
921
921
922 tfn = targetpathfn
922 tfn = targetpathfn
923 if after:
923 if after:
924 tfn = targetpathafterfn
924 tfn = targetpathafterfn
925 copylist = []
925 copylist = []
926 for pat in pats:
926 for pat in pats:
927 srcs = walkpat(pat)
927 srcs = walkpat(pat)
928 if not srcs:
928 if not srcs:
929 continue
929 continue
930 copylist.append((tfn(pat, dest, srcs), srcs))
930 copylist.append((tfn(pat, dest, srcs), srcs))
931 if not copylist:
931 if not copylist:
932 raise error.Abort(_('no files to copy'))
932 raise error.Abort(_('no files to copy'))
933
933
934 errors = 0
934 errors = 0
935 for targetpath, srcs in copylist:
935 for targetpath, srcs in copylist:
936 for abssrc, relsrc, exact in srcs:
936 for abssrc, relsrc, exact in srcs:
937 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
937 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
938 errors += 1
938 errors += 1
939
939
940 if errors:
940 if errors:
941 ui.warn(_('(consider using --after)\n'))
941 ui.warn(_('(consider using --after)\n'))
942
942
943 return errors != 0
943 return errors != 0
944
944
945 ## facility to let extension process additional data into an import patch
945 ## facility to let extension process additional data into an import patch
946 # list of identifier to be executed in order
946 # list of identifier to be executed in order
947 extrapreimport = [] # run before commit
947 extrapreimport = [] # run before commit
948 extrapostimport = [] # run after commit
948 extrapostimport = [] # run after commit
949 # mapping from identifier to actual import function
949 # mapping from identifier to actual import function
950 #
950 #
951 # 'preimport' are run before the commit is made and are provided the following
951 # 'preimport' are run before the commit is made and are provided the following
952 # arguments:
952 # arguments:
953 # - repo: the localrepository instance,
953 # - repo: the localrepository instance,
954 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
954 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
955 # - extra: the future extra dictionary of the changeset, please mutate it,
955 # - extra: the future extra dictionary of the changeset, please mutate it,
956 # - opts: the import options.
956 # - opts: the import options.
957 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
957 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
958 # mutation of in memory commit and more. Feel free to rework the code to get
958 # mutation of in memory commit and more. Feel free to rework the code to get
959 # there.
959 # there.
960 extrapreimportmap = {}
960 extrapreimportmap = {}
961 # 'postimport' are run after the commit is made and are provided the following
961 # 'postimport' are run after the commit is made and are provided the following
962 # argument:
962 # argument:
963 # - ctx: the changectx created by import.
963 # - ctx: the changectx created by import.
964 extrapostimportmap = {}
964 extrapostimportmap = {}
965
965
966 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
966 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
967 """Utility function used by commands.import to import a single patch
967 """Utility function used by commands.import to import a single patch
968
968
969 This function is explicitly defined here to help the evolve extension to
969 This function is explicitly defined here to help the evolve extension to
970 wrap this part of the import logic.
970 wrap this part of the import logic.
971
971
972 The API is currently a bit ugly because it a simple code translation from
972 The API is currently a bit ugly because it a simple code translation from
973 the import command. Feel free to make it better.
973 the import command. Feel free to make it better.
974
974
975 :hunk: a patch (as a binary string)
975 :hunk: a patch (as a binary string)
976 :parents: nodes that will be parent of the created commit
976 :parents: nodes that will be parent of the created commit
977 :opts: the full dict of option passed to the import command
977 :opts: the full dict of option passed to the import command
978 :msgs: list to save commit message to.
978 :msgs: list to save commit message to.
979 (used in case we need to save it when failing)
979 (used in case we need to save it when failing)
980 :updatefunc: a function that update a repo to a given node
980 :updatefunc: a function that update a repo to a given node
981 updatefunc(<repo>, <node>)
981 updatefunc(<repo>, <node>)
982 """
982 """
983 # avoid cycle context -> subrepo -> cmdutil
983 # avoid cycle context -> subrepo -> cmdutil
984 from . import context
984 from . import context
985 extractdata = patch.extract(ui, hunk)
985 extractdata = patch.extract(ui, hunk)
986 tmpname = extractdata.get('filename')
986 tmpname = extractdata.get('filename')
987 message = extractdata.get('message')
987 message = extractdata.get('message')
988 user = opts.get('user') or extractdata.get('user')
988 user = opts.get('user') or extractdata.get('user')
989 date = opts.get('date') or extractdata.get('date')
989 date = opts.get('date') or extractdata.get('date')
990 branch = extractdata.get('branch')
990 branch = extractdata.get('branch')
991 nodeid = extractdata.get('nodeid')
991 nodeid = extractdata.get('nodeid')
992 p1 = extractdata.get('p1')
992 p1 = extractdata.get('p1')
993 p2 = extractdata.get('p2')
993 p2 = extractdata.get('p2')
994
994
995 nocommit = opts.get('no_commit')
995 nocommit = opts.get('no_commit')
996 importbranch = opts.get('import_branch')
996 importbranch = opts.get('import_branch')
997 update = not opts.get('bypass')
997 update = not opts.get('bypass')
998 strip = opts["strip"]
998 strip = opts["strip"]
999 prefix = opts["prefix"]
999 prefix = opts["prefix"]
1000 sim = float(opts.get('similarity') or 0)
1000 sim = float(opts.get('similarity') or 0)
1001 if not tmpname:
1001 if not tmpname:
1002 return (None, None, False)
1002 return (None, None, False)
1003
1003
1004 rejects = False
1004 rejects = False
1005
1005
1006 try:
1006 try:
1007 cmdline_message = logmessage(ui, opts)
1007 cmdline_message = logmessage(ui, opts)
1008 if cmdline_message:
1008 if cmdline_message:
1009 # pickup the cmdline msg
1009 # pickup the cmdline msg
1010 message = cmdline_message
1010 message = cmdline_message
1011 elif message:
1011 elif message:
1012 # pickup the patch msg
1012 # pickup the patch msg
1013 message = message.strip()
1013 message = message.strip()
1014 else:
1014 else:
1015 # launch the editor
1015 # launch the editor
1016 message = None
1016 message = None
1017 ui.debug('message:\n%s\n' % message)
1017 ui.debug('message:\n%s\n' % message)
1018
1018
1019 if len(parents) == 1:
1019 if len(parents) == 1:
1020 parents.append(repo[nullid])
1020 parents.append(repo[nullid])
1021 if opts.get('exact'):
1021 if opts.get('exact'):
1022 if not nodeid or not p1:
1022 if not nodeid or not p1:
1023 raise error.Abort(_('not a Mercurial patch'))
1023 raise error.Abort(_('not a Mercurial patch'))
1024 p1 = repo[p1]
1024 p1 = repo[p1]
1025 p2 = repo[p2 or nullid]
1025 p2 = repo[p2 or nullid]
1026 elif p2:
1026 elif p2:
1027 try:
1027 try:
1028 p1 = repo[p1]
1028 p1 = repo[p1]
1029 p2 = repo[p2]
1029 p2 = repo[p2]
1030 # Without any options, consider p2 only if the
1030 # Without any options, consider p2 only if the
1031 # patch is being applied on top of the recorded
1031 # patch is being applied on top of the recorded
1032 # first parent.
1032 # first parent.
1033 if p1 != parents[0]:
1033 if p1 != parents[0]:
1034 p1 = parents[0]
1034 p1 = parents[0]
1035 p2 = repo[nullid]
1035 p2 = repo[nullid]
1036 except error.RepoError:
1036 except error.RepoError:
1037 p1, p2 = parents
1037 p1, p2 = parents
1038 if p2.node() == nullid:
1038 if p2.node() == nullid:
1039 ui.warn(_("warning: import the patch as a normal revision\n"
1039 ui.warn(_("warning: import the patch as a normal revision\n"
1040 "(use --exact to import the patch as a merge)\n"))
1040 "(use --exact to import the patch as a merge)\n"))
1041 else:
1041 else:
1042 p1, p2 = parents
1042 p1, p2 = parents
1043
1043
1044 n = None
1044 n = None
1045 if update:
1045 if update:
1046 if p1 != parents[0]:
1046 if p1 != parents[0]:
1047 updatefunc(repo, p1.node())
1047 updatefunc(repo, p1.node())
1048 if p2 != parents[1]:
1048 if p2 != parents[1]:
1049 repo.setparents(p1.node(), p2.node())
1049 repo.setparents(p1.node(), p2.node())
1050
1050
1051 if opts.get('exact') or importbranch:
1051 if opts.get('exact') or importbranch:
1052 repo.dirstate.setbranch(branch or 'default')
1052 repo.dirstate.setbranch(branch or 'default')
1053
1053
1054 partial = opts.get('partial', False)
1054 partial = opts.get('partial', False)
1055 files = set()
1055 files = set()
1056 try:
1056 try:
1057 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1057 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1058 files=files, eolmode=None, similarity=sim / 100.0)
1058 files=files, eolmode=None, similarity=sim / 100.0)
1059 except patch.PatchError as e:
1059 except patch.PatchError as e:
1060 if not partial:
1060 if not partial:
1061 raise error.Abort(str(e))
1061 raise error.Abort(str(e))
1062 if partial:
1062 if partial:
1063 rejects = True
1063 rejects = True
1064
1064
1065 files = list(files)
1065 files = list(files)
1066 if nocommit:
1066 if nocommit:
1067 if message:
1067 if message:
1068 msgs.append(message)
1068 msgs.append(message)
1069 else:
1069 else:
1070 if opts.get('exact') or p2:
1070 if opts.get('exact') or p2:
1071 # If you got here, you either use --force and know what
1071 # If you got here, you either use --force and know what
1072 # you are doing or used --exact or a merge patch while
1072 # you are doing or used --exact or a merge patch while
1073 # being updated to its first parent.
1073 # being updated to its first parent.
1074 m = None
1074 m = None
1075 else:
1075 else:
1076 m = scmutil.matchfiles(repo, files or [])
1076 m = scmutil.matchfiles(repo, files or [])
1077 editform = mergeeditform(repo[None], 'import.normal')
1077 editform = mergeeditform(repo[None], 'import.normal')
1078 if opts.get('exact'):
1078 if opts.get('exact'):
1079 editor = None
1079 editor = None
1080 else:
1080 else:
1081 editor = getcommiteditor(editform=editform, **opts)
1081 editor = getcommiteditor(editform=editform, **opts)
1082 extra = {}
1082 extra = {}
1083 for idfunc in extrapreimport:
1083 for idfunc in extrapreimport:
1084 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1084 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1085 overrides = {}
1085 overrides = {}
1086 if partial:
1086 if partial:
1087 overrides[('ui', 'allowemptycommit')] = True
1087 overrides[('ui', 'allowemptycommit')] = True
1088 with repo.ui.configoverride(overrides, 'import'):
1088 with repo.ui.configoverride(overrides, 'import'):
1089 n = repo.commit(message, user,
1089 n = repo.commit(message, user,
1090 date, match=m,
1090 date, match=m,
1091 editor=editor, extra=extra)
1091 editor=editor, extra=extra)
1092 for idfunc in extrapostimport:
1092 for idfunc in extrapostimport:
1093 extrapostimportmap[idfunc](repo[n])
1093 extrapostimportmap[idfunc](repo[n])
1094 else:
1094 else:
1095 if opts.get('exact') or importbranch:
1095 if opts.get('exact') or importbranch:
1096 branch = branch or 'default'
1096 branch = branch or 'default'
1097 else:
1097 else:
1098 branch = p1.branch()
1098 branch = p1.branch()
1099 store = patch.filestore()
1099 store = patch.filestore()
1100 try:
1100 try:
1101 files = set()
1101 files = set()
1102 try:
1102 try:
1103 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1103 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1104 files, eolmode=None)
1104 files, eolmode=None)
1105 except patch.PatchError as e:
1105 except patch.PatchError as e:
1106 raise error.Abort(str(e))
1106 raise error.Abort(str(e))
1107 if opts.get('exact'):
1107 if opts.get('exact'):
1108 editor = None
1108 editor = None
1109 else:
1109 else:
1110 editor = getcommiteditor(editform='import.bypass')
1110 editor = getcommiteditor(editform='import.bypass')
1111 memctx = context.memctx(repo, (p1.node(), p2.node()),
1111 memctx = context.memctx(repo, (p1.node(), p2.node()),
1112 message,
1112 message,
1113 files=files,
1113 files=files,
1114 filectxfn=store,
1114 filectxfn=store,
1115 user=user,
1115 user=user,
1116 date=date,
1116 date=date,
1117 branch=branch,
1117 branch=branch,
1118 editor=editor)
1118 editor=editor)
1119 n = memctx.commit()
1119 n = memctx.commit()
1120 finally:
1120 finally:
1121 store.close()
1121 store.close()
1122 if opts.get('exact') and nocommit:
1122 if opts.get('exact') and nocommit:
1123 # --exact with --no-commit is still useful in that it does merge
1123 # --exact with --no-commit is still useful in that it does merge
1124 # and branch bits
1124 # and branch bits
1125 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1125 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1126 elif opts.get('exact') and hex(n) != nodeid:
1126 elif opts.get('exact') and hex(n) != nodeid:
1127 raise error.Abort(_('patch is damaged or loses information'))
1127 raise error.Abort(_('patch is damaged or loses information'))
1128 msg = _('applied to working directory')
1128 msg = _('applied to working directory')
1129 if n:
1129 if n:
1130 # i18n: refers to a short changeset id
1130 # i18n: refers to a short changeset id
1131 msg = _('created %s') % short(n)
1131 msg = _('created %s') % short(n)
1132 return (msg, n, rejects)
1132 return (msg, n, rejects)
1133 finally:
1133 finally:
1134 os.unlink(tmpname)
1134 os.unlink(tmpname)
1135
1135
1136 # facility to let extensions include additional data in an exported patch
1136 # facility to let extensions include additional data in an exported patch
1137 # list of identifiers to be executed in order
1137 # list of identifiers to be executed in order
1138 extraexport = []
1138 extraexport = []
1139 # mapping from identifier to actual export function
1139 # mapping from identifier to actual export function
1140 # function as to return a string to be added to the header or None
1140 # function as to return a string to be added to the header or None
1141 # it is given two arguments (sequencenumber, changectx)
1141 # it is given two arguments (sequencenumber, changectx)
1142 extraexportmap = {}
1142 extraexportmap = {}
1143
1143
1144 def _exportsingle(repo, ctx, match, switch_parent, rev, seqno, write, diffopts):
1144 def _exportsingle(repo, ctx, match, switch_parent, rev, seqno, write, diffopts):
1145 node = scmutil.binnode(ctx)
1145 node = scmutil.binnode(ctx)
1146 parents = [p.node() for p in ctx.parents() if p]
1146 parents = [p.node() for p in ctx.parents() if p]
1147 branch = ctx.branch()
1147 branch = ctx.branch()
1148 if switch_parent:
1148 if switch_parent:
1149 parents.reverse()
1149 parents.reverse()
1150
1150
1151 if parents:
1151 if parents:
1152 prev = parents[0]
1152 prev = parents[0]
1153 else:
1153 else:
1154 prev = nullid
1154 prev = nullid
1155
1155
1156 write("# HG changeset patch\n")
1156 write("# HG changeset patch\n")
1157 write("# User %s\n" % ctx.user())
1157 write("# User %s\n" % ctx.user())
1158 write("# Date %d %d\n" % ctx.date())
1158 write("# Date %d %d\n" % ctx.date())
1159 write("# %s\n" % util.datestr(ctx.date()))
1159 write("# %s\n" % util.datestr(ctx.date()))
1160 if branch and branch != 'default':
1160 if branch and branch != 'default':
1161 write("# Branch %s\n" % branch)
1161 write("# Branch %s\n" % branch)
1162 write("# Node ID %s\n" % hex(node))
1162 write("# Node ID %s\n" % hex(node))
1163 write("# Parent %s\n" % hex(prev))
1163 write("# Parent %s\n" % hex(prev))
1164 if len(parents) > 1:
1164 if len(parents) > 1:
1165 write("# Parent %s\n" % hex(parents[1]))
1165 write("# Parent %s\n" % hex(parents[1]))
1166
1166
1167 for headerid in extraexport:
1167 for headerid in extraexport:
1168 header = extraexportmap[headerid](seqno, ctx)
1168 header = extraexportmap[headerid](seqno, ctx)
1169 if header is not None:
1169 if header is not None:
1170 write('# %s\n' % header)
1170 write('# %s\n' % header)
1171 write(ctx.description().rstrip())
1171 write(ctx.description().rstrip())
1172 write("\n\n")
1172 write("\n\n")
1173
1173
1174 for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
1174 for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
1175 write(chunk, label=label)
1175 write(chunk, label=label)
1176
1176
1177 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
1177 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
1178 opts=None, match=None):
1178 opts=None, match=None):
1179 '''export changesets as hg patches
1179 '''export changesets as hg patches
1180
1180
1181 Args:
1181 Args:
1182 repo: The repository from which we're exporting revisions.
1182 repo: The repository from which we're exporting revisions.
1183 revs: A list of revisions to export as revision numbers.
1183 revs: A list of revisions to export as revision numbers.
1184 fntemplate: An optional string to use for generating patch file names.
1184 fntemplate: An optional string to use for generating patch file names.
1185 fp: An optional file-like object to which patches should be written.
1185 fp: An optional file-like object to which patches should be written.
1186 switch_parent: If True, show diffs against second parent when not nullid.
1186 switch_parent: If True, show diffs against second parent when not nullid.
1187 Default is false, which always shows diff against p1.
1187 Default is false, which always shows diff against p1.
1188 opts: diff options to use for generating the patch.
1188 opts: diff options to use for generating the patch.
1189 match: If specified, only export changes to files matching this matcher.
1189 match: If specified, only export changes to files matching this matcher.
1190
1190
1191 Returns:
1191 Returns:
1192 Nothing.
1192 Nothing.
1193
1193
1194 Side Effect:
1194 Side Effect:
1195 "HG Changeset Patch" data is emitted to one of the following
1195 "HG Changeset Patch" data is emitted to one of the following
1196 destinations:
1196 destinations:
1197 fp is specified: All revs are written to the specified
1197 fp is specified: All revs are written to the specified
1198 file-like object.
1198 file-like object.
1199 fntemplate specified: Each rev is written to a unique file named using
1199 fntemplate specified: Each rev is written to a unique file named using
1200 the given template.
1200 the given template.
1201 Neither fp nor template specified: All revs written to repo.ui.write()
1201 Neither fp nor template specified: All revs written to repo.ui.write()
1202 '''
1202 '''
1203
1203
1204 total = len(revs)
1204 total = len(revs)
1205 revwidth = max(len(str(rev)) for rev in revs)
1205 revwidth = max(len(str(rev)) for rev in revs)
1206 filemode = {}
1206 filemode = {}
1207
1207
1208 write = None
1208 write = None
1209 dest = '<unnamed>'
1209 dest = '<unnamed>'
1210 if fp:
1210 if fp:
1211 dest = getattr(fp, 'name', dest)
1211 dest = getattr(fp, 'name', dest)
1212 def write(s, **kw):
1212 def write(s, **kw):
1213 fp.write(s)
1213 fp.write(s)
1214 elif not fntemplate:
1214 elif not fntemplate:
1215 write = repo.ui.write
1215 write = repo.ui.write
1216
1216
1217 for seqno, rev in enumerate(revs, 1):
1217 for seqno, rev in enumerate(revs, 1):
1218 ctx = repo[rev]
1218 ctx = repo[rev]
1219 fo = None
1219 fo = None
1220 if not fp and fntemplate:
1220 if not fp and fntemplate:
1221 desc_lines = ctx.description().rstrip().split('\n')
1221 desc_lines = ctx.description().rstrip().split('\n')
1222 desc = desc_lines[0] #Commit always has a first line.
1222 desc = desc_lines[0] #Commit always has a first line.
1223 fo = makefileobj(repo, fntemplate, ctx.node(), desc=desc,
1223 fo = makefileobj(repo, fntemplate, ctx.node(), desc=desc,
1224 total=total, seqno=seqno, revwidth=revwidth,
1224 total=total, seqno=seqno, revwidth=revwidth,
1225 mode='wb', modemap=filemode)
1225 mode='wb', modemap=filemode)
1226 dest = fo.name
1226 dest = fo.name
1227 def write(s, **kw):
1227 def write(s, **kw):
1228 fo.write(s)
1228 fo.write(s)
1229 if not dest.startswith('<'):
1229 if not dest.startswith('<'):
1230 repo.ui.note("%s\n" % dest)
1230 repo.ui.note("%s\n" % dest)
1231 _exportsingle(
1231 _exportsingle(
1232 repo, ctx, match, switch_parent, rev, seqno, write, opts)
1232 repo, ctx, match, switch_parent, rev, seqno, write, opts)
1233 if fo is not None:
1233 if fo is not None:
1234 fo.close()
1234 fo.close()
1235
1235
1236 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1236 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1237 changes=None, stat=False, fp=None, prefix='',
1237 changes=None, stat=False, fp=None, prefix='',
1238 root='', listsubrepos=False):
1238 root='', listsubrepos=False):
1239 '''show diff or diffstat.'''
1239 '''show diff or diffstat.'''
1240 if fp is None:
1240 if fp is None:
1241 write = ui.write
1241 write = ui.write
1242 else:
1242 else:
1243 def write(s, **kw):
1243 def write(s, **kw):
1244 fp.write(s)
1244 fp.write(s)
1245
1245
1246 if root:
1246 if root:
1247 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1247 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1248 else:
1248 else:
1249 relroot = ''
1249 relroot = ''
1250 if relroot != '':
1250 if relroot != '':
1251 # XXX relative roots currently don't work if the root is within a
1251 # XXX relative roots currently don't work if the root is within a
1252 # subrepo
1252 # subrepo
1253 uirelroot = match.uipath(relroot)
1253 uirelroot = match.uipath(relroot)
1254 relroot += '/'
1254 relroot += '/'
1255 for matchroot in match.files():
1255 for matchroot in match.files():
1256 if not matchroot.startswith(relroot):
1256 if not matchroot.startswith(relroot):
1257 ui.warn(_('warning: %s not inside relative root %s\n') % (
1257 ui.warn(_('warning: %s not inside relative root %s\n') % (
1258 match.uipath(matchroot), uirelroot))
1258 match.uipath(matchroot), uirelroot))
1259
1259
1260 if stat:
1260 if stat:
1261 diffopts = diffopts.copy(context=0)
1261 diffopts = diffopts.copy(context=0)
1262 width = 80
1262 width = 80
1263 if not ui.plain():
1263 if not ui.plain():
1264 width = ui.termwidth()
1264 width = ui.termwidth()
1265 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1265 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1266 prefix=prefix, relroot=relroot)
1266 prefix=prefix, relroot=relroot)
1267 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1267 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1268 width=width):
1268 width=width):
1269 write(chunk, label=label)
1269 write(chunk, label=label)
1270 else:
1270 else:
1271 for chunk, label in patch.diffui(repo, node1, node2, match,
1271 for chunk, label in patch.diffui(repo, node1, node2, match,
1272 changes, diffopts, prefix=prefix,
1272 changes, diffopts, prefix=prefix,
1273 relroot=relroot):
1273 relroot=relroot):
1274 write(chunk, label=label)
1274 write(chunk, label=label)
1275
1275
1276 if listsubrepos:
1276 if listsubrepos:
1277 ctx1 = repo[node1]
1277 ctx1 = repo[node1]
1278 ctx2 = repo[node2]
1278 ctx2 = repo[node2]
1279 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1279 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1280 tempnode2 = node2
1280 tempnode2 = node2
1281 try:
1281 try:
1282 if node2 is not None:
1282 if node2 is not None:
1283 tempnode2 = ctx2.substate[subpath][1]
1283 tempnode2 = ctx2.substate[subpath][1]
1284 except KeyError:
1284 except KeyError:
1285 # A subrepo that existed in node1 was deleted between node1 and
1285 # A subrepo that existed in node1 was deleted between node1 and
1286 # node2 (inclusive). Thus, ctx2's substate won't contain that
1286 # node2 (inclusive). Thus, ctx2's substate won't contain that
1287 # subpath. The best we can do is to ignore it.
1287 # subpath. The best we can do is to ignore it.
1288 tempnode2 = None
1288 tempnode2 = None
1289 submatch = matchmod.subdirmatcher(subpath, match)
1289 submatch = matchmod.subdirmatcher(subpath, match)
1290 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1290 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1291 stat=stat, fp=fp, prefix=prefix)
1291 stat=stat, fp=fp, prefix=prefix)
1292
1292
1293 def _changesetlabels(ctx):
1293 def _changesetlabels(ctx):
1294 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1294 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1295 if ctx.obsolete():
1295 if ctx.obsolete():
1296 labels.append('changeset.obsolete')
1296 labels.append('changeset.obsolete')
1297 if ctx.troubled():
1297 if ctx.troubled():
1298 labels.append('changeset.troubled')
1298 labels.append('changeset.troubled')
1299 for trouble in ctx.troubles():
1299 for trouble in ctx.troubles():
1300 labels.append('trouble.%s' % trouble)
1300 labels.append('trouble.%s' % trouble)
1301 return ' '.join(labels)
1301 return ' '.join(labels)
1302
1302
1303 class changeset_printer(object):
1303 class changeset_printer(object):
1304 '''show changeset information when templating not requested.'''
1304 '''show changeset information when templating not requested.'''
1305
1305
1306 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1306 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1307 self.ui = ui
1307 self.ui = ui
1308 self.repo = repo
1308 self.repo = repo
1309 self.buffered = buffered
1309 self.buffered = buffered
1310 self.matchfn = matchfn
1310 self.matchfn = matchfn
1311 self.diffopts = diffopts
1311 self.diffopts = diffopts
1312 self.header = {}
1312 self.header = {}
1313 self.hunk = {}
1313 self.hunk = {}
1314 self.lastheader = None
1314 self.lastheader = None
1315 self.footer = None
1315 self.footer = None
1316
1316
1317 def flush(self, ctx):
1317 def flush(self, ctx):
1318 rev = ctx.rev()
1318 rev = ctx.rev()
1319 if rev in self.header:
1319 if rev in self.header:
1320 h = self.header[rev]
1320 h = self.header[rev]
1321 if h != self.lastheader:
1321 if h != self.lastheader:
1322 self.lastheader = h
1322 self.lastheader = h
1323 self.ui.write(h)
1323 self.ui.write(h)
1324 del self.header[rev]
1324 del self.header[rev]
1325 if rev in self.hunk:
1325 if rev in self.hunk:
1326 self.ui.write(self.hunk[rev])
1326 self.ui.write(self.hunk[rev])
1327 del self.hunk[rev]
1327 del self.hunk[rev]
1328 return 1
1328 return 1
1329 return 0
1329 return 0
1330
1330
1331 def close(self):
1331 def close(self):
1332 if self.footer:
1332 if self.footer:
1333 self.ui.write(self.footer)
1333 self.ui.write(self.footer)
1334
1334
1335 def show(self, ctx, copies=None, matchfn=None, **props):
1335 def show(self, ctx, copies=None, matchfn=None, **props):
1336 if self.buffered:
1336 if self.buffered:
1337 self.ui.pushbuffer(labeled=True)
1337 self.ui.pushbuffer(labeled=True)
1338 self._show(ctx, copies, matchfn, props)
1338 self._show(ctx, copies, matchfn, props)
1339 self.hunk[ctx.rev()] = self.ui.popbuffer()
1339 self.hunk[ctx.rev()] = self.ui.popbuffer()
1340 else:
1340 else:
1341 self._show(ctx, copies, matchfn, props)
1341 self._show(ctx, copies, matchfn, props)
1342
1342
1343 def _show(self, ctx, copies, matchfn, props):
1343 def _show(self, ctx, copies, matchfn, props):
1344 '''show a single changeset or file revision'''
1344 '''show a single changeset or file revision'''
1345 changenode = ctx.node()
1345 changenode = ctx.node()
1346 rev = ctx.rev()
1346 rev = ctx.rev()
1347 if self.ui.debugflag:
1347 if self.ui.debugflag:
1348 hexfunc = hex
1348 hexfunc = hex
1349 else:
1349 else:
1350 hexfunc = short
1350 hexfunc = short
1351 # as of now, wctx.node() and wctx.rev() return None, but we want to
1351 # as of now, wctx.node() and wctx.rev() return None, but we want to
1352 # show the same values as {node} and {rev} templatekw
1352 # show the same values as {node} and {rev} templatekw
1353 revnode = (scmutil.intrev(ctx), hexfunc(scmutil.binnode(ctx)))
1353 revnode = (scmutil.intrev(ctx), hexfunc(scmutil.binnode(ctx)))
1354
1354
1355 if self.ui.quiet:
1355 if self.ui.quiet:
1356 self.ui.write("%d:%s\n" % revnode, label='log.node')
1356 self.ui.write("%d:%s\n" % revnode, label='log.node')
1357 return
1357 return
1358
1358
1359 date = util.datestr(ctx.date())
1359 date = util.datestr(ctx.date())
1360
1360
1361 # i18n: column positioning for "hg log"
1361 # i18n: column positioning for "hg log"
1362 self.ui.write(_("changeset: %d:%s\n") % revnode,
1362 self.ui.write(_("changeset: %d:%s\n") % revnode,
1363 label=_changesetlabels(ctx))
1363 label=_changesetlabels(ctx))
1364
1364
1365 # branches are shown first before any other names due to backwards
1365 # branches are shown first before any other names due to backwards
1366 # compatibility
1366 # compatibility
1367 branch = ctx.branch()
1367 branch = ctx.branch()
1368 # don't show the default branch name
1368 # don't show the default branch name
1369 if branch != 'default':
1369 if branch != 'default':
1370 # i18n: column positioning for "hg log"
1370 # i18n: column positioning for "hg log"
1371 self.ui.write(_("branch: %s\n") % branch,
1371 self.ui.write(_("branch: %s\n") % branch,
1372 label='log.branch')
1372 label='log.branch')
1373
1373
1374 for nsname, ns in self.repo.names.iteritems():
1374 for nsname, ns in self.repo.names.iteritems():
1375 # branches has special logic already handled above, so here we just
1375 # branches has special logic already handled above, so here we just
1376 # skip it
1376 # skip it
1377 if nsname == 'branches':
1377 if nsname == 'branches':
1378 continue
1378 continue
1379 # we will use the templatename as the color name since those two
1379 # we will use the templatename as the color name since those two
1380 # should be the same
1380 # should be the same
1381 for name in ns.names(self.repo, changenode):
1381 for name in ns.names(self.repo, changenode):
1382 self.ui.write(ns.logfmt % name,
1382 self.ui.write(ns.logfmt % name,
1383 label='log.%s' % ns.colorname)
1383 label='log.%s' % ns.colorname)
1384 if self.ui.debugflag:
1384 if self.ui.debugflag:
1385 # i18n: column positioning for "hg log"
1385 # i18n: column positioning for "hg log"
1386 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1386 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1387 label='log.phase')
1387 label='log.phase')
1388 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1388 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1389 label = 'log.parent changeset.%s' % pctx.phasestr()
1389 label = 'log.parent changeset.%s' % pctx.phasestr()
1390 # i18n: column positioning for "hg log"
1390 # i18n: column positioning for "hg log"
1391 self.ui.write(_("parent: %d:%s\n")
1391 self.ui.write(_("parent: %d:%s\n")
1392 % (pctx.rev(), hexfunc(pctx.node())),
1392 % (pctx.rev(), hexfunc(pctx.node())),
1393 label=label)
1393 label=label)
1394
1394
1395 if self.ui.debugflag and rev is not None:
1395 if self.ui.debugflag and rev is not None:
1396 mnode = ctx.manifestnode()
1396 mnode = ctx.manifestnode()
1397 # i18n: column positioning for "hg log"
1397 # i18n: column positioning for "hg log"
1398 self.ui.write(_("manifest: %d:%s\n") %
1398 self.ui.write(_("manifest: %d:%s\n") %
1399 (self.repo.manifestlog._revlog.rev(mnode),
1399 (self.repo.manifestlog._revlog.rev(mnode),
1400 hex(mnode)),
1400 hex(mnode)),
1401 label='ui.debug log.manifest')
1401 label='ui.debug log.manifest')
1402 # i18n: column positioning for "hg log"
1402 # i18n: column positioning for "hg log"
1403 self.ui.write(_("user: %s\n") % ctx.user(),
1403 self.ui.write(_("user: %s\n") % ctx.user(),
1404 label='log.user')
1404 label='log.user')
1405 # i18n: column positioning for "hg log"
1405 # i18n: column positioning for "hg log"
1406 self.ui.write(_("date: %s\n") % date,
1406 self.ui.write(_("date: %s\n") % date,
1407 label='log.date')
1407 label='log.date')
1408
1408
1409 if ctx.troubled():
1409 if ctx.troubled():
1410 # i18n: column positioning for "hg log"
1410 # i18n: column positioning for "hg log"
1411 self.ui.write(_("trouble: %s\n") % ', '.join(ctx.troubles()),
1411 self.ui.write(_("trouble: %s\n") % ', '.join(ctx.troubles()),
1412 label='log.trouble')
1412 label='log.trouble')
1413
1413
1414 if self.ui.debugflag:
1414 if self.ui.debugflag:
1415 files = ctx.p1().status(ctx)[:3]
1415 files = ctx.p1().status(ctx)[:3]
1416 for key, value in zip([# i18n: column positioning for "hg log"
1416 for key, value in zip([# i18n: column positioning for "hg log"
1417 _("files:"),
1417 _("files:"),
1418 # i18n: column positioning for "hg log"
1418 # i18n: column positioning for "hg log"
1419 _("files+:"),
1419 _("files+:"),
1420 # i18n: column positioning for "hg log"
1420 # i18n: column positioning for "hg log"
1421 _("files-:")], files):
1421 _("files-:")], files):
1422 if value:
1422 if value:
1423 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1423 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1424 label='ui.debug log.files')
1424 label='ui.debug log.files')
1425 elif ctx.files() and self.ui.verbose:
1425 elif ctx.files() and self.ui.verbose:
1426 # i18n: column positioning for "hg log"
1426 # i18n: column positioning for "hg log"
1427 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1427 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1428 label='ui.note log.files')
1428 label='ui.note log.files')
1429 if copies and self.ui.verbose:
1429 if copies and self.ui.verbose:
1430 copies = ['%s (%s)' % c for c in copies]
1430 copies = ['%s (%s)' % c for c in copies]
1431 # i18n: column positioning for "hg log"
1431 # i18n: column positioning for "hg log"
1432 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1432 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1433 label='ui.note log.copies')
1433 label='ui.note log.copies')
1434
1434
1435 extra = ctx.extra()
1435 extra = ctx.extra()
1436 if extra and self.ui.debugflag:
1436 if extra and self.ui.debugflag:
1437 for key, value in sorted(extra.items()):
1437 for key, value in sorted(extra.items()):
1438 # i18n: column positioning for "hg log"
1438 # i18n: column positioning for "hg log"
1439 self.ui.write(_("extra: %s=%s\n")
1439 self.ui.write(_("extra: %s=%s\n")
1440 % (key, util.escapestr(value)),
1440 % (key, util.escapestr(value)),
1441 label='ui.debug log.extra')
1441 label='ui.debug log.extra')
1442
1442
1443 description = ctx.description().strip()
1443 description = ctx.description().strip()
1444 if description:
1444 if description:
1445 if self.ui.verbose:
1445 if self.ui.verbose:
1446 self.ui.write(_("description:\n"),
1446 self.ui.write(_("description:\n"),
1447 label='ui.note log.description')
1447 label='ui.note log.description')
1448 self.ui.write(description,
1448 self.ui.write(description,
1449 label='ui.note log.description')
1449 label='ui.note log.description')
1450 self.ui.write("\n\n")
1450 self.ui.write("\n\n")
1451 else:
1451 else:
1452 # i18n: column positioning for "hg log"
1452 # i18n: column positioning for "hg log"
1453 self.ui.write(_("summary: %s\n") %
1453 self.ui.write(_("summary: %s\n") %
1454 description.splitlines()[0],
1454 description.splitlines()[0],
1455 label='log.summary')
1455 label='log.summary')
1456 self.ui.write("\n")
1456 self.ui.write("\n")
1457
1457
1458 self.showpatch(ctx, matchfn)
1458 self.showpatch(ctx, matchfn)
1459
1459
1460 def showpatch(self, ctx, matchfn):
1460 def showpatch(self, ctx, matchfn):
1461 if not matchfn:
1461 if not matchfn:
1462 matchfn = self.matchfn
1462 matchfn = self.matchfn
1463 if matchfn:
1463 if matchfn:
1464 stat = self.diffopts.get('stat')
1464 stat = self.diffopts.get('stat')
1465 diff = self.diffopts.get('patch')
1465 diff = self.diffopts.get('patch')
1466 diffopts = patch.diffallopts(self.ui, self.diffopts)
1466 diffopts = patch.diffallopts(self.ui, self.diffopts)
1467 node = ctx.node()
1467 node = ctx.node()
1468 prev = ctx.p1().node()
1468 prev = ctx.p1().node()
1469 if stat:
1469 if stat:
1470 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1470 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1471 match=matchfn, stat=True)
1471 match=matchfn, stat=True)
1472 if diff:
1472 if diff:
1473 if stat:
1473 if stat:
1474 self.ui.write("\n")
1474 self.ui.write("\n")
1475 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1475 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1476 match=matchfn, stat=False)
1476 match=matchfn, stat=False)
1477 self.ui.write("\n")
1477 self.ui.write("\n")
1478
1478
1479 class jsonchangeset(changeset_printer):
1479 class jsonchangeset(changeset_printer):
1480 '''format changeset information.'''
1480 '''format changeset information.'''
1481
1481
1482 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1482 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1483 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1483 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1484 self.cache = {}
1484 self.cache = {}
1485 self._first = True
1485 self._first = True
1486
1486
1487 def close(self):
1487 def close(self):
1488 if not self._first:
1488 if not self._first:
1489 self.ui.write("\n]\n")
1489 self.ui.write("\n]\n")
1490 else:
1490 else:
1491 self.ui.write("[]\n")
1491 self.ui.write("[]\n")
1492
1492
1493 def _show(self, ctx, copies, matchfn, props):
1493 def _show(self, ctx, copies, matchfn, props):
1494 '''show a single changeset or file revision'''
1494 '''show a single changeset or file revision'''
1495 rev = ctx.rev()
1495 rev = ctx.rev()
1496 if rev is None:
1496 if rev is None:
1497 jrev = jnode = 'null'
1497 jrev = jnode = 'null'
1498 else:
1498 else:
1499 jrev = '%d' % rev
1499 jrev = '%d' % rev
1500 jnode = '"%s"' % hex(ctx.node())
1500 jnode = '"%s"' % hex(ctx.node())
1501 j = encoding.jsonescape
1501 j = encoding.jsonescape
1502
1502
1503 if self._first:
1503 if self._first:
1504 self.ui.write("[\n {")
1504 self.ui.write("[\n {")
1505 self._first = False
1505 self._first = False
1506 else:
1506 else:
1507 self.ui.write(",\n {")
1507 self.ui.write(",\n {")
1508
1508
1509 if self.ui.quiet:
1509 if self.ui.quiet:
1510 self.ui.write(('\n "rev": %s') % jrev)
1510 self.ui.write(('\n "rev": %s') % jrev)
1511 self.ui.write((',\n "node": %s') % jnode)
1511 self.ui.write((',\n "node": %s') % jnode)
1512 self.ui.write('\n }')
1512 self.ui.write('\n }')
1513 return
1513 return
1514
1514
1515 self.ui.write(('\n "rev": %s') % jrev)
1515 self.ui.write(('\n "rev": %s') % jrev)
1516 self.ui.write((',\n "node": %s') % jnode)
1516 self.ui.write((',\n "node": %s') % jnode)
1517 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1517 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1518 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1518 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1519 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1519 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1520 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1520 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1521 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1521 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1522
1522
1523 self.ui.write((',\n "bookmarks": [%s]') %
1523 self.ui.write((',\n "bookmarks": [%s]') %
1524 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1524 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1525 self.ui.write((',\n "tags": [%s]') %
1525 self.ui.write((',\n "tags": [%s]') %
1526 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1526 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1527 self.ui.write((',\n "parents": [%s]') %
1527 self.ui.write((',\n "parents": [%s]') %
1528 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1528 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1529
1529
1530 if self.ui.debugflag:
1530 if self.ui.debugflag:
1531 if rev is None:
1531 if rev is None:
1532 jmanifestnode = 'null'
1532 jmanifestnode = 'null'
1533 else:
1533 else:
1534 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1534 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1535 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1535 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1536
1536
1537 self.ui.write((',\n "extra": {%s}') %
1537 self.ui.write((',\n "extra": {%s}') %
1538 ", ".join('"%s": "%s"' % (j(k), j(v))
1538 ", ".join('"%s": "%s"' % (j(k), j(v))
1539 for k, v in ctx.extra().items()))
1539 for k, v in ctx.extra().items()))
1540
1540
1541 files = ctx.p1().status(ctx)
1541 files = ctx.p1().status(ctx)
1542 self.ui.write((',\n "modified": [%s]') %
1542 self.ui.write((',\n "modified": [%s]') %
1543 ", ".join('"%s"' % j(f) for f in files[0]))
1543 ", ".join('"%s"' % j(f) for f in files[0]))
1544 self.ui.write((',\n "added": [%s]') %
1544 self.ui.write((',\n "added": [%s]') %
1545 ", ".join('"%s"' % j(f) for f in files[1]))
1545 ", ".join('"%s"' % j(f) for f in files[1]))
1546 self.ui.write((',\n "removed": [%s]') %
1546 self.ui.write((',\n "removed": [%s]') %
1547 ", ".join('"%s"' % j(f) for f in files[2]))
1547 ", ".join('"%s"' % j(f) for f in files[2]))
1548
1548
1549 elif self.ui.verbose:
1549 elif self.ui.verbose:
1550 self.ui.write((',\n "files": [%s]') %
1550 self.ui.write((',\n "files": [%s]') %
1551 ", ".join('"%s"' % j(f) for f in ctx.files()))
1551 ", ".join('"%s"' % j(f) for f in ctx.files()))
1552
1552
1553 if copies:
1553 if copies:
1554 self.ui.write((',\n "copies": {%s}') %
1554 self.ui.write((',\n "copies": {%s}') %
1555 ", ".join('"%s": "%s"' % (j(k), j(v))
1555 ", ".join('"%s": "%s"' % (j(k), j(v))
1556 for k, v in copies))
1556 for k, v in copies))
1557
1557
1558 matchfn = self.matchfn
1558 matchfn = self.matchfn
1559 if matchfn:
1559 if matchfn:
1560 stat = self.diffopts.get('stat')
1560 stat = self.diffopts.get('stat')
1561 diff = self.diffopts.get('patch')
1561 diff = self.diffopts.get('patch')
1562 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1562 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1563 node, prev = ctx.node(), ctx.p1().node()
1563 node, prev = ctx.node(), ctx.p1().node()
1564 if stat:
1564 if stat:
1565 self.ui.pushbuffer()
1565 self.ui.pushbuffer()
1566 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1566 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1567 match=matchfn, stat=True)
1567 match=matchfn, stat=True)
1568 self.ui.write((',\n "diffstat": "%s"')
1568 self.ui.write((',\n "diffstat": "%s"')
1569 % j(self.ui.popbuffer()))
1569 % j(self.ui.popbuffer()))
1570 if diff:
1570 if diff:
1571 self.ui.pushbuffer()
1571 self.ui.pushbuffer()
1572 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1572 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1573 match=matchfn, stat=False)
1573 match=matchfn, stat=False)
1574 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1574 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1575
1575
1576 self.ui.write("\n }")
1576 self.ui.write("\n }")
1577
1577
1578 class changeset_templater(changeset_printer):
1578 class changeset_templater(changeset_printer):
1579 '''format changeset information.'''
1579 '''format changeset information.'''
1580
1580
1581 def __init__(self, ui, repo, tmplspec, matchfn, diffopts, buffered):
1581 def __init__(self, ui, repo, tmplspec, matchfn, diffopts, buffered):
1582 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1582 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1583 self.t = formatter.loadtemplater(ui, tmplspec,
1583 self.t = formatter.loadtemplater(ui, tmplspec,
1584 cache=templatekw.defaulttempl)
1584 cache=templatekw.defaulttempl)
1585 self._counter = itertools.count()
1585 self._counter = itertools.count()
1586 self.cache = {}
1586 self.cache = {}
1587
1587
1588 # find correct templates for current mode
1588 # find correct templates for current mode
1589 tmplmodes = [
1589 tmplmodes = [
1590 (True, None),
1590 (True, None),
1591 (self.ui.verbose, 'verbose'),
1591 (self.ui.verbose, 'verbose'),
1592 (self.ui.quiet, 'quiet'),
1592 (self.ui.quiet, 'quiet'),
1593 (self.ui.debugflag, 'debug'),
1593 (self.ui.debugflag, 'debug'),
1594 ]
1594 ]
1595
1595
1596 self._tref = tmplspec.ref
1596 self._tref = tmplspec.ref
1597 self._parts = {'header': '', 'footer': '',
1597 self._parts = {'header': '', 'footer': '',
1598 tmplspec.ref: tmplspec.ref,
1598 tmplspec.ref: tmplspec.ref,
1599 'docheader': '', 'docfooter': ''}
1599 'docheader': '', 'docfooter': ''}
1600 for mode, postfix in tmplmodes:
1600 for mode, postfix in tmplmodes:
1601 for t in self._parts:
1601 for t in self._parts:
1602 cur = t
1602 cur = t
1603 if postfix:
1603 if postfix:
1604 cur += "_" + postfix
1604 cur += "_" + postfix
1605 if mode and cur in self.t:
1605 if mode and cur in self.t:
1606 self._parts[t] = cur
1606 self._parts[t] = cur
1607
1607
1608 if self._parts['docheader']:
1608 if self._parts['docheader']:
1609 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1609 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1610
1610
1611 def close(self):
1611 def close(self):
1612 if self._parts['docfooter']:
1612 if self._parts['docfooter']:
1613 if not self.footer:
1613 if not self.footer:
1614 self.footer = ""
1614 self.footer = ""
1615 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1615 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1616 return super(changeset_templater, self).close()
1616 return super(changeset_templater, self).close()
1617
1617
1618 def _show(self, ctx, copies, matchfn, props):
1618 def _show(self, ctx, copies, matchfn, props):
1619 '''show a single changeset or file revision'''
1619 '''show a single changeset or file revision'''
1620 props = props.copy()
1620 props = props.copy()
1621 props.update(templatekw.keywords)
1621 props.update(templatekw.keywords)
1622 props['templ'] = self.t
1622 props['templ'] = self.t
1623 props['ctx'] = ctx
1623 props['ctx'] = ctx
1624 props['repo'] = self.repo
1624 props['repo'] = self.repo
1625 props['ui'] = self.repo.ui
1625 props['ui'] = self.repo.ui
1626 props['index'] = next(self._counter)
1626 props['index'] = next(self._counter)
1627 props['revcache'] = {'copies': copies}
1627 props['revcache'] = {'copies': copies}
1628 props['cache'] = self.cache
1628 props['cache'] = self.cache
1629 props = pycompat.strkwargs(props)
1629 props = pycompat.strkwargs(props)
1630
1630
1631 # write header
1631 # write header
1632 if self._parts['header']:
1632 if self._parts['header']:
1633 h = templater.stringify(self.t(self._parts['header'], **props))
1633 h = templater.stringify(self.t(self._parts['header'], **props))
1634 if self.buffered:
1634 if self.buffered:
1635 self.header[ctx.rev()] = h
1635 self.header[ctx.rev()] = h
1636 else:
1636 else:
1637 if self.lastheader != h:
1637 if self.lastheader != h:
1638 self.lastheader = h
1638 self.lastheader = h
1639 self.ui.write(h)
1639 self.ui.write(h)
1640
1640
1641 # write changeset metadata, then patch if requested
1641 # write changeset metadata, then patch if requested
1642 key = self._parts[self._tref]
1642 key = self._parts[self._tref]
1643 self.ui.write(templater.stringify(self.t(key, **props)))
1643 self.ui.write(templater.stringify(self.t(key, **props)))
1644 self.showpatch(ctx, matchfn)
1644 self.showpatch(ctx, matchfn)
1645
1645
1646 if self._parts['footer']:
1646 if self._parts['footer']:
1647 if not self.footer:
1647 if not self.footer:
1648 self.footer = templater.stringify(
1648 self.footer = templater.stringify(
1649 self.t(self._parts['footer'], **props))
1649 self.t(self._parts['footer'], **props))
1650
1650
1651 def logtemplatespec(tmpl, mapfile):
1651 def logtemplatespec(tmpl, mapfile):
1652 return formatter.templatespec('changeset', tmpl, mapfile)
1652 if mapfile:
1653 return formatter.templatespec('changeset', tmpl, mapfile)
1654 else:
1655 return formatter.templatespec('', tmpl, None)
1653
1656
1654 def _lookuplogtemplate(ui, tmpl, style):
1657 def _lookuplogtemplate(ui, tmpl, style):
1655 """Find the template matching the given template spec or style
1658 """Find the template matching the given template spec or style
1656
1659
1657 See formatter.lookuptemplate() for details.
1660 See formatter.lookuptemplate() for details.
1658 """
1661 """
1659
1662
1660 # ui settings
1663 # ui settings
1661 if not tmpl and not style: # template are stronger than style
1664 if not tmpl and not style: # template are stronger than style
1662 tmpl = ui.config('ui', 'logtemplate')
1665 tmpl = ui.config('ui', 'logtemplate')
1663 if tmpl:
1666 if tmpl:
1664 return logtemplatespec(templater.unquotestring(tmpl), None)
1667 return logtemplatespec(templater.unquotestring(tmpl), None)
1665 else:
1668 else:
1666 style = util.expandpath(ui.config('ui', 'style', ''))
1669 style = util.expandpath(ui.config('ui', 'style', ''))
1667
1670
1668 if not tmpl and style:
1671 if not tmpl and style:
1669 mapfile = style
1672 mapfile = style
1670 if not os.path.split(mapfile)[0]:
1673 if not os.path.split(mapfile)[0]:
1671 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1674 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1672 or templater.templatepath(mapfile))
1675 or templater.templatepath(mapfile))
1673 if mapname:
1676 if mapname:
1674 mapfile = mapname
1677 mapfile = mapname
1675 return logtemplatespec(None, mapfile)
1678 return logtemplatespec(None, mapfile)
1676
1679
1677 if not tmpl:
1680 if not tmpl:
1678 return logtemplatespec(None, None)
1681 return logtemplatespec(None, None)
1679
1682
1680 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1683 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1681
1684
1682 def makelogtemplater(ui, repo, tmpl, buffered=False):
1685 def makelogtemplater(ui, repo, tmpl, buffered=False):
1683 """Create a changeset_templater from a literal template 'tmpl'"""
1686 """Create a changeset_templater from a literal template 'tmpl'"""
1684 spec = logtemplatespec(tmpl, None)
1687 spec = logtemplatespec(tmpl, None)
1685 return changeset_templater(ui, repo, spec, matchfn=None, diffopts={},
1688 return changeset_templater(ui, repo, spec, matchfn=None, diffopts={},
1686 buffered=buffered)
1689 buffered=buffered)
1687
1690
1688 def show_changeset(ui, repo, opts, buffered=False):
1691 def show_changeset(ui, repo, opts, buffered=False):
1689 """show one changeset using template or regular display.
1692 """show one changeset using template or regular display.
1690
1693
1691 Display format will be the first non-empty hit of:
1694 Display format will be the first non-empty hit of:
1692 1. option 'template'
1695 1. option 'template'
1693 2. option 'style'
1696 2. option 'style'
1694 3. [ui] setting 'logtemplate'
1697 3. [ui] setting 'logtemplate'
1695 4. [ui] setting 'style'
1698 4. [ui] setting 'style'
1696 If all of these values are either the unset or the empty string,
1699 If all of these values are either the unset or the empty string,
1697 regular display via changeset_printer() is done.
1700 regular display via changeset_printer() is done.
1698 """
1701 """
1699 # options
1702 # options
1700 matchfn = None
1703 matchfn = None
1701 if opts.get('patch') or opts.get('stat'):
1704 if opts.get('patch') or opts.get('stat'):
1702 matchfn = scmutil.matchall(repo)
1705 matchfn = scmutil.matchall(repo)
1703
1706
1704 if opts.get('template') == 'json':
1707 if opts.get('template') == 'json':
1705 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1708 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1706
1709
1707 spec = _lookuplogtemplate(ui, opts.get('template'), opts.get('style'))
1710 spec = _lookuplogtemplate(ui, opts.get('template'), opts.get('style'))
1708
1711
1709 if not spec.tmpl and not spec.mapfile:
1712 if not spec.ref and not spec.tmpl and not spec.mapfile:
1710 return changeset_printer(ui, repo, matchfn, opts, buffered)
1713 return changeset_printer(ui, repo, matchfn, opts, buffered)
1711
1714
1712 return changeset_templater(ui, repo, spec, matchfn, opts, buffered)
1715 return changeset_templater(ui, repo, spec, matchfn, opts, buffered)
1713
1716
1714 def showmarker(fm, marker, index=None):
1717 def showmarker(fm, marker, index=None):
1715 """utility function to display obsolescence marker in a readable way
1718 """utility function to display obsolescence marker in a readable way
1716
1719
1717 To be used by debug function."""
1720 To be used by debug function."""
1718 if index is not None:
1721 if index is not None:
1719 fm.write('index', '%i ', index)
1722 fm.write('index', '%i ', index)
1720 fm.write('precnode', '%s ', hex(marker.precnode()))
1723 fm.write('precnode', '%s ', hex(marker.precnode()))
1721 succs = marker.succnodes()
1724 succs = marker.succnodes()
1722 fm.condwrite(succs, 'succnodes', '%s ',
1725 fm.condwrite(succs, 'succnodes', '%s ',
1723 fm.formatlist(map(hex, succs), name='node'))
1726 fm.formatlist(map(hex, succs), name='node'))
1724 fm.write('flag', '%X ', marker.flags())
1727 fm.write('flag', '%X ', marker.flags())
1725 parents = marker.parentnodes()
1728 parents = marker.parentnodes()
1726 if parents is not None:
1729 if parents is not None:
1727 fm.write('parentnodes', '{%s} ',
1730 fm.write('parentnodes', '{%s} ',
1728 fm.formatlist(map(hex, parents), name='node', sep=', '))
1731 fm.formatlist(map(hex, parents), name='node', sep=', '))
1729 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
1732 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
1730 meta = marker.metadata().copy()
1733 meta = marker.metadata().copy()
1731 meta.pop('date', None)
1734 meta.pop('date', None)
1732 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
1735 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
1733 fm.plain('\n')
1736 fm.plain('\n')
1734
1737
1735 def finddate(ui, repo, date):
1738 def finddate(ui, repo, date):
1736 """Find the tipmost changeset that matches the given date spec"""
1739 """Find the tipmost changeset that matches the given date spec"""
1737
1740
1738 df = util.matchdate(date)
1741 df = util.matchdate(date)
1739 m = scmutil.matchall(repo)
1742 m = scmutil.matchall(repo)
1740 results = {}
1743 results = {}
1741
1744
1742 def prep(ctx, fns):
1745 def prep(ctx, fns):
1743 d = ctx.date()
1746 d = ctx.date()
1744 if df(d[0]):
1747 if df(d[0]):
1745 results[ctx.rev()] = d
1748 results[ctx.rev()] = d
1746
1749
1747 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1750 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1748 rev = ctx.rev()
1751 rev = ctx.rev()
1749 if rev in results:
1752 if rev in results:
1750 ui.status(_("found revision %s from %s\n") %
1753 ui.status(_("found revision %s from %s\n") %
1751 (rev, util.datestr(results[rev])))
1754 (rev, util.datestr(results[rev])))
1752 return '%d' % rev
1755 return '%d' % rev
1753
1756
1754 raise error.Abort(_("revision matching date not found"))
1757 raise error.Abort(_("revision matching date not found"))
1755
1758
1756 def increasingwindows(windowsize=8, sizelimit=512):
1759 def increasingwindows(windowsize=8, sizelimit=512):
1757 while True:
1760 while True:
1758 yield windowsize
1761 yield windowsize
1759 if windowsize < sizelimit:
1762 if windowsize < sizelimit:
1760 windowsize *= 2
1763 windowsize *= 2
1761
1764
1762 class FileWalkError(Exception):
1765 class FileWalkError(Exception):
1763 pass
1766 pass
1764
1767
1765 def walkfilerevs(repo, match, follow, revs, fncache):
1768 def walkfilerevs(repo, match, follow, revs, fncache):
1766 '''Walks the file history for the matched files.
1769 '''Walks the file history for the matched files.
1767
1770
1768 Returns the changeset revs that are involved in the file history.
1771 Returns the changeset revs that are involved in the file history.
1769
1772
1770 Throws FileWalkError if the file history can't be walked using
1773 Throws FileWalkError if the file history can't be walked using
1771 filelogs alone.
1774 filelogs alone.
1772 '''
1775 '''
1773 wanted = set()
1776 wanted = set()
1774 copies = []
1777 copies = []
1775 minrev, maxrev = min(revs), max(revs)
1778 minrev, maxrev = min(revs), max(revs)
1776 def filerevgen(filelog, last):
1779 def filerevgen(filelog, last):
1777 """
1780 """
1778 Only files, no patterns. Check the history of each file.
1781 Only files, no patterns. Check the history of each file.
1779
1782
1780 Examines filelog entries within minrev, maxrev linkrev range
1783 Examines filelog entries within minrev, maxrev linkrev range
1781 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1784 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1782 tuples in backwards order
1785 tuples in backwards order
1783 """
1786 """
1784 cl_count = len(repo)
1787 cl_count = len(repo)
1785 revs = []
1788 revs = []
1786 for j in xrange(0, last + 1):
1789 for j in xrange(0, last + 1):
1787 linkrev = filelog.linkrev(j)
1790 linkrev = filelog.linkrev(j)
1788 if linkrev < minrev:
1791 if linkrev < minrev:
1789 continue
1792 continue
1790 # only yield rev for which we have the changelog, it can
1793 # only yield rev for which we have the changelog, it can
1791 # happen while doing "hg log" during a pull or commit
1794 # happen while doing "hg log" during a pull or commit
1792 if linkrev >= cl_count:
1795 if linkrev >= cl_count:
1793 break
1796 break
1794
1797
1795 parentlinkrevs = []
1798 parentlinkrevs = []
1796 for p in filelog.parentrevs(j):
1799 for p in filelog.parentrevs(j):
1797 if p != nullrev:
1800 if p != nullrev:
1798 parentlinkrevs.append(filelog.linkrev(p))
1801 parentlinkrevs.append(filelog.linkrev(p))
1799 n = filelog.node(j)
1802 n = filelog.node(j)
1800 revs.append((linkrev, parentlinkrevs,
1803 revs.append((linkrev, parentlinkrevs,
1801 follow and filelog.renamed(n)))
1804 follow and filelog.renamed(n)))
1802
1805
1803 return reversed(revs)
1806 return reversed(revs)
1804 def iterfiles():
1807 def iterfiles():
1805 pctx = repo['.']
1808 pctx = repo['.']
1806 for filename in match.files():
1809 for filename in match.files():
1807 if follow:
1810 if follow:
1808 if filename not in pctx:
1811 if filename not in pctx:
1809 raise error.Abort(_('cannot follow file not in parent '
1812 raise error.Abort(_('cannot follow file not in parent '
1810 'revision: "%s"') % filename)
1813 'revision: "%s"') % filename)
1811 yield filename, pctx[filename].filenode()
1814 yield filename, pctx[filename].filenode()
1812 else:
1815 else:
1813 yield filename, None
1816 yield filename, None
1814 for filename_node in copies:
1817 for filename_node in copies:
1815 yield filename_node
1818 yield filename_node
1816
1819
1817 for file_, node in iterfiles():
1820 for file_, node in iterfiles():
1818 filelog = repo.file(file_)
1821 filelog = repo.file(file_)
1819 if not len(filelog):
1822 if not len(filelog):
1820 if node is None:
1823 if node is None:
1821 # A zero count may be a directory or deleted file, so
1824 # A zero count may be a directory or deleted file, so
1822 # try to find matching entries on the slow path.
1825 # try to find matching entries on the slow path.
1823 if follow:
1826 if follow:
1824 raise error.Abort(
1827 raise error.Abort(
1825 _('cannot follow nonexistent file: "%s"') % file_)
1828 _('cannot follow nonexistent file: "%s"') % file_)
1826 raise FileWalkError("Cannot walk via filelog")
1829 raise FileWalkError("Cannot walk via filelog")
1827 else:
1830 else:
1828 continue
1831 continue
1829
1832
1830 if node is None:
1833 if node is None:
1831 last = len(filelog) - 1
1834 last = len(filelog) - 1
1832 else:
1835 else:
1833 last = filelog.rev(node)
1836 last = filelog.rev(node)
1834
1837
1835 # keep track of all ancestors of the file
1838 # keep track of all ancestors of the file
1836 ancestors = {filelog.linkrev(last)}
1839 ancestors = {filelog.linkrev(last)}
1837
1840
1838 # iterate from latest to oldest revision
1841 # iterate from latest to oldest revision
1839 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1842 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1840 if not follow:
1843 if not follow:
1841 if rev > maxrev:
1844 if rev > maxrev:
1842 continue
1845 continue
1843 else:
1846 else:
1844 # Note that last might not be the first interesting
1847 # Note that last might not be the first interesting
1845 # rev to us:
1848 # rev to us:
1846 # if the file has been changed after maxrev, we'll
1849 # if the file has been changed after maxrev, we'll
1847 # have linkrev(last) > maxrev, and we still need
1850 # have linkrev(last) > maxrev, and we still need
1848 # to explore the file graph
1851 # to explore the file graph
1849 if rev not in ancestors:
1852 if rev not in ancestors:
1850 continue
1853 continue
1851 # XXX insert 1327 fix here
1854 # XXX insert 1327 fix here
1852 if flparentlinkrevs:
1855 if flparentlinkrevs:
1853 ancestors.update(flparentlinkrevs)
1856 ancestors.update(flparentlinkrevs)
1854
1857
1855 fncache.setdefault(rev, []).append(file_)
1858 fncache.setdefault(rev, []).append(file_)
1856 wanted.add(rev)
1859 wanted.add(rev)
1857 if copied:
1860 if copied:
1858 copies.append(copied)
1861 copies.append(copied)
1859
1862
1860 return wanted
1863 return wanted
1861
1864
1862 class _followfilter(object):
1865 class _followfilter(object):
1863 def __init__(self, repo, onlyfirst=False):
1866 def __init__(self, repo, onlyfirst=False):
1864 self.repo = repo
1867 self.repo = repo
1865 self.startrev = nullrev
1868 self.startrev = nullrev
1866 self.roots = set()
1869 self.roots = set()
1867 self.onlyfirst = onlyfirst
1870 self.onlyfirst = onlyfirst
1868
1871
1869 def match(self, rev):
1872 def match(self, rev):
1870 def realparents(rev):
1873 def realparents(rev):
1871 if self.onlyfirst:
1874 if self.onlyfirst:
1872 return self.repo.changelog.parentrevs(rev)[0:1]
1875 return self.repo.changelog.parentrevs(rev)[0:1]
1873 else:
1876 else:
1874 return filter(lambda x: x != nullrev,
1877 return filter(lambda x: x != nullrev,
1875 self.repo.changelog.parentrevs(rev))
1878 self.repo.changelog.parentrevs(rev))
1876
1879
1877 if self.startrev == nullrev:
1880 if self.startrev == nullrev:
1878 self.startrev = rev
1881 self.startrev = rev
1879 return True
1882 return True
1880
1883
1881 if rev > self.startrev:
1884 if rev > self.startrev:
1882 # forward: all descendants
1885 # forward: all descendants
1883 if not self.roots:
1886 if not self.roots:
1884 self.roots.add(self.startrev)
1887 self.roots.add(self.startrev)
1885 for parent in realparents(rev):
1888 for parent in realparents(rev):
1886 if parent in self.roots:
1889 if parent in self.roots:
1887 self.roots.add(rev)
1890 self.roots.add(rev)
1888 return True
1891 return True
1889 else:
1892 else:
1890 # backwards: all parents
1893 # backwards: all parents
1891 if not self.roots:
1894 if not self.roots:
1892 self.roots.update(realparents(self.startrev))
1895 self.roots.update(realparents(self.startrev))
1893 if rev in self.roots:
1896 if rev in self.roots:
1894 self.roots.remove(rev)
1897 self.roots.remove(rev)
1895 self.roots.update(realparents(rev))
1898 self.roots.update(realparents(rev))
1896 return True
1899 return True
1897
1900
1898 return False
1901 return False
1899
1902
1900 def walkchangerevs(repo, match, opts, prepare):
1903 def walkchangerevs(repo, match, opts, prepare):
1901 '''Iterate over files and the revs in which they changed.
1904 '''Iterate over files and the revs in which they changed.
1902
1905
1903 Callers most commonly need to iterate backwards over the history
1906 Callers most commonly need to iterate backwards over the history
1904 in which they are interested. Doing so has awful (quadratic-looking)
1907 in which they are interested. Doing so has awful (quadratic-looking)
1905 performance, so we use iterators in a "windowed" way.
1908 performance, so we use iterators in a "windowed" way.
1906
1909
1907 We walk a window of revisions in the desired order. Within the
1910 We walk a window of revisions in the desired order. Within the
1908 window, we first walk forwards to gather data, then in the desired
1911 window, we first walk forwards to gather data, then in the desired
1909 order (usually backwards) to display it.
1912 order (usually backwards) to display it.
1910
1913
1911 This function returns an iterator yielding contexts. Before
1914 This function returns an iterator yielding contexts. Before
1912 yielding each context, the iterator will first call the prepare
1915 yielding each context, the iterator will first call the prepare
1913 function on each context in the window in forward order.'''
1916 function on each context in the window in forward order.'''
1914
1917
1915 follow = opts.get('follow') or opts.get('follow_first')
1918 follow = opts.get('follow') or opts.get('follow_first')
1916 revs = _logrevs(repo, opts)
1919 revs = _logrevs(repo, opts)
1917 if not revs:
1920 if not revs:
1918 return []
1921 return []
1919 wanted = set()
1922 wanted = set()
1920 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1923 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1921 opts.get('removed'))
1924 opts.get('removed'))
1922 fncache = {}
1925 fncache = {}
1923 change = repo.changectx
1926 change = repo.changectx
1924
1927
1925 # First step is to fill wanted, the set of revisions that we want to yield.
1928 # First step is to fill wanted, the set of revisions that we want to yield.
1926 # When it does not induce extra cost, we also fill fncache for revisions in
1929 # When it does not induce extra cost, we also fill fncache for revisions in
1927 # wanted: a cache of filenames that were changed (ctx.files()) and that
1930 # wanted: a cache of filenames that were changed (ctx.files()) and that
1928 # match the file filtering conditions.
1931 # match the file filtering conditions.
1929
1932
1930 if match.always():
1933 if match.always():
1931 # No files, no patterns. Display all revs.
1934 # No files, no patterns. Display all revs.
1932 wanted = revs
1935 wanted = revs
1933 elif not slowpath:
1936 elif not slowpath:
1934 # We only have to read through the filelog to find wanted revisions
1937 # We only have to read through the filelog to find wanted revisions
1935
1938
1936 try:
1939 try:
1937 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1940 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1938 except FileWalkError:
1941 except FileWalkError:
1939 slowpath = True
1942 slowpath = True
1940
1943
1941 # We decided to fall back to the slowpath because at least one
1944 # We decided to fall back to the slowpath because at least one
1942 # of the paths was not a file. Check to see if at least one of them
1945 # of the paths was not a file. Check to see if at least one of them
1943 # existed in history, otherwise simply return
1946 # existed in history, otherwise simply return
1944 for path in match.files():
1947 for path in match.files():
1945 if path == '.' or path in repo.store:
1948 if path == '.' or path in repo.store:
1946 break
1949 break
1947 else:
1950 else:
1948 return []
1951 return []
1949
1952
1950 if slowpath:
1953 if slowpath:
1951 # We have to read the changelog to match filenames against
1954 # We have to read the changelog to match filenames against
1952 # changed files
1955 # changed files
1953
1956
1954 if follow:
1957 if follow:
1955 raise error.Abort(_('can only follow copies/renames for explicit '
1958 raise error.Abort(_('can only follow copies/renames for explicit '
1956 'filenames'))
1959 'filenames'))
1957
1960
1958 # The slow path checks files modified in every changeset.
1961 # The slow path checks files modified in every changeset.
1959 # This is really slow on large repos, so compute the set lazily.
1962 # This is really slow on large repos, so compute the set lazily.
1960 class lazywantedset(object):
1963 class lazywantedset(object):
1961 def __init__(self):
1964 def __init__(self):
1962 self.set = set()
1965 self.set = set()
1963 self.revs = set(revs)
1966 self.revs = set(revs)
1964
1967
1965 # No need to worry about locality here because it will be accessed
1968 # No need to worry about locality here because it will be accessed
1966 # in the same order as the increasing window below.
1969 # in the same order as the increasing window below.
1967 def __contains__(self, value):
1970 def __contains__(self, value):
1968 if value in self.set:
1971 if value in self.set:
1969 return True
1972 return True
1970 elif not value in self.revs:
1973 elif not value in self.revs:
1971 return False
1974 return False
1972 else:
1975 else:
1973 self.revs.discard(value)
1976 self.revs.discard(value)
1974 ctx = change(value)
1977 ctx = change(value)
1975 matches = filter(match, ctx.files())
1978 matches = filter(match, ctx.files())
1976 if matches:
1979 if matches:
1977 fncache[value] = matches
1980 fncache[value] = matches
1978 self.set.add(value)
1981 self.set.add(value)
1979 return True
1982 return True
1980 return False
1983 return False
1981
1984
1982 def discard(self, value):
1985 def discard(self, value):
1983 self.revs.discard(value)
1986 self.revs.discard(value)
1984 self.set.discard(value)
1987 self.set.discard(value)
1985
1988
1986 wanted = lazywantedset()
1989 wanted = lazywantedset()
1987
1990
1988 # it might be worthwhile to do this in the iterator if the rev range
1991 # it might be worthwhile to do this in the iterator if the rev range
1989 # is descending and the prune args are all within that range
1992 # is descending and the prune args are all within that range
1990 for rev in opts.get('prune', ()):
1993 for rev in opts.get('prune', ()):
1991 rev = repo[rev].rev()
1994 rev = repo[rev].rev()
1992 ff = _followfilter(repo)
1995 ff = _followfilter(repo)
1993 stop = min(revs[0], revs[-1])
1996 stop = min(revs[0], revs[-1])
1994 for x in xrange(rev, stop - 1, -1):
1997 for x in xrange(rev, stop - 1, -1):
1995 if ff.match(x):
1998 if ff.match(x):
1996 wanted = wanted - [x]
1999 wanted = wanted - [x]
1997
2000
1998 # Now that wanted is correctly initialized, we can iterate over the
2001 # Now that wanted is correctly initialized, we can iterate over the
1999 # revision range, yielding only revisions in wanted.
2002 # revision range, yielding only revisions in wanted.
2000 def iterate():
2003 def iterate():
2001 if follow and match.always():
2004 if follow and match.always():
2002 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
2005 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
2003 def want(rev):
2006 def want(rev):
2004 return ff.match(rev) and rev in wanted
2007 return ff.match(rev) and rev in wanted
2005 else:
2008 else:
2006 def want(rev):
2009 def want(rev):
2007 return rev in wanted
2010 return rev in wanted
2008
2011
2009 it = iter(revs)
2012 it = iter(revs)
2010 stopiteration = False
2013 stopiteration = False
2011 for windowsize in increasingwindows():
2014 for windowsize in increasingwindows():
2012 nrevs = []
2015 nrevs = []
2013 for i in xrange(windowsize):
2016 for i in xrange(windowsize):
2014 rev = next(it, None)
2017 rev = next(it, None)
2015 if rev is None:
2018 if rev is None:
2016 stopiteration = True
2019 stopiteration = True
2017 break
2020 break
2018 elif want(rev):
2021 elif want(rev):
2019 nrevs.append(rev)
2022 nrevs.append(rev)
2020 for rev in sorted(nrevs):
2023 for rev in sorted(nrevs):
2021 fns = fncache.get(rev)
2024 fns = fncache.get(rev)
2022 ctx = change(rev)
2025 ctx = change(rev)
2023 if not fns:
2026 if not fns:
2024 def fns_generator():
2027 def fns_generator():
2025 for f in ctx.files():
2028 for f in ctx.files():
2026 if match(f):
2029 if match(f):
2027 yield f
2030 yield f
2028 fns = fns_generator()
2031 fns = fns_generator()
2029 prepare(ctx, fns)
2032 prepare(ctx, fns)
2030 for rev in nrevs:
2033 for rev in nrevs:
2031 yield change(rev)
2034 yield change(rev)
2032
2035
2033 if stopiteration:
2036 if stopiteration:
2034 break
2037 break
2035
2038
2036 return iterate()
2039 return iterate()
2037
2040
2038 def _makefollowlogfilematcher(repo, files, followfirst):
2041 def _makefollowlogfilematcher(repo, files, followfirst):
2039 # When displaying a revision with --patch --follow FILE, we have
2042 # When displaying a revision with --patch --follow FILE, we have
2040 # to know which file of the revision must be diffed. With
2043 # to know which file of the revision must be diffed. With
2041 # --follow, we want the names of the ancestors of FILE in the
2044 # --follow, we want the names of the ancestors of FILE in the
2042 # revision, stored in "fcache". "fcache" is populated by
2045 # revision, stored in "fcache". "fcache" is populated by
2043 # reproducing the graph traversal already done by --follow revset
2046 # reproducing the graph traversal already done by --follow revset
2044 # and relating revs to file names (which is not "correct" but
2047 # and relating revs to file names (which is not "correct" but
2045 # good enough).
2048 # good enough).
2046 fcache = {}
2049 fcache = {}
2047 fcacheready = [False]
2050 fcacheready = [False]
2048 pctx = repo['.']
2051 pctx = repo['.']
2049
2052
2050 def populate():
2053 def populate():
2051 for fn in files:
2054 for fn in files:
2052 fctx = pctx[fn]
2055 fctx = pctx[fn]
2053 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
2056 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
2054 for c in fctx.ancestors(followfirst=followfirst):
2057 for c in fctx.ancestors(followfirst=followfirst):
2055 fcache.setdefault(c.rev(), set()).add(c.path())
2058 fcache.setdefault(c.rev(), set()).add(c.path())
2056
2059
2057 def filematcher(rev):
2060 def filematcher(rev):
2058 if not fcacheready[0]:
2061 if not fcacheready[0]:
2059 # Lazy initialization
2062 # Lazy initialization
2060 fcacheready[0] = True
2063 fcacheready[0] = True
2061 populate()
2064 populate()
2062 return scmutil.matchfiles(repo, fcache.get(rev, []))
2065 return scmutil.matchfiles(repo, fcache.get(rev, []))
2063
2066
2064 return filematcher
2067 return filematcher
2065
2068
2066 def _makenofollowlogfilematcher(repo, pats, opts):
2069 def _makenofollowlogfilematcher(repo, pats, opts):
2067 '''hook for extensions to override the filematcher for non-follow cases'''
2070 '''hook for extensions to override the filematcher for non-follow cases'''
2068 return None
2071 return None
2069
2072
2070 def _makelogrevset(repo, pats, opts, revs):
2073 def _makelogrevset(repo, pats, opts, revs):
2071 """Return (expr, filematcher) where expr is a revset string built
2074 """Return (expr, filematcher) where expr is a revset string built
2072 from log options and file patterns or None. If --stat or --patch
2075 from log options and file patterns or None. If --stat or --patch
2073 are not passed filematcher is None. Otherwise it is a callable
2076 are not passed filematcher is None. Otherwise it is a callable
2074 taking a revision number and returning a match objects filtering
2077 taking a revision number and returning a match objects filtering
2075 the files to be detailed when displaying the revision.
2078 the files to be detailed when displaying the revision.
2076 """
2079 """
2077 opt2revset = {
2080 opt2revset = {
2078 'no_merges': ('not merge()', None),
2081 'no_merges': ('not merge()', None),
2079 'only_merges': ('merge()', None),
2082 'only_merges': ('merge()', None),
2080 '_ancestors': ('ancestors(%(val)s)', None),
2083 '_ancestors': ('ancestors(%(val)s)', None),
2081 '_fancestors': ('_firstancestors(%(val)s)', None),
2084 '_fancestors': ('_firstancestors(%(val)s)', None),
2082 '_descendants': ('descendants(%(val)s)', None),
2085 '_descendants': ('descendants(%(val)s)', None),
2083 '_fdescendants': ('_firstdescendants(%(val)s)', None),
2086 '_fdescendants': ('_firstdescendants(%(val)s)', None),
2084 '_matchfiles': ('_matchfiles(%(val)s)', None),
2087 '_matchfiles': ('_matchfiles(%(val)s)', None),
2085 'date': ('date(%(val)r)', None),
2088 'date': ('date(%(val)r)', None),
2086 'branch': ('branch(%(val)r)', ' or '),
2089 'branch': ('branch(%(val)r)', ' or '),
2087 '_patslog': ('filelog(%(val)r)', ' or '),
2090 '_patslog': ('filelog(%(val)r)', ' or '),
2088 '_patsfollow': ('follow(%(val)r)', ' or '),
2091 '_patsfollow': ('follow(%(val)r)', ' or '),
2089 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
2092 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
2090 'keyword': ('keyword(%(val)r)', ' or '),
2093 'keyword': ('keyword(%(val)r)', ' or '),
2091 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
2094 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
2092 'user': ('user(%(val)r)', ' or '),
2095 'user': ('user(%(val)r)', ' or '),
2093 }
2096 }
2094
2097
2095 opts = dict(opts)
2098 opts = dict(opts)
2096 # follow or not follow?
2099 # follow or not follow?
2097 follow = opts.get('follow') or opts.get('follow_first')
2100 follow = opts.get('follow') or opts.get('follow_first')
2098 if opts.get('follow_first'):
2101 if opts.get('follow_first'):
2099 followfirst = 1
2102 followfirst = 1
2100 else:
2103 else:
2101 followfirst = 0
2104 followfirst = 0
2102 # --follow with FILE behavior depends on revs...
2105 # --follow with FILE behavior depends on revs...
2103 it = iter(revs)
2106 it = iter(revs)
2104 startrev = next(it)
2107 startrev = next(it)
2105 followdescendants = startrev < next(it, startrev)
2108 followdescendants = startrev < next(it, startrev)
2106
2109
2107 # branch and only_branch are really aliases and must be handled at
2110 # branch and only_branch are really aliases and must be handled at
2108 # the same time
2111 # the same time
2109 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2112 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2110 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2113 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2111 # pats/include/exclude are passed to match.match() directly in
2114 # pats/include/exclude are passed to match.match() directly in
2112 # _matchfiles() revset but walkchangerevs() builds its matcher with
2115 # _matchfiles() revset but walkchangerevs() builds its matcher with
2113 # scmutil.match(). The difference is input pats are globbed on
2116 # scmutil.match(). The difference is input pats are globbed on
2114 # platforms without shell expansion (windows).
2117 # platforms without shell expansion (windows).
2115 wctx = repo[None]
2118 wctx = repo[None]
2116 match, pats = scmutil.matchandpats(wctx, pats, opts)
2119 match, pats = scmutil.matchandpats(wctx, pats, opts)
2117 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2120 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2118 opts.get('removed'))
2121 opts.get('removed'))
2119 if not slowpath:
2122 if not slowpath:
2120 for f in match.files():
2123 for f in match.files():
2121 if follow and f not in wctx:
2124 if follow and f not in wctx:
2122 # If the file exists, it may be a directory, so let it
2125 # If the file exists, it may be a directory, so let it
2123 # take the slow path.
2126 # take the slow path.
2124 if os.path.exists(repo.wjoin(f)):
2127 if os.path.exists(repo.wjoin(f)):
2125 slowpath = True
2128 slowpath = True
2126 continue
2129 continue
2127 else:
2130 else:
2128 raise error.Abort(_('cannot follow file not in parent '
2131 raise error.Abort(_('cannot follow file not in parent '
2129 'revision: "%s"') % f)
2132 'revision: "%s"') % f)
2130 filelog = repo.file(f)
2133 filelog = repo.file(f)
2131 if not filelog:
2134 if not filelog:
2132 # A zero count may be a directory or deleted file, so
2135 # A zero count may be a directory or deleted file, so
2133 # try to find matching entries on the slow path.
2136 # try to find matching entries on the slow path.
2134 if follow:
2137 if follow:
2135 raise error.Abort(
2138 raise error.Abort(
2136 _('cannot follow nonexistent file: "%s"') % f)
2139 _('cannot follow nonexistent file: "%s"') % f)
2137 slowpath = True
2140 slowpath = True
2138
2141
2139 # We decided to fall back to the slowpath because at least one
2142 # We decided to fall back to the slowpath because at least one
2140 # of the paths was not a file. Check to see if at least one of them
2143 # of the paths was not a file. Check to see if at least one of them
2141 # existed in history - in that case, we'll continue down the
2144 # existed in history - in that case, we'll continue down the
2142 # slowpath; otherwise, we can turn off the slowpath
2145 # slowpath; otherwise, we can turn off the slowpath
2143 if slowpath:
2146 if slowpath:
2144 for path in match.files():
2147 for path in match.files():
2145 if path == '.' or path in repo.store:
2148 if path == '.' or path in repo.store:
2146 break
2149 break
2147 else:
2150 else:
2148 slowpath = False
2151 slowpath = False
2149
2152
2150 fpats = ('_patsfollow', '_patsfollowfirst')
2153 fpats = ('_patsfollow', '_patsfollowfirst')
2151 fnopats = (('_ancestors', '_fancestors'),
2154 fnopats = (('_ancestors', '_fancestors'),
2152 ('_descendants', '_fdescendants'))
2155 ('_descendants', '_fdescendants'))
2153 if slowpath:
2156 if slowpath:
2154 # See walkchangerevs() slow path.
2157 # See walkchangerevs() slow path.
2155 #
2158 #
2156 # pats/include/exclude cannot be represented as separate
2159 # pats/include/exclude cannot be represented as separate
2157 # revset expressions as their filtering logic applies at file
2160 # revset expressions as their filtering logic applies at file
2158 # level. For instance "-I a -X a" matches a revision touching
2161 # level. For instance "-I a -X a" matches a revision touching
2159 # "a" and "b" while "file(a) and not file(b)" does
2162 # "a" and "b" while "file(a) and not file(b)" does
2160 # not. Besides, filesets are evaluated against the working
2163 # not. Besides, filesets are evaluated against the working
2161 # directory.
2164 # directory.
2162 matchargs = ['r:', 'd:relpath']
2165 matchargs = ['r:', 'd:relpath']
2163 for p in pats:
2166 for p in pats:
2164 matchargs.append('p:' + p)
2167 matchargs.append('p:' + p)
2165 for p in opts.get('include', []):
2168 for p in opts.get('include', []):
2166 matchargs.append('i:' + p)
2169 matchargs.append('i:' + p)
2167 for p in opts.get('exclude', []):
2170 for p in opts.get('exclude', []):
2168 matchargs.append('x:' + p)
2171 matchargs.append('x:' + p)
2169 matchargs = ','.join(('%r' % p) for p in matchargs)
2172 matchargs = ','.join(('%r' % p) for p in matchargs)
2170 opts['_matchfiles'] = matchargs
2173 opts['_matchfiles'] = matchargs
2171 if follow:
2174 if follow:
2172 opts[fnopats[0][followfirst]] = '.'
2175 opts[fnopats[0][followfirst]] = '.'
2173 else:
2176 else:
2174 if follow:
2177 if follow:
2175 if pats:
2178 if pats:
2176 # follow() revset interprets its file argument as a
2179 # follow() revset interprets its file argument as a
2177 # manifest entry, so use match.files(), not pats.
2180 # manifest entry, so use match.files(), not pats.
2178 opts[fpats[followfirst]] = list(match.files())
2181 opts[fpats[followfirst]] = list(match.files())
2179 else:
2182 else:
2180 op = fnopats[followdescendants][followfirst]
2183 op = fnopats[followdescendants][followfirst]
2181 opts[op] = 'rev(%d)' % startrev
2184 opts[op] = 'rev(%d)' % startrev
2182 else:
2185 else:
2183 opts['_patslog'] = list(pats)
2186 opts['_patslog'] = list(pats)
2184
2187
2185 filematcher = None
2188 filematcher = None
2186 if opts.get('patch') or opts.get('stat'):
2189 if opts.get('patch') or opts.get('stat'):
2187 # When following files, track renames via a special matcher.
2190 # When following files, track renames via a special matcher.
2188 # If we're forced to take the slowpath it means we're following
2191 # If we're forced to take the slowpath it means we're following
2189 # at least one pattern/directory, so don't bother with rename tracking.
2192 # at least one pattern/directory, so don't bother with rename tracking.
2190 if follow and not match.always() and not slowpath:
2193 if follow and not match.always() and not slowpath:
2191 # _makefollowlogfilematcher expects its files argument to be
2194 # _makefollowlogfilematcher expects its files argument to be
2192 # relative to the repo root, so use match.files(), not pats.
2195 # relative to the repo root, so use match.files(), not pats.
2193 filematcher = _makefollowlogfilematcher(repo, match.files(),
2196 filematcher = _makefollowlogfilematcher(repo, match.files(),
2194 followfirst)
2197 followfirst)
2195 else:
2198 else:
2196 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2199 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2197 if filematcher is None:
2200 if filematcher is None:
2198 filematcher = lambda rev: match
2201 filematcher = lambda rev: match
2199
2202
2200 expr = []
2203 expr = []
2201 for op, val in sorted(opts.iteritems()):
2204 for op, val in sorted(opts.iteritems()):
2202 if not val:
2205 if not val:
2203 continue
2206 continue
2204 if op not in opt2revset:
2207 if op not in opt2revset:
2205 continue
2208 continue
2206 revop, andor = opt2revset[op]
2209 revop, andor = opt2revset[op]
2207 if '%(val)' not in revop:
2210 if '%(val)' not in revop:
2208 expr.append(revop)
2211 expr.append(revop)
2209 else:
2212 else:
2210 if not isinstance(val, list):
2213 if not isinstance(val, list):
2211 e = revop % {'val': val}
2214 e = revop % {'val': val}
2212 else:
2215 else:
2213 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2216 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2214 expr.append(e)
2217 expr.append(e)
2215
2218
2216 if expr:
2219 if expr:
2217 expr = '(' + ' and '.join(expr) + ')'
2220 expr = '(' + ' and '.join(expr) + ')'
2218 else:
2221 else:
2219 expr = None
2222 expr = None
2220 return expr, filematcher
2223 return expr, filematcher
2221
2224
2222 def _logrevs(repo, opts):
2225 def _logrevs(repo, opts):
2223 # Default --rev value depends on --follow but --follow behavior
2226 # Default --rev value depends on --follow but --follow behavior
2224 # depends on revisions resolved from --rev...
2227 # depends on revisions resolved from --rev...
2225 follow = opts.get('follow') or opts.get('follow_first')
2228 follow = opts.get('follow') or opts.get('follow_first')
2226 if opts.get('rev'):
2229 if opts.get('rev'):
2227 revs = scmutil.revrange(repo, opts['rev'])
2230 revs = scmutil.revrange(repo, opts['rev'])
2228 elif follow and repo.dirstate.p1() == nullid:
2231 elif follow and repo.dirstate.p1() == nullid:
2229 revs = smartset.baseset()
2232 revs = smartset.baseset()
2230 elif follow:
2233 elif follow:
2231 revs = repo.revs('reverse(:.)')
2234 revs = repo.revs('reverse(:.)')
2232 else:
2235 else:
2233 revs = smartset.spanset(repo)
2236 revs = smartset.spanset(repo)
2234 revs.reverse()
2237 revs.reverse()
2235 return revs
2238 return revs
2236
2239
2237 def getgraphlogrevs(repo, pats, opts):
2240 def getgraphlogrevs(repo, pats, opts):
2238 """Return (revs, expr, filematcher) where revs is an iterable of
2241 """Return (revs, expr, filematcher) where revs is an iterable of
2239 revision numbers, expr is a revset string built from log options
2242 revision numbers, expr is a revset string built from log options
2240 and file patterns or None, and used to filter 'revs'. If --stat or
2243 and file patterns or None, and used to filter 'revs'. If --stat or
2241 --patch are not passed filematcher is None. Otherwise it is a
2244 --patch are not passed filematcher is None. Otherwise it is a
2242 callable taking a revision number and returning a match objects
2245 callable taking a revision number and returning a match objects
2243 filtering the files to be detailed when displaying the revision.
2246 filtering the files to be detailed when displaying the revision.
2244 """
2247 """
2245 limit = loglimit(opts)
2248 limit = loglimit(opts)
2246 revs = _logrevs(repo, opts)
2249 revs = _logrevs(repo, opts)
2247 if not revs:
2250 if not revs:
2248 return smartset.baseset(), None, None
2251 return smartset.baseset(), None, None
2249 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2252 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2250 if opts.get('rev'):
2253 if opts.get('rev'):
2251 # User-specified revs might be unsorted, but don't sort before
2254 # User-specified revs might be unsorted, but don't sort before
2252 # _makelogrevset because it might depend on the order of revs
2255 # _makelogrevset because it might depend on the order of revs
2253 if not (revs.isdescending() or revs.istopo()):
2256 if not (revs.isdescending() or revs.istopo()):
2254 revs.sort(reverse=True)
2257 revs.sort(reverse=True)
2255 if expr:
2258 if expr:
2256 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2259 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2257 revs = matcher(repo, revs)
2260 revs = matcher(repo, revs)
2258 if limit is not None:
2261 if limit is not None:
2259 limitedrevs = []
2262 limitedrevs = []
2260 for idx, rev in enumerate(revs):
2263 for idx, rev in enumerate(revs):
2261 if idx >= limit:
2264 if idx >= limit:
2262 break
2265 break
2263 limitedrevs.append(rev)
2266 limitedrevs.append(rev)
2264 revs = smartset.baseset(limitedrevs)
2267 revs = smartset.baseset(limitedrevs)
2265
2268
2266 return revs, expr, filematcher
2269 return revs, expr, filematcher
2267
2270
2268 def getlogrevs(repo, pats, opts):
2271 def getlogrevs(repo, pats, opts):
2269 """Return (revs, expr, filematcher) where revs is an iterable of
2272 """Return (revs, expr, filematcher) where revs is an iterable of
2270 revision numbers, expr is a revset string built from log options
2273 revision numbers, expr is a revset string built from log options
2271 and file patterns or None, and used to filter 'revs'. If --stat or
2274 and file patterns or None, and used to filter 'revs'. If --stat or
2272 --patch are not passed filematcher is None. Otherwise it is a
2275 --patch are not passed filematcher is None. Otherwise it is a
2273 callable taking a revision number and returning a match objects
2276 callable taking a revision number and returning a match objects
2274 filtering the files to be detailed when displaying the revision.
2277 filtering the files to be detailed when displaying the revision.
2275 """
2278 """
2276 limit = loglimit(opts)
2279 limit = loglimit(opts)
2277 revs = _logrevs(repo, opts)
2280 revs = _logrevs(repo, opts)
2278 if not revs:
2281 if not revs:
2279 return smartset.baseset([]), None, None
2282 return smartset.baseset([]), None, None
2280 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2283 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2281 if expr:
2284 if expr:
2282 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2285 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2283 revs = matcher(repo, revs)
2286 revs = matcher(repo, revs)
2284 if limit is not None:
2287 if limit is not None:
2285 limitedrevs = []
2288 limitedrevs = []
2286 for idx, r in enumerate(revs):
2289 for idx, r in enumerate(revs):
2287 if limit <= idx:
2290 if limit <= idx:
2288 break
2291 break
2289 limitedrevs.append(r)
2292 limitedrevs.append(r)
2290 revs = smartset.baseset(limitedrevs)
2293 revs = smartset.baseset(limitedrevs)
2291
2294
2292 return revs, expr, filematcher
2295 return revs, expr, filematcher
2293
2296
2294 def _graphnodeformatter(ui, displayer):
2297 def _graphnodeformatter(ui, displayer):
2295 spec = ui.config('ui', 'graphnodetemplate')
2298 spec = ui.config('ui', 'graphnodetemplate')
2296 if not spec:
2299 if not spec:
2297 return templatekw.showgraphnode # fast path for "{graphnode}"
2300 return templatekw.showgraphnode # fast path for "{graphnode}"
2298
2301
2299 spec = templater.unquotestring(spec)
2302 spec = templater.unquotestring(spec)
2300 templ = formatter.maketemplater(ui, spec)
2303 templ = formatter.maketemplater(ui, spec)
2301 cache = {}
2304 cache = {}
2302 if isinstance(displayer, changeset_templater):
2305 if isinstance(displayer, changeset_templater):
2303 cache = displayer.cache # reuse cache of slow templates
2306 cache = displayer.cache # reuse cache of slow templates
2304 props = templatekw.keywords.copy()
2307 props = templatekw.keywords.copy()
2305 props['templ'] = templ
2308 props['templ'] = templ
2306 props['cache'] = cache
2309 props['cache'] = cache
2307 def formatnode(repo, ctx):
2310 def formatnode(repo, ctx):
2308 props['ctx'] = ctx
2311 props['ctx'] = ctx
2309 props['repo'] = repo
2312 props['repo'] = repo
2310 props['ui'] = repo.ui
2313 props['ui'] = repo.ui
2311 props['revcache'] = {}
2314 props['revcache'] = {}
2312 return templ.render(props)
2315 return templ.render(props)
2313 return formatnode
2316 return formatnode
2314
2317
2315 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2318 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2316 filematcher=None):
2319 filematcher=None):
2317 formatnode = _graphnodeformatter(ui, displayer)
2320 formatnode = _graphnodeformatter(ui, displayer)
2318 state = graphmod.asciistate()
2321 state = graphmod.asciistate()
2319 styles = state['styles']
2322 styles = state['styles']
2320
2323
2321 # only set graph styling if HGPLAIN is not set.
2324 # only set graph styling if HGPLAIN is not set.
2322 if ui.plain('graph'):
2325 if ui.plain('graph'):
2323 # set all edge styles to |, the default pre-3.8 behaviour
2326 # set all edge styles to |, the default pre-3.8 behaviour
2324 styles.update(dict.fromkeys(styles, '|'))
2327 styles.update(dict.fromkeys(styles, '|'))
2325 else:
2328 else:
2326 edgetypes = {
2329 edgetypes = {
2327 'parent': graphmod.PARENT,
2330 'parent': graphmod.PARENT,
2328 'grandparent': graphmod.GRANDPARENT,
2331 'grandparent': graphmod.GRANDPARENT,
2329 'missing': graphmod.MISSINGPARENT
2332 'missing': graphmod.MISSINGPARENT
2330 }
2333 }
2331 for name, key in edgetypes.items():
2334 for name, key in edgetypes.items():
2332 # experimental config: experimental.graphstyle.*
2335 # experimental config: experimental.graphstyle.*
2333 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2336 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2334 styles[key])
2337 styles[key])
2335 if not styles[key]:
2338 if not styles[key]:
2336 styles[key] = None
2339 styles[key] = None
2337
2340
2338 # experimental config: experimental.graphshorten
2341 # experimental config: experimental.graphshorten
2339 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2342 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2340
2343
2341 for rev, type, ctx, parents in dag:
2344 for rev, type, ctx, parents in dag:
2342 char = formatnode(repo, ctx)
2345 char = formatnode(repo, ctx)
2343 copies = None
2346 copies = None
2344 if getrenamed and ctx.rev():
2347 if getrenamed and ctx.rev():
2345 copies = []
2348 copies = []
2346 for fn in ctx.files():
2349 for fn in ctx.files():
2347 rename = getrenamed(fn, ctx.rev())
2350 rename = getrenamed(fn, ctx.rev())
2348 if rename:
2351 if rename:
2349 copies.append((fn, rename[0]))
2352 copies.append((fn, rename[0]))
2350 revmatchfn = None
2353 revmatchfn = None
2351 if filematcher is not None:
2354 if filematcher is not None:
2352 revmatchfn = filematcher(ctx.rev())
2355 revmatchfn = filematcher(ctx.rev())
2353 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2356 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2354 lines = displayer.hunk.pop(rev).split('\n')
2357 lines = displayer.hunk.pop(rev).split('\n')
2355 if not lines[-1]:
2358 if not lines[-1]:
2356 del lines[-1]
2359 del lines[-1]
2357 displayer.flush(ctx)
2360 displayer.flush(ctx)
2358 edges = edgefn(type, char, lines, state, rev, parents)
2361 edges = edgefn(type, char, lines, state, rev, parents)
2359 for type, char, lines, coldata in edges:
2362 for type, char, lines, coldata in edges:
2360 graphmod.ascii(ui, state, type, char, lines, coldata)
2363 graphmod.ascii(ui, state, type, char, lines, coldata)
2361 displayer.close()
2364 displayer.close()
2362
2365
2363 def graphlog(ui, repo, pats, opts):
2366 def graphlog(ui, repo, pats, opts):
2364 # Parameters are identical to log command ones
2367 # Parameters are identical to log command ones
2365 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2368 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2366 revdag = graphmod.dagwalker(repo, revs)
2369 revdag = graphmod.dagwalker(repo, revs)
2367
2370
2368 getrenamed = None
2371 getrenamed = None
2369 if opts.get('copies'):
2372 if opts.get('copies'):
2370 endrev = None
2373 endrev = None
2371 if opts.get('rev'):
2374 if opts.get('rev'):
2372 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2375 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2373 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2376 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2374
2377
2375 ui.pager('log')
2378 ui.pager('log')
2376 displayer = show_changeset(ui, repo, opts, buffered=True)
2379 displayer = show_changeset(ui, repo, opts, buffered=True)
2377 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2380 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2378 filematcher)
2381 filematcher)
2379
2382
2380 def checkunsupportedgraphflags(pats, opts):
2383 def checkunsupportedgraphflags(pats, opts):
2381 for op in ["newest_first"]:
2384 for op in ["newest_first"]:
2382 if op in opts and opts[op]:
2385 if op in opts and opts[op]:
2383 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2386 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2384 % op.replace("_", "-"))
2387 % op.replace("_", "-"))
2385
2388
2386 def graphrevs(repo, nodes, opts):
2389 def graphrevs(repo, nodes, opts):
2387 limit = loglimit(opts)
2390 limit = loglimit(opts)
2388 nodes.reverse()
2391 nodes.reverse()
2389 if limit is not None:
2392 if limit is not None:
2390 nodes = nodes[:limit]
2393 nodes = nodes[:limit]
2391 return graphmod.nodes(repo, nodes)
2394 return graphmod.nodes(repo, nodes)
2392
2395
2393 def add(ui, repo, match, prefix, explicitonly, **opts):
2396 def add(ui, repo, match, prefix, explicitonly, **opts):
2394 join = lambda f: os.path.join(prefix, f)
2397 join = lambda f: os.path.join(prefix, f)
2395 bad = []
2398 bad = []
2396
2399
2397 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2400 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2398 names = []
2401 names = []
2399 wctx = repo[None]
2402 wctx = repo[None]
2400 cca = None
2403 cca = None
2401 abort, warn = scmutil.checkportabilityalert(ui)
2404 abort, warn = scmutil.checkportabilityalert(ui)
2402 if abort or warn:
2405 if abort or warn:
2403 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2406 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2404
2407
2405 badmatch = matchmod.badmatch(match, badfn)
2408 badmatch = matchmod.badmatch(match, badfn)
2406 dirstate = repo.dirstate
2409 dirstate = repo.dirstate
2407 # We don't want to just call wctx.walk here, since it would return a lot of
2410 # We don't want to just call wctx.walk here, since it would return a lot of
2408 # clean files, which we aren't interested in and takes time.
2411 # clean files, which we aren't interested in and takes time.
2409 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2412 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2410 True, False, full=False)):
2413 True, False, full=False)):
2411 exact = match.exact(f)
2414 exact = match.exact(f)
2412 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2415 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2413 if cca:
2416 if cca:
2414 cca(f)
2417 cca(f)
2415 names.append(f)
2418 names.append(f)
2416 if ui.verbose or not exact:
2419 if ui.verbose or not exact:
2417 ui.status(_('adding %s\n') % match.rel(f))
2420 ui.status(_('adding %s\n') % match.rel(f))
2418
2421
2419 for subpath in sorted(wctx.substate):
2422 for subpath in sorted(wctx.substate):
2420 sub = wctx.sub(subpath)
2423 sub = wctx.sub(subpath)
2421 try:
2424 try:
2422 submatch = matchmod.subdirmatcher(subpath, match)
2425 submatch = matchmod.subdirmatcher(subpath, match)
2423 if opts.get(r'subrepos'):
2426 if opts.get(r'subrepos'):
2424 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2427 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2425 else:
2428 else:
2426 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2429 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2427 except error.LookupError:
2430 except error.LookupError:
2428 ui.status(_("skipping missing subrepository: %s\n")
2431 ui.status(_("skipping missing subrepository: %s\n")
2429 % join(subpath))
2432 % join(subpath))
2430
2433
2431 if not opts.get(r'dry_run'):
2434 if not opts.get(r'dry_run'):
2432 rejected = wctx.add(names, prefix)
2435 rejected = wctx.add(names, prefix)
2433 bad.extend(f for f in rejected if f in match.files())
2436 bad.extend(f for f in rejected if f in match.files())
2434 return bad
2437 return bad
2435
2438
2436 def addwebdirpath(repo, serverpath, webconf):
2439 def addwebdirpath(repo, serverpath, webconf):
2437 webconf[serverpath] = repo.root
2440 webconf[serverpath] = repo.root
2438 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2441 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2439
2442
2440 for r in repo.revs('filelog("path:.hgsub")'):
2443 for r in repo.revs('filelog("path:.hgsub")'):
2441 ctx = repo[r]
2444 ctx = repo[r]
2442 for subpath in ctx.substate:
2445 for subpath in ctx.substate:
2443 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2446 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2444
2447
2445 def forget(ui, repo, match, prefix, explicitonly):
2448 def forget(ui, repo, match, prefix, explicitonly):
2446 join = lambda f: os.path.join(prefix, f)
2449 join = lambda f: os.path.join(prefix, f)
2447 bad = []
2450 bad = []
2448 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2451 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2449 wctx = repo[None]
2452 wctx = repo[None]
2450 forgot = []
2453 forgot = []
2451
2454
2452 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2455 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2453 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2456 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2454 if explicitonly:
2457 if explicitonly:
2455 forget = [f for f in forget if match.exact(f)]
2458 forget = [f for f in forget if match.exact(f)]
2456
2459
2457 for subpath in sorted(wctx.substate):
2460 for subpath in sorted(wctx.substate):
2458 sub = wctx.sub(subpath)
2461 sub = wctx.sub(subpath)
2459 try:
2462 try:
2460 submatch = matchmod.subdirmatcher(subpath, match)
2463 submatch = matchmod.subdirmatcher(subpath, match)
2461 subbad, subforgot = sub.forget(submatch, prefix)
2464 subbad, subforgot = sub.forget(submatch, prefix)
2462 bad.extend([subpath + '/' + f for f in subbad])
2465 bad.extend([subpath + '/' + f for f in subbad])
2463 forgot.extend([subpath + '/' + f for f in subforgot])
2466 forgot.extend([subpath + '/' + f for f in subforgot])
2464 except error.LookupError:
2467 except error.LookupError:
2465 ui.status(_("skipping missing subrepository: %s\n")
2468 ui.status(_("skipping missing subrepository: %s\n")
2466 % join(subpath))
2469 % join(subpath))
2467
2470
2468 if not explicitonly:
2471 if not explicitonly:
2469 for f in match.files():
2472 for f in match.files():
2470 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2473 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2471 if f not in forgot:
2474 if f not in forgot:
2472 if repo.wvfs.exists(f):
2475 if repo.wvfs.exists(f):
2473 # Don't complain if the exact case match wasn't given.
2476 # Don't complain if the exact case match wasn't given.
2474 # But don't do this until after checking 'forgot', so
2477 # But don't do this until after checking 'forgot', so
2475 # that subrepo files aren't normalized, and this op is
2478 # that subrepo files aren't normalized, and this op is
2476 # purely from data cached by the status walk above.
2479 # purely from data cached by the status walk above.
2477 if repo.dirstate.normalize(f) in repo.dirstate:
2480 if repo.dirstate.normalize(f) in repo.dirstate:
2478 continue
2481 continue
2479 ui.warn(_('not removing %s: '
2482 ui.warn(_('not removing %s: '
2480 'file is already untracked\n')
2483 'file is already untracked\n')
2481 % match.rel(f))
2484 % match.rel(f))
2482 bad.append(f)
2485 bad.append(f)
2483
2486
2484 for f in forget:
2487 for f in forget:
2485 if ui.verbose or not match.exact(f):
2488 if ui.verbose or not match.exact(f):
2486 ui.status(_('removing %s\n') % match.rel(f))
2489 ui.status(_('removing %s\n') % match.rel(f))
2487
2490
2488 rejected = wctx.forget(forget, prefix)
2491 rejected = wctx.forget(forget, prefix)
2489 bad.extend(f for f in rejected if f in match.files())
2492 bad.extend(f for f in rejected if f in match.files())
2490 forgot.extend(f for f in forget if f not in rejected)
2493 forgot.extend(f for f in forget if f not in rejected)
2491 return bad, forgot
2494 return bad, forgot
2492
2495
2493 def files(ui, ctx, m, fm, fmt, subrepos):
2496 def files(ui, ctx, m, fm, fmt, subrepos):
2494 rev = ctx.rev()
2497 rev = ctx.rev()
2495 ret = 1
2498 ret = 1
2496 ds = ctx.repo().dirstate
2499 ds = ctx.repo().dirstate
2497
2500
2498 for f in ctx.matches(m):
2501 for f in ctx.matches(m):
2499 if rev is None and ds[f] == 'r':
2502 if rev is None and ds[f] == 'r':
2500 continue
2503 continue
2501 fm.startitem()
2504 fm.startitem()
2502 if ui.verbose:
2505 if ui.verbose:
2503 fc = ctx[f]
2506 fc = ctx[f]
2504 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2507 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2505 fm.data(abspath=f)
2508 fm.data(abspath=f)
2506 fm.write('path', fmt, m.rel(f))
2509 fm.write('path', fmt, m.rel(f))
2507 ret = 0
2510 ret = 0
2508
2511
2509 for subpath in sorted(ctx.substate):
2512 for subpath in sorted(ctx.substate):
2510 submatch = matchmod.subdirmatcher(subpath, m)
2513 submatch = matchmod.subdirmatcher(subpath, m)
2511 if (subrepos or m.exact(subpath) or any(submatch.files())):
2514 if (subrepos or m.exact(subpath) or any(submatch.files())):
2512 sub = ctx.sub(subpath)
2515 sub = ctx.sub(subpath)
2513 try:
2516 try:
2514 recurse = m.exact(subpath) or subrepos
2517 recurse = m.exact(subpath) or subrepos
2515 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2518 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2516 ret = 0
2519 ret = 0
2517 except error.LookupError:
2520 except error.LookupError:
2518 ui.status(_("skipping missing subrepository: %s\n")
2521 ui.status(_("skipping missing subrepository: %s\n")
2519 % m.abs(subpath))
2522 % m.abs(subpath))
2520
2523
2521 return ret
2524 return ret
2522
2525
2523 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2526 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2524 join = lambda f: os.path.join(prefix, f)
2527 join = lambda f: os.path.join(prefix, f)
2525 ret = 0
2528 ret = 0
2526 s = repo.status(match=m, clean=True)
2529 s = repo.status(match=m, clean=True)
2527 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2530 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2528
2531
2529 wctx = repo[None]
2532 wctx = repo[None]
2530
2533
2531 if warnings is None:
2534 if warnings is None:
2532 warnings = []
2535 warnings = []
2533 warn = True
2536 warn = True
2534 else:
2537 else:
2535 warn = False
2538 warn = False
2536
2539
2537 subs = sorted(wctx.substate)
2540 subs = sorted(wctx.substate)
2538 total = len(subs)
2541 total = len(subs)
2539 count = 0
2542 count = 0
2540 for subpath in subs:
2543 for subpath in subs:
2541 count += 1
2544 count += 1
2542 submatch = matchmod.subdirmatcher(subpath, m)
2545 submatch = matchmod.subdirmatcher(subpath, m)
2543 if subrepos or m.exact(subpath) or any(submatch.files()):
2546 if subrepos or m.exact(subpath) or any(submatch.files()):
2544 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2547 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2545 sub = wctx.sub(subpath)
2548 sub = wctx.sub(subpath)
2546 try:
2549 try:
2547 if sub.removefiles(submatch, prefix, after, force, subrepos,
2550 if sub.removefiles(submatch, prefix, after, force, subrepos,
2548 warnings):
2551 warnings):
2549 ret = 1
2552 ret = 1
2550 except error.LookupError:
2553 except error.LookupError:
2551 warnings.append(_("skipping missing subrepository: %s\n")
2554 warnings.append(_("skipping missing subrepository: %s\n")
2552 % join(subpath))
2555 % join(subpath))
2553 ui.progress(_('searching'), None)
2556 ui.progress(_('searching'), None)
2554
2557
2555 # warn about failure to delete explicit files/dirs
2558 # warn about failure to delete explicit files/dirs
2556 deleteddirs = util.dirs(deleted)
2559 deleteddirs = util.dirs(deleted)
2557 files = m.files()
2560 files = m.files()
2558 total = len(files)
2561 total = len(files)
2559 count = 0
2562 count = 0
2560 for f in files:
2563 for f in files:
2561 def insubrepo():
2564 def insubrepo():
2562 for subpath in wctx.substate:
2565 for subpath in wctx.substate:
2563 if f.startswith(subpath + '/'):
2566 if f.startswith(subpath + '/'):
2564 return True
2567 return True
2565 return False
2568 return False
2566
2569
2567 count += 1
2570 count += 1
2568 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2571 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2569 isdir = f in deleteddirs or wctx.hasdir(f)
2572 isdir = f in deleteddirs or wctx.hasdir(f)
2570 if (f in repo.dirstate or isdir or f == '.'
2573 if (f in repo.dirstate or isdir or f == '.'
2571 or insubrepo() or f in subs):
2574 or insubrepo() or f in subs):
2572 continue
2575 continue
2573
2576
2574 if repo.wvfs.exists(f):
2577 if repo.wvfs.exists(f):
2575 if repo.wvfs.isdir(f):
2578 if repo.wvfs.isdir(f):
2576 warnings.append(_('not removing %s: no tracked files\n')
2579 warnings.append(_('not removing %s: no tracked files\n')
2577 % m.rel(f))
2580 % m.rel(f))
2578 else:
2581 else:
2579 warnings.append(_('not removing %s: file is untracked\n')
2582 warnings.append(_('not removing %s: file is untracked\n')
2580 % m.rel(f))
2583 % m.rel(f))
2581 # missing files will generate a warning elsewhere
2584 # missing files will generate a warning elsewhere
2582 ret = 1
2585 ret = 1
2583 ui.progress(_('deleting'), None)
2586 ui.progress(_('deleting'), None)
2584
2587
2585 if force:
2588 if force:
2586 list = modified + deleted + clean + added
2589 list = modified + deleted + clean + added
2587 elif after:
2590 elif after:
2588 list = deleted
2591 list = deleted
2589 remaining = modified + added + clean
2592 remaining = modified + added + clean
2590 total = len(remaining)
2593 total = len(remaining)
2591 count = 0
2594 count = 0
2592 for f in remaining:
2595 for f in remaining:
2593 count += 1
2596 count += 1
2594 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2597 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2595 warnings.append(_('not removing %s: file still exists\n')
2598 warnings.append(_('not removing %s: file still exists\n')
2596 % m.rel(f))
2599 % m.rel(f))
2597 ret = 1
2600 ret = 1
2598 ui.progress(_('skipping'), None)
2601 ui.progress(_('skipping'), None)
2599 else:
2602 else:
2600 list = deleted + clean
2603 list = deleted + clean
2601 total = len(modified) + len(added)
2604 total = len(modified) + len(added)
2602 count = 0
2605 count = 0
2603 for f in modified:
2606 for f in modified:
2604 count += 1
2607 count += 1
2605 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2608 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2606 warnings.append(_('not removing %s: file is modified (use -f'
2609 warnings.append(_('not removing %s: file is modified (use -f'
2607 ' to force removal)\n') % m.rel(f))
2610 ' to force removal)\n') % m.rel(f))
2608 ret = 1
2611 ret = 1
2609 for f in added:
2612 for f in added:
2610 count += 1
2613 count += 1
2611 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2614 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2612 warnings.append(_("not removing %s: file has been marked for add"
2615 warnings.append(_("not removing %s: file has been marked for add"
2613 " (use 'hg forget' to undo add)\n") % m.rel(f))
2616 " (use 'hg forget' to undo add)\n") % m.rel(f))
2614 ret = 1
2617 ret = 1
2615 ui.progress(_('skipping'), None)
2618 ui.progress(_('skipping'), None)
2616
2619
2617 list = sorted(list)
2620 list = sorted(list)
2618 total = len(list)
2621 total = len(list)
2619 count = 0
2622 count = 0
2620 for f in list:
2623 for f in list:
2621 count += 1
2624 count += 1
2622 if ui.verbose or not m.exact(f):
2625 if ui.verbose or not m.exact(f):
2623 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2626 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2624 ui.status(_('removing %s\n') % m.rel(f))
2627 ui.status(_('removing %s\n') % m.rel(f))
2625 ui.progress(_('deleting'), None)
2628 ui.progress(_('deleting'), None)
2626
2629
2627 with repo.wlock():
2630 with repo.wlock():
2628 if not after:
2631 if not after:
2629 for f in list:
2632 for f in list:
2630 if f in added:
2633 if f in added:
2631 continue # we never unlink added files on remove
2634 continue # we never unlink added files on remove
2632 repo.wvfs.unlinkpath(f, ignoremissing=True)
2635 repo.wvfs.unlinkpath(f, ignoremissing=True)
2633 repo[None].forget(list)
2636 repo[None].forget(list)
2634
2637
2635 if warn:
2638 if warn:
2636 for warning in warnings:
2639 for warning in warnings:
2637 ui.warn(warning)
2640 ui.warn(warning)
2638
2641
2639 return ret
2642 return ret
2640
2643
2641 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2644 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2642 err = 1
2645 err = 1
2643
2646
2644 def write(path):
2647 def write(path):
2645 filename = None
2648 filename = None
2646 if fntemplate:
2649 if fntemplate:
2647 filename = makefilename(repo, fntemplate, ctx.node(),
2650 filename = makefilename(repo, fntemplate, ctx.node(),
2648 pathname=os.path.join(prefix, path))
2651 pathname=os.path.join(prefix, path))
2649 with formatter.maybereopen(basefm, filename, opts) as fm:
2652 with formatter.maybereopen(basefm, filename, opts) as fm:
2650 data = ctx[path].data()
2653 data = ctx[path].data()
2651 if opts.get('decode'):
2654 if opts.get('decode'):
2652 data = repo.wwritedata(path, data)
2655 data = repo.wwritedata(path, data)
2653 fm.startitem()
2656 fm.startitem()
2654 fm.write('data', '%s', data)
2657 fm.write('data', '%s', data)
2655 fm.data(abspath=path, path=matcher.rel(path))
2658 fm.data(abspath=path, path=matcher.rel(path))
2656
2659
2657 # Automation often uses hg cat on single files, so special case it
2660 # Automation often uses hg cat on single files, so special case it
2658 # for performance to avoid the cost of parsing the manifest.
2661 # for performance to avoid the cost of parsing the manifest.
2659 if len(matcher.files()) == 1 and not matcher.anypats():
2662 if len(matcher.files()) == 1 and not matcher.anypats():
2660 file = matcher.files()[0]
2663 file = matcher.files()[0]
2661 mfl = repo.manifestlog
2664 mfl = repo.manifestlog
2662 mfnode = ctx.manifestnode()
2665 mfnode = ctx.manifestnode()
2663 try:
2666 try:
2664 if mfnode and mfl[mfnode].find(file)[0]:
2667 if mfnode and mfl[mfnode].find(file)[0]:
2665 write(file)
2668 write(file)
2666 return 0
2669 return 0
2667 except KeyError:
2670 except KeyError:
2668 pass
2671 pass
2669
2672
2670 for abs in ctx.walk(matcher):
2673 for abs in ctx.walk(matcher):
2671 write(abs)
2674 write(abs)
2672 err = 0
2675 err = 0
2673
2676
2674 for subpath in sorted(ctx.substate):
2677 for subpath in sorted(ctx.substate):
2675 sub = ctx.sub(subpath)
2678 sub = ctx.sub(subpath)
2676 try:
2679 try:
2677 submatch = matchmod.subdirmatcher(subpath, matcher)
2680 submatch = matchmod.subdirmatcher(subpath, matcher)
2678
2681
2679 if not sub.cat(submatch, basefm, fntemplate,
2682 if not sub.cat(submatch, basefm, fntemplate,
2680 os.path.join(prefix, sub._path), **opts):
2683 os.path.join(prefix, sub._path), **opts):
2681 err = 0
2684 err = 0
2682 except error.RepoLookupError:
2685 except error.RepoLookupError:
2683 ui.status(_("skipping missing subrepository: %s\n")
2686 ui.status(_("skipping missing subrepository: %s\n")
2684 % os.path.join(prefix, subpath))
2687 % os.path.join(prefix, subpath))
2685
2688
2686 return err
2689 return err
2687
2690
2688 def commit(ui, repo, commitfunc, pats, opts):
2691 def commit(ui, repo, commitfunc, pats, opts):
2689 '''commit the specified files or all outstanding changes'''
2692 '''commit the specified files or all outstanding changes'''
2690 date = opts.get('date')
2693 date = opts.get('date')
2691 if date:
2694 if date:
2692 opts['date'] = util.parsedate(date)
2695 opts['date'] = util.parsedate(date)
2693 message = logmessage(ui, opts)
2696 message = logmessage(ui, opts)
2694 matcher = scmutil.match(repo[None], pats, opts)
2697 matcher = scmutil.match(repo[None], pats, opts)
2695
2698
2696 # extract addremove carefully -- this function can be called from a command
2699 # extract addremove carefully -- this function can be called from a command
2697 # that doesn't support addremove
2700 # that doesn't support addremove
2698 if opts.get('addremove'):
2701 if opts.get('addremove'):
2699 if scmutil.addremove(repo, matcher, "", opts) != 0:
2702 if scmutil.addremove(repo, matcher, "", opts) != 0:
2700 raise error.Abort(
2703 raise error.Abort(
2701 _("failed to mark all new/missing files as added/removed"))
2704 _("failed to mark all new/missing files as added/removed"))
2702
2705
2703 return commitfunc(ui, repo, message, matcher, opts)
2706 return commitfunc(ui, repo, message, matcher, opts)
2704
2707
2705 def samefile(f, ctx1, ctx2):
2708 def samefile(f, ctx1, ctx2):
2706 if f in ctx1.manifest():
2709 if f in ctx1.manifest():
2707 a = ctx1.filectx(f)
2710 a = ctx1.filectx(f)
2708 if f in ctx2.manifest():
2711 if f in ctx2.manifest():
2709 b = ctx2.filectx(f)
2712 b = ctx2.filectx(f)
2710 return (not a.cmp(b)
2713 return (not a.cmp(b)
2711 and a.flags() == b.flags())
2714 and a.flags() == b.flags())
2712 else:
2715 else:
2713 return False
2716 return False
2714 else:
2717 else:
2715 return f not in ctx2.manifest()
2718 return f not in ctx2.manifest()
2716
2719
2717 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2720 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2718 # avoid cycle context -> subrepo -> cmdutil
2721 # avoid cycle context -> subrepo -> cmdutil
2719 from . import context
2722 from . import context
2720
2723
2721 # amend will reuse the existing user if not specified, but the obsolete
2724 # amend will reuse the existing user if not specified, but the obsolete
2722 # marker creation requires that the current user's name is specified.
2725 # marker creation requires that the current user's name is specified.
2723 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2726 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2724 ui.username() # raise exception if username not set
2727 ui.username() # raise exception if username not set
2725
2728
2726 ui.note(_('amending changeset %s\n') % old)
2729 ui.note(_('amending changeset %s\n') % old)
2727 base = old.p1()
2730 base = old.p1()
2728 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2731 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2729
2732
2730 wlock = lock = newid = None
2733 wlock = lock = newid = None
2731 try:
2734 try:
2732 wlock = repo.wlock()
2735 wlock = repo.wlock()
2733 lock = repo.lock()
2736 lock = repo.lock()
2734 with repo.transaction('amend') as tr:
2737 with repo.transaction('amend') as tr:
2735 # See if we got a message from -m or -l, if not, open the editor
2738 # See if we got a message from -m or -l, if not, open the editor
2736 # with the message of the changeset to amend
2739 # with the message of the changeset to amend
2737 message = logmessage(ui, opts)
2740 message = logmessage(ui, opts)
2738 # ensure logfile does not conflict with later enforcement of the
2741 # ensure logfile does not conflict with later enforcement of the
2739 # message. potential logfile content has been processed by
2742 # message. potential logfile content has been processed by
2740 # `logmessage` anyway.
2743 # `logmessage` anyway.
2741 opts.pop('logfile')
2744 opts.pop('logfile')
2742 # First, do a regular commit to record all changes in the working
2745 # First, do a regular commit to record all changes in the working
2743 # directory (if there are any)
2746 # directory (if there are any)
2744 ui.callhooks = False
2747 ui.callhooks = False
2745 activebookmark = repo._bookmarks.active
2748 activebookmark = repo._bookmarks.active
2746 try:
2749 try:
2747 repo._bookmarks.active = None
2750 repo._bookmarks.active = None
2748 opts['message'] = 'temporary amend commit for %s' % old
2751 opts['message'] = 'temporary amend commit for %s' % old
2749 node = commit(ui, repo, commitfunc, pats, opts)
2752 node = commit(ui, repo, commitfunc, pats, opts)
2750 finally:
2753 finally:
2751 repo._bookmarks.active = activebookmark
2754 repo._bookmarks.active = activebookmark
2752 repo._bookmarks.recordchange(tr)
2755 repo._bookmarks.recordchange(tr)
2753 ui.callhooks = True
2756 ui.callhooks = True
2754 ctx = repo[node]
2757 ctx = repo[node]
2755
2758
2756 # Participating changesets:
2759 # Participating changesets:
2757 #
2760 #
2758 # node/ctx o - new (intermediate) commit that contains changes
2761 # node/ctx o - new (intermediate) commit that contains changes
2759 # | from working dir to go into amending commit
2762 # | from working dir to go into amending commit
2760 # | (or a workingctx if there were no changes)
2763 # | (or a workingctx if there were no changes)
2761 # |
2764 # |
2762 # old o - changeset to amend
2765 # old o - changeset to amend
2763 # |
2766 # |
2764 # base o - parent of amending changeset
2767 # base o - parent of amending changeset
2765
2768
2766 # Update extra dict from amended commit (e.g. to preserve graft
2769 # Update extra dict from amended commit (e.g. to preserve graft
2767 # source)
2770 # source)
2768 extra.update(old.extra())
2771 extra.update(old.extra())
2769
2772
2770 # Also update it from the intermediate commit or from the wctx
2773 # Also update it from the intermediate commit or from the wctx
2771 extra.update(ctx.extra())
2774 extra.update(ctx.extra())
2772
2775
2773 if len(old.parents()) > 1:
2776 if len(old.parents()) > 1:
2774 # ctx.files() isn't reliable for merges, so fall back to the
2777 # ctx.files() isn't reliable for merges, so fall back to the
2775 # slower repo.status() method
2778 # slower repo.status() method
2776 files = set([fn for st in repo.status(base, old)[:3]
2779 files = set([fn for st in repo.status(base, old)[:3]
2777 for fn in st])
2780 for fn in st])
2778 else:
2781 else:
2779 files = set(old.files())
2782 files = set(old.files())
2780
2783
2781 # Second, we use either the commit we just did, or if there were no
2784 # Second, we use either the commit we just did, or if there were no
2782 # changes the parent of the working directory as the version of the
2785 # changes the parent of the working directory as the version of the
2783 # files in the final amend commit
2786 # files in the final amend commit
2784 if node:
2787 if node:
2785 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2788 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2786
2789
2787 user = ctx.user()
2790 user = ctx.user()
2788 date = ctx.date()
2791 date = ctx.date()
2789 # Recompute copies (avoid recording a -> b -> a)
2792 # Recompute copies (avoid recording a -> b -> a)
2790 copied = copies.pathcopies(base, ctx)
2793 copied = copies.pathcopies(base, ctx)
2791 if old.p2:
2794 if old.p2:
2792 copied.update(copies.pathcopies(old.p2(), ctx))
2795 copied.update(copies.pathcopies(old.p2(), ctx))
2793
2796
2794 # Prune files which were reverted by the updates: if old
2797 # Prune files which were reverted by the updates: if old
2795 # introduced file X and our intermediate commit, node,
2798 # introduced file X and our intermediate commit, node,
2796 # renamed that file, then those two files are the same and
2799 # renamed that file, then those two files are the same and
2797 # we can discard X from our list of files. Likewise if X
2800 # we can discard X from our list of files. Likewise if X
2798 # was deleted, it's no longer relevant
2801 # was deleted, it's no longer relevant
2799 files.update(ctx.files())
2802 files.update(ctx.files())
2800 files = [f for f in files if not samefile(f, ctx, base)]
2803 files = [f for f in files if not samefile(f, ctx, base)]
2801
2804
2802 def filectxfn(repo, ctx_, path):
2805 def filectxfn(repo, ctx_, path):
2803 try:
2806 try:
2804 fctx = ctx[path]
2807 fctx = ctx[path]
2805 flags = fctx.flags()
2808 flags = fctx.flags()
2806 mctx = context.memfilectx(repo,
2809 mctx = context.memfilectx(repo,
2807 fctx.path(), fctx.data(),
2810 fctx.path(), fctx.data(),
2808 islink='l' in flags,
2811 islink='l' in flags,
2809 isexec='x' in flags,
2812 isexec='x' in flags,
2810 copied=copied.get(path))
2813 copied=copied.get(path))
2811 return mctx
2814 return mctx
2812 except KeyError:
2815 except KeyError:
2813 return None
2816 return None
2814 else:
2817 else:
2815 ui.note(_('copying changeset %s to %s\n') % (old, base))
2818 ui.note(_('copying changeset %s to %s\n') % (old, base))
2816
2819
2817 # Use version of files as in the old cset
2820 # Use version of files as in the old cset
2818 def filectxfn(repo, ctx_, path):
2821 def filectxfn(repo, ctx_, path):
2819 try:
2822 try:
2820 return old.filectx(path)
2823 return old.filectx(path)
2821 except KeyError:
2824 except KeyError:
2822 return None
2825 return None
2823
2826
2824 user = opts.get('user') or old.user()
2827 user = opts.get('user') or old.user()
2825 date = opts.get('date') or old.date()
2828 date = opts.get('date') or old.date()
2826 editform = mergeeditform(old, 'commit.amend')
2829 editform = mergeeditform(old, 'commit.amend')
2827 editor = getcommiteditor(editform=editform, **opts)
2830 editor = getcommiteditor(editform=editform, **opts)
2828 if not message:
2831 if not message:
2829 editor = getcommiteditor(edit=True, editform=editform)
2832 editor = getcommiteditor(edit=True, editform=editform)
2830 message = old.description()
2833 message = old.description()
2831
2834
2832 pureextra = extra.copy()
2835 pureextra = extra.copy()
2833 extra['amend_source'] = old.hex()
2836 extra['amend_source'] = old.hex()
2834
2837
2835 new = context.memctx(repo,
2838 new = context.memctx(repo,
2836 parents=[base.node(), old.p2().node()],
2839 parents=[base.node(), old.p2().node()],
2837 text=message,
2840 text=message,
2838 files=files,
2841 files=files,
2839 filectxfn=filectxfn,
2842 filectxfn=filectxfn,
2840 user=user,
2843 user=user,
2841 date=date,
2844 date=date,
2842 extra=extra,
2845 extra=extra,
2843 editor=editor)
2846 editor=editor)
2844
2847
2845 newdesc = changelog.stripdesc(new.description())
2848 newdesc = changelog.stripdesc(new.description())
2846 if ((not node)
2849 if ((not node)
2847 and newdesc == old.description()
2850 and newdesc == old.description()
2848 and user == old.user()
2851 and user == old.user()
2849 and date == old.date()
2852 and date == old.date()
2850 and pureextra == old.extra()):
2853 and pureextra == old.extra()):
2851 # nothing changed. continuing here would create a new node
2854 # nothing changed. continuing here would create a new node
2852 # anyway because of the amend_source noise.
2855 # anyway because of the amend_source noise.
2853 #
2856 #
2854 # This not what we expect from amend.
2857 # This not what we expect from amend.
2855 return old.node()
2858 return old.node()
2856
2859
2857 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2860 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2858 try:
2861 try:
2859 if opts.get('secret'):
2862 if opts.get('secret'):
2860 commitphase = 'secret'
2863 commitphase = 'secret'
2861 else:
2864 else:
2862 commitphase = old.phase()
2865 commitphase = old.phase()
2863 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2866 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2864 newid = repo.commitctx(new)
2867 newid = repo.commitctx(new)
2865 finally:
2868 finally:
2866 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2869 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2867 if newid != old.node():
2870 if newid != old.node():
2868 # Reroute the working copy parent to the new changeset
2871 # Reroute the working copy parent to the new changeset
2869 repo.setparents(newid, nullid)
2872 repo.setparents(newid, nullid)
2870
2873
2871 # Move bookmarks from old parent to amend commit
2874 # Move bookmarks from old parent to amend commit
2872 bms = repo.nodebookmarks(old.node())
2875 bms = repo.nodebookmarks(old.node())
2873 if bms:
2876 if bms:
2874 marks = repo._bookmarks
2877 marks = repo._bookmarks
2875 for bm in bms:
2878 for bm in bms:
2876 ui.debug('moving bookmarks %r from %s to %s\n' %
2879 ui.debug('moving bookmarks %r from %s to %s\n' %
2877 (marks, old.hex(), hex(newid)))
2880 (marks, old.hex(), hex(newid)))
2878 marks[bm] = newid
2881 marks[bm] = newid
2879 marks.recordchange(tr)
2882 marks.recordchange(tr)
2880 #commit the whole amend process
2883 #commit the whole amend process
2881 if createmarkers:
2884 if createmarkers:
2882 # mark the new changeset as successor of the rewritten one
2885 # mark the new changeset as successor of the rewritten one
2883 new = repo[newid]
2886 new = repo[newid]
2884 obs = [(old, (new,))]
2887 obs = [(old, (new,))]
2885 if node:
2888 if node:
2886 obs.append((ctx, ()))
2889 obs.append((ctx, ()))
2887
2890
2888 obsolete.createmarkers(repo, obs, operation='amend')
2891 obsolete.createmarkers(repo, obs, operation='amend')
2889 if not createmarkers and newid != old.node():
2892 if not createmarkers and newid != old.node():
2890 # Strip the intermediate commit (if there was one) and the amended
2893 # Strip the intermediate commit (if there was one) and the amended
2891 # commit
2894 # commit
2892 if node:
2895 if node:
2893 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2896 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2894 ui.note(_('stripping amended changeset %s\n') % old)
2897 ui.note(_('stripping amended changeset %s\n') % old)
2895 repair.strip(ui, repo, old.node(), topic='amend-backup')
2898 repair.strip(ui, repo, old.node(), topic='amend-backup')
2896 finally:
2899 finally:
2897 lockmod.release(lock, wlock)
2900 lockmod.release(lock, wlock)
2898 return newid
2901 return newid
2899
2902
2900 def commiteditor(repo, ctx, subs, editform=''):
2903 def commiteditor(repo, ctx, subs, editform=''):
2901 if ctx.description():
2904 if ctx.description():
2902 return ctx.description()
2905 return ctx.description()
2903 return commitforceeditor(repo, ctx, subs, editform=editform,
2906 return commitforceeditor(repo, ctx, subs, editform=editform,
2904 unchangedmessagedetection=True)
2907 unchangedmessagedetection=True)
2905
2908
2906 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2909 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2907 editform='', unchangedmessagedetection=False):
2910 editform='', unchangedmessagedetection=False):
2908 if not extramsg:
2911 if not extramsg:
2909 extramsg = _("Leave message empty to abort commit.")
2912 extramsg = _("Leave message empty to abort commit.")
2910
2913
2911 forms = [e for e in editform.split('.') if e]
2914 forms = [e for e in editform.split('.') if e]
2912 forms.insert(0, 'changeset')
2915 forms.insert(0, 'changeset')
2913 templatetext = None
2916 templatetext = None
2914 while forms:
2917 while forms:
2915 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2918 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2916 if tmpl:
2919 if tmpl:
2917 tmpl = templater.unquotestring(tmpl)
2920 tmpl = templater.unquotestring(tmpl)
2918 templatetext = committext = buildcommittemplate(
2921 templatetext = committext = buildcommittemplate(
2919 repo, ctx, subs, extramsg, tmpl)
2922 repo, ctx, subs, extramsg, tmpl)
2920 break
2923 break
2921 forms.pop()
2924 forms.pop()
2922 else:
2925 else:
2923 committext = buildcommittext(repo, ctx, subs, extramsg)
2926 committext = buildcommittext(repo, ctx, subs, extramsg)
2924
2927
2925 # run editor in the repository root
2928 # run editor in the repository root
2926 olddir = pycompat.getcwd()
2929 olddir = pycompat.getcwd()
2927 os.chdir(repo.root)
2930 os.chdir(repo.root)
2928
2931
2929 # make in-memory changes visible to external process
2932 # make in-memory changes visible to external process
2930 tr = repo.currenttransaction()
2933 tr = repo.currenttransaction()
2931 repo.dirstate.write(tr)
2934 repo.dirstate.write(tr)
2932 pending = tr and tr.writepending() and repo.root
2935 pending = tr and tr.writepending() and repo.root
2933
2936
2934 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2937 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2935 editform=editform, pending=pending,
2938 editform=editform, pending=pending,
2936 repopath=repo.path)
2939 repopath=repo.path)
2937 text = editortext
2940 text = editortext
2938
2941
2939 # strip away anything below this special string (used for editors that want
2942 # strip away anything below this special string (used for editors that want
2940 # to display the diff)
2943 # to display the diff)
2941 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
2944 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
2942 if stripbelow:
2945 if stripbelow:
2943 text = text[:stripbelow.start()]
2946 text = text[:stripbelow.start()]
2944
2947
2945 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2948 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2946 os.chdir(olddir)
2949 os.chdir(olddir)
2947
2950
2948 if finishdesc:
2951 if finishdesc:
2949 text = finishdesc(text)
2952 text = finishdesc(text)
2950 if not text.strip():
2953 if not text.strip():
2951 raise error.Abort(_("empty commit message"))
2954 raise error.Abort(_("empty commit message"))
2952 if unchangedmessagedetection and editortext == templatetext:
2955 if unchangedmessagedetection and editortext == templatetext:
2953 raise error.Abort(_("commit message unchanged"))
2956 raise error.Abort(_("commit message unchanged"))
2954
2957
2955 return text
2958 return text
2956
2959
2957 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2960 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2958 ui = repo.ui
2961 ui = repo.ui
2959 spec = _lookuplogtemplate(ui, tmpl, None)
2962 spec = _lookuplogtemplate(ui, tmpl, None)
2960 t = changeset_templater(ui, repo, spec, None, {}, False)
2963 t = changeset_templater(ui, repo, spec, None, {}, False)
2961
2964
2962 for k, v in repo.ui.configitems('committemplate'):
2965 for k, v in repo.ui.configitems('committemplate'):
2963 if k != 'changeset':
2966 if k != 'changeset':
2964 t.t.cache[k] = v
2967 t.t.cache[k] = v
2965
2968
2966 if not extramsg:
2969 if not extramsg:
2967 extramsg = '' # ensure that extramsg is string
2970 extramsg = '' # ensure that extramsg is string
2968
2971
2969 ui.pushbuffer()
2972 ui.pushbuffer()
2970 t.show(ctx, extramsg=extramsg)
2973 t.show(ctx, extramsg=extramsg)
2971 return ui.popbuffer()
2974 return ui.popbuffer()
2972
2975
2973 def hgprefix(msg):
2976 def hgprefix(msg):
2974 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2977 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2975
2978
2976 def buildcommittext(repo, ctx, subs, extramsg):
2979 def buildcommittext(repo, ctx, subs, extramsg):
2977 edittext = []
2980 edittext = []
2978 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2981 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2979 if ctx.description():
2982 if ctx.description():
2980 edittext.append(ctx.description())
2983 edittext.append(ctx.description())
2981 edittext.append("")
2984 edittext.append("")
2982 edittext.append("") # Empty line between message and comments.
2985 edittext.append("") # Empty line between message and comments.
2983 edittext.append(hgprefix(_("Enter commit message."
2986 edittext.append(hgprefix(_("Enter commit message."
2984 " Lines beginning with 'HG:' are removed.")))
2987 " Lines beginning with 'HG:' are removed.")))
2985 edittext.append(hgprefix(extramsg))
2988 edittext.append(hgprefix(extramsg))
2986 edittext.append("HG: --")
2989 edittext.append("HG: --")
2987 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2990 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2988 if ctx.p2():
2991 if ctx.p2():
2989 edittext.append(hgprefix(_("branch merge")))
2992 edittext.append(hgprefix(_("branch merge")))
2990 if ctx.branch():
2993 if ctx.branch():
2991 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2994 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2992 if bookmarks.isactivewdirparent(repo):
2995 if bookmarks.isactivewdirparent(repo):
2993 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2996 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2994 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2997 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2995 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2998 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2996 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2999 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2997 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
3000 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2998 if not added and not modified and not removed:
3001 if not added and not modified and not removed:
2999 edittext.append(hgprefix(_("no files changed")))
3002 edittext.append(hgprefix(_("no files changed")))
3000 edittext.append("")
3003 edittext.append("")
3001
3004
3002 return "\n".join(edittext)
3005 return "\n".join(edittext)
3003
3006
3004 def commitstatus(repo, node, branch, bheads=None, opts=None):
3007 def commitstatus(repo, node, branch, bheads=None, opts=None):
3005 if opts is None:
3008 if opts is None:
3006 opts = {}
3009 opts = {}
3007 ctx = repo[node]
3010 ctx = repo[node]
3008 parents = ctx.parents()
3011 parents = ctx.parents()
3009
3012
3010 if (not opts.get('amend') and bheads and node not in bheads and not
3013 if (not opts.get('amend') and bheads and node not in bheads and not
3011 [x for x in parents if x.node() in bheads and x.branch() == branch]):
3014 [x for x in parents if x.node() in bheads and x.branch() == branch]):
3012 repo.ui.status(_('created new head\n'))
3015 repo.ui.status(_('created new head\n'))
3013 # The message is not printed for initial roots. For the other
3016 # The message is not printed for initial roots. For the other
3014 # changesets, it is printed in the following situations:
3017 # changesets, it is printed in the following situations:
3015 #
3018 #
3016 # Par column: for the 2 parents with ...
3019 # Par column: for the 2 parents with ...
3017 # N: null or no parent
3020 # N: null or no parent
3018 # B: parent is on another named branch
3021 # B: parent is on another named branch
3019 # C: parent is a regular non head changeset
3022 # C: parent is a regular non head changeset
3020 # H: parent was a branch head of the current branch
3023 # H: parent was a branch head of the current branch
3021 # Msg column: whether we print "created new head" message
3024 # Msg column: whether we print "created new head" message
3022 # In the following, it is assumed that there already exists some
3025 # In the following, it is assumed that there already exists some
3023 # initial branch heads of the current branch, otherwise nothing is
3026 # initial branch heads of the current branch, otherwise nothing is
3024 # printed anyway.
3027 # printed anyway.
3025 #
3028 #
3026 # Par Msg Comment
3029 # Par Msg Comment
3027 # N N y additional topo root
3030 # N N y additional topo root
3028 #
3031 #
3029 # B N y additional branch root
3032 # B N y additional branch root
3030 # C N y additional topo head
3033 # C N y additional topo head
3031 # H N n usual case
3034 # H N n usual case
3032 #
3035 #
3033 # B B y weird additional branch root
3036 # B B y weird additional branch root
3034 # C B y branch merge
3037 # C B y branch merge
3035 # H B n merge with named branch
3038 # H B n merge with named branch
3036 #
3039 #
3037 # C C y additional head from merge
3040 # C C y additional head from merge
3038 # C H n merge with a head
3041 # C H n merge with a head
3039 #
3042 #
3040 # H H n head merge: head count decreases
3043 # H H n head merge: head count decreases
3041
3044
3042 if not opts.get('close_branch'):
3045 if not opts.get('close_branch'):
3043 for r in parents:
3046 for r in parents:
3044 if r.closesbranch() and r.branch() == branch:
3047 if r.closesbranch() and r.branch() == branch:
3045 repo.ui.status(_('reopening closed branch head %d\n') % r)
3048 repo.ui.status(_('reopening closed branch head %d\n') % r)
3046
3049
3047 if repo.ui.debugflag:
3050 if repo.ui.debugflag:
3048 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
3051 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
3049 elif repo.ui.verbose:
3052 elif repo.ui.verbose:
3050 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
3053 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
3051
3054
3052 def postcommitstatus(repo, pats, opts):
3055 def postcommitstatus(repo, pats, opts):
3053 return repo.status(match=scmutil.match(repo[None], pats, opts))
3056 return repo.status(match=scmutil.match(repo[None], pats, opts))
3054
3057
3055 def revert(ui, repo, ctx, parents, *pats, **opts):
3058 def revert(ui, repo, ctx, parents, *pats, **opts):
3056 parent, p2 = parents
3059 parent, p2 = parents
3057 node = ctx.node()
3060 node = ctx.node()
3058
3061
3059 mf = ctx.manifest()
3062 mf = ctx.manifest()
3060 if node == p2:
3063 if node == p2:
3061 parent = p2
3064 parent = p2
3062
3065
3063 # need all matching names in dirstate and manifest of target rev,
3066 # need all matching names in dirstate and manifest of target rev,
3064 # so have to walk both. do not print errors if files exist in one
3067 # so have to walk both. do not print errors if files exist in one
3065 # but not other. in both cases, filesets should be evaluated against
3068 # but not other. in both cases, filesets should be evaluated against
3066 # workingctx to get consistent result (issue4497). this means 'set:**'
3069 # workingctx to get consistent result (issue4497). this means 'set:**'
3067 # cannot be used to select missing files from target rev.
3070 # cannot be used to select missing files from target rev.
3068
3071
3069 # `names` is a mapping for all elements in working copy and target revision
3072 # `names` is a mapping for all elements in working copy and target revision
3070 # The mapping is in the form:
3073 # The mapping is in the form:
3071 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3074 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3072 names = {}
3075 names = {}
3073
3076
3074 with repo.wlock():
3077 with repo.wlock():
3075 ## filling of the `names` mapping
3078 ## filling of the `names` mapping
3076 # walk dirstate to fill `names`
3079 # walk dirstate to fill `names`
3077
3080
3078 interactive = opts.get('interactive', False)
3081 interactive = opts.get('interactive', False)
3079 wctx = repo[None]
3082 wctx = repo[None]
3080 m = scmutil.match(wctx, pats, opts)
3083 m = scmutil.match(wctx, pats, opts)
3081
3084
3082 # we'll need this later
3085 # we'll need this later
3083 targetsubs = sorted(s for s in wctx.substate if m(s))
3086 targetsubs = sorted(s for s in wctx.substate if m(s))
3084
3087
3085 if not m.always():
3088 if not m.always():
3086 matcher = matchmod.badmatch(m, lambda x, y: False)
3089 matcher = matchmod.badmatch(m, lambda x, y: False)
3087 for abs in wctx.walk(matcher):
3090 for abs in wctx.walk(matcher):
3088 names[abs] = m.rel(abs), m.exact(abs)
3091 names[abs] = m.rel(abs), m.exact(abs)
3089
3092
3090 # walk target manifest to fill `names`
3093 # walk target manifest to fill `names`
3091
3094
3092 def badfn(path, msg):
3095 def badfn(path, msg):
3093 if path in names:
3096 if path in names:
3094 return
3097 return
3095 if path in ctx.substate:
3098 if path in ctx.substate:
3096 return
3099 return
3097 path_ = path + '/'
3100 path_ = path + '/'
3098 for f in names:
3101 for f in names:
3099 if f.startswith(path_):
3102 if f.startswith(path_):
3100 return
3103 return
3101 ui.warn("%s: %s\n" % (m.rel(path), msg))
3104 ui.warn("%s: %s\n" % (m.rel(path), msg))
3102
3105
3103 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3106 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3104 if abs not in names:
3107 if abs not in names:
3105 names[abs] = m.rel(abs), m.exact(abs)
3108 names[abs] = m.rel(abs), m.exact(abs)
3106
3109
3107 # Find status of all file in `names`.
3110 # Find status of all file in `names`.
3108 m = scmutil.matchfiles(repo, names)
3111 m = scmutil.matchfiles(repo, names)
3109
3112
3110 changes = repo.status(node1=node, match=m,
3113 changes = repo.status(node1=node, match=m,
3111 unknown=True, ignored=True, clean=True)
3114 unknown=True, ignored=True, clean=True)
3112 else:
3115 else:
3113 changes = repo.status(node1=node, match=m)
3116 changes = repo.status(node1=node, match=m)
3114 for kind in changes:
3117 for kind in changes:
3115 for abs in kind:
3118 for abs in kind:
3116 names[abs] = m.rel(abs), m.exact(abs)
3119 names[abs] = m.rel(abs), m.exact(abs)
3117
3120
3118 m = scmutil.matchfiles(repo, names)
3121 m = scmutil.matchfiles(repo, names)
3119
3122
3120 modified = set(changes.modified)
3123 modified = set(changes.modified)
3121 added = set(changes.added)
3124 added = set(changes.added)
3122 removed = set(changes.removed)
3125 removed = set(changes.removed)
3123 _deleted = set(changes.deleted)
3126 _deleted = set(changes.deleted)
3124 unknown = set(changes.unknown)
3127 unknown = set(changes.unknown)
3125 unknown.update(changes.ignored)
3128 unknown.update(changes.ignored)
3126 clean = set(changes.clean)
3129 clean = set(changes.clean)
3127 modadded = set()
3130 modadded = set()
3128
3131
3129 # We need to account for the state of the file in the dirstate,
3132 # We need to account for the state of the file in the dirstate,
3130 # even when we revert against something else than parent. This will
3133 # even when we revert against something else than parent. This will
3131 # slightly alter the behavior of revert (doing back up or not, delete
3134 # slightly alter the behavior of revert (doing back up or not, delete
3132 # or just forget etc).
3135 # or just forget etc).
3133 if parent == node:
3136 if parent == node:
3134 dsmodified = modified
3137 dsmodified = modified
3135 dsadded = added
3138 dsadded = added
3136 dsremoved = removed
3139 dsremoved = removed
3137 # store all local modifications, useful later for rename detection
3140 # store all local modifications, useful later for rename detection
3138 localchanges = dsmodified | dsadded
3141 localchanges = dsmodified | dsadded
3139 modified, added, removed = set(), set(), set()
3142 modified, added, removed = set(), set(), set()
3140 else:
3143 else:
3141 changes = repo.status(node1=parent, match=m)
3144 changes = repo.status(node1=parent, match=m)
3142 dsmodified = set(changes.modified)
3145 dsmodified = set(changes.modified)
3143 dsadded = set(changes.added)
3146 dsadded = set(changes.added)
3144 dsremoved = set(changes.removed)
3147 dsremoved = set(changes.removed)
3145 # store all local modifications, useful later for rename detection
3148 # store all local modifications, useful later for rename detection
3146 localchanges = dsmodified | dsadded
3149 localchanges = dsmodified | dsadded
3147
3150
3148 # only take into account for removes between wc and target
3151 # only take into account for removes between wc and target
3149 clean |= dsremoved - removed
3152 clean |= dsremoved - removed
3150 dsremoved &= removed
3153 dsremoved &= removed
3151 # distinct between dirstate remove and other
3154 # distinct between dirstate remove and other
3152 removed -= dsremoved
3155 removed -= dsremoved
3153
3156
3154 modadded = added & dsmodified
3157 modadded = added & dsmodified
3155 added -= modadded
3158 added -= modadded
3156
3159
3157 # tell newly modified apart.
3160 # tell newly modified apart.
3158 dsmodified &= modified
3161 dsmodified &= modified
3159 dsmodified |= modified & dsadded # dirstate added may need backup
3162 dsmodified |= modified & dsadded # dirstate added may need backup
3160 modified -= dsmodified
3163 modified -= dsmodified
3161
3164
3162 # We need to wait for some post-processing to update this set
3165 # We need to wait for some post-processing to update this set
3163 # before making the distinction. The dirstate will be used for
3166 # before making the distinction. The dirstate will be used for
3164 # that purpose.
3167 # that purpose.
3165 dsadded = added
3168 dsadded = added
3166
3169
3167 # in case of merge, files that are actually added can be reported as
3170 # in case of merge, files that are actually added can be reported as
3168 # modified, we need to post process the result
3171 # modified, we need to post process the result
3169 if p2 != nullid:
3172 if p2 != nullid:
3170 mergeadd = set(dsmodified)
3173 mergeadd = set(dsmodified)
3171 for path in dsmodified:
3174 for path in dsmodified:
3172 if path in mf:
3175 if path in mf:
3173 mergeadd.remove(path)
3176 mergeadd.remove(path)
3174 dsadded |= mergeadd
3177 dsadded |= mergeadd
3175 dsmodified -= mergeadd
3178 dsmodified -= mergeadd
3176
3179
3177 # if f is a rename, update `names` to also revert the source
3180 # if f is a rename, update `names` to also revert the source
3178 cwd = repo.getcwd()
3181 cwd = repo.getcwd()
3179 for f in localchanges:
3182 for f in localchanges:
3180 src = repo.dirstate.copied(f)
3183 src = repo.dirstate.copied(f)
3181 # XXX should we check for rename down to target node?
3184 # XXX should we check for rename down to target node?
3182 if src and src not in names and repo.dirstate[src] == 'r':
3185 if src and src not in names and repo.dirstate[src] == 'r':
3183 dsremoved.add(src)
3186 dsremoved.add(src)
3184 names[src] = (repo.pathto(src, cwd), True)
3187 names[src] = (repo.pathto(src, cwd), True)
3185
3188
3186 # determine the exact nature of the deleted changesets
3189 # determine the exact nature of the deleted changesets
3187 deladded = set(_deleted)
3190 deladded = set(_deleted)
3188 for path in _deleted:
3191 for path in _deleted:
3189 if path in mf:
3192 if path in mf:
3190 deladded.remove(path)
3193 deladded.remove(path)
3191 deleted = _deleted - deladded
3194 deleted = _deleted - deladded
3192
3195
3193 # distinguish between file to forget and the other
3196 # distinguish between file to forget and the other
3194 added = set()
3197 added = set()
3195 for abs in dsadded:
3198 for abs in dsadded:
3196 if repo.dirstate[abs] != 'a':
3199 if repo.dirstate[abs] != 'a':
3197 added.add(abs)
3200 added.add(abs)
3198 dsadded -= added
3201 dsadded -= added
3199
3202
3200 for abs in deladded:
3203 for abs in deladded:
3201 if repo.dirstate[abs] == 'a':
3204 if repo.dirstate[abs] == 'a':
3202 dsadded.add(abs)
3205 dsadded.add(abs)
3203 deladded -= dsadded
3206 deladded -= dsadded
3204
3207
3205 # For files marked as removed, we check if an unknown file is present at
3208 # For files marked as removed, we check if an unknown file is present at
3206 # the same path. If a such file exists it may need to be backed up.
3209 # the same path. If a such file exists it may need to be backed up.
3207 # Making the distinction at this stage helps have simpler backup
3210 # Making the distinction at this stage helps have simpler backup
3208 # logic.
3211 # logic.
3209 removunk = set()
3212 removunk = set()
3210 for abs in removed:
3213 for abs in removed:
3211 target = repo.wjoin(abs)
3214 target = repo.wjoin(abs)
3212 if os.path.lexists(target):
3215 if os.path.lexists(target):
3213 removunk.add(abs)
3216 removunk.add(abs)
3214 removed -= removunk
3217 removed -= removunk
3215
3218
3216 dsremovunk = set()
3219 dsremovunk = set()
3217 for abs in dsremoved:
3220 for abs in dsremoved:
3218 target = repo.wjoin(abs)
3221 target = repo.wjoin(abs)
3219 if os.path.lexists(target):
3222 if os.path.lexists(target):
3220 dsremovunk.add(abs)
3223 dsremovunk.add(abs)
3221 dsremoved -= dsremovunk
3224 dsremoved -= dsremovunk
3222
3225
3223 # action to be actually performed by revert
3226 # action to be actually performed by revert
3224 # (<list of file>, message>) tuple
3227 # (<list of file>, message>) tuple
3225 actions = {'revert': ([], _('reverting %s\n')),
3228 actions = {'revert': ([], _('reverting %s\n')),
3226 'add': ([], _('adding %s\n')),
3229 'add': ([], _('adding %s\n')),
3227 'remove': ([], _('removing %s\n')),
3230 'remove': ([], _('removing %s\n')),
3228 'drop': ([], _('removing %s\n')),
3231 'drop': ([], _('removing %s\n')),
3229 'forget': ([], _('forgetting %s\n')),
3232 'forget': ([], _('forgetting %s\n')),
3230 'undelete': ([], _('undeleting %s\n')),
3233 'undelete': ([], _('undeleting %s\n')),
3231 'noop': (None, _('no changes needed to %s\n')),
3234 'noop': (None, _('no changes needed to %s\n')),
3232 'unknown': (None, _('file not managed: %s\n')),
3235 'unknown': (None, _('file not managed: %s\n')),
3233 }
3236 }
3234
3237
3235 # "constant" that convey the backup strategy.
3238 # "constant" that convey the backup strategy.
3236 # All set to `discard` if `no-backup` is set do avoid checking
3239 # All set to `discard` if `no-backup` is set do avoid checking
3237 # no_backup lower in the code.
3240 # no_backup lower in the code.
3238 # These values are ordered for comparison purposes
3241 # These values are ordered for comparison purposes
3239 backupinteractive = 3 # do backup if interactively modified
3242 backupinteractive = 3 # do backup if interactively modified
3240 backup = 2 # unconditionally do backup
3243 backup = 2 # unconditionally do backup
3241 check = 1 # check if the existing file differs from target
3244 check = 1 # check if the existing file differs from target
3242 discard = 0 # never do backup
3245 discard = 0 # never do backup
3243 if opts.get('no_backup'):
3246 if opts.get('no_backup'):
3244 backupinteractive = backup = check = discard
3247 backupinteractive = backup = check = discard
3245 if interactive:
3248 if interactive:
3246 dsmodifiedbackup = backupinteractive
3249 dsmodifiedbackup = backupinteractive
3247 else:
3250 else:
3248 dsmodifiedbackup = backup
3251 dsmodifiedbackup = backup
3249 tobackup = set()
3252 tobackup = set()
3250
3253
3251 backupanddel = actions['remove']
3254 backupanddel = actions['remove']
3252 if not opts.get('no_backup'):
3255 if not opts.get('no_backup'):
3253 backupanddel = actions['drop']
3256 backupanddel = actions['drop']
3254
3257
3255 disptable = (
3258 disptable = (
3256 # dispatch table:
3259 # dispatch table:
3257 # file state
3260 # file state
3258 # action
3261 # action
3259 # make backup
3262 # make backup
3260
3263
3261 ## Sets that results that will change file on disk
3264 ## Sets that results that will change file on disk
3262 # Modified compared to target, no local change
3265 # Modified compared to target, no local change
3263 (modified, actions['revert'], discard),
3266 (modified, actions['revert'], discard),
3264 # Modified compared to target, but local file is deleted
3267 # Modified compared to target, but local file is deleted
3265 (deleted, actions['revert'], discard),
3268 (deleted, actions['revert'], discard),
3266 # Modified compared to target, local change
3269 # Modified compared to target, local change
3267 (dsmodified, actions['revert'], dsmodifiedbackup),
3270 (dsmodified, actions['revert'], dsmodifiedbackup),
3268 # Added since target
3271 # Added since target
3269 (added, actions['remove'], discard),
3272 (added, actions['remove'], discard),
3270 # Added in working directory
3273 # Added in working directory
3271 (dsadded, actions['forget'], discard),
3274 (dsadded, actions['forget'], discard),
3272 # Added since target, have local modification
3275 # Added since target, have local modification
3273 (modadded, backupanddel, backup),
3276 (modadded, backupanddel, backup),
3274 # Added since target but file is missing in working directory
3277 # Added since target but file is missing in working directory
3275 (deladded, actions['drop'], discard),
3278 (deladded, actions['drop'], discard),
3276 # Removed since target, before working copy parent
3279 # Removed since target, before working copy parent
3277 (removed, actions['add'], discard),
3280 (removed, actions['add'], discard),
3278 # Same as `removed` but an unknown file exists at the same path
3281 # Same as `removed` but an unknown file exists at the same path
3279 (removunk, actions['add'], check),
3282 (removunk, actions['add'], check),
3280 # Removed since targe, marked as such in working copy parent
3283 # Removed since targe, marked as such in working copy parent
3281 (dsremoved, actions['undelete'], discard),
3284 (dsremoved, actions['undelete'], discard),
3282 # Same as `dsremoved` but an unknown file exists at the same path
3285 # Same as `dsremoved` but an unknown file exists at the same path
3283 (dsremovunk, actions['undelete'], check),
3286 (dsremovunk, actions['undelete'], check),
3284 ## the following sets does not result in any file changes
3287 ## the following sets does not result in any file changes
3285 # File with no modification
3288 # File with no modification
3286 (clean, actions['noop'], discard),
3289 (clean, actions['noop'], discard),
3287 # Existing file, not tracked anywhere
3290 # Existing file, not tracked anywhere
3288 (unknown, actions['unknown'], discard),
3291 (unknown, actions['unknown'], discard),
3289 )
3292 )
3290
3293
3291 for abs, (rel, exact) in sorted(names.items()):
3294 for abs, (rel, exact) in sorted(names.items()):
3292 # target file to be touch on disk (relative to cwd)
3295 # target file to be touch on disk (relative to cwd)
3293 target = repo.wjoin(abs)
3296 target = repo.wjoin(abs)
3294 # search the entry in the dispatch table.
3297 # search the entry in the dispatch table.
3295 # if the file is in any of these sets, it was touched in the working
3298 # if the file is in any of these sets, it was touched in the working
3296 # directory parent and we are sure it needs to be reverted.
3299 # directory parent and we are sure it needs to be reverted.
3297 for table, (xlist, msg), dobackup in disptable:
3300 for table, (xlist, msg), dobackup in disptable:
3298 if abs not in table:
3301 if abs not in table:
3299 continue
3302 continue
3300 if xlist is not None:
3303 if xlist is not None:
3301 xlist.append(abs)
3304 xlist.append(abs)
3302 if dobackup:
3305 if dobackup:
3303 # If in interactive mode, don't automatically create
3306 # If in interactive mode, don't automatically create
3304 # .orig files (issue4793)
3307 # .orig files (issue4793)
3305 if dobackup == backupinteractive:
3308 if dobackup == backupinteractive:
3306 tobackup.add(abs)
3309 tobackup.add(abs)
3307 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3310 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3308 bakname = scmutil.origpath(ui, repo, rel)
3311 bakname = scmutil.origpath(ui, repo, rel)
3309 ui.note(_('saving current version of %s as %s\n') %
3312 ui.note(_('saving current version of %s as %s\n') %
3310 (rel, bakname))
3313 (rel, bakname))
3311 if not opts.get('dry_run'):
3314 if not opts.get('dry_run'):
3312 if interactive:
3315 if interactive:
3313 util.copyfile(target, bakname)
3316 util.copyfile(target, bakname)
3314 else:
3317 else:
3315 util.rename(target, bakname)
3318 util.rename(target, bakname)
3316 if ui.verbose or not exact:
3319 if ui.verbose or not exact:
3317 if not isinstance(msg, basestring):
3320 if not isinstance(msg, basestring):
3318 msg = msg(abs)
3321 msg = msg(abs)
3319 ui.status(msg % rel)
3322 ui.status(msg % rel)
3320 elif exact:
3323 elif exact:
3321 ui.warn(msg % rel)
3324 ui.warn(msg % rel)
3322 break
3325 break
3323
3326
3324 if not opts.get('dry_run'):
3327 if not opts.get('dry_run'):
3325 needdata = ('revert', 'add', 'undelete')
3328 needdata = ('revert', 'add', 'undelete')
3326 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3329 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3327 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3330 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3328
3331
3329 if targetsubs:
3332 if targetsubs:
3330 # Revert the subrepos on the revert list
3333 # Revert the subrepos on the revert list
3331 for sub in targetsubs:
3334 for sub in targetsubs:
3332 try:
3335 try:
3333 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3336 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3334 except KeyError:
3337 except KeyError:
3335 raise error.Abort("subrepository '%s' does not exist in %s!"
3338 raise error.Abort("subrepository '%s' does not exist in %s!"
3336 % (sub, short(ctx.node())))
3339 % (sub, short(ctx.node())))
3337
3340
3338 def _revertprefetch(repo, ctx, *files):
3341 def _revertprefetch(repo, ctx, *files):
3339 """Let extension changing the storage layer prefetch content"""
3342 """Let extension changing the storage layer prefetch content"""
3340 pass
3343 pass
3341
3344
3342 def _performrevert(repo, parents, ctx, actions, interactive=False,
3345 def _performrevert(repo, parents, ctx, actions, interactive=False,
3343 tobackup=None):
3346 tobackup=None):
3344 """function that actually perform all the actions computed for revert
3347 """function that actually perform all the actions computed for revert
3345
3348
3346 This is an independent function to let extension to plug in and react to
3349 This is an independent function to let extension to plug in and react to
3347 the imminent revert.
3350 the imminent revert.
3348
3351
3349 Make sure you have the working directory locked when calling this function.
3352 Make sure you have the working directory locked when calling this function.
3350 """
3353 """
3351 parent, p2 = parents
3354 parent, p2 = parents
3352 node = ctx.node()
3355 node = ctx.node()
3353 excluded_files = []
3356 excluded_files = []
3354 matcher_opts = {"exclude": excluded_files}
3357 matcher_opts = {"exclude": excluded_files}
3355
3358
3356 def checkout(f):
3359 def checkout(f):
3357 fc = ctx[f]
3360 fc = ctx[f]
3358 repo.wwrite(f, fc.data(), fc.flags())
3361 repo.wwrite(f, fc.data(), fc.flags())
3359
3362
3360 def doremove(f):
3363 def doremove(f):
3361 try:
3364 try:
3362 repo.wvfs.unlinkpath(f)
3365 repo.wvfs.unlinkpath(f)
3363 except OSError:
3366 except OSError:
3364 pass
3367 pass
3365 repo.dirstate.remove(f)
3368 repo.dirstate.remove(f)
3366
3369
3367 audit_path = pathutil.pathauditor(repo.root)
3370 audit_path = pathutil.pathauditor(repo.root)
3368 for f in actions['forget'][0]:
3371 for f in actions['forget'][0]:
3369 if interactive:
3372 if interactive:
3370 choice = repo.ui.promptchoice(
3373 choice = repo.ui.promptchoice(
3371 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3374 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3372 if choice == 0:
3375 if choice == 0:
3373 repo.dirstate.drop(f)
3376 repo.dirstate.drop(f)
3374 else:
3377 else:
3375 excluded_files.append(repo.wjoin(f))
3378 excluded_files.append(repo.wjoin(f))
3376 else:
3379 else:
3377 repo.dirstate.drop(f)
3380 repo.dirstate.drop(f)
3378 for f in actions['remove'][0]:
3381 for f in actions['remove'][0]:
3379 audit_path(f)
3382 audit_path(f)
3380 if interactive:
3383 if interactive:
3381 choice = repo.ui.promptchoice(
3384 choice = repo.ui.promptchoice(
3382 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3385 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3383 if choice == 0:
3386 if choice == 0:
3384 doremove(f)
3387 doremove(f)
3385 else:
3388 else:
3386 excluded_files.append(repo.wjoin(f))
3389 excluded_files.append(repo.wjoin(f))
3387 else:
3390 else:
3388 doremove(f)
3391 doremove(f)
3389 for f in actions['drop'][0]:
3392 for f in actions['drop'][0]:
3390 audit_path(f)
3393 audit_path(f)
3391 repo.dirstate.remove(f)
3394 repo.dirstate.remove(f)
3392
3395
3393 normal = None
3396 normal = None
3394 if node == parent:
3397 if node == parent:
3395 # We're reverting to our parent. If possible, we'd like status
3398 # We're reverting to our parent. If possible, we'd like status
3396 # to report the file as clean. We have to use normallookup for
3399 # to report the file as clean. We have to use normallookup for
3397 # merges to avoid losing information about merged/dirty files.
3400 # merges to avoid losing information about merged/dirty files.
3398 if p2 != nullid:
3401 if p2 != nullid:
3399 normal = repo.dirstate.normallookup
3402 normal = repo.dirstate.normallookup
3400 else:
3403 else:
3401 normal = repo.dirstate.normal
3404 normal = repo.dirstate.normal
3402
3405
3403 newlyaddedandmodifiedfiles = set()
3406 newlyaddedandmodifiedfiles = set()
3404 if interactive:
3407 if interactive:
3405 # Prompt the user for changes to revert
3408 # Prompt the user for changes to revert
3406 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3409 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3407 m = scmutil.match(ctx, torevert, matcher_opts)
3410 m = scmutil.match(ctx, torevert, matcher_opts)
3408 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3411 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3409 diffopts.nodates = True
3412 diffopts.nodates = True
3410 diffopts.git = True
3413 diffopts.git = True
3411 operation = 'discard'
3414 operation = 'discard'
3412 reversehunks = True
3415 reversehunks = True
3413 if node != parent:
3416 if node != parent:
3414 operation = 'revert'
3417 operation = 'revert'
3415 reversehunks = repo.ui.configbool('experimental',
3418 reversehunks = repo.ui.configbool('experimental',
3416 'revertalternateinteractivemode',
3419 'revertalternateinteractivemode',
3417 True)
3420 True)
3418 if reversehunks:
3421 if reversehunks:
3419 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3422 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3420 else:
3423 else:
3421 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3424 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3422 originalchunks = patch.parsepatch(diff)
3425 originalchunks = patch.parsepatch(diff)
3423
3426
3424 try:
3427 try:
3425
3428
3426 chunks, opts = recordfilter(repo.ui, originalchunks,
3429 chunks, opts = recordfilter(repo.ui, originalchunks,
3427 operation=operation)
3430 operation=operation)
3428 if reversehunks:
3431 if reversehunks:
3429 chunks = patch.reversehunks(chunks)
3432 chunks = patch.reversehunks(chunks)
3430
3433
3431 except patch.PatchError as err:
3434 except patch.PatchError as err:
3432 raise error.Abort(_('error parsing patch: %s') % err)
3435 raise error.Abort(_('error parsing patch: %s') % err)
3433
3436
3434 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3437 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3435 if tobackup is None:
3438 if tobackup is None:
3436 tobackup = set()
3439 tobackup = set()
3437 # Apply changes
3440 # Apply changes
3438 fp = stringio()
3441 fp = stringio()
3439 for c in chunks:
3442 for c in chunks:
3440 # Create a backup file only if this hunk should be backed up
3443 # Create a backup file only if this hunk should be backed up
3441 if ishunk(c) and c.header.filename() in tobackup:
3444 if ishunk(c) and c.header.filename() in tobackup:
3442 abs = c.header.filename()
3445 abs = c.header.filename()
3443 target = repo.wjoin(abs)
3446 target = repo.wjoin(abs)
3444 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3447 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3445 util.copyfile(target, bakname)
3448 util.copyfile(target, bakname)
3446 tobackup.remove(abs)
3449 tobackup.remove(abs)
3447 c.write(fp)
3450 c.write(fp)
3448 dopatch = fp.tell()
3451 dopatch = fp.tell()
3449 fp.seek(0)
3452 fp.seek(0)
3450 if dopatch:
3453 if dopatch:
3451 try:
3454 try:
3452 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3455 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3453 except patch.PatchError as err:
3456 except patch.PatchError as err:
3454 raise error.Abort(str(err))
3457 raise error.Abort(str(err))
3455 del fp
3458 del fp
3456 else:
3459 else:
3457 for f in actions['revert'][0]:
3460 for f in actions['revert'][0]:
3458 checkout(f)
3461 checkout(f)
3459 if normal:
3462 if normal:
3460 normal(f)
3463 normal(f)
3461
3464
3462 for f in actions['add'][0]:
3465 for f in actions['add'][0]:
3463 # Don't checkout modified files, they are already created by the diff
3466 # Don't checkout modified files, they are already created by the diff
3464 if f not in newlyaddedandmodifiedfiles:
3467 if f not in newlyaddedandmodifiedfiles:
3465 checkout(f)
3468 checkout(f)
3466 repo.dirstate.add(f)
3469 repo.dirstate.add(f)
3467
3470
3468 normal = repo.dirstate.normallookup
3471 normal = repo.dirstate.normallookup
3469 if node == parent and p2 == nullid:
3472 if node == parent and p2 == nullid:
3470 normal = repo.dirstate.normal
3473 normal = repo.dirstate.normal
3471 for f in actions['undelete'][0]:
3474 for f in actions['undelete'][0]:
3472 checkout(f)
3475 checkout(f)
3473 normal(f)
3476 normal(f)
3474
3477
3475 copied = copies.pathcopies(repo[parent], ctx)
3478 copied = copies.pathcopies(repo[parent], ctx)
3476
3479
3477 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3480 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3478 if f in copied:
3481 if f in copied:
3479 repo.dirstate.copy(copied[f], f)
3482 repo.dirstate.copy(copied[f], f)
3480
3483
3481 class command(registrar.command):
3484 class command(registrar.command):
3482 def _doregister(self, func, name, *args, **kwargs):
3485 def _doregister(self, func, name, *args, **kwargs):
3483 func._deprecatedregistrar = True # flag for deprecwarn in extensions.py
3486 func._deprecatedregistrar = True # flag for deprecwarn in extensions.py
3484 return super(command, self)._doregister(func, name, *args, **kwargs)
3487 return super(command, self)._doregister(func, name, *args, **kwargs)
3485
3488
3486 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3489 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3487 # commands.outgoing. "missing" is "missing" of the result of
3490 # commands.outgoing. "missing" is "missing" of the result of
3488 # "findcommonoutgoing()"
3491 # "findcommonoutgoing()"
3489 outgoinghooks = util.hooks()
3492 outgoinghooks = util.hooks()
3490
3493
3491 # a list of (ui, repo) functions called by commands.summary
3494 # a list of (ui, repo) functions called by commands.summary
3492 summaryhooks = util.hooks()
3495 summaryhooks = util.hooks()
3493
3496
3494 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3497 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3495 #
3498 #
3496 # functions should return tuple of booleans below, if 'changes' is None:
3499 # functions should return tuple of booleans below, if 'changes' is None:
3497 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3500 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3498 #
3501 #
3499 # otherwise, 'changes' is a tuple of tuples below:
3502 # otherwise, 'changes' is a tuple of tuples below:
3500 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3503 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3501 # - (desturl, destbranch, destpeer, outgoing)
3504 # - (desturl, destbranch, destpeer, outgoing)
3502 summaryremotehooks = util.hooks()
3505 summaryremotehooks = util.hooks()
3503
3506
3504 # A list of state files kept by multistep operations like graft.
3507 # A list of state files kept by multistep operations like graft.
3505 # Since graft cannot be aborted, it is considered 'clearable' by update.
3508 # Since graft cannot be aborted, it is considered 'clearable' by update.
3506 # note: bisect is intentionally excluded
3509 # note: bisect is intentionally excluded
3507 # (state file, clearable, allowcommit, error, hint)
3510 # (state file, clearable, allowcommit, error, hint)
3508 unfinishedstates = [
3511 unfinishedstates = [
3509 ('graftstate', True, False, _('graft in progress'),
3512 ('graftstate', True, False, _('graft in progress'),
3510 _("use 'hg graft --continue' or 'hg update' to abort")),
3513 _("use 'hg graft --continue' or 'hg update' to abort")),
3511 ('updatestate', True, False, _('last update was interrupted'),
3514 ('updatestate', True, False, _('last update was interrupted'),
3512 _("use 'hg update' to get a consistent checkout"))
3515 _("use 'hg update' to get a consistent checkout"))
3513 ]
3516 ]
3514
3517
3515 def checkunfinished(repo, commit=False):
3518 def checkunfinished(repo, commit=False):
3516 '''Look for an unfinished multistep operation, like graft, and abort
3519 '''Look for an unfinished multistep operation, like graft, and abort
3517 if found. It's probably good to check this right before
3520 if found. It's probably good to check this right before
3518 bailifchanged().
3521 bailifchanged().
3519 '''
3522 '''
3520 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3523 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3521 if commit and allowcommit:
3524 if commit and allowcommit:
3522 continue
3525 continue
3523 if repo.vfs.exists(f):
3526 if repo.vfs.exists(f):
3524 raise error.Abort(msg, hint=hint)
3527 raise error.Abort(msg, hint=hint)
3525
3528
3526 def clearunfinished(repo):
3529 def clearunfinished(repo):
3527 '''Check for unfinished operations (as above), and clear the ones
3530 '''Check for unfinished operations (as above), and clear the ones
3528 that are clearable.
3531 that are clearable.
3529 '''
3532 '''
3530 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3533 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3531 if not clearable and repo.vfs.exists(f):
3534 if not clearable and repo.vfs.exists(f):
3532 raise error.Abort(msg, hint=hint)
3535 raise error.Abort(msg, hint=hint)
3533 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3536 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3534 if clearable and repo.vfs.exists(f):
3537 if clearable and repo.vfs.exists(f):
3535 util.unlink(repo.vfs.join(f))
3538 util.unlink(repo.vfs.join(f))
3536
3539
3537 afterresolvedstates = [
3540 afterresolvedstates = [
3538 ('graftstate',
3541 ('graftstate',
3539 _('hg graft --continue')),
3542 _('hg graft --continue')),
3540 ]
3543 ]
3541
3544
3542 def howtocontinue(repo):
3545 def howtocontinue(repo):
3543 '''Check for an unfinished operation and return the command to finish
3546 '''Check for an unfinished operation and return the command to finish
3544 it.
3547 it.
3545
3548
3546 afterresolvedstates tuples define a .hg/{file} and the corresponding
3549 afterresolvedstates tuples define a .hg/{file} and the corresponding
3547 command needed to finish it.
3550 command needed to finish it.
3548
3551
3549 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3552 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3550 a boolean.
3553 a boolean.
3551 '''
3554 '''
3552 contmsg = _("continue: %s")
3555 contmsg = _("continue: %s")
3553 for f, msg in afterresolvedstates:
3556 for f, msg in afterresolvedstates:
3554 if repo.vfs.exists(f):
3557 if repo.vfs.exists(f):
3555 return contmsg % msg, True
3558 return contmsg % msg, True
3556 workingctx = repo[None]
3559 workingctx = repo[None]
3557 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3560 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3558 for s in workingctx.substate)
3561 for s in workingctx.substate)
3559 if dirty:
3562 if dirty:
3560 return contmsg % _("hg commit"), False
3563 return contmsg % _("hg commit"), False
3561 return None, None
3564 return None, None
3562
3565
3563 def checkafterresolved(repo):
3566 def checkafterresolved(repo):
3564 '''Inform the user about the next action after completing hg resolve
3567 '''Inform the user about the next action after completing hg resolve
3565
3568
3566 If there's a matching afterresolvedstates, howtocontinue will yield
3569 If there's a matching afterresolvedstates, howtocontinue will yield
3567 repo.ui.warn as the reporter.
3570 repo.ui.warn as the reporter.
3568
3571
3569 Otherwise, it will yield repo.ui.note.
3572 Otherwise, it will yield repo.ui.note.
3570 '''
3573 '''
3571 msg, warning = howtocontinue(repo)
3574 msg, warning = howtocontinue(repo)
3572 if msg is not None:
3575 if msg is not None:
3573 if warning:
3576 if warning:
3574 repo.ui.warn("%s\n" % msg)
3577 repo.ui.warn("%s\n" % msg)
3575 else:
3578 else:
3576 repo.ui.note("%s\n" % msg)
3579 repo.ui.note("%s\n" % msg)
3577
3580
3578 def wrongtooltocontinue(repo, task):
3581 def wrongtooltocontinue(repo, task):
3579 '''Raise an abort suggesting how to properly continue if there is an
3582 '''Raise an abort suggesting how to properly continue if there is an
3580 active task.
3583 active task.
3581
3584
3582 Uses howtocontinue() to find the active task.
3585 Uses howtocontinue() to find the active task.
3583
3586
3584 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3587 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3585 a hint.
3588 a hint.
3586 '''
3589 '''
3587 after = howtocontinue(repo)
3590 after = howtocontinue(repo)
3588 hint = None
3591 hint = None
3589 if after[1]:
3592 if after[1]:
3590 hint = after[0]
3593 hint = after[0]
3591 raise error.Abort(_('no %s in progress') % task, hint=hint)
3594 raise error.Abort(_('no %s in progress') % task, hint=hint)
@@ -1,489 +1,493 b''
1 # formatter.py - generic output formatting for mercurial
1 # formatter.py - generic output formatting for mercurial
2 #
2 #
3 # Copyright 2012 Matt Mackall <mpm@selenic.com>
3 # Copyright 2012 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 """Generic output formatting for Mercurial
8 """Generic output formatting for Mercurial
9
9
10 The formatter provides API to show data in various ways. The following
10 The formatter provides API to show data in various ways. The following
11 functions should be used in place of ui.write():
11 functions should be used in place of ui.write():
12
12
13 - fm.write() for unconditional output
13 - fm.write() for unconditional output
14 - fm.condwrite() to show some extra data conditionally in plain output
14 - fm.condwrite() to show some extra data conditionally in plain output
15 - fm.context() to provide changectx to template output
15 - fm.context() to provide changectx to template output
16 - fm.data() to provide extra data to JSON or template output
16 - fm.data() to provide extra data to JSON or template output
17 - fm.plain() to show raw text that isn't provided to JSON or template output
17 - fm.plain() to show raw text that isn't provided to JSON or template output
18
18
19 To show structured data (e.g. date tuples, dicts, lists), apply fm.format*()
19 To show structured data (e.g. date tuples, dicts, lists), apply fm.format*()
20 beforehand so the data is converted to the appropriate data type. Use
20 beforehand so the data is converted to the appropriate data type. Use
21 fm.isplain() if you need to convert or format data conditionally which isn't
21 fm.isplain() if you need to convert or format data conditionally which isn't
22 supported by the formatter API.
22 supported by the formatter API.
23
23
24 To build nested structure (i.e. a list of dicts), use fm.nested().
24 To build nested structure (i.e. a list of dicts), use fm.nested().
25
25
26 See also https://www.mercurial-scm.org/wiki/GenericTemplatingPlan
26 See also https://www.mercurial-scm.org/wiki/GenericTemplatingPlan
27
27
28 fm.condwrite() vs 'if cond:':
28 fm.condwrite() vs 'if cond:':
29
29
30 In most cases, use fm.condwrite() so users can selectively show the data
30 In most cases, use fm.condwrite() so users can selectively show the data
31 in template output. If it's costly to build data, use plain 'if cond:' with
31 in template output. If it's costly to build data, use plain 'if cond:' with
32 fm.write().
32 fm.write().
33
33
34 fm.nested() vs fm.formatdict() (or fm.formatlist()):
34 fm.nested() vs fm.formatdict() (or fm.formatlist()):
35
35
36 fm.nested() should be used to form a tree structure (a list of dicts of
36 fm.nested() should be used to form a tree structure (a list of dicts of
37 lists of dicts...) which can be accessed through template keywords, e.g.
37 lists of dicts...) which can be accessed through template keywords, e.g.
38 "{foo % "{bar % {...}} {baz % {...}}"}". On the other hand, fm.formatdict()
38 "{foo % "{bar % {...}} {baz % {...}}"}". On the other hand, fm.formatdict()
39 exports a dict-type object to template, which can be accessed by e.g.
39 exports a dict-type object to template, which can be accessed by e.g.
40 "{get(foo, key)}" function.
40 "{get(foo, key)}" function.
41
41
42 Doctest helper:
42 Doctest helper:
43
43
44 >>> def show(fn, verbose=False, **opts):
44 >>> def show(fn, verbose=False, **opts):
45 ... import sys
45 ... import sys
46 ... from . import ui as uimod
46 ... from . import ui as uimod
47 ... ui = uimod.ui()
47 ... ui = uimod.ui()
48 ... ui.fout = sys.stdout # redirect to doctest
48 ... ui.fout = sys.stdout # redirect to doctest
49 ... ui.verbose = verbose
49 ... ui.verbose = verbose
50 ... return fn(ui, ui.formatter(fn.__name__, opts))
50 ... return fn(ui, ui.formatter(fn.__name__, opts))
51
51
52 Basic example:
52 Basic example:
53
53
54 >>> def files(ui, fm):
54 >>> def files(ui, fm):
55 ... files = [('foo', 123, (0, 0)), ('bar', 456, (1, 0))]
55 ... files = [('foo', 123, (0, 0)), ('bar', 456, (1, 0))]
56 ... for f in files:
56 ... for f in files:
57 ... fm.startitem()
57 ... fm.startitem()
58 ... fm.write('path', '%s', f[0])
58 ... fm.write('path', '%s', f[0])
59 ... fm.condwrite(ui.verbose, 'date', ' %s',
59 ... fm.condwrite(ui.verbose, 'date', ' %s',
60 ... fm.formatdate(f[2], '%Y-%m-%d %H:%M:%S'))
60 ... fm.formatdate(f[2], '%Y-%m-%d %H:%M:%S'))
61 ... fm.data(size=f[1])
61 ... fm.data(size=f[1])
62 ... fm.plain('\\n')
62 ... fm.plain('\\n')
63 ... fm.end()
63 ... fm.end()
64 >>> show(files)
64 >>> show(files)
65 foo
65 foo
66 bar
66 bar
67 >>> show(files, verbose=True)
67 >>> show(files, verbose=True)
68 foo 1970-01-01 00:00:00
68 foo 1970-01-01 00:00:00
69 bar 1970-01-01 00:00:01
69 bar 1970-01-01 00:00:01
70 >>> show(files, template='json')
70 >>> show(files, template='json')
71 [
71 [
72 {
72 {
73 "date": [0, 0],
73 "date": [0, 0],
74 "path": "foo",
74 "path": "foo",
75 "size": 123
75 "size": 123
76 },
76 },
77 {
77 {
78 "date": [1, 0],
78 "date": [1, 0],
79 "path": "bar",
79 "path": "bar",
80 "size": 456
80 "size": 456
81 }
81 }
82 ]
82 ]
83 >>> show(files, template='path: {path}\\ndate: {date|rfc3339date}\\n')
83 >>> show(files, template='path: {path}\\ndate: {date|rfc3339date}\\n')
84 path: foo
84 path: foo
85 date: 1970-01-01T00:00:00+00:00
85 date: 1970-01-01T00:00:00+00:00
86 path: bar
86 path: bar
87 date: 1970-01-01T00:00:01+00:00
87 date: 1970-01-01T00:00:01+00:00
88
88
89 Nested example:
89 Nested example:
90
90
91 >>> def subrepos(ui, fm):
91 >>> def subrepos(ui, fm):
92 ... fm.startitem()
92 ... fm.startitem()
93 ... fm.write('repo', '[%s]\\n', 'baz')
93 ... fm.write('repo', '[%s]\\n', 'baz')
94 ... files(ui, fm.nested('files'))
94 ... files(ui, fm.nested('files'))
95 ... fm.end()
95 ... fm.end()
96 >>> show(subrepos)
96 >>> show(subrepos)
97 [baz]
97 [baz]
98 foo
98 foo
99 bar
99 bar
100 >>> show(subrepos, template='{repo}: {join(files % "{path}", ", ")}\\n')
100 >>> show(subrepos, template='{repo}: {join(files % "{path}", ", ")}\\n')
101 baz: foo, bar
101 baz: foo, bar
102 """
102 """
103
103
104 from __future__ import absolute_import
104 from __future__ import absolute_import
105
105
106 import collections
106 import collections
107 import contextlib
107 import contextlib
108 import itertools
108 import itertools
109 import os
109 import os
110
110
111 from .i18n import _
111 from .i18n import _
112 from .node import (
112 from .node import (
113 hex,
113 hex,
114 short,
114 short,
115 )
115 )
116
116
117 from . import (
117 from . import (
118 error,
118 error,
119 pycompat,
119 pycompat,
120 templatefilters,
120 templatefilters,
121 templatekw,
121 templatekw,
122 templater,
122 templater,
123 util,
123 util,
124 )
124 )
125
125
126 pickle = util.pickle
126 pickle = util.pickle
127
127
128 class _nullconverter(object):
128 class _nullconverter(object):
129 '''convert non-primitive data types to be processed by formatter'''
129 '''convert non-primitive data types to be processed by formatter'''
130 @staticmethod
130 @staticmethod
131 def formatdate(date, fmt):
131 def formatdate(date, fmt):
132 '''convert date tuple to appropriate format'''
132 '''convert date tuple to appropriate format'''
133 return date
133 return date
134 @staticmethod
134 @staticmethod
135 def formatdict(data, key, value, fmt, sep):
135 def formatdict(data, key, value, fmt, sep):
136 '''convert dict or key-value pairs to appropriate dict format'''
136 '''convert dict or key-value pairs to appropriate dict format'''
137 # use plain dict instead of util.sortdict so that data can be
137 # use plain dict instead of util.sortdict so that data can be
138 # serialized as a builtin dict in pickle output
138 # serialized as a builtin dict in pickle output
139 return dict(data)
139 return dict(data)
140 @staticmethod
140 @staticmethod
141 def formatlist(data, name, fmt, sep):
141 def formatlist(data, name, fmt, sep):
142 '''convert iterable to appropriate list format'''
142 '''convert iterable to appropriate list format'''
143 return list(data)
143 return list(data)
144
144
145 class baseformatter(object):
145 class baseformatter(object):
146 def __init__(self, ui, topic, opts, converter):
146 def __init__(self, ui, topic, opts, converter):
147 self._ui = ui
147 self._ui = ui
148 self._topic = topic
148 self._topic = topic
149 self._style = opts.get("style")
149 self._style = opts.get("style")
150 self._template = opts.get("template")
150 self._template = opts.get("template")
151 self._converter = converter
151 self._converter = converter
152 self._item = None
152 self._item = None
153 # function to convert node to string suitable for this output
153 # function to convert node to string suitable for this output
154 self.hexfunc = hex
154 self.hexfunc = hex
155 def __enter__(self):
155 def __enter__(self):
156 return self
156 return self
157 def __exit__(self, exctype, excvalue, traceback):
157 def __exit__(self, exctype, excvalue, traceback):
158 if exctype is None:
158 if exctype is None:
159 self.end()
159 self.end()
160 def _showitem(self):
160 def _showitem(self):
161 '''show a formatted item once all data is collected'''
161 '''show a formatted item once all data is collected'''
162 pass
162 pass
163 def startitem(self):
163 def startitem(self):
164 '''begin an item in the format list'''
164 '''begin an item in the format list'''
165 if self._item is not None:
165 if self._item is not None:
166 self._showitem()
166 self._showitem()
167 self._item = {}
167 self._item = {}
168 def formatdate(self, date, fmt='%a %b %d %H:%M:%S %Y %1%2'):
168 def formatdate(self, date, fmt='%a %b %d %H:%M:%S %Y %1%2'):
169 '''convert date tuple to appropriate format'''
169 '''convert date tuple to appropriate format'''
170 return self._converter.formatdate(date, fmt)
170 return self._converter.formatdate(date, fmt)
171 def formatdict(self, data, key='key', value='value', fmt='%s=%s', sep=' '):
171 def formatdict(self, data, key='key', value='value', fmt='%s=%s', sep=' '):
172 '''convert dict or key-value pairs to appropriate dict format'''
172 '''convert dict or key-value pairs to appropriate dict format'''
173 return self._converter.formatdict(data, key, value, fmt, sep)
173 return self._converter.formatdict(data, key, value, fmt, sep)
174 def formatlist(self, data, name, fmt='%s', sep=' '):
174 def formatlist(self, data, name, fmt='%s', sep=' '):
175 '''convert iterable to appropriate list format'''
175 '''convert iterable to appropriate list format'''
176 # name is mandatory argument for now, but it could be optional if
176 # name is mandatory argument for now, but it could be optional if
177 # we have default template keyword, e.g. {item}
177 # we have default template keyword, e.g. {item}
178 return self._converter.formatlist(data, name, fmt, sep)
178 return self._converter.formatlist(data, name, fmt, sep)
179 def context(self, **ctxs):
179 def context(self, **ctxs):
180 '''insert context objects to be used to render template keywords'''
180 '''insert context objects to be used to render template keywords'''
181 pass
181 pass
182 def data(self, **data):
182 def data(self, **data):
183 '''insert data into item that's not shown in default output'''
183 '''insert data into item that's not shown in default output'''
184 data = pycompat.byteskwargs(data)
184 data = pycompat.byteskwargs(data)
185 self._item.update(data)
185 self._item.update(data)
186 def write(self, fields, deftext, *fielddata, **opts):
186 def write(self, fields, deftext, *fielddata, **opts):
187 '''do default text output while assigning data to item'''
187 '''do default text output while assigning data to item'''
188 fieldkeys = fields.split()
188 fieldkeys = fields.split()
189 assert len(fieldkeys) == len(fielddata)
189 assert len(fieldkeys) == len(fielddata)
190 self._item.update(zip(fieldkeys, fielddata))
190 self._item.update(zip(fieldkeys, fielddata))
191 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
191 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
192 '''do conditional write (primarily for plain formatter)'''
192 '''do conditional write (primarily for plain formatter)'''
193 fieldkeys = fields.split()
193 fieldkeys = fields.split()
194 assert len(fieldkeys) == len(fielddata)
194 assert len(fieldkeys) == len(fielddata)
195 self._item.update(zip(fieldkeys, fielddata))
195 self._item.update(zip(fieldkeys, fielddata))
196 def plain(self, text, **opts):
196 def plain(self, text, **opts):
197 '''show raw text for non-templated mode'''
197 '''show raw text for non-templated mode'''
198 pass
198 pass
199 def isplain(self):
199 def isplain(self):
200 '''check for plain formatter usage'''
200 '''check for plain formatter usage'''
201 return False
201 return False
202 def nested(self, field):
202 def nested(self, field):
203 '''sub formatter to store nested data in the specified field'''
203 '''sub formatter to store nested data in the specified field'''
204 self._item[field] = data = []
204 self._item[field] = data = []
205 return _nestedformatter(self._ui, self._converter, data)
205 return _nestedformatter(self._ui, self._converter, data)
206 def end(self):
206 def end(self):
207 '''end output for the formatter'''
207 '''end output for the formatter'''
208 if self._item is not None:
208 if self._item is not None:
209 self._showitem()
209 self._showitem()
210
210
211 def nullformatter(ui, topic):
211 def nullformatter(ui, topic):
212 '''formatter that prints nothing'''
212 '''formatter that prints nothing'''
213 return baseformatter(ui, topic, opts={}, converter=_nullconverter)
213 return baseformatter(ui, topic, opts={}, converter=_nullconverter)
214
214
215 class _nestedformatter(baseformatter):
215 class _nestedformatter(baseformatter):
216 '''build sub items and store them in the parent formatter'''
216 '''build sub items and store them in the parent formatter'''
217 def __init__(self, ui, converter, data):
217 def __init__(self, ui, converter, data):
218 baseformatter.__init__(self, ui, topic='', opts={}, converter=converter)
218 baseformatter.__init__(self, ui, topic='', opts={}, converter=converter)
219 self._data = data
219 self._data = data
220 def _showitem(self):
220 def _showitem(self):
221 self._data.append(self._item)
221 self._data.append(self._item)
222
222
223 def _iteritems(data):
223 def _iteritems(data):
224 '''iterate key-value pairs in stable order'''
224 '''iterate key-value pairs in stable order'''
225 if isinstance(data, dict):
225 if isinstance(data, dict):
226 return sorted(data.iteritems())
226 return sorted(data.iteritems())
227 return data
227 return data
228
228
229 class _plainconverter(object):
229 class _plainconverter(object):
230 '''convert non-primitive data types to text'''
230 '''convert non-primitive data types to text'''
231 @staticmethod
231 @staticmethod
232 def formatdate(date, fmt):
232 def formatdate(date, fmt):
233 '''stringify date tuple in the given format'''
233 '''stringify date tuple in the given format'''
234 return util.datestr(date, fmt)
234 return util.datestr(date, fmt)
235 @staticmethod
235 @staticmethod
236 def formatdict(data, key, value, fmt, sep):
236 def formatdict(data, key, value, fmt, sep):
237 '''stringify key-value pairs separated by sep'''
237 '''stringify key-value pairs separated by sep'''
238 return sep.join(fmt % (k, v) for k, v in _iteritems(data))
238 return sep.join(fmt % (k, v) for k, v in _iteritems(data))
239 @staticmethod
239 @staticmethod
240 def formatlist(data, name, fmt, sep):
240 def formatlist(data, name, fmt, sep):
241 '''stringify iterable separated by sep'''
241 '''stringify iterable separated by sep'''
242 return sep.join(fmt % e for e in data)
242 return sep.join(fmt % e for e in data)
243
243
244 class plainformatter(baseformatter):
244 class plainformatter(baseformatter):
245 '''the default text output scheme'''
245 '''the default text output scheme'''
246 def __init__(self, ui, out, topic, opts):
246 def __init__(self, ui, out, topic, opts):
247 baseformatter.__init__(self, ui, topic, opts, _plainconverter)
247 baseformatter.__init__(self, ui, topic, opts, _plainconverter)
248 if ui.debugflag:
248 if ui.debugflag:
249 self.hexfunc = hex
249 self.hexfunc = hex
250 else:
250 else:
251 self.hexfunc = short
251 self.hexfunc = short
252 if ui is out:
252 if ui is out:
253 self._write = ui.write
253 self._write = ui.write
254 else:
254 else:
255 self._write = lambda s, **opts: out.write(s)
255 self._write = lambda s, **opts: out.write(s)
256 def startitem(self):
256 def startitem(self):
257 pass
257 pass
258 def data(self, **data):
258 def data(self, **data):
259 pass
259 pass
260 def write(self, fields, deftext, *fielddata, **opts):
260 def write(self, fields, deftext, *fielddata, **opts):
261 self._write(deftext % fielddata, **opts)
261 self._write(deftext % fielddata, **opts)
262 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
262 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
263 '''do conditional write'''
263 '''do conditional write'''
264 if cond:
264 if cond:
265 self._write(deftext % fielddata, **opts)
265 self._write(deftext % fielddata, **opts)
266 def plain(self, text, **opts):
266 def plain(self, text, **opts):
267 self._write(text, **opts)
267 self._write(text, **opts)
268 def isplain(self):
268 def isplain(self):
269 return True
269 return True
270 def nested(self, field):
270 def nested(self, field):
271 # nested data will be directly written to ui
271 # nested data will be directly written to ui
272 return self
272 return self
273 def end(self):
273 def end(self):
274 pass
274 pass
275
275
276 class debugformatter(baseformatter):
276 class debugformatter(baseformatter):
277 def __init__(self, ui, out, topic, opts):
277 def __init__(self, ui, out, topic, opts):
278 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
278 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
279 self._out = out
279 self._out = out
280 self._out.write("%s = [\n" % self._topic)
280 self._out.write("%s = [\n" % self._topic)
281 def _showitem(self):
281 def _showitem(self):
282 self._out.write(" " + repr(self._item) + ",\n")
282 self._out.write(" " + repr(self._item) + ",\n")
283 def end(self):
283 def end(self):
284 baseformatter.end(self)
284 baseformatter.end(self)
285 self._out.write("]\n")
285 self._out.write("]\n")
286
286
287 class pickleformatter(baseformatter):
287 class pickleformatter(baseformatter):
288 def __init__(self, ui, out, topic, opts):
288 def __init__(self, ui, out, topic, opts):
289 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
289 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
290 self._out = out
290 self._out = out
291 self._data = []
291 self._data = []
292 def _showitem(self):
292 def _showitem(self):
293 self._data.append(self._item)
293 self._data.append(self._item)
294 def end(self):
294 def end(self):
295 baseformatter.end(self)
295 baseformatter.end(self)
296 self._out.write(pickle.dumps(self._data))
296 self._out.write(pickle.dumps(self._data))
297
297
298 class jsonformatter(baseformatter):
298 class jsonformatter(baseformatter):
299 def __init__(self, ui, out, topic, opts):
299 def __init__(self, ui, out, topic, opts):
300 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
300 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
301 self._out = out
301 self._out = out
302 self._out.write("[")
302 self._out.write("[")
303 self._first = True
303 self._first = True
304 def _showitem(self):
304 def _showitem(self):
305 if self._first:
305 if self._first:
306 self._first = False
306 self._first = False
307 else:
307 else:
308 self._out.write(",")
308 self._out.write(",")
309
309
310 self._out.write("\n {\n")
310 self._out.write("\n {\n")
311 first = True
311 first = True
312 for k, v in sorted(self._item.items()):
312 for k, v in sorted(self._item.items()):
313 if first:
313 if first:
314 first = False
314 first = False
315 else:
315 else:
316 self._out.write(",\n")
316 self._out.write(",\n")
317 u = templatefilters.json(v, paranoid=False)
317 u = templatefilters.json(v, paranoid=False)
318 self._out.write(' "%s": %s' % (k, u))
318 self._out.write(' "%s": %s' % (k, u))
319 self._out.write("\n }")
319 self._out.write("\n }")
320 def end(self):
320 def end(self):
321 baseformatter.end(self)
321 baseformatter.end(self)
322 self._out.write("\n]\n")
322 self._out.write("\n]\n")
323
323
324 class _templateconverter(object):
324 class _templateconverter(object):
325 '''convert non-primitive data types to be processed by templater'''
325 '''convert non-primitive data types to be processed by templater'''
326 @staticmethod
326 @staticmethod
327 def formatdate(date, fmt):
327 def formatdate(date, fmt):
328 '''return date tuple'''
328 '''return date tuple'''
329 return date
329 return date
330 @staticmethod
330 @staticmethod
331 def formatdict(data, key, value, fmt, sep):
331 def formatdict(data, key, value, fmt, sep):
332 '''build object that can be evaluated as either plain string or dict'''
332 '''build object that can be evaluated as either plain string or dict'''
333 data = util.sortdict(_iteritems(data))
333 data = util.sortdict(_iteritems(data))
334 def f():
334 def f():
335 yield _plainconverter.formatdict(data, key, value, fmt, sep)
335 yield _plainconverter.formatdict(data, key, value, fmt, sep)
336 return templatekw.hybriddict(data, key=key, value=value, fmt=fmt,
336 return templatekw.hybriddict(data, key=key, value=value, fmt=fmt,
337 gen=f())
337 gen=f())
338 @staticmethod
338 @staticmethod
339 def formatlist(data, name, fmt, sep):
339 def formatlist(data, name, fmt, sep):
340 '''build object that can be evaluated as either plain string or list'''
340 '''build object that can be evaluated as either plain string or list'''
341 data = list(data)
341 data = list(data)
342 def f():
342 def f():
343 yield _plainconverter.formatlist(data, name, fmt, sep)
343 yield _plainconverter.formatlist(data, name, fmt, sep)
344 return templatekw.hybridlist(data, name=name, fmt=fmt, gen=f())
344 return templatekw.hybridlist(data, name=name, fmt=fmt, gen=f())
345
345
346 class templateformatter(baseformatter):
346 class templateformatter(baseformatter):
347 def __init__(self, ui, out, topic, opts):
347 def __init__(self, ui, out, topic, opts):
348 baseformatter.__init__(self, ui, topic, opts, _templateconverter)
348 baseformatter.__init__(self, ui, topic, opts, _templateconverter)
349 self._out = out
349 self._out = out
350 spec = lookuptemplate(ui, topic, opts.get('template', ''))
350 spec = lookuptemplate(ui, topic, opts.get('template', ''))
351 self._tref = spec.ref
351 self._tref = spec.ref
352 self._t = loadtemplater(ui, spec, cache=templatekw.defaulttempl)
352 self._t = loadtemplater(ui, spec, cache=templatekw.defaulttempl)
353 self._counter = itertools.count()
353 self._counter = itertools.count()
354 self._cache = {} # for templatekw/funcs to store reusable data
354 self._cache = {} # for templatekw/funcs to store reusable data
355 def context(self, **ctxs):
355 def context(self, **ctxs):
356 '''insert context objects to be used to render template keywords'''
356 '''insert context objects to be used to render template keywords'''
357 assert all(k == 'ctx' for k in ctxs)
357 assert all(k == 'ctx' for k in ctxs)
358 self._item.update(ctxs)
358 self._item.update(ctxs)
359 def _showitem(self):
359 def _showitem(self):
360 # TODO: add support for filectx. probably each template keyword or
360 # TODO: add support for filectx. probably each template keyword or
361 # function will have to declare dependent resources. e.g.
361 # function will have to declare dependent resources. e.g.
362 # @templatekeyword(..., requires=('ctx',))
362 # @templatekeyword(..., requires=('ctx',))
363 props = {}
363 props = {}
364 if 'ctx' in self._item:
364 if 'ctx' in self._item:
365 props.update(templatekw.keywords)
365 props.update(templatekw.keywords)
366 props['index'] = next(self._counter)
366 props['index'] = next(self._counter)
367 # explicitly-defined fields precede templatekw
367 # explicitly-defined fields precede templatekw
368 props.update(self._item)
368 props.update(self._item)
369 if 'ctx' in self._item:
369 if 'ctx' in self._item:
370 # but template resources must be always available
370 # but template resources must be always available
371 props['templ'] = self._t
371 props['templ'] = self._t
372 props['repo'] = props['ctx'].repo()
372 props['repo'] = props['ctx'].repo()
373 props['revcache'] = {}
373 props['revcache'] = {}
374 g = self._t(self._tref, ui=self._ui, cache=self._cache, **props)
374 g = self._t(self._tref, ui=self._ui, cache=self._cache, **props)
375 self._out.write(templater.stringify(g))
375 self._out.write(templater.stringify(g))
376
376
377 templatespec = collections.namedtuple(r'templatespec',
377 templatespec = collections.namedtuple(r'templatespec',
378 r'ref tmpl mapfile')
378 r'ref tmpl mapfile')
379
379
380 def lookuptemplate(ui, topic, tmpl):
380 def lookuptemplate(ui, topic, tmpl):
381 """Find the template matching the given -T/--template spec 'tmpl'
381 """Find the template matching the given -T/--template spec 'tmpl'
382
382
383 'tmpl' can be any of the following:
383 'tmpl' can be any of the following:
384
384
385 - a literal template (e.g. '{rev}')
385 - a literal template (e.g. '{rev}')
386 - a map-file name or path (e.g. 'changelog')
386 - a map-file name or path (e.g. 'changelog')
387 - a reference to [templates] in config file
387 - a reference to [templates] in config file
388 - a path to raw template file
388 - a path to raw template file
389
389
390 A map file defines a stand-alone template environment. If a map file
390 A map file defines a stand-alone template environment. If a map file
391 selected, all templates defined in the file will be loaded, and the
391 selected, all templates defined in the file will be loaded, and the
392 template matching the given topic will be rendered. No aliases will be
392 template matching the given topic will be rendered. No aliases will be
393 loaded from user config.
393 loaded from user config.
394
395 If no map file selected, all templates in [templates] section will be
396 available as well as aliases in [templatealias].
394 """
397 """
395
398
396 # looks like a literal template?
399 # looks like a literal template?
397 if '{' in tmpl:
400 if '{' in tmpl:
398 return templatespec(topic, tmpl, None)
401 return templatespec('', tmpl, None)
399
402
400 # perhaps a stock style?
403 # perhaps a stock style?
401 if not os.path.split(tmpl)[0]:
404 if not os.path.split(tmpl)[0]:
402 mapname = (templater.templatepath('map-cmdline.' + tmpl)
405 mapname = (templater.templatepath('map-cmdline.' + tmpl)
403 or templater.templatepath(tmpl))
406 or templater.templatepath(tmpl))
404 if mapname and os.path.isfile(mapname):
407 if mapname and os.path.isfile(mapname):
405 return templatespec(topic, None, mapname)
408 return templatespec(topic, None, mapname)
406
409
407 # perhaps it's a reference to [templates]
410 # perhaps it's a reference to [templates]
408 t = ui.config('templates', tmpl)
411 if ui.config('templates', tmpl):
409 if t:
412 return templatespec(tmpl, None, None)
410 return templatespec(topic, templater.unquotestring(t), None)
411
413
412 if tmpl == 'list':
414 if tmpl == 'list':
413 ui.write(_("available styles: %s\n") % templater.stylelist())
415 ui.write(_("available styles: %s\n") % templater.stylelist())
414 raise error.Abort(_("specify a template"))
416 raise error.Abort(_("specify a template"))
415
417
416 # perhaps it's a path to a map or a template
418 # perhaps it's a path to a map or a template
417 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
419 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
418 # is it a mapfile for a style?
420 # is it a mapfile for a style?
419 if os.path.basename(tmpl).startswith("map-"):
421 if os.path.basename(tmpl).startswith("map-"):
420 return templatespec(topic, None, os.path.realpath(tmpl))
422 return templatespec(topic, None, os.path.realpath(tmpl))
421 with util.posixfile(tmpl, 'rb') as f:
423 with util.posixfile(tmpl, 'rb') as f:
422 tmpl = f.read()
424 tmpl = f.read()
423 return templatespec(topic, tmpl, None)
425 return templatespec('', tmpl, None)
424
426
425 # constant string?
427 # constant string?
426 return templatespec(topic, tmpl, None)
428 return templatespec('', tmpl, None)
427
429
428 def loadtemplater(ui, spec, cache=None):
430 def loadtemplater(ui, spec, cache=None):
429 """Create a templater from either a literal template or loading from
431 """Create a templater from either a literal template or loading from
430 a map file"""
432 a map file"""
431 assert not (spec.tmpl and spec.mapfile)
433 assert not (spec.tmpl and spec.mapfile)
432 if spec.mapfile:
434 if spec.mapfile:
433 return templater.templater.frommapfile(spec.mapfile, cache=cache)
435 return templater.templater.frommapfile(spec.mapfile, cache=cache)
434 return _maketemplater(ui, spec.ref, spec.tmpl, cache=cache)
436 return _maketemplater(ui, spec.ref, spec.tmpl, cache=cache)
435
437
436 def maketemplater(ui, tmpl, cache=None):
438 def maketemplater(ui, tmpl, cache=None):
437 """Create a templater from a string template 'tmpl'"""
439 """Create a templater from a string template 'tmpl'"""
438 return _maketemplater(ui, '', tmpl, cache=cache)
440 return _maketemplater(ui, '', tmpl, cache=cache)
439
441
440 def _maketemplater(ui, topic, tmpl, cache=None):
442 def _maketemplater(ui, topic, tmpl, cache=None):
441 aliases = ui.configitems('templatealias')
443 aliases = ui.configitems('templatealias')
442 t = templater.templater(cache=cache, aliases=aliases)
444 t = templater.templater(cache=cache, aliases=aliases)
445 t.cache.update((k, templater.unquotestring(v))
446 for k, v in ui.configitems('templates'))
443 if tmpl:
447 if tmpl:
444 t.cache[topic] = tmpl
448 t.cache[topic] = tmpl
445 return t
449 return t
446
450
447 def formatter(ui, out, topic, opts):
451 def formatter(ui, out, topic, opts):
448 template = opts.get("template", "")
452 template = opts.get("template", "")
449 if template == "json":
453 if template == "json":
450 return jsonformatter(ui, out, topic, opts)
454 return jsonformatter(ui, out, topic, opts)
451 elif template == "pickle":
455 elif template == "pickle":
452 return pickleformatter(ui, out, topic, opts)
456 return pickleformatter(ui, out, topic, opts)
453 elif template == "debug":
457 elif template == "debug":
454 return debugformatter(ui, out, topic, opts)
458 return debugformatter(ui, out, topic, opts)
455 elif template != "":
459 elif template != "":
456 return templateformatter(ui, out, topic, opts)
460 return templateformatter(ui, out, topic, opts)
457 # developer config: ui.formatdebug
461 # developer config: ui.formatdebug
458 elif ui.configbool('ui', 'formatdebug'):
462 elif ui.configbool('ui', 'formatdebug'):
459 return debugformatter(ui, out, topic, opts)
463 return debugformatter(ui, out, topic, opts)
460 # deprecated config: ui.formatjson
464 # deprecated config: ui.formatjson
461 elif ui.configbool('ui', 'formatjson'):
465 elif ui.configbool('ui', 'formatjson'):
462 return jsonformatter(ui, out, topic, opts)
466 return jsonformatter(ui, out, topic, opts)
463 return plainformatter(ui, out, topic, opts)
467 return plainformatter(ui, out, topic, opts)
464
468
465 @contextlib.contextmanager
469 @contextlib.contextmanager
466 def openformatter(ui, filename, topic, opts):
470 def openformatter(ui, filename, topic, opts):
467 """Create a formatter that writes outputs to the specified file
471 """Create a formatter that writes outputs to the specified file
468
472
469 Must be invoked using the 'with' statement.
473 Must be invoked using the 'with' statement.
470 """
474 """
471 with util.posixfile(filename, 'wb') as out:
475 with util.posixfile(filename, 'wb') as out:
472 with formatter(ui, out, topic, opts) as fm:
476 with formatter(ui, out, topic, opts) as fm:
473 yield fm
477 yield fm
474
478
475 @contextlib.contextmanager
479 @contextlib.contextmanager
476 def _neverending(fm):
480 def _neverending(fm):
477 yield fm
481 yield fm
478
482
479 def maybereopen(fm, filename, opts):
483 def maybereopen(fm, filename, opts):
480 """Create a formatter backed by file if filename specified, else return
484 """Create a formatter backed by file if filename specified, else return
481 the given formatter
485 the given formatter
482
486
483 Must be invoked using the 'with' statement. This will never call fm.end()
487 Must be invoked using the 'with' statement. This will never call fm.end()
484 of the given formatter.
488 of the given formatter.
485 """
489 """
486 if filename:
490 if filename:
487 return openformatter(fm._ui, filename, fm._topic, opts)
491 return openformatter(fm._ui, filename, fm._topic, opts)
488 else:
492 else:
489 return _neverending(fm)
493 return _neverending(fm)
@@ -1,192 +1,200 b''
1 Mercurial allows you to customize output of commands through
1 Mercurial allows you to customize output of commands through
2 templates. You can either pass in a template or select an existing
2 templates. You can either pass in a template or select an existing
3 template-style from the command line, via the --template option.
3 template-style from the command line, via the --template option.
4
4
5 You can customize output for any "log-like" command: log,
5 You can customize output for any "log-like" command: log,
6 outgoing, incoming, tip, parents, and heads.
6 outgoing, incoming, tip, parents, and heads.
7
7
8 Some built-in styles are packaged with Mercurial. These can be listed
8 Some built-in styles are packaged with Mercurial. These can be listed
9 with :hg:`log --template list`. Example usage::
9 with :hg:`log --template list`. Example usage::
10
10
11 $ hg log -r1.0::1.1 --template changelog
11 $ hg log -r1.0::1.1 --template changelog
12
12
13 A template is a piece of text, with markup to invoke variable
13 A template is a piece of text, with markup to invoke variable
14 expansion::
14 expansion::
15
15
16 $ hg log -r1 --template "{node}\n"
16 $ hg log -r1 --template "{node}\n"
17 b56ce7b07c52de7d5fd79fb89701ea538af65746
17 b56ce7b07c52de7d5fd79fb89701ea538af65746
18
18
19 Keywords
19 Keywords
20 ========
20 ========
21
21
22 Strings in curly braces are called keywords. The availability of
22 Strings in curly braces are called keywords. The availability of
23 keywords depends on the exact context of the templater. These
23 keywords depends on the exact context of the templater. These
24 keywords are usually available for templating a log-like command:
24 keywords are usually available for templating a log-like command:
25
25
26 .. keywordsmarker
26 .. keywordsmarker
27
27
28 The "date" keyword does not produce human-readable output. If you
28 The "date" keyword does not produce human-readable output. If you
29 want to use a date in your output, you can use a filter to process
29 want to use a date in your output, you can use a filter to process
30 it. Filters are functions which return a string based on the input
30 it. Filters are functions which return a string based on the input
31 variable. Be sure to use the stringify filter first when you're
31 variable. Be sure to use the stringify filter first when you're
32 applying a string-input filter to a list-like input variable.
32 applying a string-input filter to a list-like input variable.
33 You can also use a chain of filters to get the desired output::
33 You can also use a chain of filters to get the desired output::
34
34
35 $ hg tip --template "{date|isodate}\n"
35 $ hg tip --template "{date|isodate}\n"
36 2008-08-21 18:22 +0000
36 2008-08-21 18:22 +0000
37
37
38 Filters
38 Filters
39 =======
39 =======
40
40
41 List of filters:
41 List of filters:
42
42
43 .. filtersmarker
43 .. filtersmarker
44
44
45 Note that a filter is nothing more than a function call, i.e.
45 Note that a filter is nothing more than a function call, i.e.
46 ``expr|filter`` is equivalent to ``filter(expr)``.
46 ``expr|filter`` is equivalent to ``filter(expr)``.
47
47
48 Functions
48 Functions
49 =========
49 =========
50
50
51 In addition to filters, there are some basic built-in functions:
51 In addition to filters, there are some basic built-in functions:
52
52
53 .. functionsmarker
53 .. functionsmarker
54
54
55 Operators
55 Operators
56 =========
56 =========
57
57
58 We provide a limited set of infix arithmetic operations on integers::
58 We provide a limited set of infix arithmetic operations on integers::
59
59
60 + for addition
60 + for addition
61 - for subtraction
61 - for subtraction
62 * for multiplication
62 * for multiplication
63 / for floor division (division rounded to integer nearest -infinity)
63 / for floor division (division rounded to integer nearest -infinity)
64
64
65 Division fulfills the law x = x / y + mod(x, y).
65 Division fulfills the law x = x / y + mod(x, y).
66
66
67 Also, for any expression that returns a list, there is a list operator::
67 Also, for any expression that returns a list, there is a list operator::
68
68
69 expr % "{template}"
69 expr % "{template}"
70
70
71 As seen in the above example, ``{template}`` is interpreted as a template.
71 As seen in the above example, ``{template}`` is interpreted as a template.
72 To prevent it from being interpreted, you can use an escape character ``\{``
72 To prevent it from being interpreted, you can use an escape character ``\{``
73 or a raw string prefix, ``r'...'``.
73 or a raw string prefix, ``r'...'``.
74
74
75 Aliases
75 Aliases
76 =======
76 =======
77
77
78 New keywords and functions can be defined in the ``templatealias`` section of
78 New keywords and functions can be defined in the ``templatealias`` section of
79 a Mercurial configuration file::
79 a Mercurial configuration file::
80
80
81 <alias> = <definition>
81 <alias> = <definition>
82
82
83 Arguments of the form `a1`, `a2`, etc. are substituted from the alias into
83 Arguments of the form `a1`, `a2`, etc. are substituted from the alias into
84 the definition.
84 the definition.
85
85
86 For example,
86 For example,
87
87
88 ::
88 ::
89
89
90 [templatealias]
90 [templatealias]
91 r = rev
91 r = rev
92 rn = "{r}:{node|short}"
92 rn = "{r}:{node|short}"
93 leftpad(s, w) = pad(s, w, ' ', True)
93 leftpad(s, w) = pad(s, w, ' ', True)
94
94
95 defines two symbol aliases, ``r`` and ``rn``, and a function alias
95 defines two symbol aliases, ``r`` and ``rn``, and a function alias
96 ``leftpad()``.
96 ``leftpad()``.
97
97
98 It's also possible to specify complete template strings, using the
98 It's also possible to specify complete template strings, using the
99 ``templates`` section. The syntax used is the general template string syntax.
99 ``templates`` section. The syntax used is the general template string syntax.
100
100
101 For example,
101 For example,
102
102
103 ::
103 ::
104
104
105 [templates]
105 [templates]
106 nodedate = "{node|short}: {date(date, "%Y-%m-%d")}\n"
106 nodedate = "{node|short}: {date(date, "%Y-%m-%d")}\n"
107
107
108 defines a template, ``nodedate``, which can be called like::
108 defines a template, ``nodedate``, which can be called like::
109
109
110 $ hg log -r . -Tnodedate
110 $ hg log -r . -Tnodedate
111
111
112 A template defined in ``templates`` section can also be referenced from
113 another template::
114
115 $ hg log -r . -T "{rev} {nodedate}"
116
117 but be aware that the keywords cannot be overridden by templates. For example,
118 a template defined as ``templates.rev`` cannot be referenced as ``{rev}``.
119
112 Examples
120 Examples
113 ========
121 ========
114
122
115 Some sample command line templates:
123 Some sample command line templates:
116
124
117 - Format lists, e.g. files::
125 - Format lists, e.g. files::
118
126
119 $ hg log -r 0 --template "files:\n{files % ' {file}\n'}"
127 $ hg log -r 0 --template "files:\n{files % ' {file}\n'}"
120
128
121 - Join the list of files with a ", "::
129 - Join the list of files with a ", "::
122
130
123 $ hg log -r 0 --template "files: {join(files, ', ')}\n"
131 $ hg log -r 0 --template "files: {join(files, ', ')}\n"
124
132
125 - Join the list of files ending with ".py" with a ", "::
133 - Join the list of files ending with ".py" with a ", "::
126
134
127 $ hg log -r 0 --template "pythonfiles: {join(files('**.py'), ', ')}\n"
135 $ hg log -r 0 --template "pythonfiles: {join(files('**.py'), ', ')}\n"
128
136
129 - Separate non-empty arguments by a " "::
137 - Separate non-empty arguments by a " "::
130
138
131 $ hg log -r 0 --template "{separate(' ', node, bookmarks, tags}\n"
139 $ hg log -r 0 --template "{separate(' ', node, bookmarks, tags}\n"
132
140
133 - Modify each line of a commit description::
141 - Modify each line of a commit description::
134
142
135 $ hg log --template "{splitlines(desc) % '**** {line}\n'}"
143 $ hg log --template "{splitlines(desc) % '**** {line}\n'}"
136
144
137 - Format date::
145 - Format date::
138
146
139 $ hg log -r 0 --template "{date(date, '%Y')}\n"
147 $ hg log -r 0 --template "{date(date, '%Y')}\n"
140
148
141 - Display date in UTC::
149 - Display date in UTC::
142
150
143 $ hg log -r 0 --template "{localdate(date, 'UTC')|date}\n"
151 $ hg log -r 0 --template "{localdate(date, 'UTC')|date}\n"
144
152
145 - Output the description set to a fill-width of 30::
153 - Output the description set to a fill-width of 30::
146
154
147 $ hg log -r 0 --template "{fill(desc, 30)}"
155 $ hg log -r 0 --template "{fill(desc, 30)}"
148
156
149 - Use a conditional to test for the default branch::
157 - Use a conditional to test for the default branch::
150
158
151 $ hg log -r 0 --template "{ifeq(branch, 'default', 'on the main branch',
159 $ hg log -r 0 --template "{ifeq(branch, 'default', 'on the main branch',
152 'on branch {branch}')}\n"
160 'on branch {branch}')}\n"
153
161
154 - Append a newline if not empty::
162 - Append a newline if not empty::
155
163
156 $ hg tip --template "{if(author, '{author}\n')}"
164 $ hg tip --template "{if(author, '{author}\n')}"
157
165
158 - Label the output for use with the color extension::
166 - Label the output for use with the color extension::
159
167
160 $ hg log -r 0 --template "{label('changeset.{phase}', node|short)}\n"
168 $ hg log -r 0 --template "{label('changeset.{phase}', node|short)}\n"
161
169
162 - Invert the firstline filter, i.e. everything but the first line::
170 - Invert the firstline filter, i.e. everything but the first line::
163
171
164 $ hg log -r 0 --template "{sub(r'^.*\n?\n?', '', desc)}\n"
172 $ hg log -r 0 --template "{sub(r'^.*\n?\n?', '', desc)}\n"
165
173
166 - Display the contents of the 'extra' field, one per line::
174 - Display the contents of the 'extra' field, one per line::
167
175
168 $ hg log -r 0 --template "{join(extras, '\n')}\n"
176 $ hg log -r 0 --template "{join(extras, '\n')}\n"
169
177
170 - Mark the active bookmark with '*'::
178 - Mark the active bookmark with '*'::
171
179
172 $ hg log --template "{bookmarks % '{bookmark}{ifeq(bookmark, active, '*')} '}\n"
180 $ hg log --template "{bookmarks % '{bookmark}{ifeq(bookmark, active, '*')} '}\n"
173
181
174 - Find the previous release candidate tag, the distance and changes since the tag::
182 - Find the previous release candidate tag, the distance and changes since the tag::
175
183
176 $ hg log -r . --template "{latesttag('re:^.*-rc$') % '{tag}, {changes}, {distance}'}\n"
184 $ hg log -r . --template "{latesttag('re:^.*-rc$') % '{tag}, {changes}, {distance}'}\n"
177
185
178 - Mark the working copy parent with '@'::
186 - Mark the working copy parent with '@'::
179
187
180 $ hg log --template "{ifcontains(rev, revset('.'), '@')}\n"
188 $ hg log --template "{ifcontains(rev, revset('.'), '@')}\n"
181
189
182 - Show details of parent revisions::
190 - Show details of parent revisions::
183
191
184 $ hg log --template "{revset('parents(%d)', rev) % '{desc|firstline}\n'}"
192 $ hg log --template "{revset('parents(%d)', rev) % '{desc|firstline}\n'}"
185
193
186 - Show only commit descriptions that start with "template"::
194 - Show only commit descriptions that start with "template"::
187
195
188 $ hg log --template "{startswith('template', firstline(desc))}\n"
196 $ hg log --template "{startswith('template', firstline(desc))}\n"
189
197
190 - Print the first word of each line of a commit message::
198 - Print the first word of each line of a commit message::
191
199
192 $ hg log --template "{word(0, desc)}\n"
200 $ hg log --template "{word(0, desc)}\n"
@@ -1,4244 +1,4259 b''
1 $ hg init a
1 $ hg init a
2 $ cd a
2 $ cd a
3 $ echo a > a
3 $ echo a > a
4 $ hg add a
4 $ hg add a
5 $ echo line 1 > b
5 $ echo line 1 > b
6 $ echo line 2 >> b
6 $ echo line 2 >> b
7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
8
8
9 $ hg add b
9 $ hg add b
10 $ echo other 1 > c
10 $ echo other 1 > c
11 $ echo other 2 >> c
11 $ echo other 2 >> c
12 $ echo >> c
12 $ echo >> c
13 $ echo other 3 >> c
13 $ echo other 3 >> c
14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
15
15
16 $ hg add c
16 $ hg add c
17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 $ echo c >> c
18 $ echo c >> c
19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20
20
21 $ echo foo > .hg/branch
21 $ echo foo > .hg/branch
22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
23
23
24 $ hg co -q 3
24 $ hg co -q 3
25 $ echo other 4 >> d
25 $ echo other 4 >> d
26 $ hg add d
26 $ hg add d
27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
28
28
29 $ hg merge -q foo
29 $ hg merge -q foo
30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
31
31
32 Test arithmetic operators have the right precedence:
32 Test arithmetic operators have the right precedence:
33
33
34 $ hg log -l 1 -T '{date(date, "%Y") + 5 * 10} {date(date, "%Y") - 2 * 3}\n'
34 $ hg log -l 1 -T '{date(date, "%Y") + 5 * 10} {date(date, "%Y") - 2 * 3}\n'
35 2020 1964
35 2020 1964
36 $ hg log -l 1 -T '{date(date, "%Y") * 5 + 10} {date(date, "%Y") * 3 - 2}\n'
36 $ hg log -l 1 -T '{date(date, "%Y") * 5 + 10} {date(date, "%Y") * 3 - 2}\n'
37 9860 5908
37 9860 5908
38
38
39 Test division:
39 Test division:
40
40
41 $ hg debugtemplate -r0 -v '{5 / 2} {mod(5, 2)}\n'
41 $ hg debugtemplate -r0 -v '{5 / 2} {mod(5, 2)}\n'
42 (template
42 (template
43 (/
43 (/
44 ('integer', '5')
44 ('integer', '5')
45 ('integer', '2'))
45 ('integer', '2'))
46 ('string', ' ')
46 ('string', ' ')
47 (func
47 (func
48 ('symbol', 'mod')
48 ('symbol', 'mod')
49 (list
49 (list
50 ('integer', '5')
50 ('integer', '5')
51 ('integer', '2')))
51 ('integer', '2')))
52 ('string', '\n'))
52 ('string', '\n'))
53 2 1
53 2 1
54 $ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n'
54 $ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n'
55 (template
55 (template
56 (/
56 (/
57 ('integer', '5')
57 ('integer', '5')
58 (negate
58 (negate
59 ('integer', '2')))
59 ('integer', '2')))
60 ('string', ' ')
60 ('string', ' ')
61 (func
61 (func
62 ('symbol', 'mod')
62 ('symbol', 'mod')
63 (list
63 (list
64 ('integer', '5')
64 ('integer', '5')
65 (negate
65 (negate
66 ('integer', '2'))))
66 ('integer', '2'))))
67 ('string', '\n'))
67 ('string', '\n'))
68 -3 -1
68 -3 -1
69 $ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n'
69 $ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n'
70 (template
70 (template
71 (/
71 (/
72 (negate
72 (negate
73 ('integer', '5'))
73 ('integer', '5'))
74 ('integer', '2'))
74 ('integer', '2'))
75 ('string', ' ')
75 ('string', ' ')
76 (func
76 (func
77 ('symbol', 'mod')
77 ('symbol', 'mod')
78 (list
78 (list
79 (negate
79 (negate
80 ('integer', '5'))
80 ('integer', '5'))
81 ('integer', '2')))
81 ('integer', '2')))
82 ('string', '\n'))
82 ('string', '\n'))
83 -3 1
83 -3 1
84 $ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n'
84 $ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n'
85 (template
85 (template
86 (/
86 (/
87 (negate
87 (negate
88 ('integer', '5'))
88 ('integer', '5'))
89 (negate
89 (negate
90 ('integer', '2')))
90 ('integer', '2')))
91 ('string', ' ')
91 ('string', ' ')
92 (func
92 (func
93 ('symbol', 'mod')
93 ('symbol', 'mod')
94 (list
94 (list
95 (negate
95 (negate
96 ('integer', '5'))
96 ('integer', '5'))
97 (negate
97 (negate
98 ('integer', '2'))))
98 ('integer', '2'))))
99 ('string', '\n'))
99 ('string', '\n'))
100 2 -1
100 2 -1
101
101
102 Filters bind closer than arithmetic:
102 Filters bind closer than arithmetic:
103
103
104 $ hg debugtemplate -r0 -v '{revset(".")|count - 1}\n'
104 $ hg debugtemplate -r0 -v '{revset(".")|count - 1}\n'
105 (template
105 (template
106 (-
106 (-
107 (|
107 (|
108 (func
108 (func
109 ('symbol', 'revset')
109 ('symbol', 'revset')
110 ('string', '.'))
110 ('string', '.'))
111 ('symbol', 'count'))
111 ('symbol', 'count'))
112 ('integer', '1'))
112 ('integer', '1'))
113 ('string', '\n'))
113 ('string', '\n'))
114 0
114 0
115
115
116 But negate binds closer still:
116 But negate binds closer still:
117
117
118 $ hg debugtemplate -r0 -v '{1-3|stringify}\n'
118 $ hg debugtemplate -r0 -v '{1-3|stringify}\n'
119 (template
119 (template
120 (-
120 (-
121 ('integer', '1')
121 ('integer', '1')
122 (|
122 (|
123 ('integer', '3')
123 ('integer', '3')
124 ('symbol', 'stringify')))
124 ('symbol', 'stringify')))
125 ('string', '\n'))
125 ('string', '\n'))
126 hg: parse error: arithmetic only defined on integers
126 hg: parse error: arithmetic only defined on integers
127 [255]
127 [255]
128 $ hg debugtemplate -r0 -v '{-3|stringify}\n'
128 $ hg debugtemplate -r0 -v '{-3|stringify}\n'
129 (template
129 (template
130 (|
130 (|
131 (negate
131 (negate
132 ('integer', '3'))
132 ('integer', '3'))
133 ('symbol', 'stringify'))
133 ('symbol', 'stringify'))
134 ('string', '\n'))
134 ('string', '\n'))
135 -3
135 -3
136
136
137 Keyword arguments:
137 Keyword arguments:
138
138
139 $ hg debugtemplate -r0 -v '{foo=bar|baz}'
139 $ hg debugtemplate -r0 -v '{foo=bar|baz}'
140 (template
140 (template
141 (keyvalue
141 (keyvalue
142 ('symbol', 'foo')
142 ('symbol', 'foo')
143 (|
143 (|
144 ('symbol', 'bar')
144 ('symbol', 'bar')
145 ('symbol', 'baz'))))
145 ('symbol', 'baz'))))
146 hg: parse error: can't use a key-value pair in this context
146 hg: parse error: can't use a key-value pair in this context
147 [255]
147 [255]
148
148
149 $ hg debugtemplate '{pad("foo", width=10, left=true)}\n'
149 $ hg debugtemplate '{pad("foo", width=10, left=true)}\n'
150 foo
150 foo
151
151
152 Call function which takes named arguments by filter syntax:
152 Call function which takes named arguments by filter syntax:
153
153
154 $ hg debugtemplate '{" "|separate}'
154 $ hg debugtemplate '{" "|separate}'
155 $ hg debugtemplate '{("not", "an", "argument", "list")|separate}'
155 $ hg debugtemplate '{("not", "an", "argument", "list")|separate}'
156 hg: parse error: unknown method 'list'
156 hg: parse error: unknown method 'list'
157 [255]
157 [255]
158
158
159 Second branch starting at nullrev:
159 Second branch starting at nullrev:
160
160
161 $ hg update null
161 $ hg update null
162 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
162 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
163 $ echo second > second
163 $ echo second > second
164 $ hg add second
164 $ hg add second
165 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
165 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
166 created new head
166 created new head
167
167
168 $ echo third > third
168 $ echo third > third
169 $ hg add third
169 $ hg add third
170 $ hg mv second fourth
170 $ hg mv second fourth
171 $ hg commit -m third -d "2020-01-01 10:01"
171 $ hg commit -m third -d "2020-01-01 10:01"
172
172
173 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
173 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
174 fourth (second)
174 fourth (second)
175 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
175 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
176 second -> fourth
176 second -> fourth
177 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
177 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
178 8 t
178 8 t
179 7 f
179 7 f
180
180
181 Working-directory revision has special identifiers, though they are still
181 Working-directory revision has special identifiers, though they are still
182 experimental:
182 experimental:
183
183
184 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
184 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
185 2147483647:ffffffffffffffffffffffffffffffffffffffff
185 2147483647:ffffffffffffffffffffffffffffffffffffffff
186
186
187 Some keywords are invalid for working-directory revision, but they should
187 Some keywords are invalid for working-directory revision, but they should
188 never cause crash:
188 never cause crash:
189
189
190 $ hg log -r 'wdir()' -T '{manifest}\n'
190 $ hg log -r 'wdir()' -T '{manifest}\n'
191
191
192
192
193 Quoting for ui.logtemplate
193 Quoting for ui.logtemplate
194
194
195 $ hg tip --config "ui.logtemplate={rev}\n"
195 $ hg tip --config "ui.logtemplate={rev}\n"
196 8
196 8
197 $ hg tip --config "ui.logtemplate='{rev}\n'"
197 $ hg tip --config "ui.logtemplate='{rev}\n'"
198 8
198 8
199 $ hg tip --config 'ui.logtemplate="{rev}\n"'
199 $ hg tip --config 'ui.logtemplate="{rev}\n"'
200 8
200 8
201 $ hg tip --config 'ui.logtemplate=n{rev}\n'
201 $ hg tip --config 'ui.logtemplate=n{rev}\n'
202 n8
202 n8
203
203
204 Make sure user/global hgrc does not affect tests
204 Make sure user/global hgrc does not affect tests
205
205
206 $ echo '[ui]' > .hg/hgrc
206 $ echo '[ui]' > .hg/hgrc
207 $ echo 'logtemplate =' >> .hg/hgrc
207 $ echo 'logtemplate =' >> .hg/hgrc
208 $ echo 'style =' >> .hg/hgrc
208 $ echo 'style =' >> .hg/hgrc
209
209
210 Add some simple styles to settings
210 Add some simple styles to settings
211
211
212 $ echo '[templates]' >> .hg/hgrc
212 $ cat <<'EOF' >> .hg/hgrc
213 $ printf 'simple = "{rev}\\n"\n' >> .hg/hgrc
213 > [templates]
214 $ printf 'simple2 = {rev}\\n\n' >> .hg/hgrc
214 > simple = "{rev}\n"
215 > simple2 = {rev}\n
216 > rev = "should not precede {rev} keyword\n"
217 > EOF
215
218
216 $ hg log -l1 -Tsimple
219 $ hg log -l1 -Tsimple
217 8
220 8
218 $ hg log -l1 -Tsimple2
221 $ hg log -l1 -Tsimple2
219 8
222 8
223 $ hg log -l1 -Trev
224 should not precede 8 keyword
225 $ hg log -l1 -T '{simple}'
226 8
227
228 Map file shouldn't see user templates:
229
230 $ cat <<EOF > tmpl
231 > changeset = 'nothing expanded:{simple}\n'
232 > EOF
233 $ hg log -l1 --style ./tmpl
234 nothing expanded:
220
235
221 Test templates and style maps in files:
236 Test templates and style maps in files:
222
237
223 $ echo "{rev}" > tmpl
238 $ echo "{rev}" > tmpl
224 $ hg log -l1 -T./tmpl
239 $ hg log -l1 -T./tmpl
225 8
240 8
226 $ hg log -l1 -Tblah/blah
241 $ hg log -l1 -Tblah/blah
227 blah/blah (no-eol)
242 blah/blah (no-eol)
228
243
229 $ printf 'changeset = "{rev}\\n"\n' > map-simple
244 $ printf 'changeset = "{rev}\\n"\n' > map-simple
230 $ hg log -l1 -T./map-simple
245 $ hg log -l1 -T./map-simple
231 8
246 8
232
247
233 Test template map inheritance
248 Test template map inheritance
234
249
235 $ echo "__base__ = map-cmdline.default" > map-simple
250 $ echo "__base__ = map-cmdline.default" > map-simple
236 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
251 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
237 $ hg log -l1 -T./map-simple
252 $ hg log -l1 -T./map-simple
238 changeset: ***8***
253 changeset: ***8***
239 tag: tip
254 tag: tip
240 user: test
255 user: test
241 date: Wed Jan 01 10:01:00 2020 +0000
256 date: Wed Jan 01 10:01:00 2020 +0000
242 summary: third
257 summary: third
243
258
244
259
245 Template should precede style option
260 Template should precede style option
246
261
247 $ hg log -l1 --style default -T '{rev}\n'
262 $ hg log -l1 --style default -T '{rev}\n'
248 8
263 8
249
264
250 Add a commit with empty description, to ensure that the templates
265 Add a commit with empty description, to ensure that the templates
251 below will omit the description line.
266 below will omit the description line.
252
267
253 $ echo c >> c
268 $ echo c >> c
254 $ hg add c
269 $ hg add c
255 $ hg commit -qm ' '
270 $ hg commit -qm ' '
256
271
257 Default style is like normal output. Phases style should be the same
272 Default style is like normal output. Phases style should be the same
258 as default style, except for extra phase lines.
273 as default style, except for extra phase lines.
259
274
260 $ hg log > log.out
275 $ hg log > log.out
261 $ hg log --style default > style.out
276 $ hg log --style default > style.out
262 $ cmp log.out style.out || diff -u log.out style.out
277 $ cmp log.out style.out || diff -u log.out style.out
263 $ hg log -T phases > phases.out
278 $ hg log -T phases > phases.out
264 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
279 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
265 +phase: draft
280 +phase: draft
266 +phase: draft
281 +phase: draft
267 +phase: draft
282 +phase: draft
268 +phase: draft
283 +phase: draft
269 +phase: draft
284 +phase: draft
270 +phase: draft
285 +phase: draft
271 +phase: draft
286 +phase: draft
272 +phase: draft
287 +phase: draft
273 +phase: draft
288 +phase: draft
274 +phase: draft
289 +phase: draft
275
290
276 $ hg log -v > log.out
291 $ hg log -v > log.out
277 $ hg log -v --style default > style.out
292 $ hg log -v --style default > style.out
278 $ cmp log.out style.out || diff -u log.out style.out
293 $ cmp log.out style.out || diff -u log.out style.out
279 $ hg log -v -T phases > phases.out
294 $ hg log -v -T phases > phases.out
280 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
295 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
281 +phase: draft
296 +phase: draft
282 +phase: draft
297 +phase: draft
283 +phase: draft
298 +phase: draft
284 +phase: draft
299 +phase: draft
285 +phase: draft
300 +phase: draft
286 +phase: draft
301 +phase: draft
287 +phase: draft
302 +phase: draft
288 +phase: draft
303 +phase: draft
289 +phase: draft
304 +phase: draft
290 +phase: draft
305 +phase: draft
291
306
292 $ hg log -q > log.out
307 $ hg log -q > log.out
293 $ hg log -q --style default > style.out
308 $ hg log -q --style default > style.out
294 $ cmp log.out style.out || diff -u log.out style.out
309 $ cmp log.out style.out || diff -u log.out style.out
295 $ hg log -q -T phases > phases.out
310 $ hg log -q -T phases > phases.out
296 $ cmp log.out phases.out || diff -u log.out phases.out
311 $ cmp log.out phases.out || diff -u log.out phases.out
297
312
298 $ hg log --debug > log.out
313 $ hg log --debug > log.out
299 $ hg log --debug --style default > style.out
314 $ hg log --debug --style default > style.out
300 $ cmp log.out style.out || diff -u log.out style.out
315 $ cmp log.out style.out || diff -u log.out style.out
301 $ hg log --debug -T phases > phases.out
316 $ hg log --debug -T phases > phases.out
302 $ cmp log.out phases.out || diff -u log.out phases.out
317 $ cmp log.out phases.out || diff -u log.out phases.out
303
318
304 Default style of working-directory revision should also be the same (but
319 Default style of working-directory revision should also be the same (but
305 date may change while running tests):
320 date may change while running tests):
306
321
307 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
322 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
308 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
323 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
309 $ cmp log.out style.out || diff -u log.out style.out
324 $ cmp log.out style.out || diff -u log.out style.out
310
325
311 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
326 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
312 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
327 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
313 $ cmp log.out style.out || diff -u log.out style.out
328 $ cmp log.out style.out || diff -u log.out style.out
314
329
315 $ hg log -r 'wdir()' -q > log.out
330 $ hg log -r 'wdir()' -q > log.out
316 $ hg log -r 'wdir()' -q --style default > style.out
331 $ hg log -r 'wdir()' -q --style default > style.out
317 $ cmp log.out style.out || diff -u log.out style.out
332 $ cmp log.out style.out || diff -u log.out style.out
318
333
319 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
334 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
320 $ hg log -r 'wdir()' --debug --style default \
335 $ hg log -r 'wdir()' --debug --style default \
321 > | sed 's|^date:.*|date:|' > style.out
336 > | sed 's|^date:.*|date:|' > style.out
322 $ cmp log.out style.out || diff -u log.out style.out
337 $ cmp log.out style.out || diff -u log.out style.out
323
338
324 Default style should also preserve color information (issue2866):
339 Default style should also preserve color information (issue2866):
325
340
326 $ cp $HGRCPATH $HGRCPATH-bak
341 $ cp $HGRCPATH $HGRCPATH-bak
327 $ cat <<EOF >> $HGRCPATH
342 $ cat <<EOF >> $HGRCPATH
328 > [extensions]
343 > [extensions]
329 > color=
344 > color=
330 > EOF
345 > EOF
331
346
332 $ hg --color=debug log > log.out
347 $ hg --color=debug log > log.out
333 $ hg --color=debug log --style default > style.out
348 $ hg --color=debug log --style default > style.out
334 $ cmp log.out style.out || diff -u log.out style.out
349 $ cmp log.out style.out || diff -u log.out style.out
335 $ hg --color=debug log -T phases > phases.out
350 $ hg --color=debug log -T phases > phases.out
336 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
351 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
337 +[log.phase|phase: draft]
352 +[log.phase|phase: draft]
338 +[log.phase|phase: draft]
353 +[log.phase|phase: draft]
339 +[log.phase|phase: draft]
354 +[log.phase|phase: draft]
340 +[log.phase|phase: draft]
355 +[log.phase|phase: draft]
341 +[log.phase|phase: draft]
356 +[log.phase|phase: draft]
342 +[log.phase|phase: draft]
357 +[log.phase|phase: draft]
343 +[log.phase|phase: draft]
358 +[log.phase|phase: draft]
344 +[log.phase|phase: draft]
359 +[log.phase|phase: draft]
345 +[log.phase|phase: draft]
360 +[log.phase|phase: draft]
346 +[log.phase|phase: draft]
361 +[log.phase|phase: draft]
347
362
348 $ hg --color=debug -v log > log.out
363 $ hg --color=debug -v log > log.out
349 $ hg --color=debug -v log --style default > style.out
364 $ hg --color=debug -v log --style default > style.out
350 $ cmp log.out style.out || diff -u log.out style.out
365 $ cmp log.out style.out || diff -u log.out style.out
351 $ hg --color=debug -v log -T phases > phases.out
366 $ hg --color=debug -v log -T phases > phases.out
352 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
367 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
353 +[log.phase|phase: draft]
368 +[log.phase|phase: draft]
354 +[log.phase|phase: draft]
369 +[log.phase|phase: draft]
355 +[log.phase|phase: draft]
370 +[log.phase|phase: draft]
356 +[log.phase|phase: draft]
371 +[log.phase|phase: draft]
357 +[log.phase|phase: draft]
372 +[log.phase|phase: draft]
358 +[log.phase|phase: draft]
373 +[log.phase|phase: draft]
359 +[log.phase|phase: draft]
374 +[log.phase|phase: draft]
360 +[log.phase|phase: draft]
375 +[log.phase|phase: draft]
361 +[log.phase|phase: draft]
376 +[log.phase|phase: draft]
362 +[log.phase|phase: draft]
377 +[log.phase|phase: draft]
363
378
364 $ hg --color=debug -q log > log.out
379 $ hg --color=debug -q log > log.out
365 $ hg --color=debug -q log --style default > style.out
380 $ hg --color=debug -q log --style default > style.out
366 $ cmp log.out style.out || diff -u log.out style.out
381 $ cmp log.out style.out || diff -u log.out style.out
367 $ hg --color=debug -q log -T phases > phases.out
382 $ hg --color=debug -q log -T phases > phases.out
368 $ cmp log.out phases.out || diff -u log.out phases.out
383 $ cmp log.out phases.out || diff -u log.out phases.out
369
384
370 $ hg --color=debug --debug log > log.out
385 $ hg --color=debug --debug log > log.out
371 $ hg --color=debug --debug log --style default > style.out
386 $ hg --color=debug --debug log --style default > style.out
372 $ cmp log.out style.out || diff -u log.out style.out
387 $ cmp log.out style.out || diff -u log.out style.out
373 $ hg --color=debug --debug log -T phases > phases.out
388 $ hg --color=debug --debug log -T phases > phases.out
374 $ cmp log.out phases.out || diff -u log.out phases.out
389 $ cmp log.out phases.out || diff -u log.out phases.out
375
390
376 $ mv $HGRCPATH-bak $HGRCPATH
391 $ mv $HGRCPATH-bak $HGRCPATH
377
392
378 Remove commit with empty commit message, so as to not pollute further
393 Remove commit with empty commit message, so as to not pollute further
379 tests.
394 tests.
380
395
381 $ hg --config extensions.strip= strip -q .
396 $ hg --config extensions.strip= strip -q .
382
397
383 Revision with no copies (used to print a traceback):
398 Revision with no copies (used to print a traceback):
384
399
385 $ hg tip -v --template '\n'
400 $ hg tip -v --template '\n'
386
401
387
402
388 Compact style works:
403 Compact style works:
389
404
390 $ hg log -Tcompact
405 $ hg log -Tcompact
391 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
406 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
392 third
407 third
393
408
394 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
409 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
395 second
410 second
396
411
397 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
412 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
398 merge
413 merge
399
414
400 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
415 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
401 new head
416 new head
402
417
403 4 bbe44766e73d 1970-01-17 04:53 +0000 person
418 4 bbe44766e73d 1970-01-17 04:53 +0000 person
404 new branch
419 new branch
405
420
406 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
421 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
407 no user, no domain
422 no user, no domain
408
423
409 2 97054abb4ab8 1970-01-14 21:20 +0000 other
424 2 97054abb4ab8 1970-01-14 21:20 +0000 other
410 no person
425 no person
411
426
412 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
427 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
413 other 1
428 other 1
414
429
415 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
430 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
416 line 1
431 line 1
417
432
418
433
419 $ hg log -v --style compact
434 $ hg log -v --style compact
420 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
435 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
421 third
436 third
422
437
423 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
438 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
424 second
439 second
425
440
426 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
441 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
427 merge
442 merge
428
443
429 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
444 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
430 new head
445 new head
431
446
432 4 bbe44766e73d 1970-01-17 04:53 +0000 person
447 4 bbe44766e73d 1970-01-17 04:53 +0000 person
433 new branch
448 new branch
434
449
435 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
450 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
436 no user, no domain
451 no user, no domain
437
452
438 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
453 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
439 no person
454 no person
440
455
441 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
456 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
442 other 1
457 other 1
443 other 2
458 other 2
444
459
445 other 3
460 other 3
446
461
447 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
462 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
448 line 1
463 line 1
449 line 2
464 line 2
450
465
451
466
452 $ hg log --debug --style compact
467 $ hg log --debug --style compact
453 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
468 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
454 third
469 third
455
470
456 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
471 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
457 second
472 second
458
473
459 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
474 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
460 merge
475 merge
461
476
462 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
477 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
463 new head
478 new head
464
479
465 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
480 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
466 new branch
481 new branch
467
482
468 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
483 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
469 no user, no domain
484 no user, no domain
470
485
471 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
486 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
472 no person
487 no person
473
488
474 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
489 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
475 other 1
490 other 1
476 other 2
491 other 2
477
492
478 other 3
493 other 3
479
494
480 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
495 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
481 line 1
496 line 1
482 line 2
497 line 2
483
498
484
499
485 Test xml styles:
500 Test xml styles:
486
501
487 $ hg log --style xml -r 'not all()'
502 $ hg log --style xml -r 'not all()'
488 <?xml version="1.0"?>
503 <?xml version="1.0"?>
489 <log>
504 <log>
490 </log>
505 </log>
491
506
492 $ hg log --style xml
507 $ hg log --style xml
493 <?xml version="1.0"?>
508 <?xml version="1.0"?>
494 <log>
509 <log>
495 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
510 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
496 <tag>tip</tag>
511 <tag>tip</tag>
497 <author email="test">test</author>
512 <author email="test">test</author>
498 <date>2020-01-01T10:01:00+00:00</date>
513 <date>2020-01-01T10:01:00+00:00</date>
499 <msg xml:space="preserve">third</msg>
514 <msg xml:space="preserve">third</msg>
500 </logentry>
515 </logentry>
501 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
516 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
502 <parent revision="-1" node="0000000000000000000000000000000000000000" />
517 <parent revision="-1" node="0000000000000000000000000000000000000000" />
503 <author email="user@hostname">User Name</author>
518 <author email="user@hostname">User Name</author>
504 <date>1970-01-12T13:46:40+00:00</date>
519 <date>1970-01-12T13:46:40+00:00</date>
505 <msg xml:space="preserve">second</msg>
520 <msg xml:space="preserve">second</msg>
506 </logentry>
521 </logentry>
507 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
522 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
508 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
523 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
509 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
524 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
510 <author email="person">person</author>
525 <author email="person">person</author>
511 <date>1970-01-18T08:40:01+00:00</date>
526 <date>1970-01-18T08:40:01+00:00</date>
512 <msg xml:space="preserve">merge</msg>
527 <msg xml:space="preserve">merge</msg>
513 </logentry>
528 </logentry>
514 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
529 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
515 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
530 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
516 <author email="person">person</author>
531 <author email="person">person</author>
517 <date>1970-01-18T08:40:00+00:00</date>
532 <date>1970-01-18T08:40:00+00:00</date>
518 <msg xml:space="preserve">new head</msg>
533 <msg xml:space="preserve">new head</msg>
519 </logentry>
534 </logentry>
520 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
535 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
521 <branch>foo</branch>
536 <branch>foo</branch>
522 <author email="person">person</author>
537 <author email="person">person</author>
523 <date>1970-01-17T04:53:20+00:00</date>
538 <date>1970-01-17T04:53:20+00:00</date>
524 <msg xml:space="preserve">new branch</msg>
539 <msg xml:space="preserve">new branch</msg>
525 </logentry>
540 </logentry>
526 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
541 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
527 <author email="person">person</author>
542 <author email="person">person</author>
528 <date>1970-01-16T01:06:40+00:00</date>
543 <date>1970-01-16T01:06:40+00:00</date>
529 <msg xml:space="preserve">no user, no domain</msg>
544 <msg xml:space="preserve">no user, no domain</msg>
530 </logentry>
545 </logentry>
531 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
546 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
532 <author email="other@place">other</author>
547 <author email="other@place">other</author>
533 <date>1970-01-14T21:20:00+00:00</date>
548 <date>1970-01-14T21:20:00+00:00</date>
534 <msg xml:space="preserve">no person</msg>
549 <msg xml:space="preserve">no person</msg>
535 </logentry>
550 </logentry>
536 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
551 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
537 <author email="other@place">A. N. Other</author>
552 <author email="other@place">A. N. Other</author>
538 <date>1970-01-13T17:33:20+00:00</date>
553 <date>1970-01-13T17:33:20+00:00</date>
539 <msg xml:space="preserve">other 1
554 <msg xml:space="preserve">other 1
540 other 2
555 other 2
541
556
542 other 3</msg>
557 other 3</msg>
543 </logentry>
558 </logentry>
544 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
559 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
545 <author email="user@hostname">User Name</author>
560 <author email="user@hostname">User Name</author>
546 <date>1970-01-12T13:46:40+00:00</date>
561 <date>1970-01-12T13:46:40+00:00</date>
547 <msg xml:space="preserve">line 1
562 <msg xml:space="preserve">line 1
548 line 2</msg>
563 line 2</msg>
549 </logentry>
564 </logentry>
550 </log>
565 </log>
551
566
552 $ hg log -v --style xml
567 $ hg log -v --style xml
553 <?xml version="1.0"?>
568 <?xml version="1.0"?>
554 <log>
569 <log>
555 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
570 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
556 <tag>tip</tag>
571 <tag>tip</tag>
557 <author email="test">test</author>
572 <author email="test">test</author>
558 <date>2020-01-01T10:01:00+00:00</date>
573 <date>2020-01-01T10:01:00+00:00</date>
559 <msg xml:space="preserve">third</msg>
574 <msg xml:space="preserve">third</msg>
560 <paths>
575 <paths>
561 <path action="A">fourth</path>
576 <path action="A">fourth</path>
562 <path action="A">third</path>
577 <path action="A">third</path>
563 <path action="R">second</path>
578 <path action="R">second</path>
564 </paths>
579 </paths>
565 <copies>
580 <copies>
566 <copy source="second">fourth</copy>
581 <copy source="second">fourth</copy>
567 </copies>
582 </copies>
568 </logentry>
583 </logentry>
569 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
584 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
570 <parent revision="-1" node="0000000000000000000000000000000000000000" />
585 <parent revision="-1" node="0000000000000000000000000000000000000000" />
571 <author email="user@hostname">User Name</author>
586 <author email="user@hostname">User Name</author>
572 <date>1970-01-12T13:46:40+00:00</date>
587 <date>1970-01-12T13:46:40+00:00</date>
573 <msg xml:space="preserve">second</msg>
588 <msg xml:space="preserve">second</msg>
574 <paths>
589 <paths>
575 <path action="A">second</path>
590 <path action="A">second</path>
576 </paths>
591 </paths>
577 </logentry>
592 </logentry>
578 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
593 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
579 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
594 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
580 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
595 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
581 <author email="person">person</author>
596 <author email="person">person</author>
582 <date>1970-01-18T08:40:01+00:00</date>
597 <date>1970-01-18T08:40:01+00:00</date>
583 <msg xml:space="preserve">merge</msg>
598 <msg xml:space="preserve">merge</msg>
584 <paths>
599 <paths>
585 </paths>
600 </paths>
586 </logentry>
601 </logentry>
587 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
602 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
588 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
603 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
589 <author email="person">person</author>
604 <author email="person">person</author>
590 <date>1970-01-18T08:40:00+00:00</date>
605 <date>1970-01-18T08:40:00+00:00</date>
591 <msg xml:space="preserve">new head</msg>
606 <msg xml:space="preserve">new head</msg>
592 <paths>
607 <paths>
593 <path action="A">d</path>
608 <path action="A">d</path>
594 </paths>
609 </paths>
595 </logentry>
610 </logentry>
596 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
611 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
597 <branch>foo</branch>
612 <branch>foo</branch>
598 <author email="person">person</author>
613 <author email="person">person</author>
599 <date>1970-01-17T04:53:20+00:00</date>
614 <date>1970-01-17T04:53:20+00:00</date>
600 <msg xml:space="preserve">new branch</msg>
615 <msg xml:space="preserve">new branch</msg>
601 <paths>
616 <paths>
602 </paths>
617 </paths>
603 </logentry>
618 </logentry>
604 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
619 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
605 <author email="person">person</author>
620 <author email="person">person</author>
606 <date>1970-01-16T01:06:40+00:00</date>
621 <date>1970-01-16T01:06:40+00:00</date>
607 <msg xml:space="preserve">no user, no domain</msg>
622 <msg xml:space="preserve">no user, no domain</msg>
608 <paths>
623 <paths>
609 <path action="M">c</path>
624 <path action="M">c</path>
610 </paths>
625 </paths>
611 </logentry>
626 </logentry>
612 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
627 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
613 <author email="other@place">other</author>
628 <author email="other@place">other</author>
614 <date>1970-01-14T21:20:00+00:00</date>
629 <date>1970-01-14T21:20:00+00:00</date>
615 <msg xml:space="preserve">no person</msg>
630 <msg xml:space="preserve">no person</msg>
616 <paths>
631 <paths>
617 <path action="A">c</path>
632 <path action="A">c</path>
618 </paths>
633 </paths>
619 </logentry>
634 </logentry>
620 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
635 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
621 <author email="other@place">A. N. Other</author>
636 <author email="other@place">A. N. Other</author>
622 <date>1970-01-13T17:33:20+00:00</date>
637 <date>1970-01-13T17:33:20+00:00</date>
623 <msg xml:space="preserve">other 1
638 <msg xml:space="preserve">other 1
624 other 2
639 other 2
625
640
626 other 3</msg>
641 other 3</msg>
627 <paths>
642 <paths>
628 <path action="A">b</path>
643 <path action="A">b</path>
629 </paths>
644 </paths>
630 </logentry>
645 </logentry>
631 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
646 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
632 <author email="user@hostname">User Name</author>
647 <author email="user@hostname">User Name</author>
633 <date>1970-01-12T13:46:40+00:00</date>
648 <date>1970-01-12T13:46:40+00:00</date>
634 <msg xml:space="preserve">line 1
649 <msg xml:space="preserve">line 1
635 line 2</msg>
650 line 2</msg>
636 <paths>
651 <paths>
637 <path action="A">a</path>
652 <path action="A">a</path>
638 </paths>
653 </paths>
639 </logentry>
654 </logentry>
640 </log>
655 </log>
641
656
642 $ hg log --debug --style xml
657 $ hg log --debug --style xml
643 <?xml version="1.0"?>
658 <?xml version="1.0"?>
644 <log>
659 <log>
645 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
660 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
646 <tag>tip</tag>
661 <tag>tip</tag>
647 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
662 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
648 <parent revision="-1" node="0000000000000000000000000000000000000000" />
663 <parent revision="-1" node="0000000000000000000000000000000000000000" />
649 <author email="test">test</author>
664 <author email="test">test</author>
650 <date>2020-01-01T10:01:00+00:00</date>
665 <date>2020-01-01T10:01:00+00:00</date>
651 <msg xml:space="preserve">third</msg>
666 <msg xml:space="preserve">third</msg>
652 <paths>
667 <paths>
653 <path action="A">fourth</path>
668 <path action="A">fourth</path>
654 <path action="A">third</path>
669 <path action="A">third</path>
655 <path action="R">second</path>
670 <path action="R">second</path>
656 </paths>
671 </paths>
657 <copies>
672 <copies>
658 <copy source="second">fourth</copy>
673 <copy source="second">fourth</copy>
659 </copies>
674 </copies>
660 <extra key="branch">default</extra>
675 <extra key="branch">default</extra>
661 </logentry>
676 </logentry>
662 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
677 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
663 <parent revision="-1" node="0000000000000000000000000000000000000000" />
678 <parent revision="-1" node="0000000000000000000000000000000000000000" />
664 <parent revision="-1" node="0000000000000000000000000000000000000000" />
679 <parent revision="-1" node="0000000000000000000000000000000000000000" />
665 <author email="user@hostname">User Name</author>
680 <author email="user@hostname">User Name</author>
666 <date>1970-01-12T13:46:40+00:00</date>
681 <date>1970-01-12T13:46:40+00:00</date>
667 <msg xml:space="preserve">second</msg>
682 <msg xml:space="preserve">second</msg>
668 <paths>
683 <paths>
669 <path action="A">second</path>
684 <path action="A">second</path>
670 </paths>
685 </paths>
671 <extra key="branch">default</extra>
686 <extra key="branch">default</extra>
672 </logentry>
687 </logentry>
673 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
688 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
674 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
689 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
675 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
690 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
676 <author email="person">person</author>
691 <author email="person">person</author>
677 <date>1970-01-18T08:40:01+00:00</date>
692 <date>1970-01-18T08:40:01+00:00</date>
678 <msg xml:space="preserve">merge</msg>
693 <msg xml:space="preserve">merge</msg>
679 <paths>
694 <paths>
680 </paths>
695 </paths>
681 <extra key="branch">default</extra>
696 <extra key="branch">default</extra>
682 </logentry>
697 </logentry>
683 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
698 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
684 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
699 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
685 <parent revision="-1" node="0000000000000000000000000000000000000000" />
700 <parent revision="-1" node="0000000000000000000000000000000000000000" />
686 <author email="person">person</author>
701 <author email="person">person</author>
687 <date>1970-01-18T08:40:00+00:00</date>
702 <date>1970-01-18T08:40:00+00:00</date>
688 <msg xml:space="preserve">new head</msg>
703 <msg xml:space="preserve">new head</msg>
689 <paths>
704 <paths>
690 <path action="A">d</path>
705 <path action="A">d</path>
691 </paths>
706 </paths>
692 <extra key="branch">default</extra>
707 <extra key="branch">default</extra>
693 </logentry>
708 </logentry>
694 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
709 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
695 <branch>foo</branch>
710 <branch>foo</branch>
696 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
711 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
697 <parent revision="-1" node="0000000000000000000000000000000000000000" />
712 <parent revision="-1" node="0000000000000000000000000000000000000000" />
698 <author email="person">person</author>
713 <author email="person">person</author>
699 <date>1970-01-17T04:53:20+00:00</date>
714 <date>1970-01-17T04:53:20+00:00</date>
700 <msg xml:space="preserve">new branch</msg>
715 <msg xml:space="preserve">new branch</msg>
701 <paths>
716 <paths>
702 </paths>
717 </paths>
703 <extra key="branch">foo</extra>
718 <extra key="branch">foo</extra>
704 </logentry>
719 </logentry>
705 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
720 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
706 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
721 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
707 <parent revision="-1" node="0000000000000000000000000000000000000000" />
722 <parent revision="-1" node="0000000000000000000000000000000000000000" />
708 <author email="person">person</author>
723 <author email="person">person</author>
709 <date>1970-01-16T01:06:40+00:00</date>
724 <date>1970-01-16T01:06:40+00:00</date>
710 <msg xml:space="preserve">no user, no domain</msg>
725 <msg xml:space="preserve">no user, no domain</msg>
711 <paths>
726 <paths>
712 <path action="M">c</path>
727 <path action="M">c</path>
713 </paths>
728 </paths>
714 <extra key="branch">default</extra>
729 <extra key="branch">default</extra>
715 </logentry>
730 </logentry>
716 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
731 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
717 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
732 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
718 <parent revision="-1" node="0000000000000000000000000000000000000000" />
733 <parent revision="-1" node="0000000000000000000000000000000000000000" />
719 <author email="other@place">other</author>
734 <author email="other@place">other</author>
720 <date>1970-01-14T21:20:00+00:00</date>
735 <date>1970-01-14T21:20:00+00:00</date>
721 <msg xml:space="preserve">no person</msg>
736 <msg xml:space="preserve">no person</msg>
722 <paths>
737 <paths>
723 <path action="A">c</path>
738 <path action="A">c</path>
724 </paths>
739 </paths>
725 <extra key="branch">default</extra>
740 <extra key="branch">default</extra>
726 </logentry>
741 </logentry>
727 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
742 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
728 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
743 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
729 <parent revision="-1" node="0000000000000000000000000000000000000000" />
744 <parent revision="-1" node="0000000000000000000000000000000000000000" />
730 <author email="other@place">A. N. Other</author>
745 <author email="other@place">A. N. Other</author>
731 <date>1970-01-13T17:33:20+00:00</date>
746 <date>1970-01-13T17:33:20+00:00</date>
732 <msg xml:space="preserve">other 1
747 <msg xml:space="preserve">other 1
733 other 2
748 other 2
734
749
735 other 3</msg>
750 other 3</msg>
736 <paths>
751 <paths>
737 <path action="A">b</path>
752 <path action="A">b</path>
738 </paths>
753 </paths>
739 <extra key="branch">default</extra>
754 <extra key="branch">default</extra>
740 </logentry>
755 </logentry>
741 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
756 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
742 <parent revision="-1" node="0000000000000000000000000000000000000000" />
757 <parent revision="-1" node="0000000000000000000000000000000000000000" />
743 <parent revision="-1" node="0000000000000000000000000000000000000000" />
758 <parent revision="-1" node="0000000000000000000000000000000000000000" />
744 <author email="user@hostname">User Name</author>
759 <author email="user@hostname">User Name</author>
745 <date>1970-01-12T13:46:40+00:00</date>
760 <date>1970-01-12T13:46:40+00:00</date>
746 <msg xml:space="preserve">line 1
761 <msg xml:space="preserve">line 1
747 line 2</msg>
762 line 2</msg>
748 <paths>
763 <paths>
749 <path action="A">a</path>
764 <path action="A">a</path>
750 </paths>
765 </paths>
751 <extra key="branch">default</extra>
766 <extra key="branch">default</extra>
752 </logentry>
767 </logentry>
753 </log>
768 </log>
754
769
755
770
756 Test JSON style:
771 Test JSON style:
757
772
758 $ hg log -k nosuch -Tjson
773 $ hg log -k nosuch -Tjson
759 []
774 []
760
775
761 $ hg log -qr . -Tjson
776 $ hg log -qr . -Tjson
762 [
777 [
763 {
778 {
764 "rev": 8,
779 "rev": 8,
765 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
780 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
766 }
781 }
767 ]
782 ]
768
783
769 $ hg log -vpr . -Tjson --stat
784 $ hg log -vpr . -Tjson --stat
770 [
785 [
771 {
786 {
772 "rev": 8,
787 "rev": 8,
773 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
788 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
774 "branch": "default",
789 "branch": "default",
775 "phase": "draft",
790 "phase": "draft",
776 "user": "test",
791 "user": "test",
777 "date": [1577872860, 0],
792 "date": [1577872860, 0],
778 "desc": "third",
793 "desc": "third",
779 "bookmarks": [],
794 "bookmarks": [],
780 "tags": ["tip"],
795 "tags": ["tip"],
781 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
796 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
782 "files": ["fourth", "second", "third"],
797 "files": ["fourth", "second", "third"],
783 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
798 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
784 "diff": "diff -r 29114dbae42b -r 95c24699272e fourth\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/fourth\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+second\ndiff -r 29114dbae42b -r 95c24699272e second\n--- a/second\tMon Jan 12 13:46:40 1970 +0000\n+++ /dev/null\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +0,0 @@\n-second\ndiff -r 29114dbae42b -r 95c24699272e third\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/third\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+third\n"
799 "diff": "diff -r 29114dbae42b -r 95c24699272e fourth\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/fourth\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+second\ndiff -r 29114dbae42b -r 95c24699272e second\n--- a/second\tMon Jan 12 13:46:40 1970 +0000\n+++ /dev/null\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +0,0 @@\n-second\ndiff -r 29114dbae42b -r 95c24699272e third\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/third\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+third\n"
785 }
800 }
786 ]
801 ]
787
802
788 honor --git but not format-breaking diffopts
803 honor --git but not format-breaking diffopts
789 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
804 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
790 [
805 [
791 {
806 {
792 "rev": 8,
807 "rev": 8,
793 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
808 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
794 "branch": "default",
809 "branch": "default",
795 "phase": "draft",
810 "phase": "draft",
796 "user": "test",
811 "user": "test",
797 "date": [1577872860, 0],
812 "date": [1577872860, 0],
798 "desc": "third",
813 "desc": "third",
799 "bookmarks": [],
814 "bookmarks": [],
800 "tags": ["tip"],
815 "tags": ["tip"],
801 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
816 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
802 "files": ["fourth", "second", "third"],
817 "files": ["fourth", "second", "third"],
803 "diff": "diff --git a/second b/fourth\nrename from second\nrename to fourth\ndiff --git a/third b/third\nnew file mode 100644\n--- /dev/null\n+++ b/third\n@@ -0,0 +1,1 @@\n+third\n"
818 "diff": "diff --git a/second b/fourth\nrename from second\nrename to fourth\ndiff --git a/third b/third\nnew file mode 100644\n--- /dev/null\n+++ b/third\n@@ -0,0 +1,1 @@\n+third\n"
804 }
819 }
805 ]
820 ]
806
821
807 $ hg log -T json
822 $ hg log -T json
808 [
823 [
809 {
824 {
810 "rev": 8,
825 "rev": 8,
811 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
826 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
812 "branch": "default",
827 "branch": "default",
813 "phase": "draft",
828 "phase": "draft",
814 "user": "test",
829 "user": "test",
815 "date": [1577872860, 0],
830 "date": [1577872860, 0],
816 "desc": "third",
831 "desc": "third",
817 "bookmarks": [],
832 "bookmarks": [],
818 "tags": ["tip"],
833 "tags": ["tip"],
819 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
834 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
820 },
835 },
821 {
836 {
822 "rev": 7,
837 "rev": 7,
823 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
838 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
824 "branch": "default",
839 "branch": "default",
825 "phase": "draft",
840 "phase": "draft",
826 "user": "User Name <user@hostname>",
841 "user": "User Name <user@hostname>",
827 "date": [1000000, 0],
842 "date": [1000000, 0],
828 "desc": "second",
843 "desc": "second",
829 "bookmarks": [],
844 "bookmarks": [],
830 "tags": [],
845 "tags": [],
831 "parents": ["0000000000000000000000000000000000000000"]
846 "parents": ["0000000000000000000000000000000000000000"]
832 },
847 },
833 {
848 {
834 "rev": 6,
849 "rev": 6,
835 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
850 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
836 "branch": "default",
851 "branch": "default",
837 "phase": "draft",
852 "phase": "draft",
838 "user": "person",
853 "user": "person",
839 "date": [1500001, 0],
854 "date": [1500001, 0],
840 "desc": "merge",
855 "desc": "merge",
841 "bookmarks": [],
856 "bookmarks": [],
842 "tags": [],
857 "tags": [],
843 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
858 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
844 },
859 },
845 {
860 {
846 "rev": 5,
861 "rev": 5,
847 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
862 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
848 "branch": "default",
863 "branch": "default",
849 "phase": "draft",
864 "phase": "draft",
850 "user": "person",
865 "user": "person",
851 "date": [1500000, 0],
866 "date": [1500000, 0],
852 "desc": "new head",
867 "desc": "new head",
853 "bookmarks": [],
868 "bookmarks": [],
854 "tags": [],
869 "tags": [],
855 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
870 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
856 },
871 },
857 {
872 {
858 "rev": 4,
873 "rev": 4,
859 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
874 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
860 "branch": "foo",
875 "branch": "foo",
861 "phase": "draft",
876 "phase": "draft",
862 "user": "person",
877 "user": "person",
863 "date": [1400000, 0],
878 "date": [1400000, 0],
864 "desc": "new branch",
879 "desc": "new branch",
865 "bookmarks": [],
880 "bookmarks": [],
866 "tags": [],
881 "tags": [],
867 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
882 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
868 },
883 },
869 {
884 {
870 "rev": 3,
885 "rev": 3,
871 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
886 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
872 "branch": "default",
887 "branch": "default",
873 "phase": "draft",
888 "phase": "draft",
874 "user": "person",
889 "user": "person",
875 "date": [1300000, 0],
890 "date": [1300000, 0],
876 "desc": "no user, no domain",
891 "desc": "no user, no domain",
877 "bookmarks": [],
892 "bookmarks": [],
878 "tags": [],
893 "tags": [],
879 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
894 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
880 },
895 },
881 {
896 {
882 "rev": 2,
897 "rev": 2,
883 "node": "97054abb4ab824450e9164180baf491ae0078465",
898 "node": "97054abb4ab824450e9164180baf491ae0078465",
884 "branch": "default",
899 "branch": "default",
885 "phase": "draft",
900 "phase": "draft",
886 "user": "other@place",
901 "user": "other@place",
887 "date": [1200000, 0],
902 "date": [1200000, 0],
888 "desc": "no person",
903 "desc": "no person",
889 "bookmarks": [],
904 "bookmarks": [],
890 "tags": [],
905 "tags": [],
891 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
906 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
892 },
907 },
893 {
908 {
894 "rev": 1,
909 "rev": 1,
895 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
910 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
896 "branch": "default",
911 "branch": "default",
897 "phase": "draft",
912 "phase": "draft",
898 "user": "A. N. Other <other@place>",
913 "user": "A. N. Other <other@place>",
899 "date": [1100000, 0],
914 "date": [1100000, 0],
900 "desc": "other 1\nother 2\n\nother 3",
915 "desc": "other 1\nother 2\n\nother 3",
901 "bookmarks": [],
916 "bookmarks": [],
902 "tags": [],
917 "tags": [],
903 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
918 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
904 },
919 },
905 {
920 {
906 "rev": 0,
921 "rev": 0,
907 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
922 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
908 "branch": "default",
923 "branch": "default",
909 "phase": "draft",
924 "phase": "draft",
910 "user": "User Name <user@hostname>",
925 "user": "User Name <user@hostname>",
911 "date": [1000000, 0],
926 "date": [1000000, 0],
912 "desc": "line 1\nline 2",
927 "desc": "line 1\nline 2",
913 "bookmarks": [],
928 "bookmarks": [],
914 "tags": [],
929 "tags": [],
915 "parents": ["0000000000000000000000000000000000000000"]
930 "parents": ["0000000000000000000000000000000000000000"]
916 }
931 }
917 ]
932 ]
918
933
919 $ hg heads -v -Tjson
934 $ hg heads -v -Tjson
920 [
935 [
921 {
936 {
922 "rev": 8,
937 "rev": 8,
923 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
938 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
924 "branch": "default",
939 "branch": "default",
925 "phase": "draft",
940 "phase": "draft",
926 "user": "test",
941 "user": "test",
927 "date": [1577872860, 0],
942 "date": [1577872860, 0],
928 "desc": "third",
943 "desc": "third",
929 "bookmarks": [],
944 "bookmarks": [],
930 "tags": ["tip"],
945 "tags": ["tip"],
931 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
946 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
932 "files": ["fourth", "second", "third"]
947 "files": ["fourth", "second", "third"]
933 },
948 },
934 {
949 {
935 "rev": 6,
950 "rev": 6,
936 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
951 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
937 "branch": "default",
952 "branch": "default",
938 "phase": "draft",
953 "phase": "draft",
939 "user": "person",
954 "user": "person",
940 "date": [1500001, 0],
955 "date": [1500001, 0],
941 "desc": "merge",
956 "desc": "merge",
942 "bookmarks": [],
957 "bookmarks": [],
943 "tags": [],
958 "tags": [],
944 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
959 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
945 "files": []
960 "files": []
946 },
961 },
947 {
962 {
948 "rev": 4,
963 "rev": 4,
949 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
964 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
950 "branch": "foo",
965 "branch": "foo",
951 "phase": "draft",
966 "phase": "draft",
952 "user": "person",
967 "user": "person",
953 "date": [1400000, 0],
968 "date": [1400000, 0],
954 "desc": "new branch",
969 "desc": "new branch",
955 "bookmarks": [],
970 "bookmarks": [],
956 "tags": [],
971 "tags": [],
957 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
972 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
958 "files": []
973 "files": []
959 }
974 }
960 ]
975 ]
961
976
962 $ hg log --debug -Tjson
977 $ hg log --debug -Tjson
963 [
978 [
964 {
979 {
965 "rev": 8,
980 "rev": 8,
966 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
981 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
967 "branch": "default",
982 "branch": "default",
968 "phase": "draft",
983 "phase": "draft",
969 "user": "test",
984 "user": "test",
970 "date": [1577872860, 0],
985 "date": [1577872860, 0],
971 "desc": "third",
986 "desc": "third",
972 "bookmarks": [],
987 "bookmarks": [],
973 "tags": ["tip"],
988 "tags": ["tip"],
974 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
989 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
975 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
990 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
976 "extra": {"branch": "default"},
991 "extra": {"branch": "default"},
977 "modified": [],
992 "modified": [],
978 "added": ["fourth", "third"],
993 "added": ["fourth", "third"],
979 "removed": ["second"]
994 "removed": ["second"]
980 },
995 },
981 {
996 {
982 "rev": 7,
997 "rev": 7,
983 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
998 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
984 "branch": "default",
999 "branch": "default",
985 "phase": "draft",
1000 "phase": "draft",
986 "user": "User Name <user@hostname>",
1001 "user": "User Name <user@hostname>",
987 "date": [1000000, 0],
1002 "date": [1000000, 0],
988 "desc": "second",
1003 "desc": "second",
989 "bookmarks": [],
1004 "bookmarks": [],
990 "tags": [],
1005 "tags": [],
991 "parents": ["0000000000000000000000000000000000000000"],
1006 "parents": ["0000000000000000000000000000000000000000"],
992 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
1007 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
993 "extra": {"branch": "default"},
1008 "extra": {"branch": "default"},
994 "modified": [],
1009 "modified": [],
995 "added": ["second"],
1010 "added": ["second"],
996 "removed": []
1011 "removed": []
997 },
1012 },
998 {
1013 {
999 "rev": 6,
1014 "rev": 6,
1000 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1015 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1001 "branch": "default",
1016 "branch": "default",
1002 "phase": "draft",
1017 "phase": "draft",
1003 "user": "person",
1018 "user": "person",
1004 "date": [1500001, 0],
1019 "date": [1500001, 0],
1005 "desc": "merge",
1020 "desc": "merge",
1006 "bookmarks": [],
1021 "bookmarks": [],
1007 "tags": [],
1022 "tags": [],
1008 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1023 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1009 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1024 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1010 "extra": {"branch": "default"},
1025 "extra": {"branch": "default"},
1011 "modified": [],
1026 "modified": [],
1012 "added": [],
1027 "added": [],
1013 "removed": []
1028 "removed": []
1014 },
1029 },
1015 {
1030 {
1016 "rev": 5,
1031 "rev": 5,
1017 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1032 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1018 "branch": "default",
1033 "branch": "default",
1019 "phase": "draft",
1034 "phase": "draft",
1020 "user": "person",
1035 "user": "person",
1021 "date": [1500000, 0],
1036 "date": [1500000, 0],
1022 "desc": "new head",
1037 "desc": "new head",
1023 "bookmarks": [],
1038 "bookmarks": [],
1024 "tags": [],
1039 "tags": [],
1025 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1040 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1026 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1041 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1027 "extra": {"branch": "default"},
1042 "extra": {"branch": "default"},
1028 "modified": [],
1043 "modified": [],
1029 "added": ["d"],
1044 "added": ["d"],
1030 "removed": []
1045 "removed": []
1031 },
1046 },
1032 {
1047 {
1033 "rev": 4,
1048 "rev": 4,
1034 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1049 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1035 "branch": "foo",
1050 "branch": "foo",
1036 "phase": "draft",
1051 "phase": "draft",
1037 "user": "person",
1052 "user": "person",
1038 "date": [1400000, 0],
1053 "date": [1400000, 0],
1039 "desc": "new branch",
1054 "desc": "new branch",
1040 "bookmarks": [],
1055 "bookmarks": [],
1041 "tags": [],
1056 "tags": [],
1042 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1057 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1043 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1058 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1044 "extra": {"branch": "foo"},
1059 "extra": {"branch": "foo"},
1045 "modified": [],
1060 "modified": [],
1046 "added": [],
1061 "added": [],
1047 "removed": []
1062 "removed": []
1048 },
1063 },
1049 {
1064 {
1050 "rev": 3,
1065 "rev": 3,
1051 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1066 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1052 "branch": "default",
1067 "branch": "default",
1053 "phase": "draft",
1068 "phase": "draft",
1054 "user": "person",
1069 "user": "person",
1055 "date": [1300000, 0],
1070 "date": [1300000, 0],
1056 "desc": "no user, no domain",
1071 "desc": "no user, no domain",
1057 "bookmarks": [],
1072 "bookmarks": [],
1058 "tags": [],
1073 "tags": [],
1059 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1074 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1060 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1075 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1061 "extra": {"branch": "default"},
1076 "extra": {"branch": "default"},
1062 "modified": ["c"],
1077 "modified": ["c"],
1063 "added": [],
1078 "added": [],
1064 "removed": []
1079 "removed": []
1065 },
1080 },
1066 {
1081 {
1067 "rev": 2,
1082 "rev": 2,
1068 "node": "97054abb4ab824450e9164180baf491ae0078465",
1083 "node": "97054abb4ab824450e9164180baf491ae0078465",
1069 "branch": "default",
1084 "branch": "default",
1070 "phase": "draft",
1085 "phase": "draft",
1071 "user": "other@place",
1086 "user": "other@place",
1072 "date": [1200000, 0],
1087 "date": [1200000, 0],
1073 "desc": "no person",
1088 "desc": "no person",
1074 "bookmarks": [],
1089 "bookmarks": [],
1075 "tags": [],
1090 "tags": [],
1076 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1091 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1077 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1092 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1078 "extra": {"branch": "default"},
1093 "extra": {"branch": "default"},
1079 "modified": [],
1094 "modified": [],
1080 "added": ["c"],
1095 "added": ["c"],
1081 "removed": []
1096 "removed": []
1082 },
1097 },
1083 {
1098 {
1084 "rev": 1,
1099 "rev": 1,
1085 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1100 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1086 "branch": "default",
1101 "branch": "default",
1087 "phase": "draft",
1102 "phase": "draft",
1088 "user": "A. N. Other <other@place>",
1103 "user": "A. N. Other <other@place>",
1089 "date": [1100000, 0],
1104 "date": [1100000, 0],
1090 "desc": "other 1\nother 2\n\nother 3",
1105 "desc": "other 1\nother 2\n\nother 3",
1091 "bookmarks": [],
1106 "bookmarks": [],
1092 "tags": [],
1107 "tags": [],
1093 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1108 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1094 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1109 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1095 "extra": {"branch": "default"},
1110 "extra": {"branch": "default"},
1096 "modified": [],
1111 "modified": [],
1097 "added": ["b"],
1112 "added": ["b"],
1098 "removed": []
1113 "removed": []
1099 },
1114 },
1100 {
1115 {
1101 "rev": 0,
1116 "rev": 0,
1102 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1117 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1103 "branch": "default",
1118 "branch": "default",
1104 "phase": "draft",
1119 "phase": "draft",
1105 "user": "User Name <user@hostname>",
1120 "user": "User Name <user@hostname>",
1106 "date": [1000000, 0],
1121 "date": [1000000, 0],
1107 "desc": "line 1\nline 2",
1122 "desc": "line 1\nline 2",
1108 "bookmarks": [],
1123 "bookmarks": [],
1109 "tags": [],
1124 "tags": [],
1110 "parents": ["0000000000000000000000000000000000000000"],
1125 "parents": ["0000000000000000000000000000000000000000"],
1111 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1126 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1112 "extra": {"branch": "default"},
1127 "extra": {"branch": "default"},
1113 "modified": [],
1128 "modified": [],
1114 "added": ["a"],
1129 "added": ["a"],
1115 "removed": []
1130 "removed": []
1116 }
1131 }
1117 ]
1132 ]
1118
1133
1119 Error if style not readable:
1134 Error if style not readable:
1120
1135
1121 #if unix-permissions no-root
1136 #if unix-permissions no-root
1122 $ touch q
1137 $ touch q
1123 $ chmod 0 q
1138 $ chmod 0 q
1124 $ hg log --style ./q
1139 $ hg log --style ./q
1125 abort: Permission denied: ./q
1140 abort: Permission denied: ./q
1126 [255]
1141 [255]
1127 #endif
1142 #endif
1128
1143
1129 Error if no style:
1144 Error if no style:
1130
1145
1131 $ hg log --style notexist
1146 $ hg log --style notexist
1132 abort: style 'notexist' not found
1147 abort: style 'notexist' not found
1133 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1148 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1134 [255]
1149 [255]
1135
1150
1136 $ hg log -T list
1151 $ hg log -T list
1137 available styles: bisect, changelog, compact, default, phases, show, status, xml
1152 available styles: bisect, changelog, compact, default, phases, show, status, xml
1138 abort: specify a template
1153 abort: specify a template
1139 [255]
1154 [255]
1140
1155
1141 Error if style missing key:
1156 Error if style missing key:
1142
1157
1143 $ echo 'q = q' > t
1158 $ echo 'q = q' > t
1144 $ hg log --style ./t
1159 $ hg log --style ./t
1145 abort: "changeset" not in template map
1160 abort: "changeset" not in template map
1146 [255]
1161 [255]
1147
1162
1148 Error if style missing value:
1163 Error if style missing value:
1149
1164
1150 $ echo 'changeset =' > t
1165 $ echo 'changeset =' > t
1151 $ hg log --style t
1166 $ hg log --style t
1152 hg: parse error at t:1: missing value
1167 hg: parse error at t:1: missing value
1153 [255]
1168 [255]
1154
1169
1155 Error if include fails:
1170 Error if include fails:
1156
1171
1157 $ echo 'changeset = q' >> t
1172 $ echo 'changeset = q' >> t
1158 #if unix-permissions no-root
1173 #if unix-permissions no-root
1159 $ hg log --style ./t
1174 $ hg log --style ./t
1160 abort: template file ./q: Permission denied
1175 abort: template file ./q: Permission denied
1161 [255]
1176 [255]
1162 $ rm -f q
1177 $ rm -f q
1163 #endif
1178 #endif
1164
1179
1165 Include works:
1180 Include works:
1166
1181
1167 $ echo '{rev}' > q
1182 $ echo '{rev}' > q
1168 $ hg log --style ./t
1183 $ hg log --style ./t
1169 8
1184 8
1170 7
1185 7
1171 6
1186 6
1172 5
1187 5
1173 4
1188 4
1174 3
1189 3
1175 2
1190 2
1176 1
1191 1
1177 0
1192 0
1178
1193
1179 Check that recursive reference does not fall into RuntimeError (issue4758):
1194 Check that recursive reference does not fall into RuntimeError (issue4758):
1180
1195
1181 common mistake:
1196 common mistake:
1182
1197
1183 $ cat << EOF > issue4758
1198 $ cat << EOF > issue4758
1184 > changeset = '{changeset}\n'
1199 > changeset = '{changeset}\n'
1185 > EOF
1200 > EOF
1186 $ hg log --style ./issue4758
1201 $ hg log --style ./issue4758
1187 abort: recursive reference 'changeset' in template
1202 abort: recursive reference 'changeset' in template
1188 [255]
1203 [255]
1189
1204
1190 circular reference:
1205 circular reference:
1191
1206
1192 $ cat << EOF > issue4758
1207 $ cat << EOF > issue4758
1193 > changeset = '{foo}'
1208 > changeset = '{foo}'
1194 > foo = '{changeset}'
1209 > foo = '{changeset}'
1195 > EOF
1210 > EOF
1196 $ hg log --style ./issue4758
1211 $ hg log --style ./issue4758
1197 abort: recursive reference 'foo' in template
1212 abort: recursive reference 'foo' in template
1198 [255]
1213 [255]
1199
1214
1200 buildmap() -> gettemplate(), where no thunk was made:
1215 buildmap() -> gettemplate(), where no thunk was made:
1201
1216
1202 $ cat << EOF > issue4758
1217 $ cat << EOF > issue4758
1203 > changeset = '{files % changeset}\n'
1218 > changeset = '{files % changeset}\n'
1204 > EOF
1219 > EOF
1205 $ hg log --style ./issue4758
1220 $ hg log --style ./issue4758
1206 abort: recursive reference 'changeset' in template
1221 abort: recursive reference 'changeset' in template
1207 [255]
1222 [255]
1208
1223
1209 not a recursion if a keyword of the same name exists:
1224 not a recursion if a keyword of the same name exists:
1210
1225
1211 $ cat << EOF > issue4758
1226 $ cat << EOF > issue4758
1212 > changeset = '{tags % rev}'
1227 > changeset = '{tags % rev}'
1213 > rev = '{rev} {tag}\n'
1228 > rev = '{rev} {tag}\n'
1214 > EOF
1229 > EOF
1215 $ hg log --style ./issue4758 -r tip
1230 $ hg log --style ./issue4758 -r tip
1216 8 tip
1231 8 tip
1217
1232
1218 Check that {phase} works correctly on parents:
1233 Check that {phase} works correctly on parents:
1219
1234
1220 $ cat << EOF > parentphase
1235 $ cat << EOF > parentphase
1221 > changeset_debug = '{rev} ({phase}):{parents}\n'
1236 > changeset_debug = '{rev} ({phase}):{parents}\n'
1222 > parent = ' {rev} ({phase})'
1237 > parent = ' {rev} ({phase})'
1223 > EOF
1238 > EOF
1224 $ hg phase -r 5 --public
1239 $ hg phase -r 5 --public
1225 $ hg phase -r 7 --secret --force
1240 $ hg phase -r 7 --secret --force
1226 $ hg log --debug -G --style ./parentphase
1241 $ hg log --debug -G --style ./parentphase
1227 @ 8 (secret): 7 (secret) -1 (public)
1242 @ 8 (secret): 7 (secret) -1 (public)
1228 |
1243 |
1229 o 7 (secret): -1 (public) -1 (public)
1244 o 7 (secret): -1 (public) -1 (public)
1230
1245
1231 o 6 (draft): 5 (public) 4 (draft)
1246 o 6 (draft): 5 (public) 4 (draft)
1232 |\
1247 |\
1233 | o 5 (public): 3 (public) -1 (public)
1248 | o 5 (public): 3 (public) -1 (public)
1234 | |
1249 | |
1235 o | 4 (draft): 3 (public) -1 (public)
1250 o | 4 (draft): 3 (public) -1 (public)
1236 |/
1251 |/
1237 o 3 (public): 2 (public) -1 (public)
1252 o 3 (public): 2 (public) -1 (public)
1238 |
1253 |
1239 o 2 (public): 1 (public) -1 (public)
1254 o 2 (public): 1 (public) -1 (public)
1240 |
1255 |
1241 o 1 (public): 0 (public) -1 (public)
1256 o 1 (public): 0 (public) -1 (public)
1242 |
1257 |
1243 o 0 (public): -1 (public) -1 (public)
1258 o 0 (public): -1 (public) -1 (public)
1244
1259
1245
1260
1246 Missing non-standard names give no error (backward compatibility):
1261 Missing non-standard names give no error (backward compatibility):
1247
1262
1248 $ echo "changeset = '{c}'" > t
1263 $ echo "changeset = '{c}'" > t
1249 $ hg log --style ./t
1264 $ hg log --style ./t
1250
1265
1251 Defining non-standard name works:
1266 Defining non-standard name works:
1252
1267
1253 $ cat <<EOF > t
1268 $ cat <<EOF > t
1254 > changeset = '{c}'
1269 > changeset = '{c}'
1255 > c = q
1270 > c = q
1256 > EOF
1271 > EOF
1257 $ hg log --style ./t
1272 $ hg log --style ./t
1258 8
1273 8
1259 7
1274 7
1260 6
1275 6
1261 5
1276 5
1262 4
1277 4
1263 3
1278 3
1264 2
1279 2
1265 1
1280 1
1266 0
1281 0
1267
1282
1268 ui.style works:
1283 ui.style works:
1269
1284
1270 $ echo '[ui]' > .hg/hgrc
1285 $ echo '[ui]' > .hg/hgrc
1271 $ echo 'style = t' >> .hg/hgrc
1286 $ echo 'style = t' >> .hg/hgrc
1272 $ hg log
1287 $ hg log
1273 8
1288 8
1274 7
1289 7
1275 6
1290 6
1276 5
1291 5
1277 4
1292 4
1278 3
1293 3
1279 2
1294 2
1280 1
1295 1
1281 0
1296 0
1282
1297
1283
1298
1284 Issue338:
1299 Issue338:
1285
1300
1286 $ hg log --style=changelog > changelog
1301 $ hg log --style=changelog > changelog
1287
1302
1288 $ cat changelog
1303 $ cat changelog
1289 2020-01-01 test <test>
1304 2020-01-01 test <test>
1290
1305
1291 * fourth, second, third:
1306 * fourth, second, third:
1292 third
1307 third
1293 [95c24699272e] [tip]
1308 [95c24699272e] [tip]
1294
1309
1295 1970-01-12 User Name <user@hostname>
1310 1970-01-12 User Name <user@hostname>
1296
1311
1297 * second:
1312 * second:
1298 second
1313 second
1299 [29114dbae42b]
1314 [29114dbae42b]
1300
1315
1301 1970-01-18 person <person>
1316 1970-01-18 person <person>
1302
1317
1303 * merge
1318 * merge
1304 [d41e714fe50d]
1319 [d41e714fe50d]
1305
1320
1306 * d:
1321 * d:
1307 new head
1322 new head
1308 [13207e5a10d9]
1323 [13207e5a10d9]
1309
1324
1310 1970-01-17 person <person>
1325 1970-01-17 person <person>
1311
1326
1312 * new branch
1327 * new branch
1313 [bbe44766e73d] <foo>
1328 [bbe44766e73d] <foo>
1314
1329
1315 1970-01-16 person <person>
1330 1970-01-16 person <person>
1316
1331
1317 * c:
1332 * c:
1318 no user, no domain
1333 no user, no domain
1319 [10e46f2dcbf4]
1334 [10e46f2dcbf4]
1320
1335
1321 1970-01-14 other <other@place>
1336 1970-01-14 other <other@place>
1322
1337
1323 * c:
1338 * c:
1324 no person
1339 no person
1325 [97054abb4ab8]
1340 [97054abb4ab8]
1326
1341
1327 1970-01-13 A. N. Other <other@place>
1342 1970-01-13 A. N. Other <other@place>
1328
1343
1329 * b:
1344 * b:
1330 other 1 other 2
1345 other 1 other 2
1331
1346
1332 other 3
1347 other 3
1333 [b608e9d1a3f0]
1348 [b608e9d1a3f0]
1334
1349
1335 1970-01-12 User Name <user@hostname>
1350 1970-01-12 User Name <user@hostname>
1336
1351
1337 * a:
1352 * a:
1338 line 1 line 2
1353 line 1 line 2
1339 [1e4e1b8f71e0]
1354 [1e4e1b8f71e0]
1340
1355
1341
1356
1342 Issue2130: xml output for 'hg heads' is malformed
1357 Issue2130: xml output for 'hg heads' is malformed
1343
1358
1344 $ hg heads --style changelog
1359 $ hg heads --style changelog
1345 2020-01-01 test <test>
1360 2020-01-01 test <test>
1346
1361
1347 * fourth, second, third:
1362 * fourth, second, third:
1348 third
1363 third
1349 [95c24699272e] [tip]
1364 [95c24699272e] [tip]
1350
1365
1351 1970-01-18 person <person>
1366 1970-01-18 person <person>
1352
1367
1353 * merge
1368 * merge
1354 [d41e714fe50d]
1369 [d41e714fe50d]
1355
1370
1356 1970-01-17 person <person>
1371 1970-01-17 person <person>
1357
1372
1358 * new branch
1373 * new branch
1359 [bbe44766e73d] <foo>
1374 [bbe44766e73d] <foo>
1360
1375
1361
1376
1362 Keys work:
1377 Keys work:
1363
1378
1364 $ for key in author branch branches date desc file_adds file_dels file_mods \
1379 $ for key in author branch branches date desc file_adds file_dels file_mods \
1365 > file_copies file_copies_switch files \
1380 > file_copies file_copies_switch files \
1366 > manifest node parents rev tags diffstat extras \
1381 > manifest node parents rev tags diffstat extras \
1367 > p1rev p2rev p1node p2node; do
1382 > p1rev p2rev p1node p2node; do
1368 > for mode in '' --verbose --debug; do
1383 > for mode in '' --verbose --debug; do
1369 > hg log $mode --template "$key$mode: {$key}\n"
1384 > hg log $mode --template "$key$mode: {$key}\n"
1370 > done
1385 > done
1371 > done
1386 > done
1372 author: test
1387 author: test
1373 author: User Name <user@hostname>
1388 author: User Name <user@hostname>
1374 author: person
1389 author: person
1375 author: person
1390 author: person
1376 author: person
1391 author: person
1377 author: person
1392 author: person
1378 author: other@place
1393 author: other@place
1379 author: A. N. Other <other@place>
1394 author: A. N. Other <other@place>
1380 author: User Name <user@hostname>
1395 author: User Name <user@hostname>
1381 author--verbose: test
1396 author--verbose: test
1382 author--verbose: User Name <user@hostname>
1397 author--verbose: User Name <user@hostname>
1383 author--verbose: person
1398 author--verbose: person
1384 author--verbose: person
1399 author--verbose: person
1385 author--verbose: person
1400 author--verbose: person
1386 author--verbose: person
1401 author--verbose: person
1387 author--verbose: other@place
1402 author--verbose: other@place
1388 author--verbose: A. N. Other <other@place>
1403 author--verbose: A. N. Other <other@place>
1389 author--verbose: User Name <user@hostname>
1404 author--verbose: User Name <user@hostname>
1390 author--debug: test
1405 author--debug: test
1391 author--debug: User Name <user@hostname>
1406 author--debug: User Name <user@hostname>
1392 author--debug: person
1407 author--debug: person
1393 author--debug: person
1408 author--debug: person
1394 author--debug: person
1409 author--debug: person
1395 author--debug: person
1410 author--debug: person
1396 author--debug: other@place
1411 author--debug: other@place
1397 author--debug: A. N. Other <other@place>
1412 author--debug: A. N. Other <other@place>
1398 author--debug: User Name <user@hostname>
1413 author--debug: User Name <user@hostname>
1399 branch: default
1414 branch: default
1400 branch: default
1415 branch: default
1401 branch: default
1416 branch: default
1402 branch: default
1417 branch: default
1403 branch: foo
1418 branch: foo
1404 branch: default
1419 branch: default
1405 branch: default
1420 branch: default
1406 branch: default
1421 branch: default
1407 branch: default
1422 branch: default
1408 branch--verbose: default
1423 branch--verbose: default
1409 branch--verbose: default
1424 branch--verbose: default
1410 branch--verbose: default
1425 branch--verbose: default
1411 branch--verbose: default
1426 branch--verbose: default
1412 branch--verbose: foo
1427 branch--verbose: foo
1413 branch--verbose: default
1428 branch--verbose: default
1414 branch--verbose: default
1429 branch--verbose: default
1415 branch--verbose: default
1430 branch--verbose: default
1416 branch--verbose: default
1431 branch--verbose: default
1417 branch--debug: default
1432 branch--debug: default
1418 branch--debug: default
1433 branch--debug: default
1419 branch--debug: default
1434 branch--debug: default
1420 branch--debug: default
1435 branch--debug: default
1421 branch--debug: foo
1436 branch--debug: foo
1422 branch--debug: default
1437 branch--debug: default
1423 branch--debug: default
1438 branch--debug: default
1424 branch--debug: default
1439 branch--debug: default
1425 branch--debug: default
1440 branch--debug: default
1426 branches:
1441 branches:
1427 branches:
1442 branches:
1428 branches:
1443 branches:
1429 branches:
1444 branches:
1430 branches: foo
1445 branches: foo
1431 branches:
1446 branches:
1432 branches:
1447 branches:
1433 branches:
1448 branches:
1434 branches:
1449 branches:
1435 branches--verbose:
1450 branches--verbose:
1436 branches--verbose:
1451 branches--verbose:
1437 branches--verbose:
1452 branches--verbose:
1438 branches--verbose:
1453 branches--verbose:
1439 branches--verbose: foo
1454 branches--verbose: foo
1440 branches--verbose:
1455 branches--verbose:
1441 branches--verbose:
1456 branches--verbose:
1442 branches--verbose:
1457 branches--verbose:
1443 branches--verbose:
1458 branches--verbose:
1444 branches--debug:
1459 branches--debug:
1445 branches--debug:
1460 branches--debug:
1446 branches--debug:
1461 branches--debug:
1447 branches--debug:
1462 branches--debug:
1448 branches--debug: foo
1463 branches--debug: foo
1449 branches--debug:
1464 branches--debug:
1450 branches--debug:
1465 branches--debug:
1451 branches--debug:
1466 branches--debug:
1452 branches--debug:
1467 branches--debug:
1453 date: 1577872860.00
1468 date: 1577872860.00
1454 date: 1000000.00
1469 date: 1000000.00
1455 date: 1500001.00
1470 date: 1500001.00
1456 date: 1500000.00
1471 date: 1500000.00
1457 date: 1400000.00
1472 date: 1400000.00
1458 date: 1300000.00
1473 date: 1300000.00
1459 date: 1200000.00
1474 date: 1200000.00
1460 date: 1100000.00
1475 date: 1100000.00
1461 date: 1000000.00
1476 date: 1000000.00
1462 date--verbose: 1577872860.00
1477 date--verbose: 1577872860.00
1463 date--verbose: 1000000.00
1478 date--verbose: 1000000.00
1464 date--verbose: 1500001.00
1479 date--verbose: 1500001.00
1465 date--verbose: 1500000.00
1480 date--verbose: 1500000.00
1466 date--verbose: 1400000.00
1481 date--verbose: 1400000.00
1467 date--verbose: 1300000.00
1482 date--verbose: 1300000.00
1468 date--verbose: 1200000.00
1483 date--verbose: 1200000.00
1469 date--verbose: 1100000.00
1484 date--verbose: 1100000.00
1470 date--verbose: 1000000.00
1485 date--verbose: 1000000.00
1471 date--debug: 1577872860.00
1486 date--debug: 1577872860.00
1472 date--debug: 1000000.00
1487 date--debug: 1000000.00
1473 date--debug: 1500001.00
1488 date--debug: 1500001.00
1474 date--debug: 1500000.00
1489 date--debug: 1500000.00
1475 date--debug: 1400000.00
1490 date--debug: 1400000.00
1476 date--debug: 1300000.00
1491 date--debug: 1300000.00
1477 date--debug: 1200000.00
1492 date--debug: 1200000.00
1478 date--debug: 1100000.00
1493 date--debug: 1100000.00
1479 date--debug: 1000000.00
1494 date--debug: 1000000.00
1480 desc: third
1495 desc: third
1481 desc: second
1496 desc: second
1482 desc: merge
1497 desc: merge
1483 desc: new head
1498 desc: new head
1484 desc: new branch
1499 desc: new branch
1485 desc: no user, no domain
1500 desc: no user, no domain
1486 desc: no person
1501 desc: no person
1487 desc: other 1
1502 desc: other 1
1488 other 2
1503 other 2
1489
1504
1490 other 3
1505 other 3
1491 desc: line 1
1506 desc: line 1
1492 line 2
1507 line 2
1493 desc--verbose: third
1508 desc--verbose: third
1494 desc--verbose: second
1509 desc--verbose: second
1495 desc--verbose: merge
1510 desc--verbose: merge
1496 desc--verbose: new head
1511 desc--verbose: new head
1497 desc--verbose: new branch
1512 desc--verbose: new branch
1498 desc--verbose: no user, no domain
1513 desc--verbose: no user, no domain
1499 desc--verbose: no person
1514 desc--verbose: no person
1500 desc--verbose: other 1
1515 desc--verbose: other 1
1501 other 2
1516 other 2
1502
1517
1503 other 3
1518 other 3
1504 desc--verbose: line 1
1519 desc--verbose: line 1
1505 line 2
1520 line 2
1506 desc--debug: third
1521 desc--debug: third
1507 desc--debug: second
1522 desc--debug: second
1508 desc--debug: merge
1523 desc--debug: merge
1509 desc--debug: new head
1524 desc--debug: new head
1510 desc--debug: new branch
1525 desc--debug: new branch
1511 desc--debug: no user, no domain
1526 desc--debug: no user, no domain
1512 desc--debug: no person
1527 desc--debug: no person
1513 desc--debug: other 1
1528 desc--debug: other 1
1514 other 2
1529 other 2
1515
1530
1516 other 3
1531 other 3
1517 desc--debug: line 1
1532 desc--debug: line 1
1518 line 2
1533 line 2
1519 file_adds: fourth third
1534 file_adds: fourth third
1520 file_adds: second
1535 file_adds: second
1521 file_adds:
1536 file_adds:
1522 file_adds: d
1537 file_adds: d
1523 file_adds:
1538 file_adds:
1524 file_adds:
1539 file_adds:
1525 file_adds: c
1540 file_adds: c
1526 file_adds: b
1541 file_adds: b
1527 file_adds: a
1542 file_adds: a
1528 file_adds--verbose: fourth third
1543 file_adds--verbose: fourth third
1529 file_adds--verbose: second
1544 file_adds--verbose: second
1530 file_adds--verbose:
1545 file_adds--verbose:
1531 file_adds--verbose: d
1546 file_adds--verbose: d
1532 file_adds--verbose:
1547 file_adds--verbose:
1533 file_adds--verbose:
1548 file_adds--verbose:
1534 file_adds--verbose: c
1549 file_adds--verbose: c
1535 file_adds--verbose: b
1550 file_adds--verbose: b
1536 file_adds--verbose: a
1551 file_adds--verbose: a
1537 file_adds--debug: fourth third
1552 file_adds--debug: fourth third
1538 file_adds--debug: second
1553 file_adds--debug: second
1539 file_adds--debug:
1554 file_adds--debug:
1540 file_adds--debug: d
1555 file_adds--debug: d
1541 file_adds--debug:
1556 file_adds--debug:
1542 file_adds--debug:
1557 file_adds--debug:
1543 file_adds--debug: c
1558 file_adds--debug: c
1544 file_adds--debug: b
1559 file_adds--debug: b
1545 file_adds--debug: a
1560 file_adds--debug: a
1546 file_dels: second
1561 file_dels: second
1547 file_dels:
1562 file_dels:
1548 file_dels:
1563 file_dels:
1549 file_dels:
1564 file_dels:
1550 file_dels:
1565 file_dels:
1551 file_dels:
1566 file_dels:
1552 file_dels:
1567 file_dels:
1553 file_dels:
1568 file_dels:
1554 file_dels:
1569 file_dels:
1555 file_dels--verbose: second
1570 file_dels--verbose: second
1556 file_dels--verbose:
1571 file_dels--verbose:
1557 file_dels--verbose:
1572 file_dels--verbose:
1558 file_dels--verbose:
1573 file_dels--verbose:
1559 file_dels--verbose:
1574 file_dels--verbose:
1560 file_dels--verbose:
1575 file_dels--verbose:
1561 file_dels--verbose:
1576 file_dels--verbose:
1562 file_dels--verbose:
1577 file_dels--verbose:
1563 file_dels--verbose:
1578 file_dels--verbose:
1564 file_dels--debug: second
1579 file_dels--debug: second
1565 file_dels--debug:
1580 file_dels--debug:
1566 file_dels--debug:
1581 file_dels--debug:
1567 file_dels--debug:
1582 file_dels--debug:
1568 file_dels--debug:
1583 file_dels--debug:
1569 file_dels--debug:
1584 file_dels--debug:
1570 file_dels--debug:
1585 file_dels--debug:
1571 file_dels--debug:
1586 file_dels--debug:
1572 file_dels--debug:
1587 file_dels--debug:
1573 file_mods:
1588 file_mods:
1574 file_mods:
1589 file_mods:
1575 file_mods:
1590 file_mods:
1576 file_mods:
1591 file_mods:
1577 file_mods:
1592 file_mods:
1578 file_mods: c
1593 file_mods: c
1579 file_mods:
1594 file_mods:
1580 file_mods:
1595 file_mods:
1581 file_mods:
1596 file_mods:
1582 file_mods--verbose:
1597 file_mods--verbose:
1583 file_mods--verbose:
1598 file_mods--verbose:
1584 file_mods--verbose:
1599 file_mods--verbose:
1585 file_mods--verbose:
1600 file_mods--verbose:
1586 file_mods--verbose:
1601 file_mods--verbose:
1587 file_mods--verbose: c
1602 file_mods--verbose: c
1588 file_mods--verbose:
1603 file_mods--verbose:
1589 file_mods--verbose:
1604 file_mods--verbose:
1590 file_mods--verbose:
1605 file_mods--verbose:
1591 file_mods--debug:
1606 file_mods--debug:
1592 file_mods--debug:
1607 file_mods--debug:
1593 file_mods--debug:
1608 file_mods--debug:
1594 file_mods--debug:
1609 file_mods--debug:
1595 file_mods--debug:
1610 file_mods--debug:
1596 file_mods--debug: c
1611 file_mods--debug: c
1597 file_mods--debug:
1612 file_mods--debug:
1598 file_mods--debug:
1613 file_mods--debug:
1599 file_mods--debug:
1614 file_mods--debug:
1600 file_copies: fourth (second)
1615 file_copies: fourth (second)
1601 file_copies:
1616 file_copies:
1602 file_copies:
1617 file_copies:
1603 file_copies:
1618 file_copies:
1604 file_copies:
1619 file_copies:
1605 file_copies:
1620 file_copies:
1606 file_copies:
1621 file_copies:
1607 file_copies:
1622 file_copies:
1608 file_copies:
1623 file_copies:
1609 file_copies--verbose: fourth (second)
1624 file_copies--verbose: fourth (second)
1610 file_copies--verbose:
1625 file_copies--verbose:
1611 file_copies--verbose:
1626 file_copies--verbose:
1612 file_copies--verbose:
1627 file_copies--verbose:
1613 file_copies--verbose:
1628 file_copies--verbose:
1614 file_copies--verbose:
1629 file_copies--verbose:
1615 file_copies--verbose:
1630 file_copies--verbose:
1616 file_copies--verbose:
1631 file_copies--verbose:
1617 file_copies--verbose:
1632 file_copies--verbose:
1618 file_copies--debug: fourth (second)
1633 file_copies--debug: fourth (second)
1619 file_copies--debug:
1634 file_copies--debug:
1620 file_copies--debug:
1635 file_copies--debug:
1621 file_copies--debug:
1636 file_copies--debug:
1622 file_copies--debug:
1637 file_copies--debug:
1623 file_copies--debug:
1638 file_copies--debug:
1624 file_copies--debug:
1639 file_copies--debug:
1625 file_copies--debug:
1640 file_copies--debug:
1626 file_copies--debug:
1641 file_copies--debug:
1627 file_copies_switch:
1642 file_copies_switch:
1628 file_copies_switch:
1643 file_copies_switch:
1629 file_copies_switch:
1644 file_copies_switch:
1630 file_copies_switch:
1645 file_copies_switch:
1631 file_copies_switch:
1646 file_copies_switch:
1632 file_copies_switch:
1647 file_copies_switch:
1633 file_copies_switch:
1648 file_copies_switch:
1634 file_copies_switch:
1649 file_copies_switch:
1635 file_copies_switch:
1650 file_copies_switch:
1636 file_copies_switch--verbose:
1651 file_copies_switch--verbose:
1637 file_copies_switch--verbose:
1652 file_copies_switch--verbose:
1638 file_copies_switch--verbose:
1653 file_copies_switch--verbose:
1639 file_copies_switch--verbose:
1654 file_copies_switch--verbose:
1640 file_copies_switch--verbose:
1655 file_copies_switch--verbose:
1641 file_copies_switch--verbose:
1656 file_copies_switch--verbose:
1642 file_copies_switch--verbose:
1657 file_copies_switch--verbose:
1643 file_copies_switch--verbose:
1658 file_copies_switch--verbose:
1644 file_copies_switch--verbose:
1659 file_copies_switch--verbose:
1645 file_copies_switch--debug:
1660 file_copies_switch--debug:
1646 file_copies_switch--debug:
1661 file_copies_switch--debug:
1647 file_copies_switch--debug:
1662 file_copies_switch--debug:
1648 file_copies_switch--debug:
1663 file_copies_switch--debug:
1649 file_copies_switch--debug:
1664 file_copies_switch--debug:
1650 file_copies_switch--debug:
1665 file_copies_switch--debug:
1651 file_copies_switch--debug:
1666 file_copies_switch--debug:
1652 file_copies_switch--debug:
1667 file_copies_switch--debug:
1653 file_copies_switch--debug:
1668 file_copies_switch--debug:
1654 files: fourth second third
1669 files: fourth second third
1655 files: second
1670 files: second
1656 files:
1671 files:
1657 files: d
1672 files: d
1658 files:
1673 files:
1659 files: c
1674 files: c
1660 files: c
1675 files: c
1661 files: b
1676 files: b
1662 files: a
1677 files: a
1663 files--verbose: fourth second third
1678 files--verbose: fourth second third
1664 files--verbose: second
1679 files--verbose: second
1665 files--verbose:
1680 files--verbose:
1666 files--verbose: d
1681 files--verbose: d
1667 files--verbose:
1682 files--verbose:
1668 files--verbose: c
1683 files--verbose: c
1669 files--verbose: c
1684 files--verbose: c
1670 files--verbose: b
1685 files--verbose: b
1671 files--verbose: a
1686 files--verbose: a
1672 files--debug: fourth second third
1687 files--debug: fourth second third
1673 files--debug: second
1688 files--debug: second
1674 files--debug:
1689 files--debug:
1675 files--debug: d
1690 files--debug: d
1676 files--debug:
1691 files--debug:
1677 files--debug: c
1692 files--debug: c
1678 files--debug: c
1693 files--debug: c
1679 files--debug: b
1694 files--debug: b
1680 files--debug: a
1695 files--debug: a
1681 manifest: 6:94961b75a2da
1696 manifest: 6:94961b75a2da
1682 manifest: 5:f2dbc354b94e
1697 manifest: 5:f2dbc354b94e
1683 manifest: 4:4dc3def4f9b4
1698 manifest: 4:4dc3def4f9b4
1684 manifest: 4:4dc3def4f9b4
1699 manifest: 4:4dc3def4f9b4
1685 manifest: 3:cb5a1327723b
1700 manifest: 3:cb5a1327723b
1686 manifest: 3:cb5a1327723b
1701 manifest: 3:cb5a1327723b
1687 manifest: 2:6e0e82995c35
1702 manifest: 2:6e0e82995c35
1688 manifest: 1:4e8d705b1e53
1703 manifest: 1:4e8d705b1e53
1689 manifest: 0:a0c8bcbbb45c
1704 manifest: 0:a0c8bcbbb45c
1690 manifest--verbose: 6:94961b75a2da
1705 manifest--verbose: 6:94961b75a2da
1691 manifest--verbose: 5:f2dbc354b94e
1706 manifest--verbose: 5:f2dbc354b94e
1692 manifest--verbose: 4:4dc3def4f9b4
1707 manifest--verbose: 4:4dc3def4f9b4
1693 manifest--verbose: 4:4dc3def4f9b4
1708 manifest--verbose: 4:4dc3def4f9b4
1694 manifest--verbose: 3:cb5a1327723b
1709 manifest--verbose: 3:cb5a1327723b
1695 manifest--verbose: 3:cb5a1327723b
1710 manifest--verbose: 3:cb5a1327723b
1696 manifest--verbose: 2:6e0e82995c35
1711 manifest--verbose: 2:6e0e82995c35
1697 manifest--verbose: 1:4e8d705b1e53
1712 manifest--verbose: 1:4e8d705b1e53
1698 manifest--verbose: 0:a0c8bcbbb45c
1713 manifest--verbose: 0:a0c8bcbbb45c
1699 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1714 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1700 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1715 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1701 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1716 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1702 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1717 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1703 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1718 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1704 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1719 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1705 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1720 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1706 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1721 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1707 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1722 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1708 node: 95c24699272ef57d062b8bccc32c878bf841784a
1723 node: 95c24699272ef57d062b8bccc32c878bf841784a
1709 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1724 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1710 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1725 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1711 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1726 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1712 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1727 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1713 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1728 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1714 node: 97054abb4ab824450e9164180baf491ae0078465
1729 node: 97054abb4ab824450e9164180baf491ae0078465
1715 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1730 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1716 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1731 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1717 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1732 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1718 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1733 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1719 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1734 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1720 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1735 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1721 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1736 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1722 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1737 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1723 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1738 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1724 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1739 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1725 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1740 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1726 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1741 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1727 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1742 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1728 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1743 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1729 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1744 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1730 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1745 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1731 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1746 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1732 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1747 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1733 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1748 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1734 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1749 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1735 parents:
1750 parents:
1736 parents: -1:000000000000
1751 parents: -1:000000000000
1737 parents: 5:13207e5a10d9 4:bbe44766e73d
1752 parents: 5:13207e5a10d9 4:bbe44766e73d
1738 parents: 3:10e46f2dcbf4
1753 parents: 3:10e46f2dcbf4
1739 parents:
1754 parents:
1740 parents:
1755 parents:
1741 parents:
1756 parents:
1742 parents:
1757 parents:
1743 parents:
1758 parents:
1744 parents--verbose:
1759 parents--verbose:
1745 parents--verbose: -1:000000000000
1760 parents--verbose: -1:000000000000
1746 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1761 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1747 parents--verbose: 3:10e46f2dcbf4
1762 parents--verbose: 3:10e46f2dcbf4
1748 parents--verbose:
1763 parents--verbose:
1749 parents--verbose:
1764 parents--verbose:
1750 parents--verbose:
1765 parents--verbose:
1751 parents--verbose:
1766 parents--verbose:
1752 parents--verbose:
1767 parents--verbose:
1753 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1768 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1754 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1769 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1755 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1770 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1756 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1771 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1757 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1772 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1758 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1773 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1759 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1774 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1760 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1775 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1761 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1776 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1762 rev: 8
1777 rev: 8
1763 rev: 7
1778 rev: 7
1764 rev: 6
1779 rev: 6
1765 rev: 5
1780 rev: 5
1766 rev: 4
1781 rev: 4
1767 rev: 3
1782 rev: 3
1768 rev: 2
1783 rev: 2
1769 rev: 1
1784 rev: 1
1770 rev: 0
1785 rev: 0
1771 rev--verbose: 8
1786 rev--verbose: 8
1772 rev--verbose: 7
1787 rev--verbose: 7
1773 rev--verbose: 6
1788 rev--verbose: 6
1774 rev--verbose: 5
1789 rev--verbose: 5
1775 rev--verbose: 4
1790 rev--verbose: 4
1776 rev--verbose: 3
1791 rev--verbose: 3
1777 rev--verbose: 2
1792 rev--verbose: 2
1778 rev--verbose: 1
1793 rev--verbose: 1
1779 rev--verbose: 0
1794 rev--verbose: 0
1780 rev--debug: 8
1795 rev--debug: 8
1781 rev--debug: 7
1796 rev--debug: 7
1782 rev--debug: 6
1797 rev--debug: 6
1783 rev--debug: 5
1798 rev--debug: 5
1784 rev--debug: 4
1799 rev--debug: 4
1785 rev--debug: 3
1800 rev--debug: 3
1786 rev--debug: 2
1801 rev--debug: 2
1787 rev--debug: 1
1802 rev--debug: 1
1788 rev--debug: 0
1803 rev--debug: 0
1789 tags: tip
1804 tags: tip
1790 tags:
1805 tags:
1791 tags:
1806 tags:
1792 tags:
1807 tags:
1793 tags:
1808 tags:
1794 tags:
1809 tags:
1795 tags:
1810 tags:
1796 tags:
1811 tags:
1797 tags:
1812 tags:
1798 tags--verbose: tip
1813 tags--verbose: tip
1799 tags--verbose:
1814 tags--verbose:
1800 tags--verbose:
1815 tags--verbose:
1801 tags--verbose:
1816 tags--verbose:
1802 tags--verbose:
1817 tags--verbose:
1803 tags--verbose:
1818 tags--verbose:
1804 tags--verbose:
1819 tags--verbose:
1805 tags--verbose:
1820 tags--verbose:
1806 tags--verbose:
1821 tags--verbose:
1807 tags--debug: tip
1822 tags--debug: tip
1808 tags--debug:
1823 tags--debug:
1809 tags--debug:
1824 tags--debug:
1810 tags--debug:
1825 tags--debug:
1811 tags--debug:
1826 tags--debug:
1812 tags--debug:
1827 tags--debug:
1813 tags--debug:
1828 tags--debug:
1814 tags--debug:
1829 tags--debug:
1815 tags--debug:
1830 tags--debug:
1816 diffstat: 3: +2/-1
1831 diffstat: 3: +2/-1
1817 diffstat: 1: +1/-0
1832 diffstat: 1: +1/-0
1818 diffstat: 0: +0/-0
1833 diffstat: 0: +0/-0
1819 diffstat: 1: +1/-0
1834 diffstat: 1: +1/-0
1820 diffstat: 0: +0/-0
1835 diffstat: 0: +0/-0
1821 diffstat: 1: +1/-0
1836 diffstat: 1: +1/-0
1822 diffstat: 1: +4/-0
1837 diffstat: 1: +4/-0
1823 diffstat: 1: +2/-0
1838 diffstat: 1: +2/-0
1824 diffstat: 1: +1/-0
1839 diffstat: 1: +1/-0
1825 diffstat--verbose: 3: +2/-1
1840 diffstat--verbose: 3: +2/-1
1826 diffstat--verbose: 1: +1/-0
1841 diffstat--verbose: 1: +1/-0
1827 diffstat--verbose: 0: +0/-0
1842 diffstat--verbose: 0: +0/-0
1828 diffstat--verbose: 1: +1/-0
1843 diffstat--verbose: 1: +1/-0
1829 diffstat--verbose: 0: +0/-0
1844 diffstat--verbose: 0: +0/-0
1830 diffstat--verbose: 1: +1/-0
1845 diffstat--verbose: 1: +1/-0
1831 diffstat--verbose: 1: +4/-0
1846 diffstat--verbose: 1: +4/-0
1832 diffstat--verbose: 1: +2/-0
1847 diffstat--verbose: 1: +2/-0
1833 diffstat--verbose: 1: +1/-0
1848 diffstat--verbose: 1: +1/-0
1834 diffstat--debug: 3: +2/-1
1849 diffstat--debug: 3: +2/-1
1835 diffstat--debug: 1: +1/-0
1850 diffstat--debug: 1: +1/-0
1836 diffstat--debug: 0: +0/-0
1851 diffstat--debug: 0: +0/-0
1837 diffstat--debug: 1: +1/-0
1852 diffstat--debug: 1: +1/-0
1838 diffstat--debug: 0: +0/-0
1853 diffstat--debug: 0: +0/-0
1839 diffstat--debug: 1: +1/-0
1854 diffstat--debug: 1: +1/-0
1840 diffstat--debug: 1: +4/-0
1855 diffstat--debug: 1: +4/-0
1841 diffstat--debug: 1: +2/-0
1856 diffstat--debug: 1: +2/-0
1842 diffstat--debug: 1: +1/-0
1857 diffstat--debug: 1: +1/-0
1843 extras: branch=default
1858 extras: branch=default
1844 extras: branch=default
1859 extras: branch=default
1845 extras: branch=default
1860 extras: branch=default
1846 extras: branch=default
1861 extras: branch=default
1847 extras: branch=foo
1862 extras: branch=foo
1848 extras: branch=default
1863 extras: branch=default
1849 extras: branch=default
1864 extras: branch=default
1850 extras: branch=default
1865 extras: branch=default
1851 extras: branch=default
1866 extras: branch=default
1852 extras--verbose: branch=default
1867 extras--verbose: branch=default
1853 extras--verbose: branch=default
1868 extras--verbose: branch=default
1854 extras--verbose: branch=default
1869 extras--verbose: branch=default
1855 extras--verbose: branch=default
1870 extras--verbose: branch=default
1856 extras--verbose: branch=foo
1871 extras--verbose: branch=foo
1857 extras--verbose: branch=default
1872 extras--verbose: branch=default
1858 extras--verbose: branch=default
1873 extras--verbose: branch=default
1859 extras--verbose: branch=default
1874 extras--verbose: branch=default
1860 extras--verbose: branch=default
1875 extras--verbose: branch=default
1861 extras--debug: branch=default
1876 extras--debug: branch=default
1862 extras--debug: branch=default
1877 extras--debug: branch=default
1863 extras--debug: branch=default
1878 extras--debug: branch=default
1864 extras--debug: branch=default
1879 extras--debug: branch=default
1865 extras--debug: branch=foo
1880 extras--debug: branch=foo
1866 extras--debug: branch=default
1881 extras--debug: branch=default
1867 extras--debug: branch=default
1882 extras--debug: branch=default
1868 extras--debug: branch=default
1883 extras--debug: branch=default
1869 extras--debug: branch=default
1884 extras--debug: branch=default
1870 p1rev: 7
1885 p1rev: 7
1871 p1rev: -1
1886 p1rev: -1
1872 p1rev: 5
1887 p1rev: 5
1873 p1rev: 3
1888 p1rev: 3
1874 p1rev: 3
1889 p1rev: 3
1875 p1rev: 2
1890 p1rev: 2
1876 p1rev: 1
1891 p1rev: 1
1877 p1rev: 0
1892 p1rev: 0
1878 p1rev: -1
1893 p1rev: -1
1879 p1rev--verbose: 7
1894 p1rev--verbose: 7
1880 p1rev--verbose: -1
1895 p1rev--verbose: -1
1881 p1rev--verbose: 5
1896 p1rev--verbose: 5
1882 p1rev--verbose: 3
1897 p1rev--verbose: 3
1883 p1rev--verbose: 3
1898 p1rev--verbose: 3
1884 p1rev--verbose: 2
1899 p1rev--verbose: 2
1885 p1rev--verbose: 1
1900 p1rev--verbose: 1
1886 p1rev--verbose: 0
1901 p1rev--verbose: 0
1887 p1rev--verbose: -1
1902 p1rev--verbose: -1
1888 p1rev--debug: 7
1903 p1rev--debug: 7
1889 p1rev--debug: -1
1904 p1rev--debug: -1
1890 p1rev--debug: 5
1905 p1rev--debug: 5
1891 p1rev--debug: 3
1906 p1rev--debug: 3
1892 p1rev--debug: 3
1907 p1rev--debug: 3
1893 p1rev--debug: 2
1908 p1rev--debug: 2
1894 p1rev--debug: 1
1909 p1rev--debug: 1
1895 p1rev--debug: 0
1910 p1rev--debug: 0
1896 p1rev--debug: -1
1911 p1rev--debug: -1
1897 p2rev: -1
1912 p2rev: -1
1898 p2rev: -1
1913 p2rev: -1
1899 p2rev: 4
1914 p2rev: 4
1900 p2rev: -1
1915 p2rev: -1
1901 p2rev: -1
1916 p2rev: -1
1902 p2rev: -1
1917 p2rev: -1
1903 p2rev: -1
1918 p2rev: -1
1904 p2rev: -1
1919 p2rev: -1
1905 p2rev: -1
1920 p2rev: -1
1906 p2rev--verbose: -1
1921 p2rev--verbose: -1
1907 p2rev--verbose: -1
1922 p2rev--verbose: -1
1908 p2rev--verbose: 4
1923 p2rev--verbose: 4
1909 p2rev--verbose: -1
1924 p2rev--verbose: -1
1910 p2rev--verbose: -1
1925 p2rev--verbose: -1
1911 p2rev--verbose: -1
1926 p2rev--verbose: -1
1912 p2rev--verbose: -1
1927 p2rev--verbose: -1
1913 p2rev--verbose: -1
1928 p2rev--verbose: -1
1914 p2rev--verbose: -1
1929 p2rev--verbose: -1
1915 p2rev--debug: -1
1930 p2rev--debug: -1
1916 p2rev--debug: -1
1931 p2rev--debug: -1
1917 p2rev--debug: 4
1932 p2rev--debug: 4
1918 p2rev--debug: -1
1933 p2rev--debug: -1
1919 p2rev--debug: -1
1934 p2rev--debug: -1
1920 p2rev--debug: -1
1935 p2rev--debug: -1
1921 p2rev--debug: -1
1936 p2rev--debug: -1
1922 p2rev--debug: -1
1937 p2rev--debug: -1
1923 p2rev--debug: -1
1938 p2rev--debug: -1
1924 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1939 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1925 p1node: 0000000000000000000000000000000000000000
1940 p1node: 0000000000000000000000000000000000000000
1926 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1941 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1927 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1942 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1928 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1943 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1929 p1node: 97054abb4ab824450e9164180baf491ae0078465
1944 p1node: 97054abb4ab824450e9164180baf491ae0078465
1930 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1945 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1931 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1946 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1932 p1node: 0000000000000000000000000000000000000000
1947 p1node: 0000000000000000000000000000000000000000
1933 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1948 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1934 p1node--verbose: 0000000000000000000000000000000000000000
1949 p1node--verbose: 0000000000000000000000000000000000000000
1935 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1950 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1936 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1951 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1937 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1952 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1938 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1953 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1939 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1954 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1940 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1955 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1941 p1node--verbose: 0000000000000000000000000000000000000000
1956 p1node--verbose: 0000000000000000000000000000000000000000
1942 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1957 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1943 p1node--debug: 0000000000000000000000000000000000000000
1958 p1node--debug: 0000000000000000000000000000000000000000
1944 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1959 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1945 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1960 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1946 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1961 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1947 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
1962 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
1948 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1963 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1949 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1964 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1950 p1node--debug: 0000000000000000000000000000000000000000
1965 p1node--debug: 0000000000000000000000000000000000000000
1951 p2node: 0000000000000000000000000000000000000000
1966 p2node: 0000000000000000000000000000000000000000
1952 p2node: 0000000000000000000000000000000000000000
1967 p2node: 0000000000000000000000000000000000000000
1953 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1968 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1954 p2node: 0000000000000000000000000000000000000000
1969 p2node: 0000000000000000000000000000000000000000
1955 p2node: 0000000000000000000000000000000000000000
1970 p2node: 0000000000000000000000000000000000000000
1956 p2node: 0000000000000000000000000000000000000000
1971 p2node: 0000000000000000000000000000000000000000
1957 p2node: 0000000000000000000000000000000000000000
1972 p2node: 0000000000000000000000000000000000000000
1958 p2node: 0000000000000000000000000000000000000000
1973 p2node: 0000000000000000000000000000000000000000
1959 p2node: 0000000000000000000000000000000000000000
1974 p2node: 0000000000000000000000000000000000000000
1960 p2node--verbose: 0000000000000000000000000000000000000000
1975 p2node--verbose: 0000000000000000000000000000000000000000
1961 p2node--verbose: 0000000000000000000000000000000000000000
1976 p2node--verbose: 0000000000000000000000000000000000000000
1962 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1977 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1963 p2node--verbose: 0000000000000000000000000000000000000000
1978 p2node--verbose: 0000000000000000000000000000000000000000
1964 p2node--verbose: 0000000000000000000000000000000000000000
1979 p2node--verbose: 0000000000000000000000000000000000000000
1965 p2node--verbose: 0000000000000000000000000000000000000000
1980 p2node--verbose: 0000000000000000000000000000000000000000
1966 p2node--verbose: 0000000000000000000000000000000000000000
1981 p2node--verbose: 0000000000000000000000000000000000000000
1967 p2node--verbose: 0000000000000000000000000000000000000000
1982 p2node--verbose: 0000000000000000000000000000000000000000
1968 p2node--verbose: 0000000000000000000000000000000000000000
1983 p2node--verbose: 0000000000000000000000000000000000000000
1969 p2node--debug: 0000000000000000000000000000000000000000
1984 p2node--debug: 0000000000000000000000000000000000000000
1970 p2node--debug: 0000000000000000000000000000000000000000
1985 p2node--debug: 0000000000000000000000000000000000000000
1971 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1986 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1972 p2node--debug: 0000000000000000000000000000000000000000
1987 p2node--debug: 0000000000000000000000000000000000000000
1973 p2node--debug: 0000000000000000000000000000000000000000
1988 p2node--debug: 0000000000000000000000000000000000000000
1974 p2node--debug: 0000000000000000000000000000000000000000
1989 p2node--debug: 0000000000000000000000000000000000000000
1975 p2node--debug: 0000000000000000000000000000000000000000
1990 p2node--debug: 0000000000000000000000000000000000000000
1976 p2node--debug: 0000000000000000000000000000000000000000
1991 p2node--debug: 0000000000000000000000000000000000000000
1977 p2node--debug: 0000000000000000000000000000000000000000
1992 p2node--debug: 0000000000000000000000000000000000000000
1978
1993
1979 Filters work:
1994 Filters work:
1980
1995
1981 $ hg log --template '{author|domain}\n'
1996 $ hg log --template '{author|domain}\n'
1982
1997
1983 hostname
1998 hostname
1984
1999
1985
2000
1986
2001
1987
2002
1988 place
2003 place
1989 place
2004 place
1990 hostname
2005 hostname
1991
2006
1992 $ hg log --template '{author|person}\n'
2007 $ hg log --template '{author|person}\n'
1993 test
2008 test
1994 User Name
2009 User Name
1995 person
2010 person
1996 person
2011 person
1997 person
2012 person
1998 person
2013 person
1999 other
2014 other
2000 A. N. Other
2015 A. N. Other
2001 User Name
2016 User Name
2002
2017
2003 $ hg log --template '{author|user}\n'
2018 $ hg log --template '{author|user}\n'
2004 test
2019 test
2005 user
2020 user
2006 person
2021 person
2007 person
2022 person
2008 person
2023 person
2009 person
2024 person
2010 other
2025 other
2011 other
2026 other
2012 user
2027 user
2013
2028
2014 $ hg log --template '{date|date}\n'
2029 $ hg log --template '{date|date}\n'
2015 Wed Jan 01 10:01:00 2020 +0000
2030 Wed Jan 01 10:01:00 2020 +0000
2016 Mon Jan 12 13:46:40 1970 +0000
2031 Mon Jan 12 13:46:40 1970 +0000
2017 Sun Jan 18 08:40:01 1970 +0000
2032 Sun Jan 18 08:40:01 1970 +0000
2018 Sun Jan 18 08:40:00 1970 +0000
2033 Sun Jan 18 08:40:00 1970 +0000
2019 Sat Jan 17 04:53:20 1970 +0000
2034 Sat Jan 17 04:53:20 1970 +0000
2020 Fri Jan 16 01:06:40 1970 +0000
2035 Fri Jan 16 01:06:40 1970 +0000
2021 Wed Jan 14 21:20:00 1970 +0000
2036 Wed Jan 14 21:20:00 1970 +0000
2022 Tue Jan 13 17:33:20 1970 +0000
2037 Tue Jan 13 17:33:20 1970 +0000
2023 Mon Jan 12 13:46:40 1970 +0000
2038 Mon Jan 12 13:46:40 1970 +0000
2024
2039
2025 $ hg log --template '{date|isodate}\n'
2040 $ hg log --template '{date|isodate}\n'
2026 2020-01-01 10:01 +0000
2041 2020-01-01 10:01 +0000
2027 1970-01-12 13:46 +0000
2042 1970-01-12 13:46 +0000
2028 1970-01-18 08:40 +0000
2043 1970-01-18 08:40 +0000
2029 1970-01-18 08:40 +0000
2044 1970-01-18 08:40 +0000
2030 1970-01-17 04:53 +0000
2045 1970-01-17 04:53 +0000
2031 1970-01-16 01:06 +0000
2046 1970-01-16 01:06 +0000
2032 1970-01-14 21:20 +0000
2047 1970-01-14 21:20 +0000
2033 1970-01-13 17:33 +0000
2048 1970-01-13 17:33 +0000
2034 1970-01-12 13:46 +0000
2049 1970-01-12 13:46 +0000
2035
2050
2036 $ hg log --template '{date|isodatesec}\n'
2051 $ hg log --template '{date|isodatesec}\n'
2037 2020-01-01 10:01:00 +0000
2052 2020-01-01 10:01:00 +0000
2038 1970-01-12 13:46:40 +0000
2053 1970-01-12 13:46:40 +0000
2039 1970-01-18 08:40:01 +0000
2054 1970-01-18 08:40:01 +0000
2040 1970-01-18 08:40:00 +0000
2055 1970-01-18 08:40:00 +0000
2041 1970-01-17 04:53:20 +0000
2056 1970-01-17 04:53:20 +0000
2042 1970-01-16 01:06:40 +0000
2057 1970-01-16 01:06:40 +0000
2043 1970-01-14 21:20:00 +0000
2058 1970-01-14 21:20:00 +0000
2044 1970-01-13 17:33:20 +0000
2059 1970-01-13 17:33:20 +0000
2045 1970-01-12 13:46:40 +0000
2060 1970-01-12 13:46:40 +0000
2046
2061
2047 $ hg log --template '{date|rfc822date}\n'
2062 $ hg log --template '{date|rfc822date}\n'
2048 Wed, 01 Jan 2020 10:01:00 +0000
2063 Wed, 01 Jan 2020 10:01:00 +0000
2049 Mon, 12 Jan 1970 13:46:40 +0000
2064 Mon, 12 Jan 1970 13:46:40 +0000
2050 Sun, 18 Jan 1970 08:40:01 +0000
2065 Sun, 18 Jan 1970 08:40:01 +0000
2051 Sun, 18 Jan 1970 08:40:00 +0000
2066 Sun, 18 Jan 1970 08:40:00 +0000
2052 Sat, 17 Jan 1970 04:53:20 +0000
2067 Sat, 17 Jan 1970 04:53:20 +0000
2053 Fri, 16 Jan 1970 01:06:40 +0000
2068 Fri, 16 Jan 1970 01:06:40 +0000
2054 Wed, 14 Jan 1970 21:20:00 +0000
2069 Wed, 14 Jan 1970 21:20:00 +0000
2055 Tue, 13 Jan 1970 17:33:20 +0000
2070 Tue, 13 Jan 1970 17:33:20 +0000
2056 Mon, 12 Jan 1970 13:46:40 +0000
2071 Mon, 12 Jan 1970 13:46:40 +0000
2057
2072
2058 $ hg log --template '{desc|firstline}\n'
2073 $ hg log --template '{desc|firstline}\n'
2059 third
2074 third
2060 second
2075 second
2061 merge
2076 merge
2062 new head
2077 new head
2063 new branch
2078 new branch
2064 no user, no domain
2079 no user, no domain
2065 no person
2080 no person
2066 other 1
2081 other 1
2067 line 1
2082 line 1
2068
2083
2069 $ hg log --template '{node|short}\n'
2084 $ hg log --template '{node|short}\n'
2070 95c24699272e
2085 95c24699272e
2071 29114dbae42b
2086 29114dbae42b
2072 d41e714fe50d
2087 d41e714fe50d
2073 13207e5a10d9
2088 13207e5a10d9
2074 bbe44766e73d
2089 bbe44766e73d
2075 10e46f2dcbf4
2090 10e46f2dcbf4
2076 97054abb4ab8
2091 97054abb4ab8
2077 b608e9d1a3f0
2092 b608e9d1a3f0
2078 1e4e1b8f71e0
2093 1e4e1b8f71e0
2079
2094
2080 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
2095 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
2081 <changeset author="test"/>
2096 <changeset author="test"/>
2082 <changeset author="User Name &lt;user@hostname&gt;"/>
2097 <changeset author="User Name &lt;user@hostname&gt;"/>
2083 <changeset author="person"/>
2098 <changeset author="person"/>
2084 <changeset author="person"/>
2099 <changeset author="person"/>
2085 <changeset author="person"/>
2100 <changeset author="person"/>
2086 <changeset author="person"/>
2101 <changeset author="person"/>
2087 <changeset author="other@place"/>
2102 <changeset author="other@place"/>
2088 <changeset author="A. N. Other &lt;other@place&gt;"/>
2103 <changeset author="A. N. Other &lt;other@place&gt;"/>
2089 <changeset author="User Name &lt;user@hostname&gt;"/>
2104 <changeset author="User Name &lt;user@hostname&gt;"/>
2090
2105
2091 $ hg log --template '{rev}: {children}\n'
2106 $ hg log --template '{rev}: {children}\n'
2092 8:
2107 8:
2093 7: 8:95c24699272e
2108 7: 8:95c24699272e
2094 6:
2109 6:
2095 5: 6:d41e714fe50d
2110 5: 6:d41e714fe50d
2096 4: 6:d41e714fe50d
2111 4: 6:d41e714fe50d
2097 3: 4:bbe44766e73d 5:13207e5a10d9
2112 3: 4:bbe44766e73d 5:13207e5a10d9
2098 2: 3:10e46f2dcbf4
2113 2: 3:10e46f2dcbf4
2099 1: 2:97054abb4ab8
2114 1: 2:97054abb4ab8
2100 0: 1:b608e9d1a3f0
2115 0: 1:b608e9d1a3f0
2101
2116
2102 Formatnode filter works:
2117 Formatnode filter works:
2103
2118
2104 $ hg -q log -r 0 --template '{node|formatnode}\n'
2119 $ hg -q log -r 0 --template '{node|formatnode}\n'
2105 1e4e1b8f71e0
2120 1e4e1b8f71e0
2106
2121
2107 $ hg log -r 0 --template '{node|formatnode}\n'
2122 $ hg log -r 0 --template '{node|formatnode}\n'
2108 1e4e1b8f71e0
2123 1e4e1b8f71e0
2109
2124
2110 $ hg -v log -r 0 --template '{node|formatnode}\n'
2125 $ hg -v log -r 0 --template '{node|formatnode}\n'
2111 1e4e1b8f71e0
2126 1e4e1b8f71e0
2112
2127
2113 $ hg --debug log -r 0 --template '{node|formatnode}\n'
2128 $ hg --debug log -r 0 --template '{node|formatnode}\n'
2114 1e4e1b8f71e05681d422154f5421e385fec3454f
2129 1e4e1b8f71e05681d422154f5421e385fec3454f
2115
2130
2116 Age filter:
2131 Age filter:
2117
2132
2118 $ hg init unstable-hash
2133 $ hg init unstable-hash
2119 $ cd unstable-hash
2134 $ cd unstable-hash
2120 $ hg log --template '{date|age}\n' > /dev/null || exit 1
2135 $ hg log --template '{date|age}\n' > /dev/null || exit 1
2121
2136
2122 >>> from datetime import datetime, timedelta
2137 >>> from datetime import datetime, timedelta
2123 >>> fp = open('a', 'w')
2138 >>> fp = open('a', 'w')
2124 >>> n = datetime.now() + timedelta(366 * 7)
2139 >>> n = datetime.now() + timedelta(366 * 7)
2125 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
2140 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
2126 >>> fp.close()
2141 >>> fp.close()
2127 $ hg add a
2142 $ hg add a
2128 $ hg commit -m future -d "`cat a`"
2143 $ hg commit -m future -d "`cat a`"
2129
2144
2130 $ hg log -l1 --template '{date|age}\n'
2145 $ hg log -l1 --template '{date|age}\n'
2131 7 years from now
2146 7 years from now
2132
2147
2133 $ cd ..
2148 $ cd ..
2134 $ rm -rf unstable-hash
2149 $ rm -rf unstable-hash
2135
2150
2136 Add a dummy commit to make up for the instability of the above:
2151 Add a dummy commit to make up for the instability of the above:
2137
2152
2138 $ echo a > a
2153 $ echo a > a
2139 $ hg add a
2154 $ hg add a
2140 $ hg ci -m future
2155 $ hg ci -m future
2141
2156
2142 Count filter:
2157 Count filter:
2143
2158
2144 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2159 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2145 40 12
2160 40 12
2146
2161
2147 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2162 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2148 0 1 4
2163 0 1 4
2149
2164
2150 $ hg log -G --template '{rev}: children: {children|count}, \
2165 $ hg log -G --template '{rev}: children: {children|count}, \
2151 > tags: {tags|count}, file_adds: {file_adds|count}, \
2166 > tags: {tags|count}, file_adds: {file_adds|count}, \
2152 > ancestors: {revset("ancestors(%s)", rev)|count}'
2167 > ancestors: {revset("ancestors(%s)", rev)|count}'
2153 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2168 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2154 |
2169 |
2155 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2170 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2156 |
2171 |
2157 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2172 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2158
2173
2159 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2174 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2160 |\
2175 |\
2161 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2176 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2162 | |
2177 | |
2163 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2178 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2164 |/
2179 |/
2165 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2180 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2166 |
2181 |
2167 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2182 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2168 |
2183 |
2169 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2184 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2170 |
2185 |
2171 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2186 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2172
2187
2173
2188
2174 Upper/lower filters:
2189 Upper/lower filters:
2175
2190
2176 $ hg log -r0 --template '{branch|upper}\n'
2191 $ hg log -r0 --template '{branch|upper}\n'
2177 DEFAULT
2192 DEFAULT
2178 $ hg log -r0 --template '{author|lower}\n'
2193 $ hg log -r0 --template '{author|lower}\n'
2179 user name <user@hostname>
2194 user name <user@hostname>
2180 $ hg log -r0 --template '{date|upper}\n'
2195 $ hg log -r0 --template '{date|upper}\n'
2181 abort: template filter 'upper' is not compatible with keyword 'date'
2196 abort: template filter 'upper' is not compatible with keyword 'date'
2182 [255]
2197 [255]
2183
2198
2184 Add a commit that does all possible modifications at once
2199 Add a commit that does all possible modifications at once
2185
2200
2186 $ echo modify >> third
2201 $ echo modify >> third
2187 $ touch b
2202 $ touch b
2188 $ hg add b
2203 $ hg add b
2189 $ hg mv fourth fifth
2204 $ hg mv fourth fifth
2190 $ hg rm a
2205 $ hg rm a
2191 $ hg ci -m "Modify, add, remove, rename"
2206 $ hg ci -m "Modify, add, remove, rename"
2192
2207
2193 Check the status template
2208 Check the status template
2194
2209
2195 $ cat <<EOF >> $HGRCPATH
2210 $ cat <<EOF >> $HGRCPATH
2196 > [extensions]
2211 > [extensions]
2197 > color=
2212 > color=
2198 > EOF
2213 > EOF
2199
2214
2200 $ hg log -T status -r 10
2215 $ hg log -T status -r 10
2201 changeset: 10:0f9759ec227a
2216 changeset: 10:0f9759ec227a
2202 tag: tip
2217 tag: tip
2203 user: test
2218 user: test
2204 date: Thu Jan 01 00:00:00 1970 +0000
2219 date: Thu Jan 01 00:00:00 1970 +0000
2205 summary: Modify, add, remove, rename
2220 summary: Modify, add, remove, rename
2206 files:
2221 files:
2207 M third
2222 M third
2208 A b
2223 A b
2209 A fifth
2224 A fifth
2210 R a
2225 R a
2211 R fourth
2226 R fourth
2212
2227
2213 $ hg log -T status -C -r 10
2228 $ hg log -T status -C -r 10
2214 changeset: 10:0f9759ec227a
2229 changeset: 10:0f9759ec227a
2215 tag: tip
2230 tag: tip
2216 user: test
2231 user: test
2217 date: Thu Jan 01 00:00:00 1970 +0000
2232 date: Thu Jan 01 00:00:00 1970 +0000
2218 summary: Modify, add, remove, rename
2233 summary: Modify, add, remove, rename
2219 files:
2234 files:
2220 M third
2235 M third
2221 A b
2236 A b
2222 A fifth
2237 A fifth
2223 fourth
2238 fourth
2224 R a
2239 R a
2225 R fourth
2240 R fourth
2226
2241
2227 $ hg log -T status -C -r 10 -v
2242 $ hg log -T status -C -r 10 -v
2228 changeset: 10:0f9759ec227a
2243 changeset: 10:0f9759ec227a
2229 tag: tip
2244 tag: tip
2230 user: test
2245 user: test
2231 date: Thu Jan 01 00:00:00 1970 +0000
2246 date: Thu Jan 01 00:00:00 1970 +0000
2232 description:
2247 description:
2233 Modify, add, remove, rename
2248 Modify, add, remove, rename
2234
2249
2235 files:
2250 files:
2236 M third
2251 M third
2237 A b
2252 A b
2238 A fifth
2253 A fifth
2239 fourth
2254 fourth
2240 R a
2255 R a
2241 R fourth
2256 R fourth
2242
2257
2243 $ hg log -T status -C -r 10 --debug
2258 $ hg log -T status -C -r 10 --debug
2244 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2259 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2245 tag: tip
2260 tag: tip
2246 phase: secret
2261 phase: secret
2247 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2262 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2248 parent: -1:0000000000000000000000000000000000000000
2263 parent: -1:0000000000000000000000000000000000000000
2249 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2264 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2250 user: test
2265 user: test
2251 date: Thu Jan 01 00:00:00 1970 +0000
2266 date: Thu Jan 01 00:00:00 1970 +0000
2252 extra: branch=default
2267 extra: branch=default
2253 description:
2268 description:
2254 Modify, add, remove, rename
2269 Modify, add, remove, rename
2255
2270
2256 files:
2271 files:
2257 M third
2272 M third
2258 A b
2273 A b
2259 A fifth
2274 A fifth
2260 fourth
2275 fourth
2261 R a
2276 R a
2262 R fourth
2277 R fourth
2263
2278
2264 $ hg log -T status -C -r 10 --quiet
2279 $ hg log -T status -C -r 10 --quiet
2265 10:0f9759ec227a
2280 10:0f9759ec227a
2266 $ hg --color=debug log -T status -r 10
2281 $ hg --color=debug log -T status -r 10
2267 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2282 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2268 [log.tag|tag: tip]
2283 [log.tag|tag: tip]
2269 [log.user|user: test]
2284 [log.user|user: test]
2270 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2285 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2271 [log.summary|summary: Modify, add, remove, rename]
2286 [log.summary|summary: Modify, add, remove, rename]
2272 [ui.note log.files|files:]
2287 [ui.note log.files|files:]
2273 [status.modified|M third]
2288 [status.modified|M third]
2274 [status.added|A b]
2289 [status.added|A b]
2275 [status.added|A fifth]
2290 [status.added|A fifth]
2276 [status.removed|R a]
2291 [status.removed|R a]
2277 [status.removed|R fourth]
2292 [status.removed|R fourth]
2278
2293
2279 $ hg --color=debug log -T status -C -r 10
2294 $ hg --color=debug log -T status -C -r 10
2280 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2295 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2281 [log.tag|tag: tip]
2296 [log.tag|tag: tip]
2282 [log.user|user: test]
2297 [log.user|user: test]
2283 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2298 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2284 [log.summary|summary: Modify, add, remove, rename]
2299 [log.summary|summary: Modify, add, remove, rename]
2285 [ui.note log.files|files:]
2300 [ui.note log.files|files:]
2286 [status.modified|M third]
2301 [status.modified|M third]
2287 [status.added|A b]
2302 [status.added|A b]
2288 [status.added|A fifth]
2303 [status.added|A fifth]
2289 [status.copied| fourth]
2304 [status.copied| fourth]
2290 [status.removed|R a]
2305 [status.removed|R a]
2291 [status.removed|R fourth]
2306 [status.removed|R fourth]
2292
2307
2293 $ hg --color=debug log -T status -C -r 10 -v
2308 $ hg --color=debug log -T status -C -r 10 -v
2294 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2309 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2295 [log.tag|tag: tip]
2310 [log.tag|tag: tip]
2296 [log.user|user: test]
2311 [log.user|user: test]
2297 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2312 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2298 [ui.note log.description|description:]
2313 [ui.note log.description|description:]
2299 [ui.note log.description|Modify, add, remove, rename]
2314 [ui.note log.description|Modify, add, remove, rename]
2300
2315
2301 [ui.note log.files|files:]
2316 [ui.note log.files|files:]
2302 [status.modified|M third]
2317 [status.modified|M third]
2303 [status.added|A b]
2318 [status.added|A b]
2304 [status.added|A fifth]
2319 [status.added|A fifth]
2305 [status.copied| fourth]
2320 [status.copied| fourth]
2306 [status.removed|R a]
2321 [status.removed|R a]
2307 [status.removed|R fourth]
2322 [status.removed|R fourth]
2308
2323
2309 $ hg --color=debug log -T status -C -r 10 --debug
2324 $ hg --color=debug log -T status -C -r 10 --debug
2310 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2325 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2311 [log.tag|tag: tip]
2326 [log.tag|tag: tip]
2312 [log.phase|phase: secret]
2327 [log.phase|phase: secret]
2313 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2328 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2314 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2329 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2315 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2330 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2316 [log.user|user: test]
2331 [log.user|user: test]
2317 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2332 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2318 [ui.debug log.extra|extra: branch=default]
2333 [ui.debug log.extra|extra: branch=default]
2319 [ui.note log.description|description:]
2334 [ui.note log.description|description:]
2320 [ui.note log.description|Modify, add, remove, rename]
2335 [ui.note log.description|Modify, add, remove, rename]
2321
2336
2322 [ui.note log.files|files:]
2337 [ui.note log.files|files:]
2323 [status.modified|M third]
2338 [status.modified|M third]
2324 [status.added|A b]
2339 [status.added|A b]
2325 [status.added|A fifth]
2340 [status.added|A fifth]
2326 [status.copied| fourth]
2341 [status.copied| fourth]
2327 [status.removed|R a]
2342 [status.removed|R a]
2328 [status.removed|R fourth]
2343 [status.removed|R fourth]
2329
2344
2330 $ hg --color=debug log -T status -C -r 10 --quiet
2345 $ hg --color=debug log -T status -C -r 10 --quiet
2331 [log.node|10:0f9759ec227a]
2346 [log.node|10:0f9759ec227a]
2332
2347
2333 Check the bisect template
2348 Check the bisect template
2334
2349
2335 $ hg bisect -g 1
2350 $ hg bisect -g 1
2336 $ hg bisect -b 3 --noupdate
2351 $ hg bisect -b 3 --noupdate
2337 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2352 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2338 $ hg log -T bisect -r 0:4
2353 $ hg log -T bisect -r 0:4
2339 changeset: 0:1e4e1b8f71e0
2354 changeset: 0:1e4e1b8f71e0
2340 bisect: good (implicit)
2355 bisect: good (implicit)
2341 user: User Name <user@hostname>
2356 user: User Name <user@hostname>
2342 date: Mon Jan 12 13:46:40 1970 +0000
2357 date: Mon Jan 12 13:46:40 1970 +0000
2343 summary: line 1
2358 summary: line 1
2344
2359
2345 changeset: 1:b608e9d1a3f0
2360 changeset: 1:b608e9d1a3f0
2346 bisect: good
2361 bisect: good
2347 user: A. N. Other <other@place>
2362 user: A. N. Other <other@place>
2348 date: Tue Jan 13 17:33:20 1970 +0000
2363 date: Tue Jan 13 17:33:20 1970 +0000
2349 summary: other 1
2364 summary: other 1
2350
2365
2351 changeset: 2:97054abb4ab8
2366 changeset: 2:97054abb4ab8
2352 bisect: untested
2367 bisect: untested
2353 user: other@place
2368 user: other@place
2354 date: Wed Jan 14 21:20:00 1970 +0000
2369 date: Wed Jan 14 21:20:00 1970 +0000
2355 summary: no person
2370 summary: no person
2356
2371
2357 changeset: 3:10e46f2dcbf4
2372 changeset: 3:10e46f2dcbf4
2358 bisect: bad
2373 bisect: bad
2359 user: person
2374 user: person
2360 date: Fri Jan 16 01:06:40 1970 +0000
2375 date: Fri Jan 16 01:06:40 1970 +0000
2361 summary: no user, no domain
2376 summary: no user, no domain
2362
2377
2363 changeset: 4:bbe44766e73d
2378 changeset: 4:bbe44766e73d
2364 bisect: bad (implicit)
2379 bisect: bad (implicit)
2365 branch: foo
2380 branch: foo
2366 user: person
2381 user: person
2367 date: Sat Jan 17 04:53:20 1970 +0000
2382 date: Sat Jan 17 04:53:20 1970 +0000
2368 summary: new branch
2383 summary: new branch
2369
2384
2370 $ hg log --debug -T bisect -r 0:4
2385 $ hg log --debug -T bisect -r 0:4
2371 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2386 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2372 bisect: good (implicit)
2387 bisect: good (implicit)
2373 phase: public
2388 phase: public
2374 parent: -1:0000000000000000000000000000000000000000
2389 parent: -1:0000000000000000000000000000000000000000
2375 parent: -1:0000000000000000000000000000000000000000
2390 parent: -1:0000000000000000000000000000000000000000
2376 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2391 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2377 user: User Name <user@hostname>
2392 user: User Name <user@hostname>
2378 date: Mon Jan 12 13:46:40 1970 +0000
2393 date: Mon Jan 12 13:46:40 1970 +0000
2379 files+: a
2394 files+: a
2380 extra: branch=default
2395 extra: branch=default
2381 description:
2396 description:
2382 line 1
2397 line 1
2383 line 2
2398 line 2
2384
2399
2385
2400
2386 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2401 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2387 bisect: good
2402 bisect: good
2388 phase: public
2403 phase: public
2389 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2404 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2390 parent: -1:0000000000000000000000000000000000000000
2405 parent: -1:0000000000000000000000000000000000000000
2391 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2406 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2392 user: A. N. Other <other@place>
2407 user: A. N. Other <other@place>
2393 date: Tue Jan 13 17:33:20 1970 +0000
2408 date: Tue Jan 13 17:33:20 1970 +0000
2394 files+: b
2409 files+: b
2395 extra: branch=default
2410 extra: branch=default
2396 description:
2411 description:
2397 other 1
2412 other 1
2398 other 2
2413 other 2
2399
2414
2400 other 3
2415 other 3
2401
2416
2402
2417
2403 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2418 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2404 bisect: untested
2419 bisect: untested
2405 phase: public
2420 phase: public
2406 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2421 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2407 parent: -1:0000000000000000000000000000000000000000
2422 parent: -1:0000000000000000000000000000000000000000
2408 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2423 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2409 user: other@place
2424 user: other@place
2410 date: Wed Jan 14 21:20:00 1970 +0000
2425 date: Wed Jan 14 21:20:00 1970 +0000
2411 files+: c
2426 files+: c
2412 extra: branch=default
2427 extra: branch=default
2413 description:
2428 description:
2414 no person
2429 no person
2415
2430
2416
2431
2417 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2432 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2418 bisect: bad
2433 bisect: bad
2419 phase: public
2434 phase: public
2420 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2435 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2421 parent: -1:0000000000000000000000000000000000000000
2436 parent: -1:0000000000000000000000000000000000000000
2422 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2437 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2423 user: person
2438 user: person
2424 date: Fri Jan 16 01:06:40 1970 +0000
2439 date: Fri Jan 16 01:06:40 1970 +0000
2425 files: c
2440 files: c
2426 extra: branch=default
2441 extra: branch=default
2427 description:
2442 description:
2428 no user, no domain
2443 no user, no domain
2429
2444
2430
2445
2431 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2446 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2432 bisect: bad (implicit)
2447 bisect: bad (implicit)
2433 branch: foo
2448 branch: foo
2434 phase: draft
2449 phase: draft
2435 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2450 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2436 parent: -1:0000000000000000000000000000000000000000
2451 parent: -1:0000000000000000000000000000000000000000
2437 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2452 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2438 user: person
2453 user: person
2439 date: Sat Jan 17 04:53:20 1970 +0000
2454 date: Sat Jan 17 04:53:20 1970 +0000
2440 extra: branch=foo
2455 extra: branch=foo
2441 description:
2456 description:
2442 new branch
2457 new branch
2443
2458
2444
2459
2445 $ hg log -v -T bisect -r 0:4
2460 $ hg log -v -T bisect -r 0:4
2446 changeset: 0:1e4e1b8f71e0
2461 changeset: 0:1e4e1b8f71e0
2447 bisect: good (implicit)
2462 bisect: good (implicit)
2448 user: User Name <user@hostname>
2463 user: User Name <user@hostname>
2449 date: Mon Jan 12 13:46:40 1970 +0000
2464 date: Mon Jan 12 13:46:40 1970 +0000
2450 files: a
2465 files: a
2451 description:
2466 description:
2452 line 1
2467 line 1
2453 line 2
2468 line 2
2454
2469
2455
2470
2456 changeset: 1:b608e9d1a3f0
2471 changeset: 1:b608e9d1a3f0
2457 bisect: good
2472 bisect: good
2458 user: A. N. Other <other@place>
2473 user: A. N. Other <other@place>
2459 date: Tue Jan 13 17:33:20 1970 +0000
2474 date: Tue Jan 13 17:33:20 1970 +0000
2460 files: b
2475 files: b
2461 description:
2476 description:
2462 other 1
2477 other 1
2463 other 2
2478 other 2
2464
2479
2465 other 3
2480 other 3
2466
2481
2467
2482
2468 changeset: 2:97054abb4ab8
2483 changeset: 2:97054abb4ab8
2469 bisect: untested
2484 bisect: untested
2470 user: other@place
2485 user: other@place
2471 date: Wed Jan 14 21:20:00 1970 +0000
2486 date: Wed Jan 14 21:20:00 1970 +0000
2472 files: c
2487 files: c
2473 description:
2488 description:
2474 no person
2489 no person
2475
2490
2476
2491
2477 changeset: 3:10e46f2dcbf4
2492 changeset: 3:10e46f2dcbf4
2478 bisect: bad
2493 bisect: bad
2479 user: person
2494 user: person
2480 date: Fri Jan 16 01:06:40 1970 +0000
2495 date: Fri Jan 16 01:06:40 1970 +0000
2481 files: c
2496 files: c
2482 description:
2497 description:
2483 no user, no domain
2498 no user, no domain
2484
2499
2485
2500
2486 changeset: 4:bbe44766e73d
2501 changeset: 4:bbe44766e73d
2487 bisect: bad (implicit)
2502 bisect: bad (implicit)
2488 branch: foo
2503 branch: foo
2489 user: person
2504 user: person
2490 date: Sat Jan 17 04:53:20 1970 +0000
2505 date: Sat Jan 17 04:53:20 1970 +0000
2491 description:
2506 description:
2492 new branch
2507 new branch
2493
2508
2494
2509
2495 $ hg --color=debug log -T bisect -r 0:4
2510 $ hg --color=debug log -T bisect -r 0:4
2496 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2511 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2497 [log.bisect bisect.good|bisect: good (implicit)]
2512 [log.bisect bisect.good|bisect: good (implicit)]
2498 [log.user|user: User Name <user@hostname>]
2513 [log.user|user: User Name <user@hostname>]
2499 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2514 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2500 [log.summary|summary: line 1]
2515 [log.summary|summary: line 1]
2501
2516
2502 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2517 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2503 [log.bisect bisect.good|bisect: good]
2518 [log.bisect bisect.good|bisect: good]
2504 [log.user|user: A. N. Other <other@place>]
2519 [log.user|user: A. N. Other <other@place>]
2505 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2520 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2506 [log.summary|summary: other 1]
2521 [log.summary|summary: other 1]
2507
2522
2508 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2523 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2509 [log.bisect bisect.untested|bisect: untested]
2524 [log.bisect bisect.untested|bisect: untested]
2510 [log.user|user: other@place]
2525 [log.user|user: other@place]
2511 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2526 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2512 [log.summary|summary: no person]
2527 [log.summary|summary: no person]
2513
2528
2514 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2529 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2515 [log.bisect bisect.bad|bisect: bad]
2530 [log.bisect bisect.bad|bisect: bad]
2516 [log.user|user: person]
2531 [log.user|user: person]
2517 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2532 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2518 [log.summary|summary: no user, no domain]
2533 [log.summary|summary: no user, no domain]
2519
2534
2520 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2535 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2521 [log.bisect bisect.bad|bisect: bad (implicit)]
2536 [log.bisect bisect.bad|bisect: bad (implicit)]
2522 [log.branch|branch: foo]
2537 [log.branch|branch: foo]
2523 [log.user|user: person]
2538 [log.user|user: person]
2524 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2539 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2525 [log.summary|summary: new branch]
2540 [log.summary|summary: new branch]
2526
2541
2527 $ hg --color=debug log --debug -T bisect -r 0:4
2542 $ hg --color=debug log --debug -T bisect -r 0:4
2528 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2543 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2529 [log.bisect bisect.good|bisect: good (implicit)]
2544 [log.bisect bisect.good|bisect: good (implicit)]
2530 [log.phase|phase: public]
2545 [log.phase|phase: public]
2531 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2546 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2532 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2547 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2533 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2548 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2534 [log.user|user: User Name <user@hostname>]
2549 [log.user|user: User Name <user@hostname>]
2535 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2550 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2536 [ui.debug log.files|files+: a]
2551 [ui.debug log.files|files+: a]
2537 [ui.debug log.extra|extra: branch=default]
2552 [ui.debug log.extra|extra: branch=default]
2538 [ui.note log.description|description:]
2553 [ui.note log.description|description:]
2539 [ui.note log.description|line 1
2554 [ui.note log.description|line 1
2540 line 2]
2555 line 2]
2541
2556
2542
2557
2543 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2558 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2544 [log.bisect bisect.good|bisect: good]
2559 [log.bisect bisect.good|bisect: good]
2545 [log.phase|phase: public]
2560 [log.phase|phase: public]
2546 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2561 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2547 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2562 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2548 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2563 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2549 [log.user|user: A. N. Other <other@place>]
2564 [log.user|user: A. N. Other <other@place>]
2550 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2565 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2551 [ui.debug log.files|files+: b]
2566 [ui.debug log.files|files+: b]
2552 [ui.debug log.extra|extra: branch=default]
2567 [ui.debug log.extra|extra: branch=default]
2553 [ui.note log.description|description:]
2568 [ui.note log.description|description:]
2554 [ui.note log.description|other 1
2569 [ui.note log.description|other 1
2555 other 2
2570 other 2
2556
2571
2557 other 3]
2572 other 3]
2558
2573
2559
2574
2560 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2575 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2561 [log.bisect bisect.untested|bisect: untested]
2576 [log.bisect bisect.untested|bisect: untested]
2562 [log.phase|phase: public]
2577 [log.phase|phase: public]
2563 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2578 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2564 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2579 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2565 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2580 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2566 [log.user|user: other@place]
2581 [log.user|user: other@place]
2567 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2582 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2568 [ui.debug log.files|files+: c]
2583 [ui.debug log.files|files+: c]
2569 [ui.debug log.extra|extra: branch=default]
2584 [ui.debug log.extra|extra: branch=default]
2570 [ui.note log.description|description:]
2585 [ui.note log.description|description:]
2571 [ui.note log.description|no person]
2586 [ui.note log.description|no person]
2572
2587
2573
2588
2574 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2589 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2575 [log.bisect bisect.bad|bisect: bad]
2590 [log.bisect bisect.bad|bisect: bad]
2576 [log.phase|phase: public]
2591 [log.phase|phase: public]
2577 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2592 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2578 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2593 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2579 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2594 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2580 [log.user|user: person]
2595 [log.user|user: person]
2581 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2596 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2582 [ui.debug log.files|files: c]
2597 [ui.debug log.files|files: c]
2583 [ui.debug log.extra|extra: branch=default]
2598 [ui.debug log.extra|extra: branch=default]
2584 [ui.note log.description|description:]
2599 [ui.note log.description|description:]
2585 [ui.note log.description|no user, no domain]
2600 [ui.note log.description|no user, no domain]
2586
2601
2587
2602
2588 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2603 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2589 [log.bisect bisect.bad|bisect: bad (implicit)]
2604 [log.bisect bisect.bad|bisect: bad (implicit)]
2590 [log.branch|branch: foo]
2605 [log.branch|branch: foo]
2591 [log.phase|phase: draft]
2606 [log.phase|phase: draft]
2592 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2607 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2593 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2608 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2594 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2609 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2595 [log.user|user: person]
2610 [log.user|user: person]
2596 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2611 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2597 [ui.debug log.extra|extra: branch=foo]
2612 [ui.debug log.extra|extra: branch=foo]
2598 [ui.note log.description|description:]
2613 [ui.note log.description|description:]
2599 [ui.note log.description|new branch]
2614 [ui.note log.description|new branch]
2600
2615
2601
2616
2602 $ hg --color=debug log -v -T bisect -r 0:4
2617 $ hg --color=debug log -v -T bisect -r 0:4
2603 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2618 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2604 [log.bisect bisect.good|bisect: good (implicit)]
2619 [log.bisect bisect.good|bisect: good (implicit)]
2605 [log.user|user: User Name <user@hostname>]
2620 [log.user|user: User Name <user@hostname>]
2606 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2621 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2607 [ui.note log.files|files: a]
2622 [ui.note log.files|files: a]
2608 [ui.note log.description|description:]
2623 [ui.note log.description|description:]
2609 [ui.note log.description|line 1
2624 [ui.note log.description|line 1
2610 line 2]
2625 line 2]
2611
2626
2612
2627
2613 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2628 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2614 [log.bisect bisect.good|bisect: good]
2629 [log.bisect bisect.good|bisect: good]
2615 [log.user|user: A. N. Other <other@place>]
2630 [log.user|user: A. N. Other <other@place>]
2616 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2631 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2617 [ui.note log.files|files: b]
2632 [ui.note log.files|files: b]
2618 [ui.note log.description|description:]
2633 [ui.note log.description|description:]
2619 [ui.note log.description|other 1
2634 [ui.note log.description|other 1
2620 other 2
2635 other 2
2621
2636
2622 other 3]
2637 other 3]
2623
2638
2624
2639
2625 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2640 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2626 [log.bisect bisect.untested|bisect: untested]
2641 [log.bisect bisect.untested|bisect: untested]
2627 [log.user|user: other@place]
2642 [log.user|user: other@place]
2628 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2643 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2629 [ui.note log.files|files: c]
2644 [ui.note log.files|files: c]
2630 [ui.note log.description|description:]
2645 [ui.note log.description|description:]
2631 [ui.note log.description|no person]
2646 [ui.note log.description|no person]
2632
2647
2633
2648
2634 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2649 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2635 [log.bisect bisect.bad|bisect: bad]
2650 [log.bisect bisect.bad|bisect: bad]
2636 [log.user|user: person]
2651 [log.user|user: person]
2637 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2652 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2638 [ui.note log.files|files: c]
2653 [ui.note log.files|files: c]
2639 [ui.note log.description|description:]
2654 [ui.note log.description|description:]
2640 [ui.note log.description|no user, no domain]
2655 [ui.note log.description|no user, no domain]
2641
2656
2642
2657
2643 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2658 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2644 [log.bisect bisect.bad|bisect: bad (implicit)]
2659 [log.bisect bisect.bad|bisect: bad (implicit)]
2645 [log.branch|branch: foo]
2660 [log.branch|branch: foo]
2646 [log.user|user: person]
2661 [log.user|user: person]
2647 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2662 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2648 [ui.note log.description|description:]
2663 [ui.note log.description|description:]
2649 [ui.note log.description|new branch]
2664 [ui.note log.description|new branch]
2650
2665
2651
2666
2652 $ hg bisect --reset
2667 $ hg bisect --reset
2653
2668
2654 Error on syntax:
2669 Error on syntax:
2655
2670
2656 $ echo 'x = "f' >> t
2671 $ echo 'x = "f' >> t
2657 $ hg log
2672 $ hg log
2658 hg: parse error at t:3: unmatched quotes
2673 hg: parse error at t:3: unmatched quotes
2659 [255]
2674 [255]
2660
2675
2661 $ hg log -T '{date'
2676 $ hg log -T '{date'
2662 hg: parse error at 1: unterminated template expansion
2677 hg: parse error at 1: unterminated template expansion
2663 [255]
2678 [255]
2664
2679
2665 Behind the scenes, this will throw TypeError
2680 Behind the scenes, this will throw TypeError
2666
2681
2667 $ hg log -l 3 --template '{date|obfuscate}\n'
2682 $ hg log -l 3 --template '{date|obfuscate}\n'
2668 abort: template filter 'obfuscate' is not compatible with keyword 'date'
2683 abort: template filter 'obfuscate' is not compatible with keyword 'date'
2669 [255]
2684 [255]
2670
2685
2671 Behind the scenes, this will throw a ValueError
2686 Behind the scenes, this will throw a ValueError
2672
2687
2673 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2688 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2674 abort: template filter 'shortdate' is not compatible with keyword 'desc'
2689 abort: template filter 'shortdate' is not compatible with keyword 'desc'
2675 [255]
2690 [255]
2676
2691
2677 Behind the scenes, this will throw AttributeError
2692 Behind the scenes, this will throw AttributeError
2678
2693
2679 $ hg log -l 3 --template 'line: {date|escape}\n'
2694 $ hg log -l 3 --template 'line: {date|escape}\n'
2680 abort: template filter 'escape' is not compatible with keyword 'date'
2695 abort: template filter 'escape' is not compatible with keyword 'date'
2681 [255]
2696 [255]
2682
2697
2683 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2698 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2684 hg: parse error: localdate expects a date information
2699 hg: parse error: localdate expects a date information
2685 [255]
2700 [255]
2686
2701
2687 Behind the scenes, this will throw ValueError
2702 Behind the scenes, this will throw ValueError
2688
2703
2689 $ hg tip --template '{author|email|date}\n'
2704 $ hg tip --template '{author|email|date}\n'
2690 hg: parse error: date expects a date information
2705 hg: parse error: date expects a date information
2691 [255]
2706 [255]
2692
2707
2693 $ hg tip -T '{author|email|shortdate}\n'
2708 $ hg tip -T '{author|email|shortdate}\n'
2694 abort: template filter 'shortdate' is not compatible with keyword 'author'
2709 abort: template filter 'shortdate' is not compatible with keyword 'author'
2695 [255]
2710 [255]
2696
2711
2697 $ hg tip -T '{get(extras, "branch")|shortdate}\n'
2712 $ hg tip -T '{get(extras, "branch")|shortdate}\n'
2698 abort: incompatible use of template filter 'shortdate'
2713 abort: incompatible use of template filter 'shortdate'
2699 [255]
2714 [255]
2700
2715
2701 Error in nested template:
2716 Error in nested template:
2702
2717
2703 $ hg log -T '{"date'
2718 $ hg log -T '{"date'
2704 hg: parse error at 2: unterminated string
2719 hg: parse error at 2: unterminated string
2705 [255]
2720 [255]
2706
2721
2707 $ hg log -T '{"foo{date|?}"}'
2722 $ hg log -T '{"foo{date|?}"}'
2708 hg: parse error at 11: syntax error
2723 hg: parse error at 11: syntax error
2709 [255]
2724 [255]
2710
2725
2711 Thrown an error if a template function doesn't exist
2726 Thrown an error if a template function doesn't exist
2712
2727
2713 $ hg tip --template '{foo()}\n'
2728 $ hg tip --template '{foo()}\n'
2714 hg: parse error: unknown function 'foo'
2729 hg: parse error: unknown function 'foo'
2715 [255]
2730 [255]
2716
2731
2717 Pass generator object created by template function to filter
2732 Pass generator object created by template function to filter
2718
2733
2719 $ hg log -l 1 --template '{if(author, author)|user}\n'
2734 $ hg log -l 1 --template '{if(author, author)|user}\n'
2720 test
2735 test
2721
2736
2722 Test index keyword:
2737 Test index keyword:
2723
2738
2724 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
2739 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
2725 10 0:a 1:b 2:fifth 3:fourth 4:third
2740 10 0:a 1:b 2:fifth 3:fourth 4:third
2726 11 0:a
2741 11 0:a
2727
2742
2728 $ hg branches -T '{index} {branch}\n'
2743 $ hg branches -T '{index} {branch}\n'
2729 0 default
2744 0 default
2730 1 foo
2745 1 foo
2731
2746
2732 Test diff function:
2747 Test diff function:
2733
2748
2734 $ hg diff -c 8
2749 $ hg diff -c 8
2735 diff -r 29114dbae42b -r 95c24699272e fourth
2750 diff -r 29114dbae42b -r 95c24699272e fourth
2736 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2751 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2737 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2752 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2738 @@ -0,0 +1,1 @@
2753 @@ -0,0 +1,1 @@
2739 +second
2754 +second
2740 diff -r 29114dbae42b -r 95c24699272e second
2755 diff -r 29114dbae42b -r 95c24699272e second
2741 --- a/second Mon Jan 12 13:46:40 1970 +0000
2756 --- a/second Mon Jan 12 13:46:40 1970 +0000
2742 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2757 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2743 @@ -1,1 +0,0 @@
2758 @@ -1,1 +0,0 @@
2744 -second
2759 -second
2745 diff -r 29114dbae42b -r 95c24699272e third
2760 diff -r 29114dbae42b -r 95c24699272e third
2746 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2761 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2747 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2762 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2748 @@ -0,0 +1,1 @@
2763 @@ -0,0 +1,1 @@
2749 +third
2764 +third
2750
2765
2751 $ hg log -r 8 -T "{diff()}"
2766 $ hg log -r 8 -T "{diff()}"
2752 diff -r 29114dbae42b -r 95c24699272e fourth
2767 diff -r 29114dbae42b -r 95c24699272e fourth
2753 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2768 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2754 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2769 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2755 @@ -0,0 +1,1 @@
2770 @@ -0,0 +1,1 @@
2756 +second
2771 +second
2757 diff -r 29114dbae42b -r 95c24699272e second
2772 diff -r 29114dbae42b -r 95c24699272e second
2758 --- a/second Mon Jan 12 13:46:40 1970 +0000
2773 --- a/second Mon Jan 12 13:46:40 1970 +0000
2759 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2774 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2760 @@ -1,1 +0,0 @@
2775 @@ -1,1 +0,0 @@
2761 -second
2776 -second
2762 diff -r 29114dbae42b -r 95c24699272e third
2777 diff -r 29114dbae42b -r 95c24699272e third
2763 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2778 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2764 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2779 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2765 @@ -0,0 +1,1 @@
2780 @@ -0,0 +1,1 @@
2766 +third
2781 +third
2767
2782
2768 $ hg log -r 8 -T "{diff('glob:f*')}"
2783 $ hg log -r 8 -T "{diff('glob:f*')}"
2769 diff -r 29114dbae42b -r 95c24699272e fourth
2784 diff -r 29114dbae42b -r 95c24699272e fourth
2770 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2785 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2771 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2786 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2772 @@ -0,0 +1,1 @@
2787 @@ -0,0 +1,1 @@
2773 +second
2788 +second
2774
2789
2775 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2790 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2776 diff -r 29114dbae42b -r 95c24699272e second
2791 diff -r 29114dbae42b -r 95c24699272e second
2777 --- a/second Mon Jan 12 13:46:40 1970 +0000
2792 --- a/second Mon Jan 12 13:46:40 1970 +0000
2778 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2793 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2779 @@ -1,1 +0,0 @@
2794 @@ -1,1 +0,0 @@
2780 -second
2795 -second
2781 diff -r 29114dbae42b -r 95c24699272e third
2796 diff -r 29114dbae42b -r 95c24699272e third
2782 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2797 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2783 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2798 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2784 @@ -0,0 +1,1 @@
2799 @@ -0,0 +1,1 @@
2785 +third
2800 +third
2786
2801
2787 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2802 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2788 diff -r 29114dbae42b -r 95c24699272e fourth
2803 diff -r 29114dbae42b -r 95c24699272e fourth
2789 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2804 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2790 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2805 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2791 @@ -0,0 +1,1 @@
2806 @@ -0,0 +1,1 @@
2792 +second
2807 +second
2793
2808
2794 $ cd ..
2809 $ cd ..
2795
2810
2796
2811
2797 latesttag:
2812 latesttag:
2798
2813
2799 $ hg init latesttag
2814 $ hg init latesttag
2800 $ cd latesttag
2815 $ cd latesttag
2801
2816
2802 $ echo a > file
2817 $ echo a > file
2803 $ hg ci -Am a -d '0 0'
2818 $ hg ci -Am a -d '0 0'
2804 adding file
2819 adding file
2805
2820
2806 $ echo b >> file
2821 $ echo b >> file
2807 $ hg ci -m b -d '1 0'
2822 $ hg ci -m b -d '1 0'
2808
2823
2809 $ echo c >> head1
2824 $ echo c >> head1
2810 $ hg ci -Am h1c -d '2 0'
2825 $ hg ci -Am h1c -d '2 0'
2811 adding head1
2826 adding head1
2812
2827
2813 $ hg update -q 1
2828 $ hg update -q 1
2814 $ echo d >> head2
2829 $ echo d >> head2
2815 $ hg ci -Am h2d -d '3 0'
2830 $ hg ci -Am h2d -d '3 0'
2816 adding head2
2831 adding head2
2817 created new head
2832 created new head
2818
2833
2819 $ echo e >> head2
2834 $ echo e >> head2
2820 $ hg ci -m h2e -d '4 0'
2835 $ hg ci -m h2e -d '4 0'
2821
2836
2822 $ hg merge -q
2837 $ hg merge -q
2823 $ hg ci -m merge -d '5 -3600'
2838 $ hg ci -m merge -d '5 -3600'
2824
2839
2825 No tag set:
2840 No tag set:
2826
2841
2827 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2842 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2828 5: null+5
2843 5: null+5
2829 4: null+4
2844 4: null+4
2830 3: null+3
2845 3: null+3
2831 2: null+3
2846 2: null+3
2832 1: null+2
2847 1: null+2
2833 0: null+1
2848 0: null+1
2834
2849
2835 One common tag: longest path wins:
2850 One common tag: longest path wins:
2836
2851
2837 $ hg tag -r 1 -m t1 -d '6 0' t1
2852 $ hg tag -r 1 -m t1 -d '6 0' t1
2838 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2853 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2839 6: t1+4
2854 6: t1+4
2840 5: t1+3
2855 5: t1+3
2841 4: t1+2
2856 4: t1+2
2842 3: t1+1
2857 3: t1+1
2843 2: t1+1
2858 2: t1+1
2844 1: t1+0
2859 1: t1+0
2845 0: null+1
2860 0: null+1
2846
2861
2847 One ancestor tag: more recent wins:
2862 One ancestor tag: more recent wins:
2848
2863
2849 $ hg tag -r 2 -m t2 -d '7 0' t2
2864 $ hg tag -r 2 -m t2 -d '7 0' t2
2850 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2865 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2851 7: t2+3
2866 7: t2+3
2852 6: t2+2
2867 6: t2+2
2853 5: t2+1
2868 5: t2+1
2854 4: t1+2
2869 4: t1+2
2855 3: t1+1
2870 3: t1+1
2856 2: t2+0
2871 2: t2+0
2857 1: t1+0
2872 1: t1+0
2858 0: null+1
2873 0: null+1
2859
2874
2860 Two branch tags: more recent wins:
2875 Two branch tags: more recent wins:
2861
2876
2862 $ hg tag -r 3 -m t3 -d '8 0' t3
2877 $ hg tag -r 3 -m t3 -d '8 0' t3
2863 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2878 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2864 8: t3+5
2879 8: t3+5
2865 7: t3+4
2880 7: t3+4
2866 6: t3+3
2881 6: t3+3
2867 5: t3+2
2882 5: t3+2
2868 4: t3+1
2883 4: t3+1
2869 3: t3+0
2884 3: t3+0
2870 2: t2+0
2885 2: t2+0
2871 1: t1+0
2886 1: t1+0
2872 0: null+1
2887 0: null+1
2873
2888
2874 Merged tag overrides:
2889 Merged tag overrides:
2875
2890
2876 $ hg tag -r 5 -m t5 -d '9 0' t5
2891 $ hg tag -r 5 -m t5 -d '9 0' t5
2877 $ hg tag -r 3 -m at3 -d '10 0' at3
2892 $ hg tag -r 3 -m at3 -d '10 0' at3
2878 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2893 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2879 10: t5+5
2894 10: t5+5
2880 9: t5+4
2895 9: t5+4
2881 8: t5+3
2896 8: t5+3
2882 7: t5+2
2897 7: t5+2
2883 6: t5+1
2898 6: t5+1
2884 5: t5+0
2899 5: t5+0
2885 4: at3:t3+1
2900 4: at3:t3+1
2886 3: at3:t3+0
2901 3: at3:t3+0
2887 2: t2+0
2902 2: t2+0
2888 1: t1+0
2903 1: t1+0
2889 0: null+1
2904 0: null+1
2890
2905
2891 $ hg log --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
2906 $ hg log --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
2892 10: t5+5,5
2907 10: t5+5,5
2893 9: t5+4,4
2908 9: t5+4,4
2894 8: t5+3,3
2909 8: t5+3,3
2895 7: t5+2,2
2910 7: t5+2,2
2896 6: t5+1,1
2911 6: t5+1,1
2897 5: t5+0,0
2912 5: t5+0,0
2898 4: at3+1,1 t3+1,1
2913 4: at3+1,1 t3+1,1
2899 3: at3+0,0 t3+0,0
2914 3: at3+0,0 t3+0,0
2900 2: t2+0,0
2915 2: t2+0,0
2901 1: t1+0,0
2916 1: t1+0,0
2902 0: null+1,1
2917 0: null+1,1
2903
2918
2904 $ hg log --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
2919 $ hg log --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
2905 10: t3, C: 8, D: 7
2920 10: t3, C: 8, D: 7
2906 9: t3, C: 7, D: 6
2921 9: t3, C: 7, D: 6
2907 8: t3, C: 6, D: 5
2922 8: t3, C: 6, D: 5
2908 7: t3, C: 5, D: 4
2923 7: t3, C: 5, D: 4
2909 6: t3, C: 4, D: 3
2924 6: t3, C: 4, D: 3
2910 5: t3, C: 3, D: 2
2925 5: t3, C: 3, D: 2
2911 4: t3, C: 1, D: 1
2926 4: t3, C: 1, D: 1
2912 3: t3, C: 0, D: 0
2927 3: t3, C: 0, D: 0
2913 2: t1, C: 1, D: 1
2928 2: t1, C: 1, D: 1
2914 1: t1, C: 0, D: 0
2929 1: t1, C: 0, D: 0
2915 0: null, C: 1, D: 1
2930 0: null, C: 1, D: 1
2916
2931
2917 $ cd ..
2932 $ cd ..
2918
2933
2919
2934
2920 Style path expansion: issue1948 - ui.style option doesn't work on OSX
2935 Style path expansion: issue1948 - ui.style option doesn't work on OSX
2921 if it is a relative path
2936 if it is a relative path
2922
2937
2923 $ mkdir -p home/styles
2938 $ mkdir -p home/styles
2924
2939
2925 $ cat > home/styles/teststyle <<EOF
2940 $ cat > home/styles/teststyle <<EOF
2926 > changeset = 'test {rev}:{node|short}\n'
2941 > changeset = 'test {rev}:{node|short}\n'
2927 > EOF
2942 > EOF
2928
2943
2929 $ HOME=`pwd`/home; export HOME
2944 $ HOME=`pwd`/home; export HOME
2930
2945
2931 $ cat > latesttag/.hg/hgrc <<EOF
2946 $ cat > latesttag/.hg/hgrc <<EOF
2932 > [ui]
2947 > [ui]
2933 > style = ~/styles/teststyle
2948 > style = ~/styles/teststyle
2934 > EOF
2949 > EOF
2935
2950
2936 $ hg -R latesttag tip
2951 $ hg -R latesttag tip
2937 test 10:9b4a630e5f5f
2952 test 10:9b4a630e5f5f
2938
2953
2939 Test recursive showlist template (issue1989):
2954 Test recursive showlist template (issue1989):
2940
2955
2941 $ cat > style1989 <<EOF
2956 $ cat > style1989 <<EOF
2942 > changeset = '{file_mods}{manifest}{extras}'
2957 > changeset = '{file_mods}{manifest}{extras}'
2943 > file_mod = 'M|{author|person}\n'
2958 > file_mod = 'M|{author|person}\n'
2944 > manifest = '{rev},{author}\n'
2959 > manifest = '{rev},{author}\n'
2945 > extra = '{key}: {author}\n'
2960 > extra = '{key}: {author}\n'
2946 > EOF
2961 > EOF
2947
2962
2948 $ hg -R latesttag log -r tip --style=style1989
2963 $ hg -R latesttag log -r tip --style=style1989
2949 M|test
2964 M|test
2950 10,test
2965 10,test
2951 branch: test
2966 branch: test
2952
2967
2953 Test new-style inline templating:
2968 Test new-style inline templating:
2954
2969
2955 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
2970 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
2956 modified files: .hgtags
2971 modified files: .hgtags
2957
2972
2958
2973
2959 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
2974 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
2960 hg: parse error: keyword 'rev' is not iterable
2975 hg: parse error: keyword 'rev' is not iterable
2961 [255]
2976 [255]
2962 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
2977 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
2963 hg: parse error: None is not iterable
2978 hg: parse error: None is not iterable
2964 [255]
2979 [255]
2965
2980
2966 Test the sub function of templating for expansion:
2981 Test the sub function of templating for expansion:
2967
2982
2968 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
2983 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
2969 xx
2984 xx
2970
2985
2971 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
2986 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
2972 hg: parse error: sub got an invalid pattern: [
2987 hg: parse error: sub got an invalid pattern: [
2973 [255]
2988 [255]
2974 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
2989 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
2975 hg: parse error: sub got an invalid replacement: \1
2990 hg: parse error: sub got an invalid replacement: \1
2976 [255]
2991 [255]
2977
2992
2978 Test the strip function with chars specified:
2993 Test the strip function with chars specified:
2979
2994
2980 $ hg log -R latesttag --template '{desc}\n'
2995 $ hg log -R latesttag --template '{desc}\n'
2981 at3
2996 at3
2982 t5
2997 t5
2983 t3
2998 t3
2984 t2
2999 t2
2985 t1
3000 t1
2986 merge
3001 merge
2987 h2e
3002 h2e
2988 h2d
3003 h2d
2989 h1c
3004 h1c
2990 b
3005 b
2991 a
3006 a
2992
3007
2993 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
3008 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
2994 at3
3009 at3
2995 5
3010 5
2996 3
3011 3
2997 2
3012 2
2998 1
3013 1
2999 merg
3014 merg
3000 h2
3015 h2
3001 h2d
3016 h2d
3002 h1c
3017 h1c
3003 b
3018 b
3004 a
3019 a
3005
3020
3006 Test date format:
3021 Test date format:
3007
3022
3008 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3023 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3009 date: 70 01 01 10 +0000
3024 date: 70 01 01 10 +0000
3010 date: 70 01 01 09 +0000
3025 date: 70 01 01 09 +0000
3011 date: 70 01 01 08 +0000
3026 date: 70 01 01 08 +0000
3012 date: 70 01 01 07 +0000
3027 date: 70 01 01 07 +0000
3013 date: 70 01 01 06 +0000
3028 date: 70 01 01 06 +0000
3014 date: 70 01 01 05 +0100
3029 date: 70 01 01 05 +0100
3015 date: 70 01 01 04 +0000
3030 date: 70 01 01 04 +0000
3016 date: 70 01 01 03 +0000
3031 date: 70 01 01 03 +0000
3017 date: 70 01 01 02 +0000
3032 date: 70 01 01 02 +0000
3018 date: 70 01 01 01 +0000
3033 date: 70 01 01 01 +0000
3019 date: 70 01 01 00 +0000
3034 date: 70 01 01 00 +0000
3020
3035
3021 Test invalid date:
3036 Test invalid date:
3022
3037
3023 $ hg log -R latesttag -T '{date(rev)}\n'
3038 $ hg log -R latesttag -T '{date(rev)}\n'
3024 hg: parse error: date expects a date information
3039 hg: parse error: date expects a date information
3025 [255]
3040 [255]
3026
3041
3027 Test integer literal:
3042 Test integer literal:
3028
3043
3029 $ hg debugtemplate -v '{(0)}\n'
3044 $ hg debugtemplate -v '{(0)}\n'
3030 (template
3045 (template
3031 (group
3046 (group
3032 ('integer', '0'))
3047 ('integer', '0'))
3033 ('string', '\n'))
3048 ('string', '\n'))
3034 0
3049 0
3035 $ hg debugtemplate -v '{(123)}\n'
3050 $ hg debugtemplate -v '{(123)}\n'
3036 (template
3051 (template
3037 (group
3052 (group
3038 ('integer', '123'))
3053 ('integer', '123'))
3039 ('string', '\n'))
3054 ('string', '\n'))
3040 123
3055 123
3041 $ hg debugtemplate -v '{(-4)}\n'
3056 $ hg debugtemplate -v '{(-4)}\n'
3042 (template
3057 (template
3043 (group
3058 (group
3044 (negate
3059 (negate
3045 ('integer', '4')))
3060 ('integer', '4')))
3046 ('string', '\n'))
3061 ('string', '\n'))
3047 -4
3062 -4
3048 $ hg debugtemplate '{(-)}\n'
3063 $ hg debugtemplate '{(-)}\n'
3049 hg: parse error at 3: not a prefix: )
3064 hg: parse error at 3: not a prefix: )
3050 [255]
3065 [255]
3051 $ hg debugtemplate '{(-a)}\n'
3066 $ hg debugtemplate '{(-a)}\n'
3052 hg: parse error: negation needs an integer argument
3067 hg: parse error: negation needs an integer argument
3053 [255]
3068 [255]
3054
3069
3055 top-level integer literal is interpreted as symbol (i.e. variable name):
3070 top-level integer literal is interpreted as symbol (i.e. variable name):
3056
3071
3057 $ hg debugtemplate -D 1=one -v '{1}\n'
3072 $ hg debugtemplate -D 1=one -v '{1}\n'
3058 (template
3073 (template
3059 ('integer', '1')
3074 ('integer', '1')
3060 ('string', '\n'))
3075 ('string', '\n'))
3061 one
3076 one
3062 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3077 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3063 (template
3078 (template
3064 (func
3079 (func
3065 ('symbol', 'if')
3080 ('symbol', 'if')
3066 (list
3081 (list
3067 ('string', 't')
3082 ('string', 't')
3068 (template
3083 (template
3069 ('integer', '1'))))
3084 ('integer', '1'))))
3070 ('string', '\n'))
3085 ('string', '\n'))
3071 one
3086 one
3072 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3087 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3073 (template
3088 (template
3074 (|
3089 (|
3075 ('integer', '1')
3090 ('integer', '1')
3076 ('symbol', 'stringify'))
3091 ('symbol', 'stringify'))
3077 ('string', '\n'))
3092 ('string', '\n'))
3078 one
3093 one
3079
3094
3080 unless explicit symbol is expected:
3095 unless explicit symbol is expected:
3081
3096
3082 $ hg log -Ra -r0 -T '{desc|1}\n'
3097 $ hg log -Ra -r0 -T '{desc|1}\n'
3083 hg: parse error: expected a symbol, got 'integer'
3098 hg: parse error: expected a symbol, got 'integer'
3084 [255]
3099 [255]
3085 $ hg log -Ra -r0 -T '{1()}\n'
3100 $ hg log -Ra -r0 -T '{1()}\n'
3086 hg: parse error: expected a symbol, got 'integer'
3101 hg: parse error: expected a symbol, got 'integer'
3087 [255]
3102 [255]
3088
3103
3089 Test string literal:
3104 Test string literal:
3090
3105
3091 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3106 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3092 (template
3107 (template
3093 ('string', 'string with no template fragment')
3108 ('string', 'string with no template fragment')
3094 ('string', '\n'))
3109 ('string', '\n'))
3095 string with no template fragment
3110 string with no template fragment
3096 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3111 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3097 (template
3112 (template
3098 (template
3113 (template
3099 ('string', 'template: ')
3114 ('string', 'template: ')
3100 ('symbol', 'rev'))
3115 ('symbol', 'rev'))
3101 ('string', '\n'))
3116 ('string', '\n'))
3102 template: 0
3117 template: 0
3103 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3118 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3104 (template
3119 (template
3105 ('string', 'rawstring: {rev}')
3120 ('string', 'rawstring: {rev}')
3106 ('string', '\n'))
3121 ('string', '\n'))
3107 rawstring: {rev}
3122 rawstring: {rev}
3108 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3123 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3109 (template
3124 (template
3110 (%
3125 (%
3111 ('symbol', 'files')
3126 ('symbol', 'files')
3112 ('string', 'rawstring: {file}'))
3127 ('string', 'rawstring: {file}'))
3113 ('string', '\n'))
3128 ('string', '\n'))
3114 rawstring: {file}
3129 rawstring: {file}
3115
3130
3116 Test string escaping:
3131 Test string escaping:
3117
3132
3118 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3133 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3119 >
3134 >
3120 <>\n<[>
3135 <>\n<[>
3121 <>\n<]>
3136 <>\n<]>
3122 <>\n<
3137 <>\n<
3123
3138
3124 $ hg log -R latesttag -r 0 \
3139 $ hg log -R latesttag -r 0 \
3125 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3140 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3126 >
3141 >
3127 <>\n<[>
3142 <>\n<[>
3128 <>\n<]>
3143 <>\n<]>
3129 <>\n<
3144 <>\n<
3130
3145
3131 $ hg log -R latesttag -r 0 -T esc \
3146 $ hg log -R latesttag -r 0 -T esc \
3132 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3147 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3133 >
3148 >
3134 <>\n<[>
3149 <>\n<[>
3135 <>\n<]>
3150 <>\n<]>
3136 <>\n<
3151 <>\n<
3137
3152
3138 $ cat <<'EOF' > esctmpl
3153 $ cat <<'EOF' > esctmpl
3139 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3154 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3140 > EOF
3155 > EOF
3141 $ hg log -R latesttag -r 0 --style ./esctmpl
3156 $ hg log -R latesttag -r 0 --style ./esctmpl
3142 >
3157 >
3143 <>\n<[>
3158 <>\n<[>
3144 <>\n<]>
3159 <>\n<]>
3145 <>\n<
3160 <>\n<
3146
3161
3147 Test string escaping of quotes:
3162 Test string escaping of quotes:
3148
3163
3149 $ hg log -Ra -r0 -T '{"\""}\n'
3164 $ hg log -Ra -r0 -T '{"\""}\n'
3150 "
3165 "
3151 $ hg log -Ra -r0 -T '{"\\\""}\n'
3166 $ hg log -Ra -r0 -T '{"\\\""}\n'
3152 \"
3167 \"
3153 $ hg log -Ra -r0 -T '{r"\""}\n'
3168 $ hg log -Ra -r0 -T '{r"\""}\n'
3154 \"
3169 \"
3155 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3170 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3156 \\\"
3171 \\\"
3157
3172
3158
3173
3159 $ hg log -Ra -r0 -T '{"\""}\n'
3174 $ hg log -Ra -r0 -T '{"\""}\n'
3160 "
3175 "
3161 $ hg log -Ra -r0 -T '{"\\\""}\n'
3176 $ hg log -Ra -r0 -T '{"\\\""}\n'
3162 \"
3177 \"
3163 $ hg log -Ra -r0 -T '{r"\""}\n'
3178 $ hg log -Ra -r0 -T '{r"\""}\n'
3164 \"
3179 \"
3165 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3180 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3166 \\\"
3181 \\\"
3167
3182
3168 Test exception in quoted template. single backslash before quotation mark is
3183 Test exception in quoted template. single backslash before quotation mark is
3169 stripped before parsing:
3184 stripped before parsing:
3170
3185
3171 $ cat <<'EOF' > escquotetmpl
3186 $ cat <<'EOF' > escquotetmpl
3172 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3187 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3173 > EOF
3188 > EOF
3174 $ cd latesttag
3189 $ cd latesttag
3175 $ hg log -r 2 --style ../escquotetmpl
3190 $ hg log -r 2 --style ../escquotetmpl
3176 " \" \" \\" head1
3191 " \" \" \\" head1
3177
3192
3178 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3193 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3179 valid
3194 valid
3180 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3195 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3181 valid
3196 valid
3182
3197
3183 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3198 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3184 _evalifliteral() templates (issue4733):
3199 _evalifliteral() templates (issue4733):
3185
3200
3186 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3201 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3187 "2
3202 "2
3188 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3203 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3189 "2
3204 "2
3190 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3205 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3191 "2
3206 "2
3192
3207
3193 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3208 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3194 \"
3209 \"
3195 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3210 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3196 \"
3211 \"
3197 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3212 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3198 \"
3213 \"
3199
3214
3200 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3215 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3201 \\\"
3216 \\\"
3202 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3217 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3203 \\\"
3218 \\\"
3204 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3219 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3205 \\\"
3220 \\\"
3206
3221
3207 escaped single quotes and errors:
3222 escaped single quotes and errors:
3208
3223
3209 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3224 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3210 foo
3225 foo
3211 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3226 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3212 foo
3227 foo
3213 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3228 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3214 hg: parse error at 21: unterminated string
3229 hg: parse error at 21: unterminated string
3215 [255]
3230 [255]
3216 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3231 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3217 hg: parse error: trailing \ in string
3232 hg: parse error: trailing \ in string
3218 [255]
3233 [255]
3219 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3234 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3220 hg: parse error: trailing \ in string
3235 hg: parse error: trailing \ in string
3221 [255]
3236 [255]
3222
3237
3223 $ cd ..
3238 $ cd ..
3224
3239
3225 Test leading backslashes:
3240 Test leading backslashes:
3226
3241
3227 $ cd latesttag
3242 $ cd latesttag
3228 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3243 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3229 {rev} {file}
3244 {rev} {file}
3230 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3245 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3231 \2 \head1
3246 \2 \head1
3232 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3247 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3233 \{rev} \{file}
3248 \{rev} \{file}
3234 $ cd ..
3249 $ cd ..
3235
3250
3236 Test leading backslashes in "if" expression (issue4714):
3251 Test leading backslashes in "if" expression (issue4714):
3237
3252
3238 $ cd latesttag
3253 $ cd latesttag
3239 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3254 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3240 {rev} \{rev}
3255 {rev} \{rev}
3241 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3256 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3242 \2 \\{rev}
3257 \2 \\{rev}
3243 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3258 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3244 \{rev} \\\{rev}
3259 \{rev} \\\{rev}
3245 $ cd ..
3260 $ cd ..
3246
3261
3247 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3262 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3248
3263
3249 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3264 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3250 \x6e
3265 \x6e
3251 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3266 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3252 \x5c\x786e
3267 \x5c\x786e
3253 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3268 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3254 \x6e
3269 \x6e
3255 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3270 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3256 \x5c\x786e
3271 \x5c\x786e
3257
3272
3258 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3273 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3259 \x6e
3274 \x6e
3260 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3275 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3261 \x5c\x786e
3276 \x5c\x786e
3262 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3277 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3263 \x6e
3278 \x6e
3264 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3279 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3265 \x5c\x786e
3280 \x5c\x786e
3266
3281
3267 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3282 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3268 fourth
3283 fourth
3269 second
3284 second
3270 third
3285 third
3271 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3286 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3272 fourth\nsecond\nthird
3287 fourth\nsecond\nthird
3273
3288
3274 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3289 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3275 <p>
3290 <p>
3276 1st
3291 1st
3277 </p>
3292 </p>
3278 <p>
3293 <p>
3279 2nd
3294 2nd
3280 </p>
3295 </p>
3281 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3296 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3282 <p>
3297 <p>
3283 1st\n\n2nd
3298 1st\n\n2nd
3284 </p>
3299 </p>
3285 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3300 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3286 1st
3301 1st
3287
3302
3288 2nd
3303 2nd
3289
3304
3290 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3305 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3291 o perso
3306 o perso
3292 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3307 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3293 no person
3308 no person
3294 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3309 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3295 o perso
3310 o perso
3296 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3311 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3297 no perso
3312 no perso
3298
3313
3299 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3314 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3300 -o perso-
3315 -o perso-
3301 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3316 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3302 no person
3317 no person
3303 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3318 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3304 \x2do perso\x2d
3319 \x2do perso\x2d
3305 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3320 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3306 -o perso-
3321 -o perso-
3307 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3322 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3308 \x2do perso\x6e
3323 \x2do perso\x6e
3309
3324
3310 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3325 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3311 fourth
3326 fourth
3312 second
3327 second
3313 third
3328 third
3314
3329
3315 Test string escaping in nested expression:
3330 Test string escaping in nested expression:
3316
3331
3317 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3332 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3318 fourth\x6esecond\x6ethird
3333 fourth\x6esecond\x6ethird
3319 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3334 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3320 fourth\x6esecond\x6ethird
3335 fourth\x6esecond\x6ethird
3321
3336
3322 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3337 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3323 fourth\x6esecond\x6ethird
3338 fourth\x6esecond\x6ethird
3324 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3339 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3325 fourth\x5c\x786esecond\x5c\x786ethird
3340 fourth\x5c\x786esecond\x5c\x786ethird
3326
3341
3327 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3342 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3328 3:\x6eo user, \x6eo domai\x6e
3343 3:\x6eo user, \x6eo domai\x6e
3329 4:\x5c\x786eew bra\x5c\x786ech
3344 4:\x5c\x786eew bra\x5c\x786ech
3330
3345
3331 Test quotes in nested expression are evaluated just like a $(command)
3346 Test quotes in nested expression are evaluated just like a $(command)
3332 substitution in POSIX shells:
3347 substitution in POSIX shells:
3333
3348
3334 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3349 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3335 8:95c24699272e
3350 8:95c24699272e
3336 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3351 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3337 {8} "95c24699272e"
3352 {8} "95c24699272e"
3338
3353
3339 Test recursive evaluation:
3354 Test recursive evaluation:
3340
3355
3341 $ hg init r
3356 $ hg init r
3342 $ cd r
3357 $ cd r
3343 $ echo a > a
3358 $ echo a > a
3344 $ hg ci -Am '{rev}'
3359 $ hg ci -Am '{rev}'
3345 adding a
3360 adding a
3346 $ hg log -r 0 --template '{if(rev, desc)}\n'
3361 $ hg log -r 0 --template '{if(rev, desc)}\n'
3347 {rev}
3362 {rev}
3348 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3363 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3349 test 0
3364 test 0
3350
3365
3351 $ hg branch -q 'text.{rev}'
3366 $ hg branch -q 'text.{rev}'
3352 $ echo aa >> aa
3367 $ echo aa >> aa
3353 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3368 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3354
3369
3355 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3370 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3356 {node|short}desc to
3371 {node|short}desc to
3357 text.{rev}be wrapped
3372 text.{rev}be wrapped
3358 text.{rev}desc to be
3373 text.{rev}desc to be
3359 text.{rev}wrapped (no-eol)
3374 text.{rev}wrapped (no-eol)
3360 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3375 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3361 bcc7ff960b8e:desc to
3376 bcc7ff960b8e:desc to
3362 text.1:be wrapped
3377 text.1:be wrapped
3363 text.1:desc to be
3378 text.1:desc to be
3364 text.1:wrapped (no-eol)
3379 text.1:wrapped (no-eol)
3365 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3380 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3366 hg: parse error: fill expects an integer width
3381 hg: parse error: fill expects an integer width
3367 [255]
3382 [255]
3368
3383
3369 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3384 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3370 bcc7ff960b8e:desc to be
3385 bcc7ff960b8e:desc to be
3371 termwidth.1:wrapped desc
3386 termwidth.1:wrapped desc
3372 termwidth.1:to be wrapped (no-eol)
3387 termwidth.1:to be wrapped (no-eol)
3373
3388
3374 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3389 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3375 {node|short} (no-eol)
3390 {node|short} (no-eol)
3376 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3391 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3377 bcc-ff---b-e (no-eol)
3392 bcc-ff---b-e (no-eol)
3378
3393
3379 $ cat >> .hg/hgrc <<EOF
3394 $ cat >> .hg/hgrc <<EOF
3380 > [extensions]
3395 > [extensions]
3381 > color=
3396 > color=
3382 > [color]
3397 > [color]
3383 > mode=ansi
3398 > mode=ansi
3384 > text.{rev} = red
3399 > text.{rev} = red
3385 > text.1 = green
3400 > text.1 = green
3386 > EOF
3401 > EOF
3387 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3402 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3388 \x1b[0;31mtext\x1b[0m (esc)
3403 \x1b[0;31mtext\x1b[0m (esc)
3389 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3404 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3390 \x1b[0;32mtext\x1b[0m (esc)
3405 \x1b[0;32mtext\x1b[0m (esc)
3391
3406
3392 color effect can be specified without quoting:
3407 color effect can be specified without quoting:
3393
3408
3394 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3409 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3395 \x1b[0;31mtext\x1b[0m (esc)
3410 \x1b[0;31mtext\x1b[0m (esc)
3396
3411
3397 color effects can be nested (issue5413)
3412 color effects can be nested (issue5413)
3398
3413
3399 $ hg debugtemplate --color=always \
3414 $ hg debugtemplate --color=always \
3400 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3415 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3401 \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc)
3416 \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc)
3402
3417
3403 pad() should interact well with color codes (issue5416)
3418 pad() should interact well with color codes (issue5416)
3404
3419
3405 $ hg debugtemplate --color=always \
3420 $ hg debugtemplate --color=always \
3406 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3421 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3407 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3422 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3408
3423
3409 label should be no-op if color is disabled:
3424 label should be no-op if color is disabled:
3410
3425
3411 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3426 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3412 text
3427 text
3413 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3428 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3414 text
3429 text
3415
3430
3416 Test branches inside if statement:
3431 Test branches inside if statement:
3417
3432
3418 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3433 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3419 no
3434 no
3420
3435
3421 Test dict constructor:
3436 Test dict constructor:
3422
3437
3423 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3438 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3424 y=f7769ec2ab97 x=0
3439 y=f7769ec2ab97 x=0
3425 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3440 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3426 x=0
3441 x=0
3427 y=f7769ec2ab97
3442 y=f7769ec2ab97
3428 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3443 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3429 {"x": 0, "y": "f7769ec2ab97"}
3444 {"x": 0, "y": "f7769ec2ab97"}
3430 $ hg log -r 0 -T '{dict()|json}\n'
3445 $ hg log -r 0 -T '{dict()|json}\n'
3431 {}
3446 {}
3432
3447
3433 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3448 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3434 rev=0 node=f7769ec2ab97
3449 rev=0 node=f7769ec2ab97
3435 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3450 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3436 rev=0 node=f7769ec2ab97
3451 rev=0 node=f7769ec2ab97
3437
3452
3438 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3453 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3439 hg: parse error: duplicated dict key 'rev' inferred
3454 hg: parse error: duplicated dict key 'rev' inferred
3440 [255]
3455 [255]
3441 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3456 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3442 hg: parse error: duplicated dict key 'node' inferred
3457 hg: parse error: duplicated dict key 'node' inferred
3443 [255]
3458 [255]
3444 $ hg log -r 0 -T '{dict(1 + 2)}'
3459 $ hg log -r 0 -T '{dict(1 + 2)}'
3445 hg: parse error: dict key cannot be inferred
3460 hg: parse error: dict key cannot be inferred
3446 [255]
3461 [255]
3447
3462
3448 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3463 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3449 hg: parse error: dict got multiple values for keyword argument 'x'
3464 hg: parse error: dict got multiple values for keyword argument 'x'
3450 [255]
3465 [255]
3451
3466
3452 Test get function:
3467 Test get function:
3453
3468
3454 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3469 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3455 default
3470 default
3456 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3471 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3457 default
3472 default
3458 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3473 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3459 hg: parse error: get() expects a dict as first argument
3474 hg: parse error: get() expects a dict as first argument
3460 [255]
3475 [255]
3461
3476
3462 Test json filter applied to hybrid object:
3477 Test json filter applied to hybrid object:
3463
3478
3464 $ hg log -r0 -T '{files|json}\n'
3479 $ hg log -r0 -T '{files|json}\n'
3465 ["a"]
3480 ["a"]
3466 $ hg log -r0 -T '{extras|json}\n'
3481 $ hg log -r0 -T '{extras|json}\n'
3467 {"branch": "default"}
3482 {"branch": "default"}
3468
3483
3469 Test localdate(date, tz) function:
3484 Test localdate(date, tz) function:
3470
3485
3471 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3486 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3472 1970-01-01 09:00 +0900
3487 1970-01-01 09:00 +0900
3473 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3488 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3474 1970-01-01 00:00 +0000
3489 1970-01-01 00:00 +0000
3475 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3490 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3476 hg: parse error: localdate expects a timezone
3491 hg: parse error: localdate expects a timezone
3477 [255]
3492 [255]
3478 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3493 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3479 1970-01-01 02:00 +0200
3494 1970-01-01 02:00 +0200
3480 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3495 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3481 1970-01-01 00:00 +0000
3496 1970-01-01 00:00 +0000
3482 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3497 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3483 1970-01-01 00:00 +0000
3498 1970-01-01 00:00 +0000
3484 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3499 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3485 hg: parse error: localdate expects a timezone
3500 hg: parse error: localdate expects a timezone
3486 [255]
3501 [255]
3487 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3502 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3488 hg: parse error: localdate expects a timezone
3503 hg: parse error: localdate expects a timezone
3489 [255]
3504 [255]
3490
3505
3491 Test shortest(node) function:
3506 Test shortest(node) function:
3492
3507
3493 $ echo b > b
3508 $ echo b > b
3494 $ hg ci -qAm b
3509 $ hg ci -qAm b
3495 $ hg log --template '{shortest(node)}\n'
3510 $ hg log --template '{shortest(node)}\n'
3496 e777
3511 e777
3497 bcc7
3512 bcc7
3498 f776
3513 f776
3499 $ hg log --template '{shortest(node, 10)}\n'
3514 $ hg log --template '{shortest(node, 10)}\n'
3500 e777603221
3515 e777603221
3501 bcc7ff960b
3516 bcc7ff960b
3502 f7769ec2ab
3517 f7769ec2ab
3503 $ hg log --template '{node|shortest}\n' -l1
3518 $ hg log --template '{node|shortest}\n' -l1
3504 e777
3519 e777
3505
3520
3506 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3521 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3507 f7769ec2ab
3522 f7769ec2ab
3508 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3523 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3509 hg: parse error: shortest() expects an integer minlength
3524 hg: parse error: shortest() expects an integer minlength
3510 [255]
3525 [255]
3511
3526
3512 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3527 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3513 ffff
3528 ffff
3514
3529
3515 $ cd ..
3530 $ cd ..
3516
3531
3517 Test shortest(node) with the repo having short hash collision:
3532 Test shortest(node) with the repo having short hash collision:
3518
3533
3519 $ hg init hashcollision
3534 $ hg init hashcollision
3520 $ cd hashcollision
3535 $ cd hashcollision
3521 $ cat <<EOF >> .hg/hgrc
3536 $ cat <<EOF >> .hg/hgrc
3522 > [experimental]
3537 > [experimental]
3523 > evolution = createmarkers
3538 > evolution = createmarkers
3524 > EOF
3539 > EOF
3525 $ echo 0 > a
3540 $ echo 0 > a
3526 $ hg ci -qAm 0
3541 $ hg ci -qAm 0
3527 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3542 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3528 > hg up -q 0
3543 > hg up -q 0
3529 > echo $i > a
3544 > echo $i > a
3530 > hg ci -qm $i
3545 > hg ci -qm $i
3531 > done
3546 > done
3532 $ hg up -q null
3547 $ hg up -q null
3533 $ hg log -r0: -T '{rev}:{node}\n'
3548 $ hg log -r0: -T '{rev}:{node}\n'
3534 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3549 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3535 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3550 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3536 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3551 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3537 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3552 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3538 4:10776689e627b465361ad5c296a20a487e153ca4
3553 4:10776689e627b465361ad5c296a20a487e153ca4
3539 5:a00be79088084cb3aff086ab799f8790e01a976b
3554 5:a00be79088084cb3aff086ab799f8790e01a976b
3540 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3555 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3541 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3556 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3542 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3557 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3543 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3558 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3544 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3559 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3545 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3560 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3546 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3561 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3547 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3562 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3548
3563
3549 nodes starting with '11' (we don't have the revision number '11' though)
3564 nodes starting with '11' (we don't have the revision number '11' though)
3550
3565
3551 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3566 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3552 1:1142
3567 1:1142
3553 2:1140
3568 2:1140
3554 3:11d
3569 3:11d
3555
3570
3556 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3571 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3557
3572
3558 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3573 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3559 6:a0b
3574 6:a0b
3560 7:a04
3575 7:a04
3561
3576
3562 node '10' conflicts with the revision number '10' even if it is hidden
3577 node '10' conflicts with the revision number '10' even if it is hidden
3563 (we could exclude hidden revision numbers, but currently we don't)
3578 (we could exclude hidden revision numbers, but currently we don't)
3564
3579
3565 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3580 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3566 4:107
3581 4:107
3567 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3582 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3568 4:107
3583 4:107
3569
3584
3570 node 'c562' should be unique if the other 'c562' nodes are hidden
3585 node 'c562' should be unique if the other 'c562' nodes are hidden
3571 (but we don't try the slow path to filter out hidden nodes for now)
3586 (but we don't try the slow path to filter out hidden nodes for now)
3572
3587
3573 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
3588 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
3574 8:c5625
3589 8:c5625
3575 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
3590 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
3576 8:c5625
3591 8:c5625
3577 9:c5623
3592 9:c5623
3578 10:c562d
3593 10:c562d
3579
3594
3580 $ cd ..
3595 $ cd ..
3581
3596
3582 Test pad function
3597 Test pad function
3583
3598
3584 $ cd r
3599 $ cd r
3585
3600
3586 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3601 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3587 2 test
3602 2 test
3588 1 {node|short}
3603 1 {node|short}
3589 0 test
3604 0 test
3590
3605
3591 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3606 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3592 2 test
3607 2 test
3593 1 {node|short}
3608 1 {node|short}
3594 0 test
3609 0 test
3595
3610
3596 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3611 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3597 2------------------- test
3612 2------------------- test
3598 1------------------- {node|short}
3613 1------------------- {node|short}
3599 0------------------- test
3614 0------------------- test
3600
3615
3601 Test template string in pad function
3616 Test template string in pad function
3602
3617
3603 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3618 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3604 {0} test
3619 {0} test
3605
3620
3606 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3621 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3607 \{rev} test
3622 \{rev} test
3608
3623
3609 Test width argument passed to pad function
3624 Test width argument passed to pad function
3610
3625
3611 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
3626 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
3612 0 test
3627 0 test
3613 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
3628 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
3614 hg: parse error: pad() expects an integer width
3629 hg: parse error: pad() expects an integer width
3615 [255]
3630 [255]
3616
3631
3617 Test invalid fillchar passed to pad function
3632 Test invalid fillchar passed to pad function
3618
3633
3619 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
3634 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
3620 hg: parse error: pad() expects a single fill character
3635 hg: parse error: pad() expects a single fill character
3621 [255]
3636 [255]
3622 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
3637 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
3623 hg: parse error: pad() expects a single fill character
3638 hg: parse error: pad() expects a single fill character
3624 [255]
3639 [255]
3625
3640
3626 Test boolean argument passed to pad function
3641 Test boolean argument passed to pad function
3627
3642
3628 no crash
3643 no crash
3629
3644
3630 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
3645 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
3631 ---------0
3646 ---------0
3632
3647
3633 string/literal
3648 string/literal
3634
3649
3635 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
3650 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
3636 ---------0
3651 ---------0
3637 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
3652 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
3638 0---------
3653 0---------
3639 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
3654 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
3640 0---------
3655 0---------
3641
3656
3642 unknown keyword is evaluated to ''
3657 unknown keyword is evaluated to ''
3643
3658
3644 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
3659 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
3645 0---------
3660 0---------
3646
3661
3647 Test separate function
3662 Test separate function
3648
3663
3649 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
3664 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
3650 a-b-c
3665 a-b-c
3651 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
3666 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
3652 0:f7769ec2ab97 test default
3667 0:f7769ec2ab97 test default
3653 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
3668 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
3654 a \x1b[0;31mb\x1b[0m c d (esc)
3669 a \x1b[0;31mb\x1b[0m c d (esc)
3655
3670
3656 Test boolean expression/literal passed to if function
3671 Test boolean expression/literal passed to if function
3657
3672
3658 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
3673 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
3659 rev 0 is True
3674 rev 0 is True
3660 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
3675 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
3661 literal 0 is True as well
3676 literal 0 is True as well
3662 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
3677 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
3663 empty string is False
3678 empty string is False
3664 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
3679 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
3665 empty list is False
3680 empty list is False
3666 $ hg log -r 0 -T '{if(true, "true is True")}\n'
3681 $ hg log -r 0 -T '{if(true, "true is True")}\n'
3667 true is True
3682 true is True
3668 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
3683 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
3669 false is False
3684 false is False
3670 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
3685 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
3671 non-empty string is True
3686 non-empty string is True
3672
3687
3673 Test ifcontains function
3688 Test ifcontains function
3674
3689
3675 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3690 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3676 2 is in the string
3691 2 is in the string
3677 1 is not
3692 1 is not
3678 0 is in the string
3693 0 is in the string
3679
3694
3680 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
3695 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
3681 2 is in the string
3696 2 is in the string
3682 1 is not
3697 1 is not
3683 0 is in the string
3698 0 is in the string
3684
3699
3685 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3700 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3686 2 did not add a
3701 2 did not add a
3687 1 did not add a
3702 1 did not add a
3688 0 added a
3703 0 added a
3689
3704
3690 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
3705 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
3691 2 is parent of 1
3706 2 is parent of 1
3692 1
3707 1
3693 0
3708 0
3694
3709
3695 Test revset function
3710 Test revset function
3696
3711
3697 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3712 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3698 2 current rev
3713 2 current rev
3699 1 not current rev
3714 1 not current rev
3700 0 not current rev
3715 0 not current rev
3701
3716
3702 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3717 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3703 2 match rev
3718 2 match rev
3704 1 match rev
3719 1 match rev
3705 0 not match rev
3720 0 not match rev
3706
3721
3707 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3722 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3708 2 Parents: 1
3723 2 Parents: 1
3709 1 Parents: 0
3724 1 Parents: 0
3710 0 Parents:
3725 0 Parents:
3711
3726
3712 $ cat >> .hg/hgrc <<EOF
3727 $ cat >> .hg/hgrc <<EOF
3713 > [revsetalias]
3728 > [revsetalias]
3714 > myparents(\$1) = parents(\$1)
3729 > myparents(\$1) = parents(\$1)
3715 > EOF
3730 > EOF
3716 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
3731 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
3717 2 Parents: 1
3732 2 Parents: 1
3718 1 Parents: 0
3733 1 Parents: 0
3719 0 Parents:
3734 0 Parents:
3720
3735
3721 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
3736 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
3722 Rev: 2
3737 Rev: 2
3723 Ancestor: 0
3738 Ancestor: 0
3724 Ancestor: 1
3739 Ancestor: 1
3725 Ancestor: 2
3740 Ancestor: 2
3726
3741
3727 Rev: 1
3742 Rev: 1
3728 Ancestor: 0
3743 Ancestor: 0
3729 Ancestor: 1
3744 Ancestor: 1
3730
3745
3731 Rev: 0
3746 Rev: 0
3732 Ancestor: 0
3747 Ancestor: 0
3733
3748
3734 $ hg log --template '{revset("TIP"|lower)}\n' -l1
3749 $ hg log --template '{revset("TIP"|lower)}\n' -l1
3735 2
3750 2
3736
3751
3737 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
3752 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
3738 2
3753 2
3739
3754
3740 a list template is evaluated for each item of revset/parents
3755 a list template is evaluated for each item of revset/parents
3741
3756
3742 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
3757 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
3743 2 p: 1:bcc7ff960b8e
3758 2 p: 1:bcc7ff960b8e
3744 1 p: 0:f7769ec2ab97
3759 1 p: 0:f7769ec2ab97
3745 0 p:
3760 0 p:
3746
3761
3747 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
3762 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
3748 2 p: 1:bcc7ff960b8e -1:000000000000
3763 2 p: 1:bcc7ff960b8e -1:000000000000
3749 1 p: 0:f7769ec2ab97 -1:000000000000
3764 1 p: 0:f7769ec2ab97 -1:000000000000
3750 0 p: -1:000000000000 -1:000000000000
3765 0 p: -1:000000000000 -1:000000000000
3751
3766
3752 therefore, 'revcache' should be recreated for each rev
3767 therefore, 'revcache' should be recreated for each rev
3753
3768
3754 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
3769 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
3755 2 aa b
3770 2 aa b
3756 p
3771 p
3757 1
3772 1
3758 p a
3773 p a
3759 0 a
3774 0 a
3760 p
3775 p
3761
3776
3762 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
3777 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
3763 2 aa b
3778 2 aa b
3764 p
3779 p
3765 1
3780 1
3766 p a
3781 p a
3767 0 a
3782 0 a
3768 p
3783 p
3769
3784
3770 a revset item must be evaluated as an integer revision, not an offset from tip
3785 a revset item must be evaluated as an integer revision, not an offset from tip
3771
3786
3772 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
3787 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
3773 -1:000000000000
3788 -1:000000000000
3774 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
3789 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
3775 -1:000000000000
3790 -1:000000000000
3776
3791
3777 join() should pick '{rev}' from revset items:
3792 join() should pick '{rev}' from revset items:
3778
3793
3779 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
3794 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
3780 4, 5
3795 4, 5
3781
3796
3782 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
3797 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
3783 default. join() should agree with the default formatting:
3798 default. join() should agree with the default formatting:
3784
3799
3785 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
3800 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
3786 5:13207e5a10d9, 4:bbe44766e73d
3801 5:13207e5a10d9, 4:bbe44766e73d
3787
3802
3788 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
3803 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
3789 5:13207e5a10d9fd28ec424934298e176197f2c67f,
3804 5:13207e5a10d9fd28ec424934298e176197f2c67f,
3790 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
3805 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
3791
3806
3792 Test files function
3807 Test files function
3793
3808
3794 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
3809 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
3795 2
3810 2
3796 a
3811 a
3797 aa
3812 aa
3798 b
3813 b
3799 1
3814 1
3800 a
3815 a
3801 0
3816 0
3802 a
3817 a
3803
3818
3804 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
3819 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
3805 2
3820 2
3806 aa
3821 aa
3807 1
3822 1
3808
3823
3809 0
3824 0
3810
3825
3811
3826
3812 Test relpath function
3827 Test relpath function
3813
3828
3814 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
3829 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
3815 a
3830 a
3816 $ cd ..
3831 $ cd ..
3817 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
3832 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
3818 r/a
3833 r/a
3819 $ cd r
3834 $ cd r
3820
3835
3821 Test active bookmark templating
3836 Test active bookmark templating
3822
3837
3823 $ hg book foo
3838 $ hg book foo
3824 $ hg book bar
3839 $ hg book bar
3825 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
3840 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
3826 2 bar* foo
3841 2 bar* foo
3827 1
3842 1
3828 0
3843 0
3829 $ hg log --template "{rev} {activebookmark}\n"
3844 $ hg log --template "{rev} {activebookmark}\n"
3830 2 bar
3845 2 bar
3831 1
3846 1
3832 0
3847 0
3833 $ hg bookmarks --inactive bar
3848 $ hg bookmarks --inactive bar
3834 $ hg log --template "{rev} {activebookmark}\n"
3849 $ hg log --template "{rev} {activebookmark}\n"
3835 2
3850 2
3836 1
3851 1
3837 0
3852 0
3838 $ hg book -r1 baz
3853 $ hg book -r1 baz
3839 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
3854 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
3840 2 bar foo
3855 2 bar foo
3841 1 baz
3856 1 baz
3842 0
3857 0
3843 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
3858 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
3844 2 t
3859 2 t
3845 1 f
3860 1 f
3846 0 f
3861 0 f
3847
3862
3848 Test namespaces dict
3863 Test namespaces dict
3849
3864
3850 $ hg log -T '{rev}{namespaces % " {namespace}={join(names, ",")}"}\n'
3865 $ hg log -T '{rev}{namespaces % " {namespace}={join(names, ",")}"}\n'
3851 2 bookmarks=bar,foo tags=tip branches=text.{rev}
3866 2 bookmarks=bar,foo tags=tip branches=text.{rev}
3852 1 bookmarks=baz tags= branches=text.{rev}
3867 1 bookmarks=baz tags= branches=text.{rev}
3853 0 bookmarks= tags= branches=default
3868 0 bookmarks= tags= branches=default
3854 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
3869 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
3855 bookmarks: bar foo
3870 bookmarks: bar foo
3856 tags: tip
3871 tags: tip
3857 branches: text.{rev}
3872 branches: text.{rev}
3858 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
3873 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
3859 bookmarks:
3874 bookmarks:
3860 bar
3875 bar
3861 foo
3876 foo
3862 tags:
3877 tags:
3863 tip
3878 tip
3864 branches:
3879 branches:
3865 text.{rev}
3880 text.{rev}
3866 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
3881 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
3867 bar
3882 bar
3868 foo
3883 foo
3869
3884
3870 Test stringify on sub expressions
3885 Test stringify on sub expressions
3871
3886
3872 $ cd ..
3887 $ cd ..
3873 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
3888 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
3874 fourth, second, third
3889 fourth, second, third
3875 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
3890 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
3876 abc
3891 abc
3877
3892
3878 Test splitlines
3893 Test splitlines
3879
3894
3880 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
3895 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
3881 @ foo Modify, add, remove, rename
3896 @ foo Modify, add, remove, rename
3882 |
3897 |
3883 o foo future
3898 o foo future
3884 |
3899 |
3885 o foo third
3900 o foo third
3886 |
3901 |
3887 o foo second
3902 o foo second
3888
3903
3889 o foo merge
3904 o foo merge
3890 |\
3905 |\
3891 | o foo new head
3906 | o foo new head
3892 | |
3907 | |
3893 o | foo new branch
3908 o | foo new branch
3894 |/
3909 |/
3895 o foo no user, no domain
3910 o foo no user, no domain
3896 |
3911 |
3897 o foo no person
3912 o foo no person
3898 |
3913 |
3899 o foo other 1
3914 o foo other 1
3900 | foo other 2
3915 | foo other 2
3901 | foo
3916 | foo
3902 | foo other 3
3917 | foo other 3
3903 o foo line 1
3918 o foo line 1
3904 foo line 2
3919 foo line 2
3905
3920
3906 $ hg log -R a -r0 -T '{desc|splitlines}\n'
3921 $ hg log -R a -r0 -T '{desc|splitlines}\n'
3907 line 1 line 2
3922 line 1 line 2
3908 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
3923 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
3909 line 1|line 2
3924 line 1|line 2
3910
3925
3911 Test startswith
3926 Test startswith
3912 $ hg log -Gv -R a --template "{startswith(desc)}"
3927 $ hg log -Gv -R a --template "{startswith(desc)}"
3913 hg: parse error: startswith expects two arguments
3928 hg: parse error: startswith expects two arguments
3914 [255]
3929 [255]
3915
3930
3916 $ hg log -Gv -R a --template "{startswith('line', desc)}"
3931 $ hg log -Gv -R a --template "{startswith('line', desc)}"
3917 @
3932 @
3918 |
3933 |
3919 o
3934 o
3920 |
3935 |
3921 o
3936 o
3922 |
3937 |
3923 o
3938 o
3924
3939
3925 o
3940 o
3926 |\
3941 |\
3927 | o
3942 | o
3928 | |
3943 | |
3929 o |
3944 o |
3930 |/
3945 |/
3931 o
3946 o
3932 |
3947 |
3933 o
3948 o
3934 |
3949 |
3935 o
3950 o
3936 |
3951 |
3937 o line 1
3952 o line 1
3938 line 2
3953 line 2
3939
3954
3940 Test bad template with better error message
3955 Test bad template with better error message
3941
3956
3942 $ hg log -Gv -R a --template '{desc|user()}'
3957 $ hg log -Gv -R a --template '{desc|user()}'
3943 hg: parse error: expected a symbol, got 'func'
3958 hg: parse error: expected a symbol, got 'func'
3944 [255]
3959 [255]
3945
3960
3946 Test word function (including index out of bounds graceful failure)
3961 Test word function (including index out of bounds graceful failure)
3947
3962
3948 $ hg log -Gv -R a --template "{word('1', desc)}"
3963 $ hg log -Gv -R a --template "{word('1', desc)}"
3949 @ add,
3964 @ add,
3950 |
3965 |
3951 o
3966 o
3952 |
3967 |
3953 o
3968 o
3954 |
3969 |
3955 o
3970 o
3956
3971
3957 o
3972 o
3958 |\
3973 |\
3959 | o head
3974 | o head
3960 | |
3975 | |
3961 o | branch
3976 o | branch
3962 |/
3977 |/
3963 o user,
3978 o user,
3964 |
3979 |
3965 o person
3980 o person
3966 |
3981 |
3967 o 1
3982 o 1
3968 |
3983 |
3969 o 1
3984 o 1
3970
3985
3971
3986
3972 Test word third parameter used as splitter
3987 Test word third parameter used as splitter
3973
3988
3974 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
3989 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
3975 @ M
3990 @ M
3976 |
3991 |
3977 o future
3992 o future
3978 |
3993 |
3979 o third
3994 o third
3980 |
3995 |
3981 o sec
3996 o sec
3982
3997
3983 o merge
3998 o merge
3984 |\
3999 |\
3985 | o new head
4000 | o new head
3986 | |
4001 | |
3987 o | new branch
4002 o | new branch
3988 |/
4003 |/
3989 o n
4004 o n
3990 |
4005 |
3991 o n
4006 o n
3992 |
4007 |
3993 o
4008 o
3994 |
4009 |
3995 o line 1
4010 o line 1
3996 line 2
4011 line 2
3997
4012
3998 Test word error messages for not enough and too many arguments
4013 Test word error messages for not enough and too many arguments
3999
4014
4000 $ hg log -Gv -R a --template "{word('0')}"
4015 $ hg log -Gv -R a --template "{word('0')}"
4001 hg: parse error: word expects two or three arguments, got 1
4016 hg: parse error: word expects two or three arguments, got 1
4002 [255]
4017 [255]
4003
4018
4004 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4019 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4005 hg: parse error: word expects two or three arguments, got 7
4020 hg: parse error: word expects two or three arguments, got 7
4006 [255]
4021 [255]
4007
4022
4008 Test word for integer literal
4023 Test word for integer literal
4009
4024
4010 $ hg log -R a --template "{word(2, desc)}\n" -r0
4025 $ hg log -R a --template "{word(2, desc)}\n" -r0
4011 line
4026 line
4012
4027
4013 Test word for invalid numbers
4028 Test word for invalid numbers
4014
4029
4015 $ hg log -Gv -R a --template "{word('a', desc)}"
4030 $ hg log -Gv -R a --template "{word('a', desc)}"
4016 hg: parse error: word expects an integer index
4031 hg: parse error: word expects an integer index
4017 [255]
4032 [255]
4018
4033
4019 Test word for out of range
4034 Test word for out of range
4020
4035
4021 $ hg log -R a --template "{word(10000, desc)}"
4036 $ hg log -R a --template "{word(10000, desc)}"
4022 $ hg log -R a --template "{word(-10000, desc)}"
4037 $ hg log -R a --template "{word(-10000, desc)}"
4023
4038
4024 Test indent and not adding to empty lines
4039 Test indent and not adding to empty lines
4025
4040
4026 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4041 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4027 -----
4042 -----
4028 > line 1
4043 > line 1
4029 >> line 2
4044 >> line 2
4030 -----
4045 -----
4031 > other 1
4046 > other 1
4032 >> other 2
4047 >> other 2
4033
4048
4034 >> other 3
4049 >> other 3
4035
4050
4036 Test with non-strings like dates
4051 Test with non-strings like dates
4037
4052
4038 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4053 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4039 1200000.00
4054 1200000.00
4040 1300000.00
4055 1300000.00
4041
4056
4042 Test broken string escapes:
4057 Test broken string escapes:
4043
4058
4044 $ hg log -T "bogus\\" -R a
4059 $ hg log -T "bogus\\" -R a
4045 hg: parse error: trailing \ in string
4060 hg: parse error: trailing \ in string
4046 [255]
4061 [255]
4047 $ hg log -T "\\xy" -R a
4062 $ hg log -T "\\xy" -R a
4048 hg: parse error: invalid \x escape
4063 hg: parse error: invalid \x escape
4049 [255]
4064 [255]
4050
4065
4051 json filter should escape HTML tags so that the output can be embedded in hgweb:
4066 json filter should escape HTML tags so that the output can be embedded in hgweb:
4052
4067
4053 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4068 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4054 "\u003cfoo@example.org\u003e"
4069 "\u003cfoo@example.org\u003e"
4055
4070
4056 Templater supports aliases of symbol and func() styles:
4071 Templater supports aliases of symbol and func() styles:
4057
4072
4058 $ hg clone -q a aliases
4073 $ hg clone -q a aliases
4059 $ cd aliases
4074 $ cd aliases
4060 $ cat <<EOF >> .hg/hgrc
4075 $ cat <<EOF >> .hg/hgrc
4061 > [templatealias]
4076 > [templatealias]
4062 > r = rev
4077 > r = rev
4063 > rn = "{r}:{node|short}"
4078 > rn = "{r}:{node|short}"
4064 > status(c, files) = files % "{c} {file}\n"
4079 > status(c, files) = files % "{c} {file}\n"
4065 > utcdate(d) = localdate(d, "UTC")
4080 > utcdate(d) = localdate(d, "UTC")
4066 > EOF
4081 > EOF
4067
4082
4068 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4083 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4069 (template
4084 (template
4070 ('symbol', 'rn')
4085 ('symbol', 'rn')
4071 ('string', ' ')
4086 ('string', ' ')
4072 (|
4087 (|
4073 (func
4088 (func
4074 ('symbol', 'utcdate')
4089 ('symbol', 'utcdate')
4075 ('symbol', 'date'))
4090 ('symbol', 'date'))
4076 ('symbol', 'isodate'))
4091 ('symbol', 'isodate'))
4077 ('string', '\n'))
4092 ('string', '\n'))
4078 * expanded:
4093 * expanded:
4079 (template
4094 (template
4080 (template
4095 (template
4081 ('symbol', 'rev')
4096 ('symbol', 'rev')
4082 ('string', ':')
4097 ('string', ':')
4083 (|
4098 (|
4084 ('symbol', 'node')
4099 ('symbol', 'node')
4085 ('symbol', 'short')))
4100 ('symbol', 'short')))
4086 ('string', ' ')
4101 ('string', ' ')
4087 (|
4102 (|
4088 (func
4103 (func
4089 ('symbol', 'localdate')
4104 ('symbol', 'localdate')
4090 (list
4105 (list
4091 ('symbol', 'date')
4106 ('symbol', 'date')
4092 ('string', 'UTC')))
4107 ('string', 'UTC')))
4093 ('symbol', 'isodate'))
4108 ('symbol', 'isodate'))
4094 ('string', '\n'))
4109 ('string', '\n'))
4095 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4110 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4096
4111
4097 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4112 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4098 (template
4113 (template
4099 (func
4114 (func
4100 ('symbol', 'status')
4115 ('symbol', 'status')
4101 (list
4116 (list
4102 ('string', 'A')
4117 ('string', 'A')
4103 ('symbol', 'file_adds'))))
4118 ('symbol', 'file_adds'))))
4104 * expanded:
4119 * expanded:
4105 (template
4120 (template
4106 (%
4121 (%
4107 ('symbol', 'file_adds')
4122 ('symbol', 'file_adds')
4108 (template
4123 (template
4109 ('string', 'A')
4124 ('string', 'A')
4110 ('string', ' ')
4125 ('string', ' ')
4111 ('symbol', 'file')
4126 ('symbol', 'file')
4112 ('string', '\n'))))
4127 ('string', '\n'))))
4113 A a
4128 A a
4114
4129
4115 A unary function alias can be called as a filter:
4130 A unary function alias can be called as a filter:
4116
4131
4117 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4132 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4118 (template
4133 (template
4119 (|
4134 (|
4120 (|
4135 (|
4121 ('symbol', 'date')
4136 ('symbol', 'date')
4122 ('symbol', 'utcdate'))
4137 ('symbol', 'utcdate'))
4123 ('symbol', 'isodate'))
4138 ('symbol', 'isodate'))
4124 ('string', '\n'))
4139 ('string', '\n'))
4125 * expanded:
4140 * expanded:
4126 (template
4141 (template
4127 (|
4142 (|
4128 (func
4143 (func
4129 ('symbol', 'localdate')
4144 ('symbol', 'localdate')
4130 (list
4145 (list
4131 ('symbol', 'date')
4146 ('symbol', 'date')
4132 ('string', 'UTC')))
4147 ('string', 'UTC')))
4133 ('symbol', 'isodate'))
4148 ('symbol', 'isodate'))
4134 ('string', '\n'))
4149 ('string', '\n'))
4135 1970-01-12 13:46 +0000
4150 1970-01-12 13:46 +0000
4136
4151
4137 Aliases should be applied only to command arguments and templates in hgrc.
4152 Aliases should be applied only to command arguments and templates in hgrc.
4138 Otherwise, our stock styles and web templates could be corrupted:
4153 Otherwise, our stock styles and web templates could be corrupted:
4139
4154
4140 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4155 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4141 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4156 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4142
4157
4143 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4158 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4144 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4159 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4145
4160
4146 $ cat <<EOF > tmpl
4161 $ cat <<EOF > tmpl
4147 > changeset = 'nothing expanded:{rn}\n'
4162 > changeset = 'nothing expanded:{rn}\n'
4148 > EOF
4163 > EOF
4149 $ hg log -r0 --style ./tmpl
4164 $ hg log -r0 --style ./tmpl
4150 nothing expanded:
4165 nothing expanded:
4151
4166
4152 Aliases in formatter:
4167 Aliases in formatter:
4153
4168
4154 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4169 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4155 default 6:d41e714fe50d
4170 default 6:d41e714fe50d
4156 foo 4:bbe44766e73d
4171 foo 4:bbe44766e73d
4157
4172
4158 Aliases should honor HGPLAIN:
4173 Aliases should honor HGPLAIN:
4159
4174
4160 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4175 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4161 nothing expanded:
4176 nothing expanded:
4162 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4177 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4163 0:1e4e1b8f71e0
4178 0:1e4e1b8f71e0
4164
4179
4165 Unparsable alias:
4180 Unparsable alias:
4166
4181
4167 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4182 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4168 (template
4183 (template
4169 ('symbol', 'bad'))
4184 ('symbol', 'bad'))
4170 abort: bad definition of template alias "bad": at 2: not a prefix: end
4185 abort: bad definition of template alias "bad": at 2: not a prefix: end
4171 [255]
4186 [255]
4172 $ hg log --config templatealias.bad='x(' -T '{bad}'
4187 $ hg log --config templatealias.bad='x(' -T '{bad}'
4173 abort: bad definition of template alias "bad": at 2: not a prefix: end
4188 abort: bad definition of template alias "bad": at 2: not a prefix: end
4174 [255]
4189 [255]
4175
4190
4176 $ cd ..
4191 $ cd ..
4177
4192
4178 Set up repository for non-ascii encoding tests:
4193 Set up repository for non-ascii encoding tests:
4179
4194
4180 $ hg init nonascii
4195 $ hg init nonascii
4181 $ cd nonascii
4196 $ cd nonascii
4182 $ python <<EOF
4197 $ python <<EOF
4183 > open('latin1', 'w').write('\xe9')
4198 > open('latin1', 'w').write('\xe9')
4184 > open('utf-8', 'w').write('\xc3\xa9')
4199 > open('utf-8', 'w').write('\xc3\xa9')
4185 > EOF
4200 > EOF
4186 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4201 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4187 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4202 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4188
4203
4189 json filter should try round-trip conversion to utf-8:
4204 json filter should try round-trip conversion to utf-8:
4190
4205
4191 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4206 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4192 "\u00e9"
4207 "\u00e9"
4193 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4208 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4194 "non-ascii branch: \u00e9"
4209 "non-ascii branch: \u00e9"
4195
4210
4196 json filter takes input as utf-8b:
4211 json filter takes input as utf-8b:
4197
4212
4198 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4213 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4199 "\u00e9"
4214 "\u00e9"
4200 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4215 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4201 "\udce9"
4216 "\udce9"
4202
4217
4203 utf8 filter:
4218 utf8 filter:
4204
4219
4205 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4220 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4206 round-trip: c3a9
4221 round-trip: c3a9
4207 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4222 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4208 decoded: c3a9
4223 decoded: c3a9
4209 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4224 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4210 abort: decoding near * (glob)
4225 abort: decoding near * (glob)
4211 [255]
4226 [255]
4212 $ hg log -T "invalid type: {rev|utf8}\n" -r0
4227 $ hg log -T "invalid type: {rev|utf8}\n" -r0
4213 abort: template filter 'utf8' is not compatible with keyword 'rev'
4228 abort: template filter 'utf8' is not compatible with keyword 'rev'
4214 [255]
4229 [255]
4215
4230
4216 pad width:
4231 pad width:
4217
4232
4218 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4233 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4219 \xc3\xa9- (esc)
4234 \xc3\xa9- (esc)
4220
4235
4221 $ cd ..
4236 $ cd ..
4222
4237
4223 Test that template function in extension is registered as expected
4238 Test that template function in extension is registered as expected
4224
4239
4225 $ cd a
4240 $ cd a
4226
4241
4227 $ cat <<EOF > $TESTTMP/customfunc.py
4242 $ cat <<EOF > $TESTTMP/customfunc.py
4228 > from mercurial import registrar
4243 > from mercurial import registrar
4229 >
4244 >
4230 > templatefunc = registrar.templatefunc()
4245 > templatefunc = registrar.templatefunc()
4231 >
4246 >
4232 > @templatefunc('custom()')
4247 > @templatefunc('custom()')
4233 > def custom(context, mapping, args):
4248 > def custom(context, mapping, args):
4234 > return 'custom'
4249 > return 'custom'
4235 > EOF
4250 > EOF
4236 $ cat <<EOF > .hg/hgrc
4251 $ cat <<EOF > .hg/hgrc
4237 > [extensions]
4252 > [extensions]
4238 > customfunc = $TESTTMP/customfunc.py
4253 > customfunc = $TESTTMP/customfunc.py
4239 > EOF
4254 > EOF
4240
4255
4241 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4256 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4242 custom
4257 custom
4243
4258
4244 $ cd ..
4259 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now