##// END OF EJS Templates
scmutil: make shortesthexnodeidprefix() take a full binary nodeid...
Martin von Zweigbergk -
r37963:7b295562 default
parent child Browse files
Show More
@@ -1,470 +1,469 b''
1 # show.py - Extension implementing `hg show`
1 # show.py - Extension implementing `hg show`
2 #
2 #
3 # Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2017 Gregory Szorc <gregory.szorc@gmail.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 """unified command to show various repository information (EXPERIMENTAL)
8 """unified command to show various repository information (EXPERIMENTAL)
9
9
10 This extension provides the :hg:`show` command, which provides a central
10 This extension provides the :hg:`show` command, which provides a central
11 command for displaying commonly-accessed repository data and views of that
11 command for displaying commonly-accessed repository data and views of that
12 data.
12 data.
13
13
14 The following config options can influence operation.
14 The following config options can influence operation.
15
15
16 ``commands``
16 ``commands``
17 ------------
17 ------------
18
18
19 ``show.aliasprefix``
19 ``show.aliasprefix``
20 List of strings that will register aliases for views. e.g. ``s`` will
20 List of strings that will register aliases for views. e.g. ``s`` will
21 effectively set config options ``alias.s<view> = show <view>`` for all
21 effectively set config options ``alias.s<view> = show <view>`` for all
22 views. i.e. `hg swork` would execute `hg show work`.
22 views. i.e. `hg swork` would execute `hg show work`.
23
23
24 Aliases that would conflict with existing registrations will not be
24 Aliases that would conflict with existing registrations will not be
25 performed.
25 performed.
26 """
26 """
27
27
28 from __future__ import absolute_import
28 from __future__ import absolute_import
29
29
30 from mercurial.i18n import _
30 from mercurial.i18n import _
31 from mercurial.node import (
31 from mercurial.node import (
32 hex,
33 nullrev,
32 nullrev,
34 )
33 )
35 from mercurial import (
34 from mercurial import (
36 cmdutil,
35 cmdutil,
37 commands,
36 commands,
38 destutil,
37 destutil,
39 error,
38 error,
40 formatter,
39 formatter,
41 graphmod,
40 graphmod,
42 logcmdutil,
41 logcmdutil,
43 phases,
42 phases,
44 pycompat,
43 pycompat,
45 registrar,
44 registrar,
46 revset,
45 revset,
47 revsetlang,
46 revsetlang,
48 scmutil,
47 scmutil,
49 )
48 )
50
49
51 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
50 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
52 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
51 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
53 # be specifying the version(s) of Mercurial they are tested with, or
52 # be specifying the version(s) of Mercurial they are tested with, or
54 # leave the attribute unspecified.
53 # leave the attribute unspecified.
55 testedwith = 'ships-with-hg-core'
54 testedwith = 'ships-with-hg-core'
56
55
57 cmdtable = {}
56 cmdtable = {}
58 command = registrar.command(cmdtable)
57 command = registrar.command(cmdtable)
59
58
60 revsetpredicate = registrar.revsetpredicate()
59 revsetpredicate = registrar.revsetpredicate()
61
60
62 class showcmdfunc(registrar._funcregistrarbase):
61 class showcmdfunc(registrar._funcregistrarbase):
63 """Register a function to be invoked for an `hg show <thing>`."""
62 """Register a function to be invoked for an `hg show <thing>`."""
64
63
65 # Used by _formatdoc().
64 # Used by _formatdoc().
66 _docformat = '%s -- %s'
65 _docformat = '%s -- %s'
67
66
68 def _extrasetup(self, name, func, fmtopic=None, csettopic=None):
67 def _extrasetup(self, name, func, fmtopic=None, csettopic=None):
69 """Called with decorator arguments to register a show view.
68 """Called with decorator arguments to register a show view.
70
69
71 ``name`` is the sub-command name.
70 ``name`` is the sub-command name.
72
71
73 ``func`` is the function being decorated.
72 ``func`` is the function being decorated.
74
73
75 ``fmtopic`` is the topic in the style that will be rendered for
74 ``fmtopic`` is the topic in the style that will be rendered for
76 this view.
75 this view.
77
76
78 ``csettopic`` is the topic in the style to be used for a changeset
77 ``csettopic`` is the topic in the style to be used for a changeset
79 printer.
78 printer.
80
79
81 If ``fmtopic`` is specified, the view function will receive a
80 If ``fmtopic`` is specified, the view function will receive a
82 formatter instance. If ``csettopic`` is specified, the view
81 formatter instance. If ``csettopic`` is specified, the view
83 function will receive a changeset printer.
82 function will receive a changeset printer.
84 """
83 """
85 func._fmtopic = fmtopic
84 func._fmtopic = fmtopic
86 func._csettopic = csettopic
85 func._csettopic = csettopic
87
86
88 showview = showcmdfunc()
87 showview = showcmdfunc()
89
88
90 @command('show', [
89 @command('show', [
91 # TODO: Switch this template flag to use cmdutil.formatteropts if
90 # TODO: Switch this template flag to use cmdutil.formatteropts if
92 # 'hg show' becomes stable before --template/-T is stable. For now,
91 # 'hg show' becomes stable before --template/-T is stable. For now,
93 # we are putting it here without the '(EXPERIMENTAL)' flag because it
92 # we are putting it here without the '(EXPERIMENTAL)' flag because it
94 # is an important part of the 'hg show' user experience and the entire
93 # is an important part of the 'hg show' user experience and the entire
95 # 'hg show' experience is experimental.
94 # 'hg show' experience is experimental.
96 ('T', 'template', '', ('display with template'), _('TEMPLATE')),
95 ('T', 'template', '', ('display with template'), _('TEMPLATE')),
97 ], _('VIEW'))
96 ], _('VIEW'))
98 def show(ui, repo, view=None, template=None):
97 def show(ui, repo, view=None, template=None):
99 """show various repository information
98 """show various repository information
100
99
101 A requested view of repository data is displayed.
100 A requested view of repository data is displayed.
102
101
103 If no view is requested, the list of available views is shown and the
102 If no view is requested, the list of available views is shown and the
104 command aborts.
103 command aborts.
105
104
106 .. note::
105 .. note::
107
106
108 There are no backwards compatibility guarantees for the output of this
107 There are no backwards compatibility guarantees for the output of this
109 command. Output may change in any future Mercurial release.
108 command. Output may change in any future Mercurial release.
110
109
111 Consumers wanting stable command output should specify a template via
110 Consumers wanting stable command output should specify a template via
112 ``-T/--template``.
111 ``-T/--template``.
113
112
114 List of available views:
113 List of available views:
115 """
114 """
116 if ui.plain() and not template:
115 if ui.plain() and not template:
117 hint = _('invoke with -T/--template to control output format')
116 hint = _('invoke with -T/--template to control output format')
118 raise error.Abort(_('must specify a template in plain mode'), hint=hint)
117 raise error.Abort(_('must specify a template in plain mode'), hint=hint)
119
118
120 views = showview._table
119 views = showview._table
121
120
122 if not view:
121 if not view:
123 ui.pager('show')
122 ui.pager('show')
124 # TODO consider using formatter here so available views can be
123 # TODO consider using formatter here so available views can be
125 # rendered to custom format.
124 # rendered to custom format.
126 ui.write(_('available views:\n'))
125 ui.write(_('available views:\n'))
127 ui.write('\n')
126 ui.write('\n')
128
127
129 for name, func in sorted(views.items()):
128 for name, func in sorted(views.items()):
130 ui.write(('%s\n') % pycompat.sysbytes(func.__doc__))
129 ui.write(('%s\n') % pycompat.sysbytes(func.__doc__))
131
130
132 ui.write('\n')
131 ui.write('\n')
133 raise error.Abort(_('no view requested'),
132 raise error.Abort(_('no view requested'),
134 hint=_('use "hg show VIEW" to choose a view'))
133 hint=_('use "hg show VIEW" to choose a view'))
135
134
136 # TODO use same logic as dispatch to perform prefix matching.
135 # TODO use same logic as dispatch to perform prefix matching.
137 if view not in views:
136 if view not in views:
138 raise error.Abort(_('unknown view: %s') % view,
137 raise error.Abort(_('unknown view: %s') % view,
139 hint=_('run "hg show" to see available views'))
138 hint=_('run "hg show" to see available views'))
140
139
141 template = template or 'show'
140 template = template or 'show'
142
141
143 fn = views[view]
142 fn = views[view]
144 ui.pager('show')
143 ui.pager('show')
145
144
146 if fn._fmtopic:
145 if fn._fmtopic:
147 fmtopic = 'show%s' % fn._fmtopic
146 fmtopic = 'show%s' % fn._fmtopic
148 with ui.formatter(fmtopic, {'template': template}) as fm:
147 with ui.formatter(fmtopic, {'template': template}) as fm:
149 return fn(ui, repo, fm)
148 return fn(ui, repo, fm)
150 elif fn._csettopic:
149 elif fn._csettopic:
151 ref = 'show%s' % fn._csettopic
150 ref = 'show%s' % fn._csettopic
152 spec = formatter.lookuptemplate(ui, ref, template)
151 spec = formatter.lookuptemplate(ui, ref, template)
153 displayer = logcmdutil.changesettemplater(ui, repo, spec, buffered=True)
152 displayer = logcmdutil.changesettemplater(ui, repo, spec, buffered=True)
154 return fn(ui, repo, displayer)
153 return fn(ui, repo, displayer)
155 else:
154 else:
156 return fn(ui, repo)
155 return fn(ui, repo)
157
156
158 @showview('bookmarks', fmtopic='bookmarks')
157 @showview('bookmarks', fmtopic='bookmarks')
159 def showbookmarks(ui, repo, fm):
158 def showbookmarks(ui, repo, fm):
160 """bookmarks and their associated changeset"""
159 """bookmarks and their associated changeset"""
161 marks = repo._bookmarks
160 marks = repo._bookmarks
162 if not len(marks):
161 if not len(marks):
163 # This is a bit hacky. Ideally, templates would have a way to
162 # This is a bit hacky. Ideally, templates would have a way to
164 # specify an empty output, but we shouldn't corrupt JSON while
163 # specify an empty output, but we shouldn't corrupt JSON while
165 # waiting for this functionality.
164 # waiting for this functionality.
166 if not isinstance(fm, formatter.jsonformatter):
165 if not isinstance(fm, formatter.jsonformatter):
167 ui.write(_('(no bookmarks set)\n'))
166 ui.write(_('(no bookmarks set)\n'))
168 return
167 return
169
168
170 revs = [repo[node].rev() for node in marks.values()]
169 revs = [repo[node].rev() for node in marks.values()]
171 active = repo._activebookmark
170 active = repo._activebookmark
172 longestname = max(len(b) for b in marks)
171 longestname = max(len(b) for b in marks)
173 nodelen = longestshortest(repo, revs)
172 nodelen = longestshortest(repo, revs)
174
173
175 for bm, node in sorted(marks.items()):
174 for bm, node in sorted(marks.items()):
176 fm.startitem()
175 fm.startitem()
177 fm.context(ctx=repo[node])
176 fm.context(ctx=repo[node])
178 fm.write('bookmark', '%s', bm)
177 fm.write('bookmark', '%s', bm)
179 fm.write('node', fm.hexfunc(node), fm.hexfunc(node))
178 fm.write('node', fm.hexfunc(node), fm.hexfunc(node))
180 fm.data(active=bm == active,
179 fm.data(active=bm == active,
181 longestbookmarklen=longestname,
180 longestbookmarklen=longestname,
182 nodelen=nodelen)
181 nodelen=nodelen)
183
182
184 @showview('stack', csettopic='stack')
183 @showview('stack', csettopic='stack')
185 def showstack(ui, repo, displayer):
184 def showstack(ui, repo, displayer):
186 """current line of work"""
185 """current line of work"""
187 wdirctx = repo['.']
186 wdirctx = repo['.']
188 if wdirctx.rev() == nullrev:
187 if wdirctx.rev() == nullrev:
189 raise error.Abort(_('stack view only available when there is a '
188 raise error.Abort(_('stack view only available when there is a '
190 'working directory'))
189 'working directory'))
191
190
192 if wdirctx.phase() == phases.public:
191 if wdirctx.phase() == phases.public:
193 ui.write(_('(empty stack; working directory parent is a published '
192 ui.write(_('(empty stack; working directory parent is a published '
194 'changeset)\n'))
193 'changeset)\n'))
195 return
194 return
196
195
197 # TODO extract "find stack" into a function to facilitate
196 # TODO extract "find stack" into a function to facilitate
198 # customization and reuse.
197 # customization and reuse.
199
198
200 baserev = destutil.stackbase(ui, repo)
199 baserev = destutil.stackbase(ui, repo)
201 basectx = None
200 basectx = None
202
201
203 if baserev is None:
202 if baserev is None:
204 baserev = wdirctx.rev()
203 baserev = wdirctx.rev()
205 stackrevs = {wdirctx.rev()}
204 stackrevs = {wdirctx.rev()}
206 else:
205 else:
207 stackrevs = set(repo.revs('%d::.', baserev))
206 stackrevs = set(repo.revs('%d::.', baserev))
208
207
209 ctx = repo[baserev]
208 ctx = repo[baserev]
210 if ctx.p1().rev() != nullrev:
209 if ctx.p1().rev() != nullrev:
211 basectx = ctx.p1()
210 basectx = ctx.p1()
212
211
213 # And relevant descendants.
212 # And relevant descendants.
214 branchpointattip = False
213 branchpointattip = False
215 cl = repo.changelog
214 cl = repo.changelog
216
215
217 for rev in cl.descendants([wdirctx.rev()]):
216 for rev in cl.descendants([wdirctx.rev()]):
218 ctx = repo[rev]
217 ctx = repo[rev]
219
218
220 # Will only happen if . is public.
219 # Will only happen if . is public.
221 if ctx.phase() == phases.public:
220 if ctx.phase() == phases.public:
222 break
221 break
223
222
224 stackrevs.add(ctx.rev())
223 stackrevs.add(ctx.rev())
225
224
226 # ctx.children() within a function iterating on descandants
225 # ctx.children() within a function iterating on descandants
227 # potentially has severe performance concerns because revlog.children()
226 # potentially has severe performance concerns because revlog.children()
228 # iterates over all revisions after ctx's node. However, the number of
227 # iterates over all revisions after ctx's node. However, the number of
229 # draft changesets should be a reasonably small number. So even if
228 # draft changesets should be a reasonably small number. So even if
230 # this is quadratic, the perf impact should be minimal.
229 # this is quadratic, the perf impact should be minimal.
231 if len(ctx.children()) > 1:
230 if len(ctx.children()) > 1:
232 branchpointattip = True
231 branchpointattip = True
233 break
232 break
234
233
235 stackrevs = list(sorted(stackrevs, reverse=True))
234 stackrevs = list(sorted(stackrevs, reverse=True))
236
235
237 # Find likely target heads for the current stack. These are likely
236 # Find likely target heads for the current stack. These are likely
238 # merge or rebase targets.
237 # merge or rebase targets.
239 if basectx:
238 if basectx:
240 # TODO make this customizable?
239 # TODO make this customizable?
241 newheads = set(repo.revs('heads(%d::) - %ld - not public()',
240 newheads = set(repo.revs('heads(%d::) - %ld - not public()',
242 basectx.rev(), stackrevs))
241 basectx.rev(), stackrevs))
243 else:
242 else:
244 newheads = set()
243 newheads = set()
245
244
246 allrevs = set(stackrevs) | newheads | set([baserev])
245 allrevs = set(stackrevs) | newheads | set([baserev])
247 nodelen = longestshortest(repo, allrevs)
246 nodelen = longestshortest(repo, allrevs)
248
247
249 try:
248 try:
250 cmdutil.findcmd('rebase', commands.table)
249 cmdutil.findcmd('rebase', commands.table)
251 haverebase = True
250 haverebase = True
252 except (error.AmbiguousCommand, error.UnknownCommand):
251 except (error.AmbiguousCommand, error.UnknownCommand):
253 haverebase = False
252 haverebase = False
254
253
255 # TODO use templating.
254 # TODO use templating.
256 # TODO consider using graphmod. But it may not be necessary given
255 # TODO consider using graphmod. But it may not be necessary given
257 # our simplicity and the customizations required.
256 # our simplicity and the customizations required.
258 # TODO use proper graph symbols from graphmod
257 # TODO use proper graph symbols from graphmod
259
258
260 tres = formatter.templateresources(ui, repo)
259 tres = formatter.templateresources(ui, repo)
261 shortesttmpl = formatter.maketemplater(ui, '{shortest(node, %d)}' % nodelen,
260 shortesttmpl = formatter.maketemplater(ui, '{shortest(node, %d)}' % nodelen,
262 resources=tres)
261 resources=tres)
263 def shortest(ctx):
262 def shortest(ctx):
264 return shortesttmpl.renderdefault({'ctx': ctx, 'node': ctx.hex()})
263 return shortesttmpl.renderdefault({'ctx': ctx, 'node': ctx.hex()})
265
264
266 # We write out new heads to aid in DAG awareness and to help with decision
265 # We write out new heads to aid in DAG awareness and to help with decision
267 # making on how the stack should be reconciled with commits made since the
266 # making on how the stack should be reconciled with commits made since the
268 # branch point.
267 # branch point.
269 if newheads:
268 if newheads:
270 # Calculate distance from base so we can render the count and so we can
269 # Calculate distance from base so we can render the count and so we can
271 # sort display order by commit distance.
270 # sort display order by commit distance.
272 revdistance = {}
271 revdistance = {}
273 for head in newheads:
272 for head in newheads:
274 # There is some redundancy in DAG traversal here and therefore
273 # There is some redundancy in DAG traversal here and therefore
275 # room to optimize.
274 # room to optimize.
276 ancestors = cl.ancestors([head], stoprev=basectx.rev())
275 ancestors = cl.ancestors([head], stoprev=basectx.rev())
277 revdistance[head] = len(list(ancestors))
276 revdistance[head] = len(list(ancestors))
278
277
279 sourcectx = repo[stackrevs[-1]]
278 sourcectx = repo[stackrevs[-1]]
280
279
281 sortedheads = sorted(newheads, key=lambda x: revdistance[x],
280 sortedheads = sorted(newheads, key=lambda x: revdistance[x],
282 reverse=True)
281 reverse=True)
283
282
284 for i, rev in enumerate(sortedheads):
283 for i, rev in enumerate(sortedheads):
285 ctx = repo[rev]
284 ctx = repo[rev]
286
285
287 if i:
286 if i:
288 ui.write(': ')
287 ui.write(': ')
289 else:
288 else:
290 ui.write(' ')
289 ui.write(' ')
291
290
292 ui.write(('o '))
291 ui.write(('o '))
293 displayer.show(ctx, nodelen=nodelen)
292 displayer.show(ctx, nodelen=nodelen)
294 displayer.flush(ctx)
293 displayer.flush(ctx)
295 ui.write('\n')
294 ui.write('\n')
296
295
297 if i:
296 if i:
298 ui.write(':/')
297 ui.write(':/')
299 else:
298 else:
300 ui.write(' /')
299 ui.write(' /')
301
300
302 ui.write(' (')
301 ui.write(' (')
303 ui.write(_('%d commits ahead') % revdistance[rev],
302 ui.write(_('%d commits ahead') % revdistance[rev],
304 label='stack.commitdistance')
303 label='stack.commitdistance')
305
304
306 if haverebase:
305 if haverebase:
307 # TODO may be able to omit --source in some scenarios
306 # TODO may be able to omit --source in some scenarios
308 ui.write('; ')
307 ui.write('; ')
309 ui.write(('hg rebase --source %s --dest %s' % (
308 ui.write(('hg rebase --source %s --dest %s' % (
310 shortest(sourcectx), shortest(ctx))),
309 shortest(sourcectx), shortest(ctx))),
311 label='stack.rebasehint')
310 label='stack.rebasehint')
312
311
313 ui.write(')\n')
312 ui.write(')\n')
314
313
315 ui.write(':\n: ')
314 ui.write(':\n: ')
316 ui.write(_('(stack head)\n'), label='stack.label')
315 ui.write(_('(stack head)\n'), label='stack.label')
317
316
318 if branchpointattip:
317 if branchpointattip:
319 ui.write(' \\ / ')
318 ui.write(' \\ / ')
320 ui.write(_('(multiple children)\n'), label='stack.label')
319 ui.write(_('(multiple children)\n'), label='stack.label')
321 ui.write(' |\n')
320 ui.write(' |\n')
322
321
323 for rev in stackrevs:
322 for rev in stackrevs:
324 ctx = repo[rev]
323 ctx = repo[rev]
325 symbol = '@' if rev == wdirctx.rev() else 'o'
324 symbol = '@' if rev == wdirctx.rev() else 'o'
326
325
327 if newheads:
326 if newheads:
328 ui.write(': ')
327 ui.write(': ')
329 else:
328 else:
330 ui.write(' ')
329 ui.write(' ')
331
330
332 ui.write(symbol, ' ')
331 ui.write(symbol, ' ')
333 displayer.show(ctx, nodelen=nodelen)
332 displayer.show(ctx, nodelen=nodelen)
334 displayer.flush(ctx)
333 displayer.flush(ctx)
335 ui.write('\n')
334 ui.write('\n')
336
335
337 # TODO display histedit hint?
336 # TODO display histedit hint?
338
337
339 if basectx:
338 if basectx:
340 # Vertically and horizontally separate stack base from parent
339 # Vertically and horizontally separate stack base from parent
341 # to reinforce stack boundary.
340 # to reinforce stack boundary.
342 if newheads:
341 if newheads:
343 ui.write(':/ ')
342 ui.write(':/ ')
344 else:
343 else:
345 ui.write(' / ')
344 ui.write(' / ')
346
345
347 ui.write(_('(stack base)'), '\n', label='stack.label')
346 ui.write(_('(stack base)'), '\n', label='stack.label')
348 ui.write(('o '))
347 ui.write(('o '))
349
348
350 displayer.show(basectx, nodelen=nodelen)
349 displayer.show(basectx, nodelen=nodelen)
351 displayer.flush(basectx)
350 displayer.flush(basectx)
352 ui.write('\n')
351 ui.write('\n')
353
352
354 @revsetpredicate('_underway([commitage[, headage]])')
353 @revsetpredicate('_underway([commitage[, headage]])')
355 def underwayrevset(repo, subset, x):
354 def underwayrevset(repo, subset, x):
356 args = revset.getargsdict(x, 'underway', 'commitage headage')
355 args = revset.getargsdict(x, 'underway', 'commitage headage')
357 if 'commitage' not in args:
356 if 'commitage' not in args:
358 args['commitage'] = None
357 args['commitage'] = None
359 if 'headage' not in args:
358 if 'headage' not in args:
360 args['headage'] = None
359 args['headage'] = None
361
360
362 # We assume callers of this revset add a topographical sort on the
361 # We assume callers of this revset add a topographical sort on the
363 # result. This means there is no benefit to making the revset lazy
362 # result. This means there is no benefit to making the revset lazy
364 # since the topographical sort needs to consume all revs.
363 # since the topographical sort needs to consume all revs.
365 #
364 #
366 # With this in mind, we build up the set manually instead of constructing
365 # With this in mind, we build up the set manually instead of constructing
367 # a complex revset. This enables faster execution.
366 # a complex revset. This enables faster execution.
368
367
369 # Mutable changesets (non-public) are the most important changesets
368 # Mutable changesets (non-public) are the most important changesets
370 # to return. ``not public()`` will also pull in obsolete changesets if
369 # to return. ``not public()`` will also pull in obsolete changesets if
371 # there is a non-obsolete changeset with obsolete ancestors. This is
370 # there is a non-obsolete changeset with obsolete ancestors. This is
372 # why we exclude obsolete changesets from this query.
371 # why we exclude obsolete changesets from this query.
373 rs = 'not public() and not obsolete()'
372 rs = 'not public() and not obsolete()'
374 rsargs = []
373 rsargs = []
375 if args['commitage']:
374 if args['commitage']:
376 rs += ' and date(%s)'
375 rs += ' and date(%s)'
377 rsargs.append(revsetlang.getstring(args['commitage'],
376 rsargs.append(revsetlang.getstring(args['commitage'],
378 _('commitage requires a string')))
377 _('commitage requires a string')))
379
378
380 mutable = repo.revs(rs, *rsargs)
379 mutable = repo.revs(rs, *rsargs)
381 relevant = revset.baseset(mutable)
380 relevant = revset.baseset(mutable)
382
381
383 # Add parents of mutable changesets to provide context.
382 # Add parents of mutable changesets to provide context.
384 relevant += repo.revs('parents(%ld)', mutable)
383 relevant += repo.revs('parents(%ld)', mutable)
385
384
386 # We also pull in (public) heads if they a) aren't closing a branch
385 # We also pull in (public) heads if they a) aren't closing a branch
387 # b) are recent.
386 # b) are recent.
388 rs = 'head() and not closed()'
387 rs = 'head() and not closed()'
389 rsargs = []
388 rsargs = []
390 if args['headage']:
389 if args['headage']:
391 rs += ' and date(%s)'
390 rs += ' and date(%s)'
392 rsargs.append(revsetlang.getstring(args['headage'],
391 rsargs.append(revsetlang.getstring(args['headage'],
393 _('headage requires a string')))
392 _('headage requires a string')))
394
393
395 relevant += repo.revs(rs, *rsargs)
394 relevant += repo.revs(rs, *rsargs)
396
395
397 # Add working directory parent.
396 # Add working directory parent.
398 wdirrev = repo['.'].rev()
397 wdirrev = repo['.'].rev()
399 if wdirrev != nullrev:
398 if wdirrev != nullrev:
400 relevant += revset.baseset({wdirrev})
399 relevant += revset.baseset({wdirrev})
401
400
402 return subset & relevant
401 return subset & relevant
403
402
404 @showview('work', csettopic='work')
403 @showview('work', csettopic='work')
405 def showwork(ui, repo, displayer):
404 def showwork(ui, repo, displayer):
406 """changesets that aren't finished"""
405 """changesets that aren't finished"""
407 # TODO support date-based limiting when calling revset.
406 # TODO support date-based limiting when calling revset.
408 revs = repo.revs('sort(_underway(), topo)')
407 revs = repo.revs('sort(_underway(), topo)')
409 nodelen = longestshortest(repo, revs)
408 nodelen = longestshortest(repo, revs)
410
409
411 revdag = graphmod.dagwalker(repo, revs)
410 revdag = graphmod.dagwalker(repo, revs)
412
411
413 ui.setconfig('experimental', 'graphshorten', True)
412 ui.setconfig('experimental', 'graphshorten', True)
414 logcmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges,
413 logcmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges,
415 props={'nodelen': nodelen})
414 props={'nodelen': nodelen})
416
415
417 def extsetup(ui):
416 def extsetup(ui):
418 # Alias `hg <prefix><view>` to `hg show <view>`.
417 # Alias `hg <prefix><view>` to `hg show <view>`.
419 for prefix in ui.configlist('commands', 'show.aliasprefix'):
418 for prefix in ui.configlist('commands', 'show.aliasprefix'):
420 for view in showview._table:
419 for view in showview._table:
421 name = '%s%s' % (prefix, view)
420 name = '%s%s' % (prefix, view)
422
421
423 choice, allcommands = cmdutil.findpossible(name, commands.table,
422 choice, allcommands = cmdutil.findpossible(name, commands.table,
424 strict=True)
423 strict=True)
425
424
426 # This alias is already a command name. Don't set it.
425 # This alias is already a command name. Don't set it.
427 if name in choice:
426 if name in choice:
428 continue
427 continue
429
428
430 # Same for aliases.
429 # Same for aliases.
431 if ui.config('alias', name, None):
430 if ui.config('alias', name, None):
432 continue
431 continue
433
432
434 ui.setconfig('alias', name, 'show %s' % view, source='show')
433 ui.setconfig('alias', name, 'show %s' % view, source='show')
435
434
436 def longestshortest(repo, revs, minlen=4):
435 def longestshortest(repo, revs, minlen=4):
437 """Return the length of the longest shortest node to identify revisions.
436 """Return the length of the longest shortest node to identify revisions.
438
437
439 The result of this function can be used with the ``shortest()`` template
438 The result of this function can be used with the ``shortest()`` template
440 function to ensure that a value is unique and unambiguous for a given
439 function to ensure that a value is unique and unambiguous for a given
441 set of nodes.
440 set of nodes.
442
441
443 The number of revisions in the repo is taken into account to prevent
442 The number of revisions in the repo is taken into account to prevent
444 a numeric node prefix from conflicting with an integer revision number.
443 a numeric node prefix from conflicting with an integer revision number.
445 If we fail to do this, a value of e.g. ``10023`` could mean either
444 If we fail to do this, a value of e.g. ``10023`` could mean either
446 revision 10023 or node ``10023abc...``.
445 revision 10023 or node ``10023abc...``.
447 """
446 """
448 if not revs:
447 if not revs:
449 return minlen
448 return minlen
450 cl = repo.changelog
449 cl = repo.changelog
451 return max(len(scmutil.shortesthexnodeidprefix(repo, hex(cl.node(r)),
450 return max(len(scmutil.shortesthexnodeidprefix(repo, cl.node(r), minlen))
452 minlen)) for r in revs)
451 for r in revs)
453
452
454 # Adjust the docstring of the show command so it shows all registered views.
453 # Adjust the docstring of the show command so it shows all registered views.
455 # This is a bit hacky because it runs at the end of module load. When moved
454 # This is a bit hacky because it runs at the end of module load. When moved
456 # into core or when another extension wants to provide a view, we'll need
455 # into core or when another extension wants to provide a view, we'll need
457 # to do this more robustly.
456 # to do this more robustly.
458 # TODO make this more robust.
457 # TODO make this more robust.
459 def _updatedocstring():
458 def _updatedocstring():
460 longest = max(map(len, showview._table.keys()))
459 longest = max(map(len, showview._table.keys()))
461 entries = []
460 entries = []
462 for key in sorted(showview._table.keys()):
461 for key in sorted(showview._table.keys()):
463 entries.append(pycompat.sysstr(' %s %s' % (
462 entries.append(pycompat.sysstr(' %s %s' % (
464 key.ljust(longest), showview._table[key]._origdoc)))
463 key.ljust(longest), showview._table[key]._origdoc)))
465
464
466 cmdtable['show'][0].__doc__ = pycompat.sysstr('%s\n\n%s\n ') % (
465 cmdtable['show'][0].__doc__ = pycompat.sysstr('%s\n\n%s\n ') % (
467 cmdtable['show'][0].__doc__.rstrip(),
466 cmdtable['show'][0].__doc__.rstrip(),
468 pycompat.sysstr('\n\n').join(entries))
467 pycompat.sysstr('\n\n').join(entries))
469
468
470 _updatedocstring()
469 _updatedocstring()
@@ -1,1555 +1,1555 b''
1 # scmutil.py - Mercurial core utility functions
1 # scmutil.py - Mercurial core utility functions
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright 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 glob
11 import glob
12 import hashlib
12 import hashlib
13 import os
13 import os
14 import re
14 import re
15 import socket
15 import socket
16 import subprocess
16 import subprocess
17 import weakref
17 import weakref
18
18
19 from .i18n import _
19 from .i18n import _
20 from .node import (
20 from .node import (
21 bin,
21 bin,
22 hex,
22 hex,
23 nullid,
23 nullid,
24 short,
24 short,
25 wdirid,
25 wdirid,
26 wdirrev,
26 wdirrev,
27 )
27 )
28
28
29 from . import (
29 from . import (
30 encoding,
30 encoding,
31 error,
31 error,
32 match as matchmod,
32 match as matchmod,
33 obsolete,
33 obsolete,
34 obsutil,
34 obsutil,
35 pathutil,
35 pathutil,
36 phases,
36 phases,
37 pycompat,
37 pycompat,
38 revsetlang,
38 revsetlang,
39 similar,
39 similar,
40 url,
40 url,
41 util,
41 util,
42 vfs,
42 vfs,
43 )
43 )
44
44
45 from .utils import (
45 from .utils import (
46 procutil,
46 procutil,
47 stringutil,
47 stringutil,
48 )
48 )
49
49
50 if pycompat.iswindows:
50 if pycompat.iswindows:
51 from . import scmwindows as scmplatform
51 from . import scmwindows as scmplatform
52 else:
52 else:
53 from . import scmposix as scmplatform
53 from . import scmposix as scmplatform
54
54
55 termsize = scmplatform.termsize
55 termsize = scmplatform.termsize
56
56
57 class status(tuple):
57 class status(tuple):
58 '''Named tuple with a list of files per status. The 'deleted', 'unknown'
58 '''Named tuple with a list of files per status. The 'deleted', 'unknown'
59 and 'ignored' properties are only relevant to the working copy.
59 and 'ignored' properties are only relevant to the working copy.
60 '''
60 '''
61
61
62 __slots__ = ()
62 __slots__ = ()
63
63
64 def __new__(cls, modified, added, removed, deleted, unknown, ignored,
64 def __new__(cls, modified, added, removed, deleted, unknown, ignored,
65 clean):
65 clean):
66 return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
66 return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
67 ignored, clean))
67 ignored, clean))
68
68
69 @property
69 @property
70 def modified(self):
70 def modified(self):
71 '''files that have been modified'''
71 '''files that have been modified'''
72 return self[0]
72 return self[0]
73
73
74 @property
74 @property
75 def added(self):
75 def added(self):
76 '''files that have been added'''
76 '''files that have been added'''
77 return self[1]
77 return self[1]
78
78
79 @property
79 @property
80 def removed(self):
80 def removed(self):
81 '''files that have been removed'''
81 '''files that have been removed'''
82 return self[2]
82 return self[2]
83
83
84 @property
84 @property
85 def deleted(self):
85 def deleted(self):
86 '''files that are in the dirstate, but have been deleted from the
86 '''files that are in the dirstate, but have been deleted from the
87 working copy (aka "missing")
87 working copy (aka "missing")
88 '''
88 '''
89 return self[3]
89 return self[3]
90
90
91 @property
91 @property
92 def unknown(self):
92 def unknown(self):
93 '''files not in the dirstate that are not ignored'''
93 '''files not in the dirstate that are not ignored'''
94 return self[4]
94 return self[4]
95
95
96 @property
96 @property
97 def ignored(self):
97 def ignored(self):
98 '''files not in the dirstate that are ignored (by _dirignore())'''
98 '''files not in the dirstate that are ignored (by _dirignore())'''
99 return self[5]
99 return self[5]
100
100
101 @property
101 @property
102 def clean(self):
102 def clean(self):
103 '''files that have not been modified'''
103 '''files that have not been modified'''
104 return self[6]
104 return self[6]
105
105
106 def __repr__(self, *args, **kwargs):
106 def __repr__(self, *args, **kwargs):
107 return (('<status modified=%r, added=%r, removed=%r, deleted=%r, '
107 return (('<status modified=%r, added=%r, removed=%r, deleted=%r, '
108 'unknown=%r, ignored=%r, clean=%r>') % self)
108 'unknown=%r, ignored=%r, clean=%r>') % self)
109
109
110 def itersubrepos(ctx1, ctx2):
110 def itersubrepos(ctx1, ctx2):
111 """find subrepos in ctx1 or ctx2"""
111 """find subrepos in ctx1 or ctx2"""
112 # Create a (subpath, ctx) mapping where we prefer subpaths from
112 # Create a (subpath, ctx) mapping where we prefer subpaths from
113 # ctx1. The subpaths from ctx2 are important when the .hgsub file
113 # ctx1. The subpaths from ctx2 are important when the .hgsub file
114 # has been modified (in ctx2) but not yet committed (in ctx1).
114 # has been modified (in ctx2) but not yet committed (in ctx1).
115 subpaths = dict.fromkeys(ctx2.substate, ctx2)
115 subpaths = dict.fromkeys(ctx2.substate, ctx2)
116 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
116 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
117
117
118 missing = set()
118 missing = set()
119
119
120 for subpath in ctx2.substate:
120 for subpath in ctx2.substate:
121 if subpath not in ctx1.substate:
121 if subpath not in ctx1.substate:
122 del subpaths[subpath]
122 del subpaths[subpath]
123 missing.add(subpath)
123 missing.add(subpath)
124
124
125 for subpath, ctx in sorted(subpaths.iteritems()):
125 for subpath, ctx in sorted(subpaths.iteritems()):
126 yield subpath, ctx.sub(subpath)
126 yield subpath, ctx.sub(subpath)
127
127
128 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
128 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
129 # status and diff will have an accurate result when it does
129 # status and diff will have an accurate result when it does
130 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
130 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
131 # against itself.
131 # against itself.
132 for subpath in missing:
132 for subpath in missing:
133 yield subpath, ctx2.nullsub(subpath, ctx1)
133 yield subpath, ctx2.nullsub(subpath, ctx1)
134
134
135 def nochangesfound(ui, repo, excluded=None):
135 def nochangesfound(ui, repo, excluded=None):
136 '''Report no changes for push/pull, excluded is None or a list of
136 '''Report no changes for push/pull, excluded is None or a list of
137 nodes excluded from the push/pull.
137 nodes excluded from the push/pull.
138 '''
138 '''
139 secretlist = []
139 secretlist = []
140 if excluded:
140 if excluded:
141 for n in excluded:
141 for n in excluded:
142 ctx = repo[n]
142 ctx = repo[n]
143 if ctx.phase() >= phases.secret and not ctx.extinct():
143 if ctx.phase() >= phases.secret and not ctx.extinct():
144 secretlist.append(n)
144 secretlist.append(n)
145
145
146 if secretlist:
146 if secretlist:
147 ui.status(_("no changes found (ignored %d secret changesets)\n")
147 ui.status(_("no changes found (ignored %d secret changesets)\n")
148 % len(secretlist))
148 % len(secretlist))
149 else:
149 else:
150 ui.status(_("no changes found\n"))
150 ui.status(_("no changes found\n"))
151
151
152 def callcatch(ui, func):
152 def callcatch(ui, func):
153 """call func() with global exception handling
153 """call func() with global exception handling
154
154
155 return func() if no exception happens. otherwise do some error handling
155 return func() if no exception happens. otherwise do some error handling
156 and return an exit code accordingly. does not handle all exceptions.
156 and return an exit code accordingly. does not handle all exceptions.
157 """
157 """
158 try:
158 try:
159 try:
159 try:
160 return func()
160 return func()
161 except: # re-raises
161 except: # re-raises
162 ui.traceback()
162 ui.traceback()
163 raise
163 raise
164 # Global exception handling, alphabetically
164 # Global exception handling, alphabetically
165 # Mercurial-specific first, followed by built-in and library exceptions
165 # Mercurial-specific first, followed by built-in and library exceptions
166 except error.LockHeld as inst:
166 except error.LockHeld as inst:
167 if inst.errno == errno.ETIMEDOUT:
167 if inst.errno == errno.ETIMEDOUT:
168 reason = _('timed out waiting for lock held by %r') % inst.locker
168 reason = _('timed out waiting for lock held by %r') % inst.locker
169 else:
169 else:
170 reason = _('lock held by %r') % inst.locker
170 reason = _('lock held by %r') % inst.locker
171 ui.warn(_("abort: %s: %s\n")
171 ui.warn(_("abort: %s: %s\n")
172 % (inst.desc or stringutil.forcebytestr(inst.filename), reason))
172 % (inst.desc or stringutil.forcebytestr(inst.filename), reason))
173 if not inst.locker:
173 if not inst.locker:
174 ui.warn(_("(lock might be very busy)\n"))
174 ui.warn(_("(lock might be very busy)\n"))
175 except error.LockUnavailable as inst:
175 except error.LockUnavailable as inst:
176 ui.warn(_("abort: could not lock %s: %s\n") %
176 ui.warn(_("abort: could not lock %s: %s\n") %
177 (inst.desc or stringutil.forcebytestr(inst.filename),
177 (inst.desc or stringutil.forcebytestr(inst.filename),
178 encoding.strtolocal(inst.strerror)))
178 encoding.strtolocal(inst.strerror)))
179 except error.OutOfBandError as inst:
179 except error.OutOfBandError as inst:
180 if inst.args:
180 if inst.args:
181 msg = _("abort: remote error:\n")
181 msg = _("abort: remote error:\n")
182 else:
182 else:
183 msg = _("abort: remote error\n")
183 msg = _("abort: remote error\n")
184 ui.warn(msg)
184 ui.warn(msg)
185 if inst.args:
185 if inst.args:
186 ui.warn(''.join(inst.args))
186 ui.warn(''.join(inst.args))
187 if inst.hint:
187 if inst.hint:
188 ui.warn('(%s)\n' % inst.hint)
188 ui.warn('(%s)\n' % inst.hint)
189 except error.RepoError as inst:
189 except error.RepoError as inst:
190 ui.warn(_("abort: %s!\n") % inst)
190 ui.warn(_("abort: %s!\n") % inst)
191 if inst.hint:
191 if inst.hint:
192 ui.warn(_("(%s)\n") % inst.hint)
192 ui.warn(_("(%s)\n") % inst.hint)
193 except error.ResponseError as inst:
193 except error.ResponseError as inst:
194 ui.warn(_("abort: %s") % inst.args[0])
194 ui.warn(_("abort: %s") % inst.args[0])
195 msg = inst.args[1]
195 msg = inst.args[1]
196 if isinstance(msg, type(u'')):
196 if isinstance(msg, type(u'')):
197 msg = pycompat.sysbytes(msg)
197 msg = pycompat.sysbytes(msg)
198 if not isinstance(msg, bytes):
198 if not isinstance(msg, bytes):
199 ui.warn(" %r\n" % (msg,))
199 ui.warn(" %r\n" % (msg,))
200 elif not msg:
200 elif not msg:
201 ui.warn(_(" empty string\n"))
201 ui.warn(_(" empty string\n"))
202 else:
202 else:
203 ui.warn("\n%r\n" % stringutil.ellipsis(msg))
203 ui.warn("\n%r\n" % stringutil.ellipsis(msg))
204 except error.CensoredNodeError as inst:
204 except error.CensoredNodeError as inst:
205 ui.warn(_("abort: file censored %s!\n") % inst)
205 ui.warn(_("abort: file censored %s!\n") % inst)
206 except error.RevlogError as inst:
206 except error.RevlogError as inst:
207 ui.warn(_("abort: %s!\n") % inst)
207 ui.warn(_("abort: %s!\n") % inst)
208 except error.InterventionRequired as inst:
208 except error.InterventionRequired as inst:
209 ui.warn("%s\n" % inst)
209 ui.warn("%s\n" % inst)
210 if inst.hint:
210 if inst.hint:
211 ui.warn(_("(%s)\n") % inst.hint)
211 ui.warn(_("(%s)\n") % inst.hint)
212 return 1
212 return 1
213 except error.WdirUnsupported:
213 except error.WdirUnsupported:
214 ui.warn(_("abort: working directory revision cannot be specified\n"))
214 ui.warn(_("abort: working directory revision cannot be specified\n"))
215 except error.Abort as inst:
215 except error.Abort as inst:
216 ui.warn(_("abort: %s\n") % inst)
216 ui.warn(_("abort: %s\n") % inst)
217 if inst.hint:
217 if inst.hint:
218 ui.warn(_("(%s)\n") % inst.hint)
218 ui.warn(_("(%s)\n") % inst.hint)
219 except ImportError as inst:
219 except ImportError as inst:
220 ui.warn(_("abort: %s!\n") % stringutil.forcebytestr(inst))
220 ui.warn(_("abort: %s!\n") % stringutil.forcebytestr(inst))
221 m = stringutil.forcebytestr(inst).split()[-1]
221 m = stringutil.forcebytestr(inst).split()[-1]
222 if m in "mpatch bdiff".split():
222 if m in "mpatch bdiff".split():
223 ui.warn(_("(did you forget to compile extensions?)\n"))
223 ui.warn(_("(did you forget to compile extensions?)\n"))
224 elif m in "zlib".split():
224 elif m in "zlib".split():
225 ui.warn(_("(is your Python install correct?)\n"))
225 ui.warn(_("(is your Python install correct?)\n"))
226 except IOError as inst:
226 except IOError as inst:
227 if util.safehasattr(inst, "code"):
227 if util.safehasattr(inst, "code"):
228 ui.warn(_("abort: %s\n") % stringutil.forcebytestr(inst))
228 ui.warn(_("abort: %s\n") % stringutil.forcebytestr(inst))
229 elif util.safehasattr(inst, "reason"):
229 elif util.safehasattr(inst, "reason"):
230 try: # usually it is in the form (errno, strerror)
230 try: # usually it is in the form (errno, strerror)
231 reason = inst.reason.args[1]
231 reason = inst.reason.args[1]
232 except (AttributeError, IndexError):
232 except (AttributeError, IndexError):
233 # it might be anything, for example a string
233 # it might be anything, for example a string
234 reason = inst.reason
234 reason = inst.reason
235 if isinstance(reason, unicode):
235 if isinstance(reason, unicode):
236 # SSLError of Python 2.7.9 contains a unicode
236 # SSLError of Python 2.7.9 contains a unicode
237 reason = encoding.unitolocal(reason)
237 reason = encoding.unitolocal(reason)
238 ui.warn(_("abort: error: %s\n") % reason)
238 ui.warn(_("abort: error: %s\n") % reason)
239 elif (util.safehasattr(inst, "args")
239 elif (util.safehasattr(inst, "args")
240 and inst.args and inst.args[0] == errno.EPIPE):
240 and inst.args and inst.args[0] == errno.EPIPE):
241 pass
241 pass
242 elif getattr(inst, "strerror", None):
242 elif getattr(inst, "strerror", None):
243 if getattr(inst, "filename", None):
243 if getattr(inst, "filename", None):
244 ui.warn(_("abort: %s: %s\n") % (
244 ui.warn(_("abort: %s: %s\n") % (
245 encoding.strtolocal(inst.strerror),
245 encoding.strtolocal(inst.strerror),
246 stringutil.forcebytestr(inst.filename)))
246 stringutil.forcebytestr(inst.filename)))
247 else:
247 else:
248 ui.warn(_("abort: %s\n") % encoding.strtolocal(inst.strerror))
248 ui.warn(_("abort: %s\n") % encoding.strtolocal(inst.strerror))
249 else:
249 else:
250 raise
250 raise
251 except OSError as inst:
251 except OSError as inst:
252 if getattr(inst, "filename", None) is not None:
252 if getattr(inst, "filename", None) is not None:
253 ui.warn(_("abort: %s: '%s'\n") % (
253 ui.warn(_("abort: %s: '%s'\n") % (
254 encoding.strtolocal(inst.strerror),
254 encoding.strtolocal(inst.strerror),
255 stringutil.forcebytestr(inst.filename)))
255 stringutil.forcebytestr(inst.filename)))
256 else:
256 else:
257 ui.warn(_("abort: %s\n") % encoding.strtolocal(inst.strerror))
257 ui.warn(_("abort: %s\n") % encoding.strtolocal(inst.strerror))
258 except MemoryError:
258 except MemoryError:
259 ui.warn(_("abort: out of memory\n"))
259 ui.warn(_("abort: out of memory\n"))
260 except SystemExit as inst:
260 except SystemExit as inst:
261 # Commands shouldn't sys.exit directly, but give a return code.
261 # Commands shouldn't sys.exit directly, but give a return code.
262 # Just in case catch this and and pass exit code to caller.
262 # Just in case catch this and and pass exit code to caller.
263 return inst.code
263 return inst.code
264 except socket.error as inst:
264 except socket.error as inst:
265 ui.warn(_("abort: %s\n") % stringutil.forcebytestr(inst.args[-1]))
265 ui.warn(_("abort: %s\n") % stringutil.forcebytestr(inst.args[-1]))
266
266
267 return -1
267 return -1
268
268
269 def checknewlabel(repo, lbl, kind):
269 def checknewlabel(repo, lbl, kind):
270 # Do not use the "kind" parameter in ui output.
270 # Do not use the "kind" parameter in ui output.
271 # It makes strings difficult to translate.
271 # It makes strings difficult to translate.
272 if lbl in ['tip', '.', 'null']:
272 if lbl in ['tip', '.', 'null']:
273 raise error.Abort(_("the name '%s' is reserved") % lbl)
273 raise error.Abort(_("the name '%s' is reserved") % lbl)
274 for c in (':', '\0', '\n', '\r'):
274 for c in (':', '\0', '\n', '\r'):
275 if c in lbl:
275 if c in lbl:
276 raise error.Abort(
276 raise error.Abort(
277 _("%r cannot be used in a name") % pycompat.bytestr(c))
277 _("%r cannot be used in a name") % pycompat.bytestr(c))
278 try:
278 try:
279 int(lbl)
279 int(lbl)
280 raise error.Abort(_("cannot use an integer as a name"))
280 raise error.Abort(_("cannot use an integer as a name"))
281 except ValueError:
281 except ValueError:
282 pass
282 pass
283 if lbl.strip() != lbl:
283 if lbl.strip() != lbl:
284 raise error.Abort(_("leading or trailing whitespace in name %r") % lbl)
284 raise error.Abort(_("leading or trailing whitespace in name %r") % lbl)
285
285
286 def checkfilename(f):
286 def checkfilename(f):
287 '''Check that the filename f is an acceptable filename for a tracked file'''
287 '''Check that the filename f is an acceptable filename for a tracked file'''
288 if '\r' in f or '\n' in f:
288 if '\r' in f or '\n' in f:
289 raise error.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
289 raise error.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
290
290
291 def checkportable(ui, f):
291 def checkportable(ui, f):
292 '''Check if filename f is portable and warn or abort depending on config'''
292 '''Check if filename f is portable and warn or abort depending on config'''
293 checkfilename(f)
293 checkfilename(f)
294 abort, warn = checkportabilityalert(ui)
294 abort, warn = checkportabilityalert(ui)
295 if abort or warn:
295 if abort or warn:
296 msg = util.checkwinfilename(f)
296 msg = util.checkwinfilename(f)
297 if msg:
297 if msg:
298 msg = "%s: %s" % (msg, procutil.shellquote(f))
298 msg = "%s: %s" % (msg, procutil.shellquote(f))
299 if abort:
299 if abort:
300 raise error.Abort(msg)
300 raise error.Abort(msg)
301 ui.warn(_("warning: %s\n") % msg)
301 ui.warn(_("warning: %s\n") % msg)
302
302
303 def checkportabilityalert(ui):
303 def checkportabilityalert(ui):
304 '''check if the user's config requests nothing, a warning, or abort for
304 '''check if the user's config requests nothing, a warning, or abort for
305 non-portable filenames'''
305 non-portable filenames'''
306 val = ui.config('ui', 'portablefilenames')
306 val = ui.config('ui', 'portablefilenames')
307 lval = val.lower()
307 lval = val.lower()
308 bval = stringutil.parsebool(val)
308 bval = stringutil.parsebool(val)
309 abort = pycompat.iswindows or lval == 'abort'
309 abort = pycompat.iswindows or lval == 'abort'
310 warn = bval or lval == 'warn'
310 warn = bval or lval == 'warn'
311 if bval is None and not (warn or abort or lval == 'ignore'):
311 if bval is None and not (warn or abort or lval == 'ignore'):
312 raise error.ConfigError(
312 raise error.ConfigError(
313 _("ui.portablefilenames value is invalid ('%s')") % val)
313 _("ui.portablefilenames value is invalid ('%s')") % val)
314 return abort, warn
314 return abort, warn
315
315
316 class casecollisionauditor(object):
316 class casecollisionauditor(object):
317 def __init__(self, ui, abort, dirstate):
317 def __init__(self, ui, abort, dirstate):
318 self._ui = ui
318 self._ui = ui
319 self._abort = abort
319 self._abort = abort
320 allfiles = '\0'.join(dirstate._map)
320 allfiles = '\0'.join(dirstate._map)
321 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
321 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
322 self._dirstate = dirstate
322 self._dirstate = dirstate
323 # The purpose of _newfiles is so that we don't complain about
323 # The purpose of _newfiles is so that we don't complain about
324 # case collisions if someone were to call this object with the
324 # case collisions if someone were to call this object with the
325 # same filename twice.
325 # same filename twice.
326 self._newfiles = set()
326 self._newfiles = set()
327
327
328 def __call__(self, f):
328 def __call__(self, f):
329 if f in self._newfiles:
329 if f in self._newfiles:
330 return
330 return
331 fl = encoding.lower(f)
331 fl = encoding.lower(f)
332 if fl in self._loweredfiles and f not in self._dirstate:
332 if fl in self._loweredfiles and f not in self._dirstate:
333 msg = _('possible case-folding collision for %s') % f
333 msg = _('possible case-folding collision for %s') % f
334 if self._abort:
334 if self._abort:
335 raise error.Abort(msg)
335 raise error.Abort(msg)
336 self._ui.warn(_("warning: %s\n") % msg)
336 self._ui.warn(_("warning: %s\n") % msg)
337 self._loweredfiles.add(fl)
337 self._loweredfiles.add(fl)
338 self._newfiles.add(f)
338 self._newfiles.add(f)
339
339
340 def filteredhash(repo, maxrev):
340 def filteredhash(repo, maxrev):
341 """build hash of filtered revisions in the current repoview.
341 """build hash of filtered revisions in the current repoview.
342
342
343 Multiple caches perform up-to-date validation by checking that the
343 Multiple caches perform up-to-date validation by checking that the
344 tiprev and tipnode stored in the cache file match the current repository.
344 tiprev and tipnode stored in the cache file match the current repository.
345 However, this is not sufficient for validating repoviews because the set
345 However, this is not sufficient for validating repoviews because the set
346 of revisions in the view may change without the repository tiprev and
346 of revisions in the view may change without the repository tiprev and
347 tipnode changing.
347 tipnode changing.
348
348
349 This function hashes all the revs filtered from the view and returns
349 This function hashes all the revs filtered from the view and returns
350 that SHA-1 digest.
350 that SHA-1 digest.
351 """
351 """
352 cl = repo.changelog
352 cl = repo.changelog
353 if not cl.filteredrevs:
353 if not cl.filteredrevs:
354 return None
354 return None
355 key = None
355 key = None
356 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
356 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
357 if revs:
357 if revs:
358 s = hashlib.sha1()
358 s = hashlib.sha1()
359 for rev in revs:
359 for rev in revs:
360 s.update('%d;' % rev)
360 s.update('%d;' % rev)
361 key = s.digest()
361 key = s.digest()
362 return key
362 return key
363
363
364 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
364 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
365 '''yield every hg repository under path, always recursively.
365 '''yield every hg repository under path, always recursively.
366 The recurse flag will only control recursion into repo working dirs'''
366 The recurse flag will only control recursion into repo working dirs'''
367 def errhandler(err):
367 def errhandler(err):
368 if err.filename == path:
368 if err.filename == path:
369 raise err
369 raise err
370 samestat = getattr(os.path, 'samestat', None)
370 samestat = getattr(os.path, 'samestat', None)
371 if followsym and samestat is not None:
371 if followsym and samestat is not None:
372 def adddir(dirlst, dirname):
372 def adddir(dirlst, dirname):
373 dirstat = os.stat(dirname)
373 dirstat = os.stat(dirname)
374 match = any(samestat(dirstat, lstdirstat) for lstdirstat in dirlst)
374 match = any(samestat(dirstat, lstdirstat) for lstdirstat in dirlst)
375 if not match:
375 if not match:
376 dirlst.append(dirstat)
376 dirlst.append(dirstat)
377 return not match
377 return not match
378 else:
378 else:
379 followsym = False
379 followsym = False
380
380
381 if (seen_dirs is None) and followsym:
381 if (seen_dirs is None) and followsym:
382 seen_dirs = []
382 seen_dirs = []
383 adddir(seen_dirs, path)
383 adddir(seen_dirs, path)
384 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
384 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
385 dirs.sort()
385 dirs.sort()
386 if '.hg' in dirs:
386 if '.hg' in dirs:
387 yield root # found a repository
387 yield root # found a repository
388 qroot = os.path.join(root, '.hg', 'patches')
388 qroot = os.path.join(root, '.hg', 'patches')
389 if os.path.isdir(os.path.join(qroot, '.hg')):
389 if os.path.isdir(os.path.join(qroot, '.hg')):
390 yield qroot # we have a patch queue repo here
390 yield qroot # we have a patch queue repo here
391 if recurse:
391 if recurse:
392 # avoid recursing inside the .hg directory
392 # avoid recursing inside the .hg directory
393 dirs.remove('.hg')
393 dirs.remove('.hg')
394 else:
394 else:
395 dirs[:] = [] # don't descend further
395 dirs[:] = [] # don't descend further
396 elif followsym:
396 elif followsym:
397 newdirs = []
397 newdirs = []
398 for d in dirs:
398 for d in dirs:
399 fname = os.path.join(root, d)
399 fname = os.path.join(root, d)
400 if adddir(seen_dirs, fname):
400 if adddir(seen_dirs, fname):
401 if os.path.islink(fname):
401 if os.path.islink(fname):
402 for hgname in walkrepos(fname, True, seen_dirs):
402 for hgname in walkrepos(fname, True, seen_dirs):
403 yield hgname
403 yield hgname
404 else:
404 else:
405 newdirs.append(d)
405 newdirs.append(d)
406 dirs[:] = newdirs
406 dirs[:] = newdirs
407
407
408 def binnode(ctx):
408 def binnode(ctx):
409 """Return binary node id for a given basectx"""
409 """Return binary node id for a given basectx"""
410 node = ctx.node()
410 node = ctx.node()
411 if node is None:
411 if node is None:
412 return wdirid
412 return wdirid
413 return node
413 return node
414
414
415 def intrev(ctx):
415 def intrev(ctx):
416 """Return integer for a given basectx that can be used in comparison or
416 """Return integer for a given basectx that can be used in comparison or
417 arithmetic operation"""
417 arithmetic operation"""
418 rev = ctx.rev()
418 rev = ctx.rev()
419 if rev is None:
419 if rev is None:
420 return wdirrev
420 return wdirrev
421 return rev
421 return rev
422
422
423 def formatchangeid(ctx):
423 def formatchangeid(ctx):
424 """Format changectx as '{rev}:{node|formatnode}', which is the default
424 """Format changectx as '{rev}:{node|formatnode}', which is the default
425 template provided by logcmdutil.changesettemplater"""
425 template provided by logcmdutil.changesettemplater"""
426 repo = ctx.repo()
426 repo = ctx.repo()
427 return formatrevnode(repo.ui, intrev(ctx), binnode(ctx))
427 return formatrevnode(repo.ui, intrev(ctx), binnode(ctx))
428
428
429 def formatrevnode(ui, rev, node):
429 def formatrevnode(ui, rev, node):
430 """Format given revision and node depending on the current verbosity"""
430 """Format given revision and node depending on the current verbosity"""
431 if ui.debugflag:
431 if ui.debugflag:
432 hexfunc = hex
432 hexfunc = hex
433 else:
433 else:
434 hexfunc = short
434 hexfunc = short
435 return '%d:%s' % (rev, hexfunc(node))
435 return '%d:%s' % (rev, hexfunc(node))
436
436
437 def resolvehexnodeidprefix(repo, prefix):
437 def resolvehexnodeidprefix(repo, prefix):
438 # Uses unfiltered repo because it's faster when prefix is ambiguous/
438 # Uses unfiltered repo because it's faster when prefix is ambiguous/
439 # This matches the shortesthexnodeidprefix() function below.
439 # This matches the shortesthexnodeidprefix() function below.
440 node = repo.unfiltered().changelog._partialmatch(prefix)
440 node = repo.unfiltered().changelog._partialmatch(prefix)
441 if node is None:
441 if node is None:
442 return
442 return
443 repo.changelog.rev(node) # make sure node isn't filtered
443 repo.changelog.rev(node) # make sure node isn't filtered
444 return node
444 return node
445
445
446 def shortesthexnodeidprefix(repo, hexnode, minlength=1):
446 def shortesthexnodeidprefix(repo, node, minlength=1):
447 """Find the shortest unambiguous prefix that matches hexnode."""
447 """Find the shortest unambiguous prefix that matches hexnode."""
448 # _partialmatch() of filtered changelog could take O(len(repo)) time,
448 # _partialmatch() of filtered changelog could take O(len(repo)) time,
449 # which would be unacceptably slow. so we look for hash collision in
449 # which would be unacceptably slow. so we look for hash collision in
450 # unfiltered space, which means some hashes may be slightly longer.
450 # unfiltered space, which means some hashes may be slightly longer.
451 return repo.unfiltered().changelog.shortest(hexnode, minlength)
451 return repo.unfiltered().changelog.shortest(hex(node), minlength)
452
452
453 def isrevsymbol(repo, symbol):
453 def isrevsymbol(repo, symbol):
454 """Checks if a symbol exists in the repo.
454 """Checks if a symbol exists in the repo.
455
455
456 See revsymbol() for details. Raises error.LookupError if the symbol is an
456 See revsymbol() for details. Raises error.LookupError if the symbol is an
457 ambiguous nodeid prefix.
457 ambiguous nodeid prefix.
458 """
458 """
459 try:
459 try:
460 revsymbol(repo, symbol)
460 revsymbol(repo, symbol)
461 return True
461 return True
462 except error.RepoLookupError:
462 except error.RepoLookupError:
463 return False
463 return False
464
464
465 def revsymbol(repo, symbol):
465 def revsymbol(repo, symbol):
466 """Returns a context given a single revision symbol (as string).
466 """Returns a context given a single revision symbol (as string).
467
467
468 This is similar to revsingle(), but accepts only a single revision symbol,
468 This is similar to revsingle(), but accepts only a single revision symbol,
469 i.e. things like ".", "tip", "1234", "deadbeef", "my-bookmark" work, but
469 i.e. things like ".", "tip", "1234", "deadbeef", "my-bookmark" work, but
470 not "max(public())".
470 not "max(public())".
471 """
471 """
472 if not isinstance(symbol, bytes):
472 if not isinstance(symbol, bytes):
473 msg = ("symbol (%s of type %s) was not a string, did you mean "
473 msg = ("symbol (%s of type %s) was not a string, did you mean "
474 "repo[symbol]?" % (symbol, type(symbol)))
474 "repo[symbol]?" % (symbol, type(symbol)))
475 raise error.ProgrammingError(msg)
475 raise error.ProgrammingError(msg)
476 try:
476 try:
477 if symbol in ('.', 'tip', 'null'):
477 if symbol in ('.', 'tip', 'null'):
478 return repo[symbol]
478 return repo[symbol]
479
479
480 try:
480 try:
481 r = int(symbol)
481 r = int(symbol)
482 if '%d' % r != symbol:
482 if '%d' % r != symbol:
483 raise ValueError
483 raise ValueError
484 l = len(repo.changelog)
484 l = len(repo.changelog)
485 if r < 0:
485 if r < 0:
486 r += l
486 r += l
487 if r < 0 or r >= l and r != wdirrev:
487 if r < 0 or r >= l and r != wdirrev:
488 raise ValueError
488 raise ValueError
489 return repo[r]
489 return repo[r]
490 except error.FilteredIndexError:
490 except error.FilteredIndexError:
491 raise
491 raise
492 except (ValueError, OverflowError, IndexError):
492 except (ValueError, OverflowError, IndexError):
493 pass
493 pass
494
494
495 if len(symbol) == 40:
495 if len(symbol) == 40:
496 try:
496 try:
497 node = bin(symbol)
497 node = bin(symbol)
498 rev = repo.changelog.rev(node)
498 rev = repo.changelog.rev(node)
499 return repo[rev]
499 return repo[rev]
500 except error.FilteredLookupError:
500 except error.FilteredLookupError:
501 raise
501 raise
502 except (TypeError, LookupError):
502 except (TypeError, LookupError):
503 pass
503 pass
504
504
505 # look up bookmarks through the name interface
505 # look up bookmarks through the name interface
506 try:
506 try:
507 node = repo.names.singlenode(repo, symbol)
507 node = repo.names.singlenode(repo, symbol)
508 rev = repo.changelog.rev(node)
508 rev = repo.changelog.rev(node)
509 return repo[rev]
509 return repo[rev]
510 except KeyError:
510 except KeyError:
511 pass
511 pass
512
512
513 node = resolvehexnodeidprefix(repo, symbol)
513 node = resolvehexnodeidprefix(repo, symbol)
514 if node is not None:
514 if node is not None:
515 rev = repo.changelog.rev(node)
515 rev = repo.changelog.rev(node)
516 return repo[rev]
516 return repo[rev]
517
517
518 raise error.RepoLookupError(_("unknown revision '%s'") % symbol)
518 raise error.RepoLookupError(_("unknown revision '%s'") % symbol)
519
519
520 except error.WdirUnsupported:
520 except error.WdirUnsupported:
521 return repo[None]
521 return repo[None]
522 except (error.FilteredIndexError, error.FilteredLookupError,
522 except (error.FilteredIndexError, error.FilteredLookupError,
523 error.FilteredRepoLookupError):
523 error.FilteredRepoLookupError):
524 raise _filterederror(repo, symbol)
524 raise _filterederror(repo, symbol)
525
525
526 def _filterederror(repo, changeid):
526 def _filterederror(repo, changeid):
527 """build an exception to be raised about a filtered changeid
527 """build an exception to be raised about a filtered changeid
528
528
529 This is extracted in a function to help extensions (eg: evolve) to
529 This is extracted in a function to help extensions (eg: evolve) to
530 experiment with various message variants."""
530 experiment with various message variants."""
531 if repo.filtername.startswith('visible'):
531 if repo.filtername.startswith('visible'):
532
532
533 # Check if the changeset is obsolete
533 # Check if the changeset is obsolete
534 unfilteredrepo = repo.unfiltered()
534 unfilteredrepo = repo.unfiltered()
535 ctx = revsymbol(unfilteredrepo, changeid)
535 ctx = revsymbol(unfilteredrepo, changeid)
536
536
537 # If the changeset is obsolete, enrich the message with the reason
537 # If the changeset is obsolete, enrich the message with the reason
538 # that made this changeset not visible
538 # that made this changeset not visible
539 if ctx.obsolete():
539 if ctx.obsolete():
540 msg = obsutil._getfilteredreason(repo, changeid, ctx)
540 msg = obsutil._getfilteredreason(repo, changeid, ctx)
541 else:
541 else:
542 msg = _("hidden revision '%s'") % changeid
542 msg = _("hidden revision '%s'") % changeid
543
543
544 hint = _('use --hidden to access hidden revisions')
544 hint = _('use --hidden to access hidden revisions')
545
545
546 return error.FilteredRepoLookupError(msg, hint=hint)
546 return error.FilteredRepoLookupError(msg, hint=hint)
547 msg = _("filtered revision '%s' (not in '%s' subset)")
547 msg = _("filtered revision '%s' (not in '%s' subset)")
548 msg %= (changeid, repo.filtername)
548 msg %= (changeid, repo.filtername)
549 return error.FilteredRepoLookupError(msg)
549 return error.FilteredRepoLookupError(msg)
550
550
551 def revsingle(repo, revspec, default='.', localalias=None):
551 def revsingle(repo, revspec, default='.', localalias=None):
552 if not revspec and revspec != 0:
552 if not revspec and revspec != 0:
553 return repo[default]
553 return repo[default]
554
554
555 l = revrange(repo, [revspec], localalias=localalias)
555 l = revrange(repo, [revspec], localalias=localalias)
556 if not l:
556 if not l:
557 raise error.Abort(_('empty revision set'))
557 raise error.Abort(_('empty revision set'))
558 return repo[l.last()]
558 return repo[l.last()]
559
559
560 def _pairspec(revspec):
560 def _pairspec(revspec):
561 tree = revsetlang.parse(revspec)
561 tree = revsetlang.parse(revspec)
562 return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
562 return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
563
563
564 def revpairnodes(repo, revs):
564 def revpairnodes(repo, revs):
565 repo.ui.deprecwarn("revpairnodes is deprecated, please use revpair", "4.6")
565 repo.ui.deprecwarn("revpairnodes is deprecated, please use revpair", "4.6")
566 ctx1, ctx2 = revpair(repo, revs)
566 ctx1, ctx2 = revpair(repo, revs)
567 return ctx1.node(), ctx2.node()
567 return ctx1.node(), ctx2.node()
568
568
569 def revpair(repo, revs):
569 def revpair(repo, revs):
570 if not revs:
570 if not revs:
571 return repo['.'], repo[None]
571 return repo['.'], repo[None]
572
572
573 l = revrange(repo, revs)
573 l = revrange(repo, revs)
574
574
575 if not l:
575 if not l:
576 first = second = None
576 first = second = None
577 elif l.isascending():
577 elif l.isascending():
578 first = l.min()
578 first = l.min()
579 second = l.max()
579 second = l.max()
580 elif l.isdescending():
580 elif l.isdescending():
581 first = l.max()
581 first = l.max()
582 second = l.min()
582 second = l.min()
583 else:
583 else:
584 first = l.first()
584 first = l.first()
585 second = l.last()
585 second = l.last()
586
586
587 if first is None:
587 if first is None:
588 raise error.Abort(_('empty revision range'))
588 raise error.Abort(_('empty revision range'))
589 if (first == second and len(revs) >= 2
589 if (first == second and len(revs) >= 2
590 and not all(revrange(repo, [r]) for r in revs)):
590 and not all(revrange(repo, [r]) for r in revs)):
591 raise error.Abort(_('empty revision on one side of range'))
591 raise error.Abort(_('empty revision on one side of range'))
592
592
593 # if top-level is range expression, the result must always be a pair
593 # if top-level is range expression, the result must always be a pair
594 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
594 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
595 return repo[first], repo[None]
595 return repo[first], repo[None]
596
596
597 return repo[first], repo[second]
597 return repo[first], repo[second]
598
598
599 def revrange(repo, specs, localalias=None):
599 def revrange(repo, specs, localalias=None):
600 """Execute 1 to many revsets and return the union.
600 """Execute 1 to many revsets and return the union.
601
601
602 This is the preferred mechanism for executing revsets using user-specified
602 This is the preferred mechanism for executing revsets using user-specified
603 config options, such as revset aliases.
603 config options, such as revset aliases.
604
604
605 The revsets specified by ``specs`` will be executed via a chained ``OR``
605 The revsets specified by ``specs`` will be executed via a chained ``OR``
606 expression. If ``specs`` is empty, an empty result is returned.
606 expression. If ``specs`` is empty, an empty result is returned.
607
607
608 ``specs`` can contain integers, in which case they are assumed to be
608 ``specs`` can contain integers, in which case they are assumed to be
609 revision numbers.
609 revision numbers.
610
610
611 It is assumed the revsets are already formatted. If you have arguments
611 It is assumed the revsets are already formatted. If you have arguments
612 that need to be expanded in the revset, call ``revsetlang.formatspec()``
612 that need to be expanded in the revset, call ``revsetlang.formatspec()``
613 and pass the result as an element of ``specs``.
613 and pass the result as an element of ``specs``.
614
614
615 Specifying a single revset is allowed.
615 Specifying a single revset is allowed.
616
616
617 Returns a ``revset.abstractsmartset`` which is a list-like interface over
617 Returns a ``revset.abstractsmartset`` which is a list-like interface over
618 integer revisions.
618 integer revisions.
619 """
619 """
620 allspecs = []
620 allspecs = []
621 for spec in specs:
621 for spec in specs:
622 if isinstance(spec, int):
622 if isinstance(spec, int):
623 spec = revsetlang.formatspec('rev(%d)', spec)
623 spec = revsetlang.formatspec('rev(%d)', spec)
624 allspecs.append(spec)
624 allspecs.append(spec)
625 return repo.anyrevs(allspecs, user=True, localalias=localalias)
625 return repo.anyrevs(allspecs, user=True, localalias=localalias)
626
626
627 def meaningfulparents(repo, ctx):
627 def meaningfulparents(repo, ctx):
628 """Return list of meaningful (or all if debug) parentrevs for rev.
628 """Return list of meaningful (or all if debug) parentrevs for rev.
629
629
630 For merges (two non-nullrev revisions) both parents are meaningful.
630 For merges (two non-nullrev revisions) both parents are meaningful.
631 Otherwise the first parent revision is considered meaningful if it
631 Otherwise the first parent revision is considered meaningful if it
632 is not the preceding revision.
632 is not the preceding revision.
633 """
633 """
634 parents = ctx.parents()
634 parents = ctx.parents()
635 if len(parents) > 1:
635 if len(parents) > 1:
636 return parents
636 return parents
637 if repo.ui.debugflag:
637 if repo.ui.debugflag:
638 return [parents[0], repo['null']]
638 return [parents[0], repo['null']]
639 if parents[0].rev() >= intrev(ctx) - 1:
639 if parents[0].rev() >= intrev(ctx) - 1:
640 return []
640 return []
641 return parents
641 return parents
642
642
643 def expandpats(pats):
643 def expandpats(pats):
644 '''Expand bare globs when running on windows.
644 '''Expand bare globs when running on windows.
645 On posix we assume it already has already been done by sh.'''
645 On posix we assume it already has already been done by sh.'''
646 if not util.expandglobs:
646 if not util.expandglobs:
647 return list(pats)
647 return list(pats)
648 ret = []
648 ret = []
649 for kindpat in pats:
649 for kindpat in pats:
650 kind, pat = matchmod._patsplit(kindpat, None)
650 kind, pat = matchmod._patsplit(kindpat, None)
651 if kind is None:
651 if kind is None:
652 try:
652 try:
653 globbed = glob.glob(pat)
653 globbed = glob.glob(pat)
654 except re.error:
654 except re.error:
655 globbed = [pat]
655 globbed = [pat]
656 if globbed:
656 if globbed:
657 ret.extend(globbed)
657 ret.extend(globbed)
658 continue
658 continue
659 ret.append(kindpat)
659 ret.append(kindpat)
660 return ret
660 return ret
661
661
662 def matchandpats(ctx, pats=(), opts=None, globbed=False, default='relpath',
662 def matchandpats(ctx, pats=(), opts=None, globbed=False, default='relpath',
663 badfn=None):
663 badfn=None):
664 '''Return a matcher and the patterns that were used.
664 '''Return a matcher and the patterns that were used.
665 The matcher will warn about bad matches, unless an alternate badfn callback
665 The matcher will warn about bad matches, unless an alternate badfn callback
666 is provided.'''
666 is provided.'''
667 if pats == ("",):
667 if pats == ("",):
668 pats = []
668 pats = []
669 if opts is None:
669 if opts is None:
670 opts = {}
670 opts = {}
671 if not globbed and default == 'relpath':
671 if not globbed and default == 'relpath':
672 pats = expandpats(pats or [])
672 pats = expandpats(pats or [])
673
673
674 def bad(f, msg):
674 def bad(f, msg):
675 ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
675 ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
676
676
677 if badfn is None:
677 if badfn is None:
678 badfn = bad
678 badfn = bad
679
679
680 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
680 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
681 default, listsubrepos=opts.get('subrepos'), badfn=badfn)
681 default, listsubrepos=opts.get('subrepos'), badfn=badfn)
682
682
683 if m.always():
683 if m.always():
684 pats = []
684 pats = []
685 return m, pats
685 return m, pats
686
686
687 def match(ctx, pats=(), opts=None, globbed=False, default='relpath',
687 def match(ctx, pats=(), opts=None, globbed=False, default='relpath',
688 badfn=None):
688 badfn=None):
689 '''Return a matcher that will warn about bad matches.'''
689 '''Return a matcher that will warn about bad matches.'''
690 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
690 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
691
691
692 def matchall(repo):
692 def matchall(repo):
693 '''Return a matcher that will efficiently match everything.'''
693 '''Return a matcher that will efficiently match everything.'''
694 return matchmod.always(repo.root, repo.getcwd())
694 return matchmod.always(repo.root, repo.getcwd())
695
695
696 def matchfiles(repo, files, badfn=None):
696 def matchfiles(repo, files, badfn=None):
697 '''Return a matcher that will efficiently match exactly these files.'''
697 '''Return a matcher that will efficiently match exactly these files.'''
698 return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn)
698 return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn)
699
699
700 def parsefollowlinespattern(repo, rev, pat, msg):
700 def parsefollowlinespattern(repo, rev, pat, msg):
701 """Return a file name from `pat` pattern suitable for usage in followlines
701 """Return a file name from `pat` pattern suitable for usage in followlines
702 logic.
702 logic.
703 """
703 """
704 if not matchmod.patkind(pat):
704 if not matchmod.patkind(pat):
705 return pathutil.canonpath(repo.root, repo.getcwd(), pat)
705 return pathutil.canonpath(repo.root, repo.getcwd(), pat)
706 else:
706 else:
707 ctx = repo[rev]
707 ctx = repo[rev]
708 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=ctx)
708 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=ctx)
709 files = [f for f in ctx if m(f)]
709 files = [f for f in ctx if m(f)]
710 if len(files) != 1:
710 if len(files) != 1:
711 raise error.ParseError(msg)
711 raise error.ParseError(msg)
712 return files[0]
712 return files[0]
713
713
714 def origpath(ui, repo, filepath):
714 def origpath(ui, repo, filepath):
715 '''customize where .orig files are created
715 '''customize where .orig files are created
716
716
717 Fetch user defined path from config file: [ui] origbackuppath = <path>
717 Fetch user defined path from config file: [ui] origbackuppath = <path>
718 Fall back to default (filepath with .orig suffix) if not specified
718 Fall back to default (filepath with .orig suffix) if not specified
719 '''
719 '''
720 origbackuppath = ui.config('ui', 'origbackuppath')
720 origbackuppath = ui.config('ui', 'origbackuppath')
721 if not origbackuppath:
721 if not origbackuppath:
722 return filepath + ".orig"
722 return filepath + ".orig"
723
723
724 # Convert filepath from an absolute path into a path inside the repo.
724 # Convert filepath from an absolute path into a path inside the repo.
725 filepathfromroot = util.normpath(os.path.relpath(filepath,
725 filepathfromroot = util.normpath(os.path.relpath(filepath,
726 start=repo.root))
726 start=repo.root))
727
727
728 origvfs = vfs.vfs(repo.wjoin(origbackuppath))
728 origvfs = vfs.vfs(repo.wjoin(origbackuppath))
729 origbackupdir = origvfs.dirname(filepathfromroot)
729 origbackupdir = origvfs.dirname(filepathfromroot)
730 if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir):
730 if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir):
731 ui.note(_('creating directory: %s\n') % origvfs.join(origbackupdir))
731 ui.note(_('creating directory: %s\n') % origvfs.join(origbackupdir))
732
732
733 # Remove any files that conflict with the backup file's path
733 # Remove any files that conflict with the backup file's path
734 for f in reversed(list(util.finddirs(filepathfromroot))):
734 for f in reversed(list(util.finddirs(filepathfromroot))):
735 if origvfs.isfileorlink(f):
735 if origvfs.isfileorlink(f):
736 ui.note(_('removing conflicting file: %s\n')
736 ui.note(_('removing conflicting file: %s\n')
737 % origvfs.join(f))
737 % origvfs.join(f))
738 origvfs.unlink(f)
738 origvfs.unlink(f)
739 break
739 break
740
740
741 origvfs.makedirs(origbackupdir)
741 origvfs.makedirs(origbackupdir)
742
742
743 if origvfs.isdir(filepathfromroot) and not origvfs.islink(filepathfromroot):
743 if origvfs.isdir(filepathfromroot) and not origvfs.islink(filepathfromroot):
744 ui.note(_('removing conflicting directory: %s\n')
744 ui.note(_('removing conflicting directory: %s\n')
745 % origvfs.join(filepathfromroot))
745 % origvfs.join(filepathfromroot))
746 origvfs.rmtree(filepathfromroot, forcibly=True)
746 origvfs.rmtree(filepathfromroot, forcibly=True)
747
747
748 return origvfs.join(filepathfromroot)
748 return origvfs.join(filepathfromroot)
749
749
750 class _containsnode(object):
750 class _containsnode(object):
751 """proxy __contains__(node) to container.__contains__ which accepts revs"""
751 """proxy __contains__(node) to container.__contains__ which accepts revs"""
752
752
753 def __init__(self, repo, revcontainer):
753 def __init__(self, repo, revcontainer):
754 self._torev = repo.changelog.rev
754 self._torev = repo.changelog.rev
755 self._revcontains = revcontainer.__contains__
755 self._revcontains = revcontainer.__contains__
756
756
757 def __contains__(self, node):
757 def __contains__(self, node):
758 return self._revcontains(self._torev(node))
758 return self._revcontains(self._torev(node))
759
759
760 def cleanupnodes(repo, replacements, operation, moves=None, metadata=None):
760 def cleanupnodes(repo, replacements, operation, moves=None, metadata=None):
761 """do common cleanups when old nodes are replaced by new nodes
761 """do common cleanups when old nodes are replaced by new nodes
762
762
763 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
763 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
764 (we might also want to move working directory parent in the future)
764 (we might also want to move working directory parent in the future)
765
765
766 By default, bookmark moves are calculated automatically from 'replacements',
766 By default, bookmark moves are calculated automatically from 'replacements',
767 but 'moves' can be used to override that. Also, 'moves' may include
767 but 'moves' can be used to override that. Also, 'moves' may include
768 additional bookmark moves that should not have associated obsmarkers.
768 additional bookmark moves that should not have associated obsmarkers.
769
769
770 replacements is {oldnode: [newnode]} or a iterable of nodes if they do not
770 replacements is {oldnode: [newnode]} or a iterable of nodes if they do not
771 have replacements. operation is a string, like "rebase".
771 have replacements. operation is a string, like "rebase".
772
772
773 metadata is dictionary containing metadata to be stored in obsmarker if
773 metadata is dictionary containing metadata to be stored in obsmarker if
774 obsolescence is enabled.
774 obsolescence is enabled.
775 """
775 """
776 if not replacements and not moves:
776 if not replacements and not moves:
777 return
777 return
778
778
779 # translate mapping's other forms
779 # translate mapping's other forms
780 if not util.safehasattr(replacements, 'items'):
780 if not util.safehasattr(replacements, 'items'):
781 replacements = {n: () for n in replacements}
781 replacements = {n: () for n in replacements}
782
782
783 # Calculate bookmark movements
783 # Calculate bookmark movements
784 if moves is None:
784 if moves is None:
785 moves = {}
785 moves = {}
786 # Unfiltered repo is needed since nodes in replacements might be hidden.
786 # Unfiltered repo is needed since nodes in replacements might be hidden.
787 unfi = repo.unfiltered()
787 unfi = repo.unfiltered()
788 for oldnode, newnodes in replacements.items():
788 for oldnode, newnodes in replacements.items():
789 if oldnode in moves:
789 if oldnode in moves:
790 continue
790 continue
791 if len(newnodes) > 1:
791 if len(newnodes) > 1:
792 # usually a split, take the one with biggest rev number
792 # usually a split, take the one with biggest rev number
793 newnode = next(unfi.set('max(%ln)', newnodes)).node()
793 newnode = next(unfi.set('max(%ln)', newnodes)).node()
794 elif len(newnodes) == 0:
794 elif len(newnodes) == 0:
795 # move bookmark backwards
795 # move bookmark backwards
796 roots = list(unfi.set('max((::%n) - %ln)', oldnode,
796 roots = list(unfi.set('max((::%n) - %ln)', oldnode,
797 list(replacements)))
797 list(replacements)))
798 if roots:
798 if roots:
799 newnode = roots[0].node()
799 newnode = roots[0].node()
800 else:
800 else:
801 newnode = nullid
801 newnode = nullid
802 else:
802 else:
803 newnode = newnodes[0]
803 newnode = newnodes[0]
804 moves[oldnode] = newnode
804 moves[oldnode] = newnode
805
805
806 with repo.transaction('cleanup') as tr:
806 with repo.transaction('cleanup') as tr:
807 # Move bookmarks
807 # Move bookmarks
808 bmarks = repo._bookmarks
808 bmarks = repo._bookmarks
809 bmarkchanges = []
809 bmarkchanges = []
810 allnewnodes = [n for ns in replacements.values() for n in ns]
810 allnewnodes = [n for ns in replacements.values() for n in ns]
811 for oldnode, newnode in moves.items():
811 for oldnode, newnode in moves.items():
812 oldbmarks = repo.nodebookmarks(oldnode)
812 oldbmarks = repo.nodebookmarks(oldnode)
813 if not oldbmarks:
813 if not oldbmarks:
814 continue
814 continue
815 from . import bookmarks # avoid import cycle
815 from . import bookmarks # avoid import cycle
816 repo.ui.debug('moving bookmarks %r from %s to %s\n' %
816 repo.ui.debug('moving bookmarks %r from %s to %s\n' %
817 (util.rapply(pycompat.maybebytestr, oldbmarks),
817 (util.rapply(pycompat.maybebytestr, oldbmarks),
818 hex(oldnode), hex(newnode)))
818 hex(oldnode), hex(newnode)))
819 # Delete divergent bookmarks being parents of related newnodes
819 # Delete divergent bookmarks being parents of related newnodes
820 deleterevs = repo.revs('parents(roots(%ln & (::%n))) - parents(%n)',
820 deleterevs = repo.revs('parents(roots(%ln & (::%n))) - parents(%n)',
821 allnewnodes, newnode, oldnode)
821 allnewnodes, newnode, oldnode)
822 deletenodes = _containsnode(repo, deleterevs)
822 deletenodes = _containsnode(repo, deleterevs)
823 for name in oldbmarks:
823 for name in oldbmarks:
824 bmarkchanges.append((name, newnode))
824 bmarkchanges.append((name, newnode))
825 for b in bookmarks.divergent2delete(repo, deletenodes, name):
825 for b in bookmarks.divergent2delete(repo, deletenodes, name):
826 bmarkchanges.append((b, None))
826 bmarkchanges.append((b, None))
827
827
828 if bmarkchanges:
828 if bmarkchanges:
829 bmarks.applychanges(repo, tr, bmarkchanges)
829 bmarks.applychanges(repo, tr, bmarkchanges)
830
830
831 # Obsolete or strip nodes
831 # Obsolete or strip nodes
832 if obsolete.isenabled(repo, obsolete.createmarkersopt):
832 if obsolete.isenabled(repo, obsolete.createmarkersopt):
833 # If a node is already obsoleted, and we want to obsolete it
833 # If a node is already obsoleted, and we want to obsolete it
834 # without a successor, skip that obssolete request since it's
834 # without a successor, skip that obssolete request since it's
835 # unnecessary. That's the "if s or not isobs(n)" check below.
835 # unnecessary. That's the "if s or not isobs(n)" check below.
836 # Also sort the node in topology order, that might be useful for
836 # Also sort the node in topology order, that might be useful for
837 # some obsstore logic.
837 # some obsstore logic.
838 # NOTE: the filtering and sorting might belong to createmarkers.
838 # NOTE: the filtering and sorting might belong to createmarkers.
839 isobs = unfi.obsstore.successors.__contains__
839 isobs = unfi.obsstore.successors.__contains__
840 torev = unfi.changelog.rev
840 torev = unfi.changelog.rev
841 sortfunc = lambda ns: torev(ns[0])
841 sortfunc = lambda ns: torev(ns[0])
842 rels = [(unfi[n], tuple(unfi[m] for m in s))
842 rels = [(unfi[n], tuple(unfi[m] for m in s))
843 for n, s in sorted(replacements.items(), key=sortfunc)
843 for n, s in sorted(replacements.items(), key=sortfunc)
844 if s or not isobs(n)]
844 if s or not isobs(n)]
845 if rels:
845 if rels:
846 obsolete.createmarkers(repo, rels, operation=operation,
846 obsolete.createmarkers(repo, rels, operation=operation,
847 metadata=metadata)
847 metadata=metadata)
848 else:
848 else:
849 from . import repair # avoid import cycle
849 from . import repair # avoid import cycle
850 tostrip = list(replacements)
850 tostrip = list(replacements)
851 if tostrip:
851 if tostrip:
852 repair.delayedstrip(repo.ui, repo, tostrip, operation)
852 repair.delayedstrip(repo.ui, repo, tostrip, operation)
853
853
854 def addremove(repo, matcher, prefix, opts=None):
854 def addremove(repo, matcher, prefix, opts=None):
855 if opts is None:
855 if opts is None:
856 opts = {}
856 opts = {}
857 m = matcher
857 m = matcher
858 dry_run = opts.get('dry_run')
858 dry_run = opts.get('dry_run')
859 try:
859 try:
860 similarity = float(opts.get('similarity') or 0)
860 similarity = float(opts.get('similarity') or 0)
861 except ValueError:
861 except ValueError:
862 raise error.Abort(_('similarity must be a number'))
862 raise error.Abort(_('similarity must be a number'))
863 if similarity < 0 or similarity > 100:
863 if similarity < 0 or similarity > 100:
864 raise error.Abort(_('similarity must be between 0 and 100'))
864 raise error.Abort(_('similarity must be between 0 and 100'))
865 similarity /= 100.0
865 similarity /= 100.0
866
866
867 ret = 0
867 ret = 0
868 join = lambda f: os.path.join(prefix, f)
868 join = lambda f: os.path.join(prefix, f)
869
869
870 wctx = repo[None]
870 wctx = repo[None]
871 for subpath in sorted(wctx.substate):
871 for subpath in sorted(wctx.substate):
872 submatch = matchmod.subdirmatcher(subpath, m)
872 submatch = matchmod.subdirmatcher(subpath, m)
873 if opts.get('subrepos') or m.exact(subpath) or any(submatch.files()):
873 if opts.get('subrepos') or m.exact(subpath) or any(submatch.files()):
874 sub = wctx.sub(subpath)
874 sub = wctx.sub(subpath)
875 try:
875 try:
876 if sub.addremove(submatch, prefix, opts):
876 if sub.addremove(submatch, prefix, opts):
877 ret = 1
877 ret = 1
878 except error.LookupError:
878 except error.LookupError:
879 repo.ui.status(_("skipping missing subrepository: %s\n")
879 repo.ui.status(_("skipping missing subrepository: %s\n")
880 % join(subpath))
880 % join(subpath))
881
881
882 rejected = []
882 rejected = []
883 def badfn(f, msg):
883 def badfn(f, msg):
884 if f in m.files():
884 if f in m.files():
885 m.bad(f, msg)
885 m.bad(f, msg)
886 rejected.append(f)
886 rejected.append(f)
887
887
888 badmatch = matchmod.badmatch(m, badfn)
888 badmatch = matchmod.badmatch(m, badfn)
889 added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
889 added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
890 badmatch)
890 badmatch)
891
891
892 unknownset = set(unknown + forgotten)
892 unknownset = set(unknown + forgotten)
893 toprint = unknownset.copy()
893 toprint = unknownset.copy()
894 toprint.update(deleted)
894 toprint.update(deleted)
895 for abs in sorted(toprint):
895 for abs in sorted(toprint):
896 if repo.ui.verbose or not m.exact(abs):
896 if repo.ui.verbose or not m.exact(abs):
897 if abs in unknownset:
897 if abs in unknownset:
898 status = _('adding %s\n') % m.uipath(abs)
898 status = _('adding %s\n') % m.uipath(abs)
899 else:
899 else:
900 status = _('removing %s\n') % m.uipath(abs)
900 status = _('removing %s\n') % m.uipath(abs)
901 repo.ui.status(status)
901 repo.ui.status(status)
902
902
903 renames = _findrenames(repo, m, added + unknown, removed + deleted,
903 renames = _findrenames(repo, m, added + unknown, removed + deleted,
904 similarity)
904 similarity)
905
905
906 if not dry_run:
906 if not dry_run:
907 _markchanges(repo, unknown + forgotten, deleted, renames)
907 _markchanges(repo, unknown + forgotten, deleted, renames)
908
908
909 for f in rejected:
909 for f in rejected:
910 if f in m.files():
910 if f in m.files():
911 return 1
911 return 1
912 return ret
912 return ret
913
913
914 def marktouched(repo, files, similarity=0.0):
914 def marktouched(repo, files, similarity=0.0):
915 '''Assert that files have somehow been operated upon. files are relative to
915 '''Assert that files have somehow been operated upon. files are relative to
916 the repo root.'''
916 the repo root.'''
917 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
917 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
918 rejected = []
918 rejected = []
919
919
920 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
920 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
921
921
922 if repo.ui.verbose:
922 if repo.ui.verbose:
923 unknownset = set(unknown + forgotten)
923 unknownset = set(unknown + forgotten)
924 toprint = unknownset.copy()
924 toprint = unknownset.copy()
925 toprint.update(deleted)
925 toprint.update(deleted)
926 for abs in sorted(toprint):
926 for abs in sorted(toprint):
927 if abs in unknownset:
927 if abs in unknownset:
928 status = _('adding %s\n') % abs
928 status = _('adding %s\n') % abs
929 else:
929 else:
930 status = _('removing %s\n') % abs
930 status = _('removing %s\n') % abs
931 repo.ui.status(status)
931 repo.ui.status(status)
932
932
933 renames = _findrenames(repo, m, added + unknown, removed + deleted,
933 renames = _findrenames(repo, m, added + unknown, removed + deleted,
934 similarity)
934 similarity)
935
935
936 _markchanges(repo, unknown + forgotten, deleted, renames)
936 _markchanges(repo, unknown + forgotten, deleted, renames)
937
937
938 for f in rejected:
938 for f in rejected:
939 if f in m.files():
939 if f in m.files():
940 return 1
940 return 1
941 return 0
941 return 0
942
942
943 def _interestingfiles(repo, matcher):
943 def _interestingfiles(repo, matcher):
944 '''Walk dirstate with matcher, looking for files that addremove would care
944 '''Walk dirstate with matcher, looking for files that addremove would care
945 about.
945 about.
946
946
947 This is different from dirstate.status because it doesn't care about
947 This is different from dirstate.status because it doesn't care about
948 whether files are modified or clean.'''
948 whether files are modified or clean.'''
949 added, unknown, deleted, removed, forgotten = [], [], [], [], []
949 added, unknown, deleted, removed, forgotten = [], [], [], [], []
950 audit_path = pathutil.pathauditor(repo.root, cached=True)
950 audit_path = pathutil.pathauditor(repo.root, cached=True)
951
951
952 ctx = repo[None]
952 ctx = repo[None]
953 dirstate = repo.dirstate
953 dirstate = repo.dirstate
954 walkresults = dirstate.walk(matcher, subrepos=sorted(ctx.substate),
954 walkresults = dirstate.walk(matcher, subrepos=sorted(ctx.substate),
955 unknown=True, ignored=False, full=False)
955 unknown=True, ignored=False, full=False)
956 for abs, st in walkresults.iteritems():
956 for abs, st in walkresults.iteritems():
957 dstate = dirstate[abs]
957 dstate = dirstate[abs]
958 if dstate == '?' and audit_path.check(abs):
958 if dstate == '?' and audit_path.check(abs):
959 unknown.append(abs)
959 unknown.append(abs)
960 elif dstate != 'r' and not st:
960 elif dstate != 'r' and not st:
961 deleted.append(abs)
961 deleted.append(abs)
962 elif dstate == 'r' and st:
962 elif dstate == 'r' and st:
963 forgotten.append(abs)
963 forgotten.append(abs)
964 # for finding renames
964 # for finding renames
965 elif dstate == 'r' and not st:
965 elif dstate == 'r' and not st:
966 removed.append(abs)
966 removed.append(abs)
967 elif dstate == 'a':
967 elif dstate == 'a':
968 added.append(abs)
968 added.append(abs)
969
969
970 return added, unknown, deleted, removed, forgotten
970 return added, unknown, deleted, removed, forgotten
971
971
972 def _findrenames(repo, matcher, added, removed, similarity):
972 def _findrenames(repo, matcher, added, removed, similarity):
973 '''Find renames from removed files to added ones.'''
973 '''Find renames from removed files to added ones.'''
974 renames = {}
974 renames = {}
975 if similarity > 0:
975 if similarity > 0:
976 for old, new, score in similar.findrenames(repo, added, removed,
976 for old, new, score in similar.findrenames(repo, added, removed,
977 similarity):
977 similarity):
978 if (repo.ui.verbose or not matcher.exact(old)
978 if (repo.ui.verbose or not matcher.exact(old)
979 or not matcher.exact(new)):
979 or not matcher.exact(new)):
980 repo.ui.status(_('recording removal of %s as rename to %s '
980 repo.ui.status(_('recording removal of %s as rename to %s '
981 '(%d%% similar)\n') %
981 '(%d%% similar)\n') %
982 (matcher.rel(old), matcher.rel(new),
982 (matcher.rel(old), matcher.rel(new),
983 score * 100))
983 score * 100))
984 renames[new] = old
984 renames[new] = old
985 return renames
985 return renames
986
986
987 def _markchanges(repo, unknown, deleted, renames):
987 def _markchanges(repo, unknown, deleted, renames):
988 '''Marks the files in unknown as added, the files in deleted as removed,
988 '''Marks the files in unknown as added, the files in deleted as removed,
989 and the files in renames as copied.'''
989 and the files in renames as copied.'''
990 wctx = repo[None]
990 wctx = repo[None]
991 with repo.wlock():
991 with repo.wlock():
992 wctx.forget(deleted)
992 wctx.forget(deleted)
993 wctx.add(unknown)
993 wctx.add(unknown)
994 for new, old in renames.iteritems():
994 for new, old in renames.iteritems():
995 wctx.copy(old, new)
995 wctx.copy(old, new)
996
996
997 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
997 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
998 """Update the dirstate to reflect the intent of copying src to dst. For
998 """Update the dirstate to reflect the intent of copying src to dst. For
999 different reasons it might not end with dst being marked as copied from src.
999 different reasons it might not end with dst being marked as copied from src.
1000 """
1000 """
1001 origsrc = repo.dirstate.copied(src) or src
1001 origsrc = repo.dirstate.copied(src) or src
1002 if dst == origsrc: # copying back a copy?
1002 if dst == origsrc: # copying back a copy?
1003 if repo.dirstate[dst] not in 'mn' and not dryrun:
1003 if repo.dirstate[dst] not in 'mn' and not dryrun:
1004 repo.dirstate.normallookup(dst)
1004 repo.dirstate.normallookup(dst)
1005 else:
1005 else:
1006 if repo.dirstate[origsrc] == 'a' and origsrc == src:
1006 if repo.dirstate[origsrc] == 'a' and origsrc == src:
1007 if not ui.quiet:
1007 if not ui.quiet:
1008 ui.warn(_("%s has not been committed yet, so no copy "
1008 ui.warn(_("%s has not been committed yet, so no copy "
1009 "data will be stored for %s.\n")
1009 "data will be stored for %s.\n")
1010 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
1010 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
1011 if repo.dirstate[dst] in '?r' and not dryrun:
1011 if repo.dirstate[dst] in '?r' and not dryrun:
1012 wctx.add([dst])
1012 wctx.add([dst])
1013 elif not dryrun:
1013 elif not dryrun:
1014 wctx.copy(origsrc, dst)
1014 wctx.copy(origsrc, dst)
1015
1015
1016 def readrequires(opener, supported):
1016 def readrequires(opener, supported):
1017 '''Reads and parses .hg/requires and checks if all entries found
1017 '''Reads and parses .hg/requires and checks if all entries found
1018 are in the list of supported features.'''
1018 are in the list of supported features.'''
1019 requirements = set(opener.read("requires").splitlines())
1019 requirements = set(opener.read("requires").splitlines())
1020 missings = []
1020 missings = []
1021 for r in requirements:
1021 for r in requirements:
1022 if r not in supported:
1022 if r not in supported:
1023 if not r or not r[0:1].isalnum():
1023 if not r or not r[0:1].isalnum():
1024 raise error.RequirementError(_(".hg/requires file is corrupt"))
1024 raise error.RequirementError(_(".hg/requires file is corrupt"))
1025 missings.append(r)
1025 missings.append(r)
1026 missings.sort()
1026 missings.sort()
1027 if missings:
1027 if missings:
1028 raise error.RequirementError(
1028 raise error.RequirementError(
1029 _("repository requires features unknown to this Mercurial: %s")
1029 _("repository requires features unknown to this Mercurial: %s")
1030 % " ".join(missings),
1030 % " ".join(missings),
1031 hint=_("see https://mercurial-scm.org/wiki/MissingRequirement"
1031 hint=_("see https://mercurial-scm.org/wiki/MissingRequirement"
1032 " for more information"))
1032 " for more information"))
1033 return requirements
1033 return requirements
1034
1034
1035 def writerequires(opener, requirements):
1035 def writerequires(opener, requirements):
1036 with opener('requires', 'w') as fp:
1036 with opener('requires', 'w') as fp:
1037 for r in sorted(requirements):
1037 for r in sorted(requirements):
1038 fp.write("%s\n" % r)
1038 fp.write("%s\n" % r)
1039
1039
1040 class filecachesubentry(object):
1040 class filecachesubentry(object):
1041 def __init__(self, path, stat):
1041 def __init__(self, path, stat):
1042 self.path = path
1042 self.path = path
1043 self.cachestat = None
1043 self.cachestat = None
1044 self._cacheable = None
1044 self._cacheable = None
1045
1045
1046 if stat:
1046 if stat:
1047 self.cachestat = filecachesubentry.stat(self.path)
1047 self.cachestat = filecachesubentry.stat(self.path)
1048
1048
1049 if self.cachestat:
1049 if self.cachestat:
1050 self._cacheable = self.cachestat.cacheable()
1050 self._cacheable = self.cachestat.cacheable()
1051 else:
1051 else:
1052 # None means we don't know yet
1052 # None means we don't know yet
1053 self._cacheable = None
1053 self._cacheable = None
1054
1054
1055 def refresh(self):
1055 def refresh(self):
1056 if self.cacheable():
1056 if self.cacheable():
1057 self.cachestat = filecachesubentry.stat(self.path)
1057 self.cachestat = filecachesubentry.stat(self.path)
1058
1058
1059 def cacheable(self):
1059 def cacheable(self):
1060 if self._cacheable is not None:
1060 if self._cacheable is not None:
1061 return self._cacheable
1061 return self._cacheable
1062
1062
1063 # we don't know yet, assume it is for now
1063 # we don't know yet, assume it is for now
1064 return True
1064 return True
1065
1065
1066 def changed(self):
1066 def changed(self):
1067 # no point in going further if we can't cache it
1067 # no point in going further if we can't cache it
1068 if not self.cacheable():
1068 if not self.cacheable():
1069 return True
1069 return True
1070
1070
1071 newstat = filecachesubentry.stat(self.path)
1071 newstat = filecachesubentry.stat(self.path)
1072
1072
1073 # we may not know if it's cacheable yet, check again now
1073 # we may not know if it's cacheable yet, check again now
1074 if newstat and self._cacheable is None:
1074 if newstat and self._cacheable is None:
1075 self._cacheable = newstat.cacheable()
1075 self._cacheable = newstat.cacheable()
1076
1076
1077 # check again
1077 # check again
1078 if not self._cacheable:
1078 if not self._cacheable:
1079 return True
1079 return True
1080
1080
1081 if self.cachestat != newstat:
1081 if self.cachestat != newstat:
1082 self.cachestat = newstat
1082 self.cachestat = newstat
1083 return True
1083 return True
1084 else:
1084 else:
1085 return False
1085 return False
1086
1086
1087 @staticmethod
1087 @staticmethod
1088 def stat(path):
1088 def stat(path):
1089 try:
1089 try:
1090 return util.cachestat(path)
1090 return util.cachestat(path)
1091 except OSError as e:
1091 except OSError as e:
1092 if e.errno != errno.ENOENT:
1092 if e.errno != errno.ENOENT:
1093 raise
1093 raise
1094
1094
1095 class filecacheentry(object):
1095 class filecacheentry(object):
1096 def __init__(self, paths, stat=True):
1096 def __init__(self, paths, stat=True):
1097 self._entries = []
1097 self._entries = []
1098 for path in paths:
1098 for path in paths:
1099 self._entries.append(filecachesubentry(path, stat))
1099 self._entries.append(filecachesubentry(path, stat))
1100
1100
1101 def changed(self):
1101 def changed(self):
1102 '''true if any entry has changed'''
1102 '''true if any entry has changed'''
1103 for entry in self._entries:
1103 for entry in self._entries:
1104 if entry.changed():
1104 if entry.changed():
1105 return True
1105 return True
1106 return False
1106 return False
1107
1107
1108 def refresh(self):
1108 def refresh(self):
1109 for entry in self._entries:
1109 for entry in self._entries:
1110 entry.refresh()
1110 entry.refresh()
1111
1111
1112 class filecache(object):
1112 class filecache(object):
1113 '''A property like decorator that tracks files under .hg/ for updates.
1113 '''A property like decorator that tracks files under .hg/ for updates.
1114
1114
1115 Records stat info when called in _filecache.
1115 Records stat info when called in _filecache.
1116
1116
1117 On subsequent calls, compares old stat info with new info, and recreates the
1117 On subsequent calls, compares old stat info with new info, and recreates the
1118 object when any of the files changes, updating the new stat info in
1118 object when any of the files changes, updating the new stat info in
1119 _filecache.
1119 _filecache.
1120
1120
1121 Mercurial either atomic renames or appends for files under .hg,
1121 Mercurial either atomic renames or appends for files under .hg,
1122 so to ensure the cache is reliable we need the filesystem to be able
1122 so to ensure the cache is reliable we need the filesystem to be able
1123 to tell us if a file has been replaced. If it can't, we fallback to
1123 to tell us if a file has been replaced. If it can't, we fallback to
1124 recreating the object on every call (essentially the same behavior as
1124 recreating the object on every call (essentially the same behavior as
1125 propertycache).
1125 propertycache).
1126
1126
1127 '''
1127 '''
1128 def __init__(self, *paths):
1128 def __init__(self, *paths):
1129 self.paths = paths
1129 self.paths = paths
1130
1130
1131 def join(self, obj, fname):
1131 def join(self, obj, fname):
1132 """Used to compute the runtime path of a cached file.
1132 """Used to compute the runtime path of a cached file.
1133
1133
1134 Users should subclass filecache and provide their own version of this
1134 Users should subclass filecache and provide their own version of this
1135 function to call the appropriate join function on 'obj' (an instance
1135 function to call the appropriate join function on 'obj' (an instance
1136 of the class that its member function was decorated).
1136 of the class that its member function was decorated).
1137 """
1137 """
1138 raise NotImplementedError
1138 raise NotImplementedError
1139
1139
1140 def __call__(self, func):
1140 def __call__(self, func):
1141 self.func = func
1141 self.func = func
1142 self.name = func.__name__.encode('ascii')
1142 self.name = func.__name__.encode('ascii')
1143 return self
1143 return self
1144
1144
1145 def __get__(self, obj, type=None):
1145 def __get__(self, obj, type=None):
1146 # if accessed on the class, return the descriptor itself.
1146 # if accessed on the class, return the descriptor itself.
1147 if obj is None:
1147 if obj is None:
1148 return self
1148 return self
1149 # do we need to check if the file changed?
1149 # do we need to check if the file changed?
1150 if self.name in obj.__dict__:
1150 if self.name in obj.__dict__:
1151 assert self.name in obj._filecache, self.name
1151 assert self.name in obj._filecache, self.name
1152 return obj.__dict__[self.name]
1152 return obj.__dict__[self.name]
1153
1153
1154 entry = obj._filecache.get(self.name)
1154 entry = obj._filecache.get(self.name)
1155
1155
1156 if entry:
1156 if entry:
1157 if entry.changed():
1157 if entry.changed():
1158 entry.obj = self.func(obj)
1158 entry.obj = self.func(obj)
1159 else:
1159 else:
1160 paths = [self.join(obj, path) for path in self.paths]
1160 paths = [self.join(obj, path) for path in self.paths]
1161
1161
1162 # We stat -before- creating the object so our cache doesn't lie if
1162 # We stat -before- creating the object so our cache doesn't lie if
1163 # a writer modified between the time we read and stat
1163 # a writer modified between the time we read and stat
1164 entry = filecacheentry(paths, True)
1164 entry = filecacheentry(paths, True)
1165 entry.obj = self.func(obj)
1165 entry.obj = self.func(obj)
1166
1166
1167 obj._filecache[self.name] = entry
1167 obj._filecache[self.name] = entry
1168
1168
1169 obj.__dict__[self.name] = entry.obj
1169 obj.__dict__[self.name] = entry.obj
1170 return entry.obj
1170 return entry.obj
1171
1171
1172 def __set__(self, obj, value):
1172 def __set__(self, obj, value):
1173 if self.name not in obj._filecache:
1173 if self.name not in obj._filecache:
1174 # we add an entry for the missing value because X in __dict__
1174 # we add an entry for the missing value because X in __dict__
1175 # implies X in _filecache
1175 # implies X in _filecache
1176 paths = [self.join(obj, path) for path in self.paths]
1176 paths = [self.join(obj, path) for path in self.paths]
1177 ce = filecacheentry(paths, False)
1177 ce = filecacheentry(paths, False)
1178 obj._filecache[self.name] = ce
1178 obj._filecache[self.name] = ce
1179 else:
1179 else:
1180 ce = obj._filecache[self.name]
1180 ce = obj._filecache[self.name]
1181
1181
1182 ce.obj = value # update cached copy
1182 ce.obj = value # update cached copy
1183 obj.__dict__[self.name] = value # update copy returned by obj.x
1183 obj.__dict__[self.name] = value # update copy returned by obj.x
1184
1184
1185 def __delete__(self, obj):
1185 def __delete__(self, obj):
1186 try:
1186 try:
1187 del obj.__dict__[self.name]
1187 del obj.__dict__[self.name]
1188 except KeyError:
1188 except KeyError:
1189 raise AttributeError(self.name)
1189 raise AttributeError(self.name)
1190
1190
1191 def extdatasource(repo, source):
1191 def extdatasource(repo, source):
1192 """Gather a map of rev -> value dict from the specified source
1192 """Gather a map of rev -> value dict from the specified source
1193
1193
1194 A source spec is treated as a URL, with a special case shell: type
1194 A source spec is treated as a URL, with a special case shell: type
1195 for parsing the output from a shell command.
1195 for parsing the output from a shell command.
1196
1196
1197 The data is parsed as a series of newline-separated records where
1197 The data is parsed as a series of newline-separated records where
1198 each record is a revision specifier optionally followed by a space
1198 each record is a revision specifier optionally followed by a space
1199 and a freeform string value. If the revision is known locally, it
1199 and a freeform string value. If the revision is known locally, it
1200 is converted to a rev, otherwise the record is skipped.
1200 is converted to a rev, otherwise the record is skipped.
1201
1201
1202 Note that both key and value are treated as UTF-8 and converted to
1202 Note that both key and value are treated as UTF-8 and converted to
1203 the local encoding. This allows uniformity between local and
1203 the local encoding. This allows uniformity between local and
1204 remote data sources.
1204 remote data sources.
1205 """
1205 """
1206
1206
1207 spec = repo.ui.config("extdata", source)
1207 spec = repo.ui.config("extdata", source)
1208 if not spec:
1208 if not spec:
1209 raise error.Abort(_("unknown extdata source '%s'") % source)
1209 raise error.Abort(_("unknown extdata source '%s'") % source)
1210
1210
1211 data = {}
1211 data = {}
1212 src = proc = None
1212 src = proc = None
1213 try:
1213 try:
1214 if spec.startswith("shell:"):
1214 if spec.startswith("shell:"):
1215 # external commands should be run relative to the repo root
1215 # external commands should be run relative to the repo root
1216 cmd = spec[6:]
1216 cmd = spec[6:]
1217 proc = subprocess.Popen(cmd, shell=True, bufsize=-1,
1217 proc = subprocess.Popen(cmd, shell=True, bufsize=-1,
1218 close_fds=procutil.closefds,
1218 close_fds=procutil.closefds,
1219 stdout=subprocess.PIPE, cwd=repo.root)
1219 stdout=subprocess.PIPE, cwd=repo.root)
1220 src = proc.stdout
1220 src = proc.stdout
1221 else:
1221 else:
1222 # treat as a URL or file
1222 # treat as a URL or file
1223 src = url.open(repo.ui, spec)
1223 src = url.open(repo.ui, spec)
1224 for l in src:
1224 for l in src:
1225 if " " in l:
1225 if " " in l:
1226 k, v = l.strip().split(" ", 1)
1226 k, v = l.strip().split(" ", 1)
1227 else:
1227 else:
1228 k, v = l.strip(), ""
1228 k, v = l.strip(), ""
1229
1229
1230 k = encoding.tolocal(k)
1230 k = encoding.tolocal(k)
1231 try:
1231 try:
1232 data[revsingle(repo, k).rev()] = encoding.tolocal(v)
1232 data[revsingle(repo, k).rev()] = encoding.tolocal(v)
1233 except (error.LookupError, error.RepoLookupError):
1233 except (error.LookupError, error.RepoLookupError):
1234 pass # we ignore data for nodes that don't exist locally
1234 pass # we ignore data for nodes that don't exist locally
1235 finally:
1235 finally:
1236 if proc:
1236 if proc:
1237 proc.communicate()
1237 proc.communicate()
1238 if src:
1238 if src:
1239 src.close()
1239 src.close()
1240 if proc and proc.returncode != 0:
1240 if proc and proc.returncode != 0:
1241 raise error.Abort(_("extdata command '%s' failed: %s")
1241 raise error.Abort(_("extdata command '%s' failed: %s")
1242 % (cmd, procutil.explainexit(proc.returncode)))
1242 % (cmd, procutil.explainexit(proc.returncode)))
1243
1243
1244 return data
1244 return data
1245
1245
1246 def _locksub(repo, lock, envvar, cmd, environ=None, *args, **kwargs):
1246 def _locksub(repo, lock, envvar, cmd, environ=None, *args, **kwargs):
1247 if lock is None:
1247 if lock is None:
1248 raise error.LockInheritanceContractViolation(
1248 raise error.LockInheritanceContractViolation(
1249 'lock can only be inherited while held')
1249 'lock can only be inherited while held')
1250 if environ is None:
1250 if environ is None:
1251 environ = {}
1251 environ = {}
1252 with lock.inherit() as locker:
1252 with lock.inherit() as locker:
1253 environ[envvar] = locker
1253 environ[envvar] = locker
1254 return repo.ui.system(cmd, environ=environ, *args, **kwargs)
1254 return repo.ui.system(cmd, environ=environ, *args, **kwargs)
1255
1255
1256 def wlocksub(repo, cmd, *args, **kwargs):
1256 def wlocksub(repo, cmd, *args, **kwargs):
1257 """run cmd as a subprocess that allows inheriting repo's wlock
1257 """run cmd as a subprocess that allows inheriting repo's wlock
1258
1258
1259 This can only be called while the wlock is held. This takes all the
1259 This can only be called while the wlock is held. This takes all the
1260 arguments that ui.system does, and returns the exit code of the
1260 arguments that ui.system does, and returns the exit code of the
1261 subprocess."""
1261 subprocess."""
1262 return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args,
1262 return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args,
1263 **kwargs)
1263 **kwargs)
1264
1264
1265 def gdinitconfig(ui):
1265 def gdinitconfig(ui):
1266 """helper function to know if a repo should be created as general delta
1266 """helper function to know if a repo should be created as general delta
1267 """
1267 """
1268 # experimental config: format.generaldelta
1268 # experimental config: format.generaldelta
1269 return (ui.configbool('format', 'generaldelta')
1269 return (ui.configbool('format', 'generaldelta')
1270 or ui.configbool('format', 'usegeneraldelta'))
1270 or ui.configbool('format', 'usegeneraldelta'))
1271
1271
1272 def gddeltaconfig(ui):
1272 def gddeltaconfig(ui):
1273 """helper function to know if incoming delta should be optimised
1273 """helper function to know if incoming delta should be optimised
1274 """
1274 """
1275 # experimental config: format.generaldelta
1275 # experimental config: format.generaldelta
1276 return ui.configbool('format', 'generaldelta')
1276 return ui.configbool('format', 'generaldelta')
1277
1277
1278 class simplekeyvaluefile(object):
1278 class simplekeyvaluefile(object):
1279 """A simple file with key=value lines
1279 """A simple file with key=value lines
1280
1280
1281 Keys must be alphanumerics and start with a letter, values must not
1281 Keys must be alphanumerics and start with a letter, values must not
1282 contain '\n' characters"""
1282 contain '\n' characters"""
1283 firstlinekey = '__firstline'
1283 firstlinekey = '__firstline'
1284
1284
1285 def __init__(self, vfs, path, keys=None):
1285 def __init__(self, vfs, path, keys=None):
1286 self.vfs = vfs
1286 self.vfs = vfs
1287 self.path = path
1287 self.path = path
1288
1288
1289 def read(self, firstlinenonkeyval=False):
1289 def read(self, firstlinenonkeyval=False):
1290 """Read the contents of a simple key-value file
1290 """Read the contents of a simple key-value file
1291
1291
1292 'firstlinenonkeyval' indicates whether the first line of file should
1292 'firstlinenonkeyval' indicates whether the first line of file should
1293 be treated as a key-value pair or reuturned fully under the
1293 be treated as a key-value pair or reuturned fully under the
1294 __firstline key."""
1294 __firstline key."""
1295 lines = self.vfs.readlines(self.path)
1295 lines = self.vfs.readlines(self.path)
1296 d = {}
1296 d = {}
1297 if firstlinenonkeyval:
1297 if firstlinenonkeyval:
1298 if not lines:
1298 if not lines:
1299 e = _("empty simplekeyvalue file")
1299 e = _("empty simplekeyvalue file")
1300 raise error.CorruptedState(e)
1300 raise error.CorruptedState(e)
1301 # we don't want to include '\n' in the __firstline
1301 # we don't want to include '\n' in the __firstline
1302 d[self.firstlinekey] = lines[0][:-1]
1302 d[self.firstlinekey] = lines[0][:-1]
1303 del lines[0]
1303 del lines[0]
1304
1304
1305 try:
1305 try:
1306 # the 'if line.strip()' part prevents us from failing on empty
1306 # the 'if line.strip()' part prevents us from failing on empty
1307 # lines which only contain '\n' therefore are not skipped
1307 # lines which only contain '\n' therefore are not skipped
1308 # by 'if line'
1308 # by 'if line'
1309 updatedict = dict(line[:-1].split('=', 1) for line in lines
1309 updatedict = dict(line[:-1].split('=', 1) for line in lines
1310 if line.strip())
1310 if line.strip())
1311 if self.firstlinekey in updatedict:
1311 if self.firstlinekey in updatedict:
1312 e = _("%r can't be used as a key")
1312 e = _("%r can't be used as a key")
1313 raise error.CorruptedState(e % self.firstlinekey)
1313 raise error.CorruptedState(e % self.firstlinekey)
1314 d.update(updatedict)
1314 d.update(updatedict)
1315 except ValueError as e:
1315 except ValueError as e:
1316 raise error.CorruptedState(str(e))
1316 raise error.CorruptedState(str(e))
1317 return d
1317 return d
1318
1318
1319 def write(self, data, firstline=None):
1319 def write(self, data, firstline=None):
1320 """Write key=>value mapping to a file
1320 """Write key=>value mapping to a file
1321 data is a dict. Keys must be alphanumerical and start with a letter.
1321 data is a dict. Keys must be alphanumerical and start with a letter.
1322 Values must not contain newline characters.
1322 Values must not contain newline characters.
1323
1323
1324 If 'firstline' is not None, it is written to file before
1324 If 'firstline' is not None, it is written to file before
1325 everything else, as it is, not in a key=value form"""
1325 everything else, as it is, not in a key=value form"""
1326 lines = []
1326 lines = []
1327 if firstline is not None:
1327 if firstline is not None:
1328 lines.append('%s\n' % firstline)
1328 lines.append('%s\n' % firstline)
1329
1329
1330 for k, v in data.items():
1330 for k, v in data.items():
1331 if k == self.firstlinekey:
1331 if k == self.firstlinekey:
1332 e = "key name '%s' is reserved" % self.firstlinekey
1332 e = "key name '%s' is reserved" % self.firstlinekey
1333 raise error.ProgrammingError(e)
1333 raise error.ProgrammingError(e)
1334 if not k[0:1].isalpha():
1334 if not k[0:1].isalpha():
1335 e = "keys must start with a letter in a key-value file"
1335 e = "keys must start with a letter in a key-value file"
1336 raise error.ProgrammingError(e)
1336 raise error.ProgrammingError(e)
1337 if not k.isalnum():
1337 if not k.isalnum():
1338 e = "invalid key name in a simple key-value file"
1338 e = "invalid key name in a simple key-value file"
1339 raise error.ProgrammingError(e)
1339 raise error.ProgrammingError(e)
1340 if '\n' in v:
1340 if '\n' in v:
1341 e = "invalid value in a simple key-value file"
1341 e = "invalid value in a simple key-value file"
1342 raise error.ProgrammingError(e)
1342 raise error.ProgrammingError(e)
1343 lines.append("%s=%s\n" % (k, v))
1343 lines.append("%s=%s\n" % (k, v))
1344 with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
1344 with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
1345 fp.write(''.join(lines))
1345 fp.write(''.join(lines))
1346
1346
1347 _reportobsoletedsource = [
1347 _reportobsoletedsource = [
1348 'debugobsolete',
1348 'debugobsolete',
1349 'pull',
1349 'pull',
1350 'push',
1350 'push',
1351 'serve',
1351 'serve',
1352 'unbundle',
1352 'unbundle',
1353 ]
1353 ]
1354
1354
1355 _reportnewcssource = [
1355 _reportnewcssource = [
1356 'pull',
1356 'pull',
1357 'unbundle',
1357 'unbundle',
1358 ]
1358 ]
1359
1359
1360 # a list of (repo, ctx, files) functions called by various commands to allow
1360 # a list of (repo, ctx, files) functions called by various commands to allow
1361 # extensions to ensure the corresponding files are available locally, before the
1361 # extensions to ensure the corresponding files are available locally, before the
1362 # command uses them.
1362 # command uses them.
1363 fileprefetchhooks = util.hooks()
1363 fileprefetchhooks = util.hooks()
1364
1364
1365 # A marker that tells the evolve extension to suppress its own reporting
1365 # A marker that tells the evolve extension to suppress its own reporting
1366 _reportstroubledchangesets = True
1366 _reportstroubledchangesets = True
1367
1367
1368 def registersummarycallback(repo, otr, txnname=''):
1368 def registersummarycallback(repo, otr, txnname=''):
1369 """register a callback to issue a summary after the transaction is closed
1369 """register a callback to issue a summary after the transaction is closed
1370 """
1370 """
1371 def txmatch(sources):
1371 def txmatch(sources):
1372 return any(txnname.startswith(source) for source in sources)
1372 return any(txnname.startswith(source) for source in sources)
1373
1373
1374 categories = []
1374 categories = []
1375
1375
1376 def reportsummary(func):
1376 def reportsummary(func):
1377 """decorator for report callbacks."""
1377 """decorator for report callbacks."""
1378 # The repoview life cycle is shorter than the one of the actual
1378 # The repoview life cycle is shorter than the one of the actual
1379 # underlying repository. So the filtered object can die before the
1379 # underlying repository. So the filtered object can die before the
1380 # weakref is used leading to troubles. We keep a reference to the
1380 # weakref is used leading to troubles. We keep a reference to the
1381 # unfiltered object and restore the filtering when retrieving the
1381 # unfiltered object and restore the filtering when retrieving the
1382 # repository through the weakref.
1382 # repository through the weakref.
1383 filtername = repo.filtername
1383 filtername = repo.filtername
1384 reporef = weakref.ref(repo.unfiltered())
1384 reporef = weakref.ref(repo.unfiltered())
1385 def wrapped(tr):
1385 def wrapped(tr):
1386 repo = reporef()
1386 repo = reporef()
1387 if filtername:
1387 if filtername:
1388 repo = repo.filtered(filtername)
1388 repo = repo.filtered(filtername)
1389 func(repo, tr)
1389 func(repo, tr)
1390 newcat = '%02i-txnreport' % len(categories)
1390 newcat = '%02i-txnreport' % len(categories)
1391 otr.addpostclose(newcat, wrapped)
1391 otr.addpostclose(newcat, wrapped)
1392 categories.append(newcat)
1392 categories.append(newcat)
1393 return wrapped
1393 return wrapped
1394
1394
1395 if txmatch(_reportobsoletedsource):
1395 if txmatch(_reportobsoletedsource):
1396 @reportsummary
1396 @reportsummary
1397 def reportobsoleted(repo, tr):
1397 def reportobsoleted(repo, tr):
1398 obsoleted = obsutil.getobsoleted(repo, tr)
1398 obsoleted = obsutil.getobsoleted(repo, tr)
1399 if obsoleted:
1399 if obsoleted:
1400 repo.ui.status(_('obsoleted %i changesets\n')
1400 repo.ui.status(_('obsoleted %i changesets\n')
1401 % len(obsoleted))
1401 % len(obsoleted))
1402
1402
1403 if (obsolete.isenabled(repo, obsolete.createmarkersopt) and
1403 if (obsolete.isenabled(repo, obsolete.createmarkersopt) and
1404 repo.ui.configbool('experimental', 'evolution.report-instabilities')):
1404 repo.ui.configbool('experimental', 'evolution.report-instabilities')):
1405 instabilitytypes = [
1405 instabilitytypes = [
1406 ('orphan', 'orphan'),
1406 ('orphan', 'orphan'),
1407 ('phase-divergent', 'phasedivergent'),
1407 ('phase-divergent', 'phasedivergent'),
1408 ('content-divergent', 'contentdivergent'),
1408 ('content-divergent', 'contentdivergent'),
1409 ]
1409 ]
1410
1410
1411 def getinstabilitycounts(repo):
1411 def getinstabilitycounts(repo):
1412 filtered = repo.changelog.filteredrevs
1412 filtered = repo.changelog.filteredrevs
1413 counts = {}
1413 counts = {}
1414 for instability, revset in instabilitytypes:
1414 for instability, revset in instabilitytypes:
1415 counts[instability] = len(set(obsolete.getrevs(repo, revset)) -
1415 counts[instability] = len(set(obsolete.getrevs(repo, revset)) -
1416 filtered)
1416 filtered)
1417 return counts
1417 return counts
1418
1418
1419 oldinstabilitycounts = getinstabilitycounts(repo)
1419 oldinstabilitycounts = getinstabilitycounts(repo)
1420 @reportsummary
1420 @reportsummary
1421 def reportnewinstabilities(repo, tr):
1421 def reportnewinstabilities(repo, tr):
1422 newinstabilitycounts = getinstabilitycounts(repo)
1422 newinstabilitycounts = getinstabilitycounts(repo)
1423 for instability, revset in instabilitytypes:
1423 for instability, revset in instabilitytypes:
1424 delta = (newinstabilitycounts[instability] -
1424 delta = (newinstabilitycounts[instability] -
1425 oldinstabilitycounts[instability])
1425 oldinstabilitycounts[instability])
1426 if delta > 0:
1426 if delta > 0:
1427 repo.ui.warn(_('%i new %s changesets\n') %
1427 repo.ui.warn(_('%i new %s changesets\n') %
1428 (delta, instability))
1428 (delta, instability))
1429
1429
1430 if txmatch(_reportnewcssource):
1430 if txmatch(_reportnewcssource):
1431 @reportsummary
1431 @reportsummary
1432 def reportnewcs(repo, tr):
1432 def reportnewcs(repo, tr):
1433 """Report the range of new revisions pulled/unbundled."""
1433 """Report the range of new revisions pulled/unbundled."""
1434 newrevs = tr.changes.get('revs', xrange(0, 0))
1434 newrevs = tr.changes.get('revs', xrange(0, 0))
1435 if not newrevs:
1435 if not newrevs:
1436 return
1436 return
1437
1437
1438 # Compute the bounds of new revisions' range, excluding obsoletes.
1438 # Compute the bounds of new revisions' range, excluding obsoletes.
1439 unfi = repo.unfiltered()
1439 unfi = repo.unfiltered()
1440 revs = unfi.revs('%ld and not obsolete()', newrevs)
1440 revs = unfi.revs('%ld and not obsolete()', newrevs)
1441 if not revs:
1441 if not revs:
1442 # Got only obsoletes.
1442 # Got only obsoletes.
1443 return
1443 return
1444 minrev, maxrev = repo[revs.min()], repo[revs.max()]
1444 minrev, maxrev = repo[revs.min()], repo[revs.max()]
1445
1445
1446 if minrev == maxrev:
1446 if minrev == maxrev:
1447 revrange = minrev
1447 revrange = minrev
1448 else:
1448 else:
1449 revrange = '%s:%s' % (minrev, maxrev)
1449 revrange = '%s:%s' % (minrev, maxrev)
1450 repo.ui.status(_('new changesets %s\n') % revrange)
1450 repo.ui.status(_('new changesets %s\n') % revrange)
1451
1451
1452 def nodesummaries(repo, nodes, maxnumnodes=4):
1452 def nodesummaries(repo, nodes, maxnumnodes=4):
1453 if len(nodes) <= maxnumnodes or repo.ui.verbose:
1453 if len(nodes) <= maxnumnodes or repo.ui.verbose:
1454 return ' '.join(short(h) for h in nodes)
1454 return ' '.join(short(h) for h in nodes)
1455 first = ' '.join(short(h) for h in nodes[:maxnumnodes])
1455 first = ' '.join(short(h) for h in nodes[:maxnumnodes])
1456 return _("%s and %d others") % (first, len(nodes) - maxnumnodes)
1456 return _("%s and %d others") % (first, len(nodes) - maxnumnodes)
1457
1457
1458 def enforcesinglehead(repo, tr, desc):
1458 def enforcesinglehead(repo, tr, desc):
1459 """check that no named branch has multiple heads"""
1459 """check that no named branch has multiple heads"""
1460 if desc in ('strip', 'repair'):
1460 if desc in ('strip', 'repair'):
1461 # skip the logic during strip
1461 # skip the logic during strip
1462 return
1462 return
1463 visible = repo.filtered('visible')
1463 visible = repo.filtered('visible')
1464 # possible improvement: we could restrict the check to affected branch
1464 # possible improvement: we could restrict the check to affected branch
1465 for name, heads in visible.branchmap().iteritems():
1465 for name, heads in visible.branchmap().iteritems():
1466 if len(heads) > 1:
1466 if len(heads) > 1:
1467 msg = _('rejecting multiple heads on branch "%s"')
1467 msg = _('rejecting multiple heads on branch "%s"')
1468 msg %= name
1468 msg %= name
1469 hint = _('%d heads: %s')
1469 hint = _('%d heads: %s')
1470 hint %= (len(heads), nodesummaries(repo, heads))
1470 hint %= (len(heads), nodesummaries(repo, heads))
1471 raise error.Abort(msg, hint=hint)
1471 raise error.Abort(msg, hint=hint)
1472
1472
1473 def wrapconvertsink(sink):
1473 def wrapconvertsink(sink):
1474 """Allow extensions to wrap the sink returned by convcmd.convertsink()
1474 """Allow extensions to wrap the sink returned by convcmd.convertsink()
1475 before it is used, whether or not the convert extension was formally loaded.
1475 before it is used, whether or not the convert extension was formally loaded.
1476 """
1476 """
1477 return sink
1477 return sink
1478
1478
1479 def unhidehashlikerevs(repo, specs, hiddentype):
1479 def unhidehashlikerevs(repo, specs, hiddentype):
1480 """parse the user specs and unhide changesets whose hash or revision number
1480 """parse the user specs and unhide changesets whose hash or revision number
1481 is passed.
1481 is passed.
1482
1482
1483 hiddentype can be: 1) 'warn': warn while unhiding changesets
1483 hiddentype can be: 1) 'warn': warn while unhiding changesets
1484 2) 'nowarn': don't warn while unhiding changesets
1484 2) 'nowarn': don't warn while unhiding changesets
1485
1485
1486 returns a repo object with the required changesets unhidden
1486 returns a repo object with the required changesets unhidden
1487 """
1487 """
1488 if not repo.filtername or not repo.ui.configbool('experimental',
1488 if not repo.filtername or not repo.ui.configbool('experimental',
1489 'directaccess'):
1489 'directaccess'):
1490 return repo
1490 return repo
1491
1491
1492 if repo.filtername not in ('visible', 'visible-hidden'):
1492 if repo.filtername not in ('visible', 'visible-hidden'):
1493 return repo
1493 return repo
1494
1494
1495 symbols = set()
1495 symbols = set()
1496 for spec in specs:
1496 for spec in specs:
1497 try:
1497 try:
1498 tree = revsetlang.parse(spec)
1498 tree = revsetlang.parse(spec)
1499 except error.ParseError: # will be reported by scmutil.revrange()
1499 except error.ParseError: # will be reported by scmutil.revrange()
1500 continue
1500 continue
1501
1501
1502 symbols.update(revsetlang.gethashlikesymbols(tree))
1502 symbols.update(revsetlang.gethashlikesymbols(tree))
1503
1503
1504 if not symbols:
1504 if not symbols:
1505 return repo
1505 return repo
1506
1506
1507 revs = _getrevsfromsymbols(repo, symbols)
1507 revs = _getrevsfromsymbols(repo, symbols)
1508
1508
1509 if not revs:
1509 if not revs:
1510 return repo
1510 return repo
1511
1511
1512 if hiddentype == 'warn':
1512 if hiddentype == 'warn':
1513 unfi = repo.unfiltered()
1513 unfi = repo.unfiltered()
1514 revstr = ", ".join([pycompat.bytestr(unfi[l]) for l in revs])
1514 revstr = ", ".join([pycompat.bytestr(unfi[l]) for l in revs])
1515 repo.ui.warn(_("warning: accessing hidden changesets for write "
1515 repo.ui.warn(_("warning: accessing hidden changesets for write "
1516 "operation: %s\n") % revstr)
1516 "operation: %s\n") % revstr)
1517
1517
1518 # we have to use new filtername to separate branch/tags cache until we can
1518 # we have to use new filtername to separate branch/tags cache until we can
1519 # disbale these cache when revisions are dynamically pinned.
1519 # disbale these cache when revisions are dynamically pinned.
1520 return repo.filtered('visible-hidden', revs)
1520 return repo.filtered('visible-hidden', revs)
1521
1521
1522 def _getrevsfromsymbols(repo, symbols):
1522 def _getrevsfromsymbols(repo, symbols):
1523 """parse the list of symbols and returns a set of revision numbers of hidden
1523 """parse the list of symbols and returns a set of revision numbers of hidden
1524 changesets present in symbols"""
1524 changesets present in symbols"""
1525 revs = set()
1525 revs = set()
1526 unfi = repo.unfiltered()
1526 unfi = repo.unfiltered()
1527 unficl = unfi.changelog
1527 unficl = unfi.changelog
1528 cl = repo.changelog
1528 cl = repo.changelog
1529 tiprev = len(unficl)
1529 tiprev = len(unficl)
1530 pmatch = unficl._partialmatch
1530 pmatch = unficl._partialmatch
1531 allowrevnums = repo.ui.configbool('experimental', 'directaccess.revnums')
1531 allowrevnums = repo.ui.configbool('experimental', 'directaccess.revnums')
1532 for s in symbols:
1532 for s in symbols:
1533 try:
1533 try:
1534 n = int(s)
1534 n = int(s)
1535 if n <= tiprev:
1535 if n <= tiprev:
1536 if not allowrevnums:
1536 if not allowrevnums:
1537 continue
1537 continue
1538 else:
1538 else:
1539 if n not in cl:
1539 if n not in cl:
1540 revs.add(n)
1540 revs.add(n)
1541 continue
1541 continue
1542 except ValueError:
1542 except ValueError:
1543 pass
1543 pass
1544
1544
1545 try:
1545 try:
1546 s = pmatch(s)
1546 s = pmatch(s)
1547 except (error.LookupError, error.WdirUnsupported):
1547 except (error.LookupError, error.WdirUnsupported):
1548 s = None
1548 s = None
1549
1549
1550 if s is not None:
1550 if s is not None:
1551 rev = unficl.rev(s)
1551 rev = unficl.rev(s)
1552 if rev not in cl:
1552 if rev not in cl:
1553 revs.add(rev)
1553 revs.add(rev)
1554
1554
1555 return revs
1555 return revs
@@ -1,673 +1,690 b''
1 # templatefuncs.py - common template functions
1 # templatefuncs.py - common template functions
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from .node import (
14 bin,
15 )
13 from . import (
16 from . import (
14 color,
17 color,
15 encoding,
18 encoding,
16 error,
19 error,
17 minirst,
20 minirst,
18 obsutil,
21 obsutil,
19 pycompat,
22 pycompat,
20 registrar,
23 registrar,
21 revset as revsetmod,
24 revset as revsetmod,
22 revsetlang,
25 revsetlang,
23 scmutil,
26 scmutil,
24 templatefilters,
27 templatefilters,
25 templatekw,
28 templatekw,
26 templateutil,
29 templateutil,
27 util,
30 util,
28 )
31 )
29 from .utils import (
32 from .utils import (
30 dateutil,
33 dateutil,
31 stringutil,
34 stringutil,
32 )
35 )
33
36
34 evalrawexp = templateutil.evalrawexp
37 evalrawexp = templateutil.evalrawexp
35 evalfuncarg = templateutil.evalfuncarg
38 evalfuncarg = templateutil.evalfuncarg
36 evalboolean = templateutil.evalboolean
39 evalboolean = templateutil.evalboolean
37 evaldate = templateutil.evaldate
40 evaldate = templateutil.evaldate
38 evalinteger = templateutil.evalinteger
41 evalinteger = templateutil.evalinteger
39 evalstring = templateutil.evalstring
42 evalstring = templateutil.evalstring
40 evalstringliteral = templateutil.evalstringliteral
43 evalstringliteral = templateutil.evalstringliteral
41
44
42 # dict of template built-in functions
45 # dict of template built-in functions
43 funcs = {}
46 funcs = {}
44 templatefunc = registrar.templatefunc(funcs)
47 templatefunc = registrar.templatefunc(funcs)
45
48
46 @templatefunc('date(date[, fmt])')
49 @templatefunc('date(date[, fmt])')
47 def date(context, mapping, args):
50 def date(context, mapping, args):
48 """Format a date. See :hg:`help dates` for formatting
51 """Format a date. See :hg:`help dates` for formatting
49 strings. The default is a Unix date format, including the timezone:
52 strings. The default is a Unix date format, including the timezone:
50 "Mon Sep 04 15:13:13 2006 0700"."""
53 "Mon Sep 04 15:13:13 2006 0700"."""
51 if not (1 <= len(args) <= 2):
54 if not (1 <= len(args) <= 2):
52 # i18n: "date" is a keyword
55 # i18n: "date" is a keyword
53 raise error.ParseError(_("date expects one or two arguments"))
56 raise error.ParseError(_("date expects one or two arguments"))
54
57
55 date = evaldate(context, mapping, args[0],
58 date = evaldate(context, mapping, args[0],
56 # i18n: "date" is a keyword
59 # i18n: "date" is a keyword
57 _("date expects a date information"))
60 _("date expects a date information"))
58 fmt = None
61 fmt = None
59 if len(args) == 2:
62 if len(args) == 2:
60 fmt = evalstring(context, mapping, args[1])
63 fmt = evalstring(context, mapping, args[1])
61 if fmt is None:
64 if fmt is None:
62 return dateutil.datestr(date)
65 return dateutil.datestr(date)
63 else:
66 else:
64 return dateutil.datestr(date, fmt)
67 return dateutil.datestr(date, fmt)
65
68
66 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
69 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
67 def dict_(context, mapping, args):
70 def dict_(context, mapping, args):
68 """Construct a dict from key-value pairs. A key may be omitted if
71 """Construct a dict from key-value pairs. A key may be omitted if
69 a value expression can provide an unambiguous name."""
72 a value expression can provide an unambiguous name."""
70 data = util.sortdict()
73 data = util.sortdict()
71
74
72 for v in args['args']:
75 for v in args['args']:
73 k = templateutil.findsymbolicname(v)
76 k = templateutil.findsymbolicname(v)
74 if not k:
77 if not k:
75 raise error.ParseError(_('dict key cannot be inferred'))
78 raise error.ParseError(_('dict key cannot be inferred'))
76 if k in data or k in args['kwargs']:
79 if k in data or k in args['kwargs']:
77 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
80 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
78 data[k] = evalfuncarg(context, mapping, v)
81 data[k] = evalfuncarg(context, mapping, v)
79
82
80 data.update((k, evalfuncarg(context, mapping, v))
83 data.update((k, evalfuncarg(context, mapping, v))
81 for k, v in args['kwargs'].iteritems())
84 for k, v in args['kwargs'].iteritems())
82 return templateutil.hybriddict(data)
85 return templateutil.hybriddict(data)
83
86
84 @templatefunc('diff([includepattern [, excludepattern]])')
87 @templatefunc('diff([includepattern [, excludepattern]])')
85 def diff(context, mapping, args):
88 def diff(context, mapping, args):
86 """Show a diff, optionally
89 """Show a diff, optionally
87 specifying files to include or exclude."""
90 specifying files to include or exclude."""
88 if len(args) > 2:
91 if len(args) > 2:
89 # i18n: "diff" is a keyword
92 # i18n: "diff" is a keyword
90 raise error.ParseError(_("diff expects zero, one, or two arguments"))
93 raise error.ParseError(_("diff expects zero, one, or two arguments"))
91
94
92 def getpatterns(i):
95 def getpatterns(i):
93 if i < len(args):
96 if i < len(args):
94 s = evalstring(context, mapping, args[i]).strip()
97 s = evalstring(context, mapping, args[i]).strip()
95 if s:
98 if s:
96 return [s]
99 return [s]
97 return []
100 return []
98
101
99 ctx = context.resource(mapping, 'ctx')
102 ctx = context.resource(mapping, 'ctx')
100 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
103 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
101
104
102 return ''.join(chunks)
105 return ''.join(chunks)
103
106
104 @templatefunc('extdata(source)', argspec='source')
107 @templatefunc('extdata(source)', argspec='source')
105 def extdata(context, mapping, args):
108 def extdata(context, mapping, args):
106 """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
109 """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
107 if 'source' not in args:
110 if 'source' not in args:
108 # i18n: "extdata" is a keyword
111 # i18n: "extdata" is a keyword
109 raise error.ParseError(_('extdata expects one argument'))
112 raise error.ParseError(_('extdata expects one argument'))
110
113
111 source = evalstring(context, mapping, args['source'])
114 source = evalstring(context, mapping, args['source'])
112 cache = context.resource(mapping, 'cache').setdefault('extdata', {})
115 cache = context.resource(mapping, 'cache').setdefault('extdata', {})
113 ctx = context.resource(mapping, 'ctx')
116 ctx = context.resource(mapping, 'ctx')
114 if source in cache:
117 if source in cache:
115 data = cache[source]
118 data = cache[source]
116 else:
119 else:
117 data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
120 data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
118 return data.get(ctx.rev(), '')
121 return data.get(ctx.rev(), '')
119
122
120 @templatefunc('files(pattern)')
123 @templatefunc('files(pattern)')
121 def files(context, mapping, args):
124 def files(context, mapping, args):
122 """All files of the current changeset matching the pattern. See
125 """All files of the current changeset matching the pattern. See
123 :hg:`help patterns`."""
126 :hg:`help patterns`."""
124 if not len(args) == 1:
127 if not len(args) == 1:
125 # i18n: "files" is a keyword
128 # i18n: "files" is a keyword
126 raise error.ParseError(_("files expects one argument"))
129 raise error.ParseError(_("files expects one argument"))
127
130
128 raw = evalstring(context, mapping, args[0])
131 raw = evalstring(context, mapping, args[0])
129 ctx = context.resource(mapping, 'ctx')
132 ctx = context.resource(mapping, 'ctx')
130 m = ctx.match([raw])
133 m = ctx.match([raw])
131 files = list(ctx.matches(m))
134 files = list(ctx.matches(m))
132 return templateutil.compatlist(context, mapping, "file", files)
135 return templateutil.compatlist(context, mapping, "file", files)
133
136
134 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
137 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
135 def fill(context, mapping, args):
138 def fill(context, mapping, args):
136 """Fill many
139 """Fill many
137 paragraphs with optional indentation. See the "fill" filter."""
140 paragraphs with optional indentation. See the "fill" filter."""
138 if not (1 <= len(args) <= 4):
141 if not (1 <= len(args) <= 4):
139 # i18n: "fill" is a keyword
142 # i18n: "fill" is a keyword
140 raise error.ParseError(_("fill expects one to four arguments"))
143 raise error.ParseError(_("fill expects one to four arguments"))
141
144
142 text = evalstring(context, mapping, args[0])
145 text = evalstring(context, mapping, args[0])
143 width = 76
146 width = 76
144 initindent = ''
147 initindent = ''
145 hangindent = ''
148 hangindent = ''
146 if 2 <= len(args) <= 4:
149 if 2 <= len(args) <= 4:
147 width = evalinteger(context, mapping, args[1],
150 width = evalinteger(context, mapping, args[1],
148 # i18n: "fill" is a keyword
151 # i18n: "fill" is a keyword
149 _("fill expects an integer width"))
152 _("fill expects an integer width"))
150 try:
153 try:
151 initindent = evalstring(context, mapping, args[2])
154 initindent = evalstring(context, mapping, args[2])
152 hangindent = evalstring(context, mapping, args[3])
155 hangindent = evalstring(context, mapping, args[3])
153 except IndexError:
156 except IndexError:
154 pass
157 pass
155
158
156 return templatefilters.fill(text, width, initindent, hangindent)
159 return templatefilters.fill(text, width, initindent, hangindent)
157
160
158 @templatefunc('formatnode(node)')
161 @templatefunc('formatnode(node)')
159 def formatnode(context, mapping, args):
162 def formatnode(context, mapping, args):
160 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
163 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
161 if len(args) != 1:
164 if len(args) != 1:
162 # i18n: "formatnode" is a keyword
165 # i18n: "formatnode" is a keyword
163 raise error.ParseError(_("formatnode expects one argument"))
166 raise error.ParseError(_("formatnode expects one argument"))
164
167
165 ui = context.resource(mapping, 'ui')
168 ui = context.resource(mapping, 'ui')
166 node = evalstring(context, mapping, args[0])
169 node = evalstring(context, mapping, args[0])
167 if ui.debugflag:
170 if ui.debugflag:
168 return node
171 return node
169 return templatefilters.short(node)
172 return templatefilters.short(node)
170
173
171 @templatefunc('mailmap(author)')
174 @templatefunc('mailmap(author)')
172 def mailmap(context, mapping, args):
175 def mailmap(context, mapping, args):
173 """Return the author, updated according to the value
176 """Return the author, updated according to the value
174 set in the .mailmap file"""
177 set in the .mailmap file"""
175 if len(args) != 1:
178 if len(args) != 1:
176 raise error.ParseError(_("mailmap expects one argument"))
179 raise error.ParseError(_("mailmap expects one argument"))
177
180
178 author = evalstring(context, mapping, args[0])
181 author = evalstring(context, mapping, args[0])
179
182
180 cache = context.resource(mapping, 'cache')
183 cache = context.resource(mapping, 'cache')
181 repo = context.resource(mapping, 'repo')
184 repo = context.resource(mapping, 'repo')
182
185
183 if 'mailmap' not in cache:
186 if 'mailmap' not in cache:
184 data = repo.wvfs.tryread('.mailmap')
187 data = repo.wvfs.tryread('.mailmap')
185 cache['mailmap'] = stringutil.parsemailmap(data)
188 cache['mailmap'] = stringutil.parsemailmap(data)
186
189
187 return stringutil.mapname(cache['mailmap'], author)
190 return stringutil.mapname(cache['mailmap'], author)
188
191
189 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
192 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
190 argspec='text width fillchar left')
193 argspec='text width fillchar left')
191 def pad(context, mapping, args):
194 def pad(context, mapping, args):
192 """Pad text with a
195 """Pad text with a
193 fill character."""
196 fill character."""
194 if 'text' not in args or 'width' not in args:
197 if 'text' not in args or 'width' not in args:
195 # i18n: "pad" is a keyword
198 # i18n: "pad" is a keyword
196 raise error.ParseError(_("pad() expects two to four arguments"))
199 raise error.ParseError(_("pad() expects two to four arguments"))
197
200
198 width = evalinteger(context, mapping, args['width'],
201 width = evalinteger(context, mapping, args['width'],
199 # i18n: "pad" is a keyword
202 # i18n: "pad" is a keyword
200 _("pad() expects an integer width"))
203 _("pad() expects an integer width"))
201
204
202 text = evalstring(context, mapping, args['text'])
205 text = evalstring(context, mapping, args['text'])
203
206
204 left = False
207 left = False
205 fillchar = ' '
208 fillchar = ' '
206 if 'fillchar' in args:
209 if 'fillchar' in args:
207 fillchar = evalstring(context, mapping, args['fillchar'])
210 fillchar = evalstring(context, mapping, args['fillchar'])
208 if len(color.stripeffects(fillchar)) != 1:
211 if len(color.stripeffects(fillchar)) != 1:
209 # i18n: "pad" is a keyword
212 # i18n: "pad" is a keyword
210 raise error.ParseError(_("pad() expects a single fill character"))
213 raise error.ParseError(_("pad() expects a single fill character"))
211 if 'left' in args:
214 if 'left' in args:
212 left = evalboolean(context, mapping, args['left'])
215 left = evalboolean(context, mapping, args['left'])
213
216
214 fillwidth = width - encoding.colwidth(color.stripeffects(text))
217 fillwidth = width - encoding.colwidth(color.stripeffects(text))
215 if fillwidth <= 0:
218 if fillwidth <= 0:
216 return text
219 return text
217 if left:
220 if left:
218 return fillchar * fillwidth + text
221 return fillchar * fillwidth + text
219 else:
222 else:
220 return text + fillchar * fillwidth
223 return text + fillchar * fillwidth
221
224
222 @templatefunc('indent(text, indentchars[, firstline])')
225 @templatefunc('indent(text, indentchars[, firstline])')
223 def indent(context, mapping, args):
226 def indent(context, mapping, args):
224 """Indents all non-empty lines
227 """Indents all non-empty lines
225 with the characters given in the indentchars string. An optional
228 with the characters given in the indentchars string. An optional
226 third parameter will override the indent for the first line only
229 third parameter will override the indent for the first line only
227 if present."""
230 if present."""
228 if not (2 <= len(args) <= 3):
231 if not (2 <= len(args) <= 3):
229 # i18n: "indent" is a keyword
232 # i18n: "indent" is a keyword
230 raise error.ParseError(_("indent() expects two or three arguments"))
233 raise error.ParseError(_("indent() expects two or three arguments"))
231
234
232 text = evalstring(context, mapping, args[0])
235 text = evalstring(context, mapping, args[0])
233 indent = evalstring(context, mapping, args[1])
236 indent = evalstring(context, mapping, args[1])
234
237
235 if len(args) == 3:
238 if len(args) == 3:
236 firstline = evalstring(context, mapping, args[2])
239 firstline = evalstring(context, mapping, args[2])
237 else:
240 else:
238 firstline = indent
241 firstline = indent
239
242
240 # the indent function doesn't indent the first line, so we do it here
243 # the indent function doesn't indent the first line, so we do it here
241 return templatefilters.indent(firstline + text, indent)
244 return templatefilters.indent(firstline + text, indent)
242
245
243 @templatefunc('get(dict, key)')
246 @templatefunc('get(dict, key)')
244 def get(context, mapping, args):
247 def get(context, mapping, args):
245 """Get an attribute/key from an object. Some keywords
248 """Get an attribute/key from an object. Some keywords
246 are complex types. This function allows you to obtain the value of an
249 are complex types. This function allows you to obtain the value of an
247 attribute on these types."""
250 attribute on these types."""
248 if len(args) != 2:
251 if len(args) != 2:
249 # i18n: "get" is a keyword
252 # i18n: "get" is a keyword
250 raise error.ParseError(_("get() expects two arguments"))
253 raise error.ParseError(_("get() expects two arguments"))
251
254
252 dictarg = evalfuncarg(context, mapping, args[0])
255 dictarg = evalfuncarg(context, mapping, args[0])
253 if not util.safehasattr(dictarg, 'get'):
256 if not util.safehasattr(dictarg, 'get'):
254 # i18n: "get" is a keyword
257 # i18n: "get" is a keyword
255 raise error.ParseError(_("get() expects a dict as first argument"))
258 raise error.ParseError(_("get() expects a dict as first argument"))
256
259
257 key = evalfuncarg(context, mapping, args[1])
260 key = evalfuncarg(context, mapping, args[1])
258 return templateutil.getdictitem(dictarg, key)
261 return templateutil.getdictitem(dictarg, key)
259
262
260 @templatefunc('if(expr, then[, else])')
263 @templatefunc('if(expr, then[, else])')
261 def if_(context, mapping, args):
264 def if_(context, mapping, args):
262 """Conditionally execute based on the result of
265 """Conditionally execute based on the result of
263 an expression."""
266 an expression."""
264 if not (2 <= len(args) <= 3):
267 if not (2 <= len(args) <= 3):
265 # i18n: "if" is a keyword
268 # i18n: "if" is a keyword
266 raise error.ParseError(_("if expects two or three arguments"))
269 raise error.ParseError(_("if expects two or three arguments"))
267
270
268 test = evalboolean(context, mapping, args[0])
271 test = evalboolean(context, mapping, args[0])
269 if test:
272 if test:
270 return evalrawexp(context, mapping, args[1])
273 return evalrawexp(context, mapping, args[1])
271 elif len(args) == 3:
274 elif len(args) == 3:
272 return evalrawexp(context, mapping, args[2])
275 return evalrawexp(context, mapping, args[2])
273
276
274 @templatefunc('ifcontains(needle, haystack, then[, else])')
277 @templatefunc('ifcontains(needle, haystack, then[, else])')
275 def ifcontains(context, mapping, args):
278 def ifcontains(context, mapping, args):
276 """Conditionally execute based
279 """Conditionally execute based
277 on whether the item "needle" is in "haystack"."""
280 on whether the item "needle" is in "haystack"."""
278 if not (3 <= len(args) <= 4):
281 if not (3 <= len(args) <= 4):
279 # i18n: "ifcontains" is a keyword
282 # i18n: "ifcontains" is a keyword
280 raise error.ParseError(_("ifcontains expects three or four arguments"))
283 raise error.ParseError(_("ifcontains expects three or four arguments"))
281
284
282 haystack = evalfuncarg(context, mapping, args[1])
285 haystack = evalfuncarg(context, mapping, args[1])
283 keytype = getattr(haystack, 'keytype', None)
286 keytype = getattr(haystack, 'keytype', None)
284 try:
287 try:
285 needle = evalrawexp(context, mapping, args[0])
288 needle = evalrawexp(context, mapping, args[0])
286 needle = templateutil.unwrapastype(context, mapping, needle,
289 needle = templateutil.unwrapastype(context, mapping, needle,
287 keytype or bytes)
290 keytype or bytes)
288 found = (needle in haystack)
291 found = (needle in haystack)
289 except error.ParseError:
292 except error.ParseError:
290 found = False
293 found = False
291
294
292 if found:
295 if found:
293 return evalrawexp(context, mapping, args[2])
296 return evalrawexp(context, mapping, args[2])
294 elif len(args) == 4:
297 elif len(args) == 4:
295 return evalrawexp(context, mapping, args[3])
298 return evalrawexp(context, mapping, args[3])
296
299
297 @templatefunc('ifeq(expr1, expr2, then[, else])')
300 @templatefunc('ifeq(expr1, expr2, then[, else])')
298 def ifeq(context, mapping, args):
301 def ifeq(context, mapping, args):
299 """Conditionally execute based on
302 """Conditionally execute based on
300 whether 2 items are equivalent."""
303 whether 2 items are equivalent."""
301 if not (3 <= len(args) <= 4):
304 if not (3 <= len(args) <= 4):
302 # i18n: "ifeq" is a keyword
305 # i18n: "ifeq" is a keyword
303 raise error.ParseError(_("ifeq expects three or four arguments"))
306 raise error.ParseError(_("ifeq expects three or four arguments"))
304
307
305 test = evalstring(context, mapping, args[0])
308 test = evalstring(context, mapping, args[0])
306 match = evalstring(context, mapping, args[1])
309 match = evalstring(context, mapping, args[1])
307 if test == match:
310 if test == match:
308 return evalrawexp(context, mapping, args[2])
311 return evalrawexp(context, mapping, args[2])
309 elif len(args) == 4:
312 elif len(args) == 4:
310 return evalrawexp(context, mapping, args[3])
313 return evalrawexp(context, mapping, args[3])
311
314
312 @templatefunc('join(list, sep)')
315 @templatefunc('join(list, sep)')
313 def join(context, mapping, args):
316 def join(context, mapping, args):
314 """Join items in a list with a delimiter."""
317 """Join items in a list with a delimiter."""
315 if not (1 <= len(args) <= 2):
318 if not (1 <= len(args) <= 2):
316 # i18n: "join" is a keyword
319 # i18n: "join" is a keyword
317 raise error.ParseError(_("join expects one or two arguments"))
320 raise error.ParseError(_("join expects one or two arguments"))
318
321
319 joinset = evalrawexp(context, mapping, args[0])
322 joinset = evalrawexp(context, mapping, args[0])
320 joiner = " "
323 joiner = " "
321 if len(args) > 1:
324 if len(args) > 1:
322 joiner = evalstring(context, mapping, args[1])
325 joiner = evalstring(context, mapping, args[1])
323 if isinstance(joinset, templateutil.wrapped):
326 if isinstance(joinset, templateutil.wrapped):
324 return joinset.join(context, mapping, joiner)
327 return joinset.join(context, mapping, joiner)
325 # TODO: perhaps a generator should be stringify()-ed here, but we can't
328 # TODO: perhaps a generator should be stringify()-ed here, but we can't
326 # because hgweb abuses it as a keyword that returns a list of dicts.
329 # because hgweb abuses it as a keyword that returns a list of dicts.
327 joinset = templateutil.unwrapvalue(context, mapping, joinset)
330 joinset = templateutil.unwrapvalue(context, mapping, joinset)
328 return templateutil.joinitems(pycompat.maybebytestr(joinset), joiner)
331 return templateutil.joinitems(pycompat.maybebytestr(joinset), joiner)
329
332
330 @templatefunc('label(label, expr)')
333 @templatefunc('label(label, expr)')
331 def label(context, mapping, args):
334 def label(context, mapping, args):
332 """Apply a label to generated content. Content with
335 """Apply a label to generated content. Content with
333 a label applied can result in additional post-processing, such as
336 a label applied can result in additional post-processing, such as
334 automatic colorization."""
337 automatic colorization."""
335 if len(args) != 2:
338 if len(args) != 2:
336 # i18n: "label" is a keyword
339 # i18n: "label" is a keyword
337 raise error.ParseError(_("label expects two arguments"))
340 raise error.ParseError(_("label expects two arguments"))
338
341
339 ui = context.resource(mapping, 'ui')
342 ui = context.resource(mapping, 'ui')
340 thing = evalstring(context, mapping, args[1])
343 thing = evalstring(context, mapping, args[1])
341 # preserve unknown symbol as literal so effects like 'red', 'bold',
344 # preserve unknown symbol as literal so effects like 'red', 'bold',
342 # etc. don't need to be quoted
345 # etc. don't need to be quoted
343 label = evalstringliteral(context, mapping, args[0])
346 label = evalstringliteral(context, mapping, args[0])
344
347
345 return ui.label(thing, label)
348 return ui.label(thing, label)
346
349
347 @templatefunc('latesttag([pattern])')
350 @templatefunc('latesttag([pattern])')
348 def latesttag(context, mapping, args):
351 def latesttag(context, mapping, args):
349 """The global tags matching the given pattern on the
352 """The global tags matching the given pattern on the
350 most recent globally tagged ancestor of this changeset.
353 most recent globally tagged ancestor of this changeset.
351 If no such tags exist, the "{tag}" template resolves to
354 If no such tags exist, the "{tag}" template resolves to
352 the string "null"."""
355 the string "null"."""
353 if len(args) > 1:
356 if len(args) > 1:
354 # i18n: "latesttag" is a keyword
357 # i18n: "latesttag" is a keyword
355 raise error.ParseError(_("latesttag expects at most one argument"))
358 raise error.ParseError(_("latesttag expects at most one argument"))
356
359
357 pattern = None
360 pattern = None
358 if len(args) == 1:
361 if len(args) == 1:
359 pattern = evalstring(context, mapping, args[0])
362 pattern = evalstring(context, mapping, args[0])
360 return templatekw.showlatesttags(context, mapping, pattern)
363 return templatekw.showlatesttags(context, mapping, pattern)
361
364
362 @templatefunc('localdate(date[, tz])')
365 @templatefunc('localdate(date[, tz])')
363 def localdate(context, mapping, args):
366 def localdate(context, mapping, args):
364 """Converts a date to the specified timezone.
367 """Converts a date to the specified timezone.
365 The default is local date."""
368 The default is local date."""
366 if not (1 <= len(args) <= 2):
369 if not (1 <= len(args) <= 2):
367 # i18n: "localdate" is a keyword
370 # i18n: "localdate" is a keyword
368 raise error.ParseError(_("localdate expects one or two arguments"))
371 raise error.ParseError(_("localdate expects one or two arguments"))
369
372
370 date = evaldate(context, mapping, args[0],
373 date = evaldate(context, mapping, args[0],
371 # i18n: "localdate" is a keyword
374 # i18n: "localdate" is a keyword
372 _("localdate expects a date information"))
375 _("localdate expects a date information"))
373 if len(args) >= 2:
376 if len(args) >= 2:
374 tzoffset = None
377 tzoffset = None
375 tz = evalfuncarg(context, mapping, args[1])
378 tz = evalfuncarg(context, mapping, args[1])
376 if isinstance(tz, bytes):
379 if isinstance(tz, bytes):
377 tzoffset, remainder = dateutil.parsetimezone(tz)
380 tzoffset, remainder = dateutil.parsetimezone(tz)
378 if remainder:
381 if remainder:
379 tzoffset = None
382 tzoffset = None
380 if tzoffset is None:
383 if tzoffset is None:
381 try:
384 try:
382 tzoffset = int(tz)
385 tzoffset = int(tz)
383 except (TypeError, ValueError):
386 except (TypeError, ValueError):
384 # i18n: "localdate" is a keyword
387 # i18n: "localdate" is a keyword
385 raise error.ParseError(_("localdate expects a timezone"))
388 raise error.ParseError(_("localdate expects a timezone"))
386 else:
389 else:
387 tzoffset = dateutil.makedate()[1]
390 tzoffset = dateutil.makedate()[1]
388 return (date[0], tzoffset)
391 return (date[0], tzoffset)
389
392
390 @templatefunc('max(iterable)')
393 @templatefunc('max(iterable)')
391 def max_(context, mapping, args, **kwargs):
394 def max_(context, mapping, args, **kwargs):
392 """Return the max of an iterable"""
395 """Return the max of an iterable"""
393 if len(args) != 1:
396 if len(args) != 1:
394 # i18n: "max" is a keyword
397 # i18n: "max" is a keyword
395 raise error.ParseError(_("max expects one argument"))
398 raise error.ParseError(_("max expects one argument"))
396
399
397 iterable = evalfuncarg(context, mapping, args[0])
400 iterable = evalfuncarg(context, mapping, args[0])
398 try:
401 try:
399 x = max(pycompat.maybebytestr(iterable))
402 x = max(pycompat.maybebytestr(iterable))
400 except (TypeError, ValueError):
403 except (TypeError, ValueError):
401 # i18n: "max" is a keyword
404 # i18n: "max" is a keyword
402 raise error.ParseError(_("max first argument should be an iterable"))
405 raise error.ParseError(_("max first argument should be an iterable"))
403 return templateutil.wraphybridvalue(iterable, x, x)
406 return templateutil.wraphybridvalue(iterable, x, x)
404
407
405 @templatefunc('min(iterable)')
408 @templatefunc('min(iterable)')
406 def min_(context, mapping, args, **kwargs):
409 def min_(context, mapping, args, **kwargs):
407 """Return the min of an iterable"""
410 """Return the min of an iterable"""
408 if len(args) != 1:
411 if len(args) != 1:
409 # i18n: "min" is a keyword
412 # i18n: "min" is a keyword
410 raise error.ParseError(_("min expects one argument"))
413 raise error.ParseError(_("min expects one argument"))
411
414
412 iterable = evalfuncarg(context, mapping, args[0])
415 iterable = evalfuncarg(context, mapping, args[0])
413 try:
416 try:
414 x = min(pycompat.maybebytestr(iterable))
417 x = min(pycompat.maybebytestr(iterable))
415 except (TypeError, ValueError):
418 except (TypeError, ValueError):
416 # i18n: "min" is a keyword
419 # i18n: "min" is a keyword
417 raise error.ParseError(_("min first argument should be an iterable"))
420 raise error.ParseError(_("min first argument should be an iterable"))
418 return templateutil.wraphybridvalue(iterable, x, x)
421 return templateutil.wraphybridvalue(iterable, x, x)
419
422
420 @templatefunc('mod(a, b)')
423 @templatefunc('mod(a, b)')
421 def mod(context, mapping, args):
424 def mod(context, mapping, args):
422 """Calculate a mod b such that a / b + a mod b == a"""
425 """Calculate a mod b such that a / b + a mod b == a"""
423 if not len(args) == 2:
426 if not len(args) == 2:
424 # i18n: "mod" is a keyword
427 # i18n: "mod" is a keyword
425 raise error.ParseError(_("mod expects two arguments"))
428 raise error.ParseError(_("mod expects two arguments"))
426
429
427 func = lambda a, b: a % b
430 func = lambda a, b: a % b
428 return templateutil.runarithmetic(context, mapping,
431 return templateutil.runarithmetic(context, mapping,
429 (func, args[0], args[1]))
432 (func, args[0], args[1]))
430
433
431 @templatefunc('obsfateoperations(markers)')
434 @templatefunc('obsfateoperations(markers)')
432 def obsfateoperations(context, mapping, args):
435 def obsfateoperations(context, mapping, args):
433 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
436 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
434 if len(args) != 1:
437 if len(args) != 1:
435 # i18n: "obsfateoperations" is a keyword
438 # i18n: "obsfateoperations" is a keyword
436 raise error.ParseError(_("obsfateoperations expects one argument"))
439 raise error.ParseError(_("obsfateoperations expects one argument"))
437
440
438 markers = evalfuncarg(context, mapping, args[0])
441 markers = evalfuncarg(context, mapping, args[0])
439
442
440 try:
443 try:
441 data = obsutil.markersoperations(markers)
444 data = obsutil.markersoperations(markers)
442 return templateutil.hybridlist(data, name='operation')
445 return templateutil.hybridlist(data, name='operation')
443 except (TypeError, KeyError):
446 except (TypeError, KeyError):
444 # i18n: "obsfateoperations" is a keyword
447 # i18n: "obsfateoperations" is a keyword
445 errmsg = _("obsfateoperations first argument should be an iterable")
448 errmsg = _("obsfateoperations first argument should be an iterable")
446 raise error.ParseError(errmsg)
449 raise error.ParseError(errmsg)
447
450
448 @templatefunc('obsfatedate(markers)')
451 @templatefunc('obsfatedate(markers)')
449 def obsfatedate(context, mapping, args):
452 def obsfatedate(context, mapping, args):
450 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
453 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
451 if len(args) != 1:
454 if len(args) != 1:
452 # i18n: "obsfatedate" is a keyword
455 # i18n: "obsfatedate" is a keyword
453 raise error.ParseError(_("obsfatedate expects one argument"))
456 raise error.ParseError(_("obsfatedate expects one argument"))
454
457
455 markers = evalfuncarg(context, mapping, args[0])
458 markers = evalfuncarg(context, mapping, args[0])
456
459
457 try:
460 try:
458 data = obsutil.markersdates(markers)
461 data = obsutil.markersdates(markers)
459 return templateutil.hybridlist(data, name='date', fmt='%d %d')
462 return templateutil.hybridlist(data, name='date', fmt='%d %d')
460 except (TypeError, KeyError):
463 except (TypeError, KeyError):
461 # i18n: "obsfatedate" is a keyword
464 # i18n: "obsfatedate" is a keyword
462 errmsg = _("obsfatedate first argument should be an iterable")
465 errmsg = _("obsfatedate first argument should be an iterable")
463 raise error.ParseError(errmsg)
466 raise error.ParseError(errmsg)
464
467
465 @templatefunc('obsfateusers(markers)')
468 @templatefunc('obsfateusers(markers)')
466 def obsfateusers(context, mapping, args):
469 def obsfateusers(context, mapping, args):
467 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
470 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
468 if len(args) != 1:
471 if len(args) != 1:
469 # i18n: "obsfateusers" is a keyword
472 # i18n: "obsfateusers" is a keyword
470 raise error.ParseError(_("obsfateusers expects one argument"))
473 raise error.ParseError(_("obsfateusers expects one argument"))
471
474
472 markers = evalfuncarg(context, mapping, args[0])
475 markers = evalfuncarg(context, mapping, args[0])
473
476
474 try:
477 try:
475 data = obsutil.markersusers(markers)
478 data = obsutil.markersusers(markers)
476 return templateutil.hybridlist(data, name='user')
479 return templateutil.hybridlist(data, name='user')
477 except (TypeError, KeyError, ValueError):
480 except (TypeError, KeyError, ValueError):
478 # i18n: "obsfateusers" is a keyword
481 # i18n: "obsfateusers" is a keyword
479 msg = _("obsfateusers first argument should be an iterable of "
482 msg = _("obsfateusers first argument should be an iterable of "
480 "obsmakers")
483 "obsmakers")
481 raise error.ParseError(msg)
484 raise error.ParseError(msg)
482
485
483 @templatefunc('obsfateverb(successors, markers)')
486 @templatefunc('obsfateverb(successors, markers)')
484 def obsfateverb(context, mapping, args):
487 def obsfateverb(context, mapping, args):
485 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
488 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
486 if len(args) != 2:
489 if len(args) != 2:
487 # i18n: "obsfateverb" is a keyword
490 # i18n: "obsfateverb" is a keyword
488 raise error.ParseError(_("obsfateverb expects two arguments"))
491 raise error.ParseError(_("obsfateverb expects two arguments"))
489
492
490 successors = evalfuncarg(context, mapping, args[0])
493 successors = evalfuncarg(context, mapping, args[0])
491 markers = evalfuncarg(context, mapping, args[1])
494 markers = evalfuncarg(context, mapping, args[1])
492
495
493 try:
496 try:
494 return obsutil.obsfateverb(successors, markers)
497 return obsutil.obsfateverb(successors, markers)
495 except TypeError:
498 except TypeError:
496 # i18n: "obsfateverb" is a keyword
499 # i18n: "obsfateverb" is a keyword
497 errmsg = _("obsfateverb first argument should be countable")
500 errmsg = _("obsfateverb first argument should be countable")
498 raise error.ParseError(errmsg)
501 raise error.ParseError(errmsg)
499
502
500 @templatefunc('relpath(path)')
503 @templatefunc('relpath(path)')
501 def relpath(context, mapping, args):
504 def relpath(context, mapping, args):
502 """Convert a repository-absolute path into a filesystem path relative to
505 """Convert a repository-absolute path into a filesystem path relative to
503 the current working directory."""
506 the current working directory."""
504 if len(args) != 1:
507 if len(args) != 1:
505 # i18n: "relpath" is a keyword
508 # i18n: "relpath" is a keyword
506 raise error.ParseError(_("relpath expects one argument"))
509 raise error.ParseError(_("relpath expects one argument"))
507
510
508 repo = context.resource(mapping, 'ctx').repo()
511 repo = context.resource(mapping, 'ctx').repo()
509 path = evalstring(context, mapping, args[0])
512 path = evalstring(context, mapping, args[0])
510 return repo.pathto(path)
513 return repo.pathto(path)
511
514
512 @templatefunc('revset(query[, formatargs...])')
515 @templatefunc('revset(query[, formatargs...])')
513 def revset(context, mapping, args):
516 def revset(context, mapping, args):
514 """Execute a revision set query. See
517 """Execute a revision set query. See
515 :hg:`help revset`."""
518 :hg:`help revset`."""
516 if not len(args) > 0:
519 if not len(args) > 0:
517 # i18n: "revset" is a keyword
520 # i18n: "revset" is a keyword
518 raise error.ParseError(_("revset expects one or more arguments"))
521 raise error.ParseError(_("revset expects one or more arguments"))
519
522
520 raw = evalstring(context, mapping, args[0])
523 raw = evalstring(context, mapping, args[0])
521 ctx = context.resource(mapping, 'ctx')
524 ctx = context.resource(mapping, 'ctx')
522 repo = ctx.repo()
525 repo = ctx.repo()
523
526
524 def query(expr):
527 def query(expr):
525 m = revsetmod.match(repo.ui, expr, lookup=revsetmod.lookupfn(repo))
528 m = revsetmod.match(repo.ui, expr, lookup=revsetmod.lookupfn(repo))
526 return m(repo)
529 return m(repo)
527
530
528 if len(args) > 1:
531 if len(args) > 1:
529 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
532 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
530 revs = query(revsetlang.formatspec(raw, *formatargs))
533 revs = query(revsetlang.formatspec(raw, *formatargs))
531 revs = list(revs)
534 revs = list(revs)
532 else:
535 else:
533 cache = context.resource(mapping, 'cache')
536 cache = context.resource(mapping, 'cache')
534 revsetcache = cache.setdefault("revsetcache", {})
537 revsetcache = cache.setdefault("revsetcache", {})
535 if raw in revsetcache:
538 if raw in revsetcache:
536 revs = revsetcache[raw]
539 revs = revsetcache[raw]
537 else:
540 else:
538 revs = query(raw)
541 revs = query(raw)
539 revs = list(revs)
542 revs = list(revs)
540 revsetcache[raw] = revs
543 revsetcache[raw] = revs
541 return templatekw.showrevslist(context, mapping, "revision", revs)
544 return templatekw.showrevslist(context, mapping, "revision", revs)
542
545
543 @templatefunc('rstdoc(text, style)')
546 @templatefunc('rstdoc(text, style)')
544 def rstdoc(context, mapping, args):
547 def rstdoc(context, mapping, args):
545 """Format reStructuredText."""
548 """Format reStructuredText."""
546 if len(args) != 2:
549 if len(args) != 2:
547 # i18n: "rstdoc" is a keyword
550 # i18n: "rstdoc" is a keyword
548 raise error.ParseError(_("rstdoc expects two arguments"))
551 raise error.ParseError(_("rstdoc expects two arguments"))
549
552
550 text = evalstring(context, mapping, args[0])
553 text = evalstring(context, mapping, args[0])
551 style = evalstring(context, mapping, args[1])
554 style = evalstring(context, mapping, args[1])
552
555
553 return minirst.format(text, style=style, keep=['verbose'])
556 return minirst.format(text, style=style, keep=['verbose'])
554
557
555 @templatefunc('separate(sep, args)', argspec='sep *args')
558 @templatefunc('separate(sep, args)', argspec='sep *args')
556 def separate(context, mapping, args):
559 def separate(context, mapping, args):
557 """Add a separator between non-empty arguments."""
560 """Add a separator between non-empty arguments."""
558 if 'sep' not in args:
561 if 'sep' not in args:
559 # i18n: "separate" is a keyword
562 # i18n: "separate" is a keyword
560 raise error.ParseError(_("separate expects at least one argument"))
563 raise error.ParseError(_("separate expects at least one argument"))
561
564
562 sep = evalstring(context, mapping, args['sep'])
565 sep = evalstring(context, mapping, args['sep'])
563 first = True
566 first = True
564 for arg in args['args']:
567 for arg in args['args']:
565 argstr = evalstring(context, mapping, arg)
568 argstr = evalstring(context, mapping, arg)
566 if not argstr:
569 if not argstr:
567 continue
570 continue
568 if first:
571 if first:
569 first = False
572 first = False
570 else:
573 else:
571 yield sep
574 yield sep
572 yield argstr
575 yield argstr
573
576
574 @templatefunc('shortest(node, minlength=4)')
577 @templatefunc('shortest(node, minlength=4)')
575 def shortest(context, mapping, args):
578 def shortest(context, mapping, args):
576 """Obtain the shortest representation of
579 """Obtain the shortest representation of
577 a node."""
580 a node."""
578 if not (1 <= len(args) <= 2):
581 if not (1 <= len(args) <= 2):
579 # i18n: "shortest" is a keyword
582 # i18n: "shortest" is a keyword
580 raise error.ParseError(_("shortest() expects one or two arguments"))
583 raise error.ParseError(_("shortest() expects one or two arguments"))
581
584
582 node = evalstring(context, mapping, args[0])
585 hexnode = evalstring(context, mapping, args[0])
583
586
584 minlength = 4
587 minlength = 4
585 if len(args) > 1:
588 if len(args) > 1:
586 minlength = evalinteger(context, mapping, args[1],
589 minlength = evalinteger(context, mapping, args[1],
587 # i18n: "shortest" is a keyword
590 # i18n: "shortest" is a keyword
588 _("shortest() expects an integer minlength"))
591 _("shortest() expects an integer minlength"))
589
592
590 repo = context.resource(mapping, 'ctx')._repo
593 repo = context.resource(mapping, 'ctx')._repo
594 if len(hexnode) > 40:
595 return hexnode
596 elif len(hexnode) == 40:
597 try:
598 node = bin(hexnode)
599 except TypeError:
600 return hexnode
601 else:
602 try:
603 node = scmutil.resolvehexnodeidprefix(repo, hexnode)
604 except (error.LookupError, error.WdirUnsupported):
605 return hexnode
606 if not node:
607 return hexnode
591 return scmutil.shortesthexnodeidprefix(repo, node, minlength)
608 return scmutil.shortesthexnodeidprefix(repo, node, minlength)
592
609
593 @templatefunc('strip(text[, chars])')
610 @templatefunc('strip(text[, chars])')
594 def strip(context, mapping, args):
611 def strip(context, mapping, args):
595 """Strip characters from a string. By default,
612 """Strip characters from a string. By default,
596 strips all leading and trailing whitespace."""
613 strips all leading and trailing whitespace."""
597 if not (1 <= len(args) <= 2):
614 if not (1 <= len(args) <= 2):
598 # i18n: "strip" is a keyword
615 # i18n: "strip" is a keyword
599 raise error.ParseError(_("strip expects one or two arguments"))
616 raise error.ParseError(_("strip expects one or two arguments"))
600
617
601 text = evalstring(context, mapping, args[0])
618 text = evalstring(context, mapping, args[0])
602 if len(args) == 2:
619 if len(args) == 2:
603 chars = evalstring(context, mapping, args[1])
620 chars = evalstring(context, mapping, args[1])
604 return text.strip(chars)
621 return text.strip(chars)
605 return text.strip()
622 return text.strip()
606
623
607 @templatefunc('sub(pattern, replacement, expression)')
624 @templatefunc('sub(pattern, replacement, expression)')
608 def sub(context, mapping, args):
625 def sub(context, mapping, args):
609 """Perform text substitution
626 """Perform text substitution
610 using regular expressions."""
627 using regular expressions."""
611 if len(args) != 3:
628 if len(args) != 3:
612 # i18n: "sub" is a keyword
629 # i18n: "sub" is a keyword
613 raise error.ParseError(_("sub expects three arguments"))
630 raise error.ParseError(_("sub expects three arguments"))
614
631
615 pat = evalstring(context, mapping, args[0])
632 pat = evalstring(context, mapping, args[0])
616 rpl = evalstring(context, mapping, args[1])
633 rpl = evalstring(context, mapping, args[1])
617 src = evalstring(context, mapping, args[2])
634 src = evalstring(context, mapping, args[2])
618 try:
635 try:
619 patre = re.compile(pat)
636 patre = re.compile(pat)
620 except re.error:
637 except re.error:
621 # i18n: "sub" is a keyword
638 # i18n: "sub" is a keyword
622 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
639 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
623 try:
640 try:
624 yield patre.sub(rpl, src)
641 yield patre.sub(rpl, src)
625 except re.error:
642 except re.error:
626 # i18n: "sub" is a keyword
643 # i18n: "sub" is a keyword
627 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
644 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
628
645
629 @templatefunc('startswith(pattern, text)')
646 @templatefunc('startswith(pattern, text)')
630 def startswith(context, mapping, args):
647 def startswith(context, mapping, args):
631 """Returns the value from the "text" argument
648 """Returns the value from the "text" argument
632 if it begins with the content from the "pattern" argument."""
649 if it begins with the content from the "pattern" argument."""
633 if len(args) != 2:
650 if len(args) != 2:
634 # i18n: "startswith" is a keyword
651 # i18n: "startswith" is a keyword
635 raise error.ParseError(_("startswith expects two arguments"))
652 raise error.ParseError(_("startswith expects two arguments"))
636
653
637 patn = evalstring(context, mapping, args[0])
654 patn = evalstring(context, mapping, args[0])
638 text = evalstring(context, mapping, args[1])
655 text = evalstring(context, mapping, args[1])
639 if text.startswith(patn):
656 if text.startswith(patn):
640 return text
657 return text
641 return ''
658 return ''
642
659
643 @templatefunc('word(number, text[, separator])')
660 @templatefunc('word(number, text[, separator])')
644 def word(context, mapping, args):
661 def word(context, mapping, args):
645 """Return the nth word from a string."""
662 """Return the nth word from a string."""
646 if not (2 <= len(args) <= 3):
663 if not (2 <= len(args) <= 3):
647 # i18n: "word" is a keyword
664 # i18n: "word" is a keyword
648 raise error.ParseError(_("word expects two or three arguments, got %d")
665 raise error.ParseError(_("word expects two or three arguments, got %d")
649 % len(args))
666 % len(args))
650
667
651 num = evalinteger(context, mapping, args[0],
668 num = evalinteger(context, mapping, args[0],
652 # i18n: "word" is a keyword
669 # i18n: "word" is a keyword
653 _("word expects an integer index"))
670 _("word expects an integer index"))
654 text = evalstring(context, mapping, args[1])
671 text = evalstring(context, mapping, args[1])
655 if len(args) == 3:
672 if len(args) == 3:
656 splitter = evalstring(context, mapping, args[2])
673 splitter = evalstring(context, mapping, args[2])
657 else:
674 else:
658 splitter = None
675 splitter = None
659
676
660 tokens = text.split(splitter)
677 tokens = text.split(splitter)
661 if num >= len(tokens) or num < -len(tokens):
678 if num >= len(tokens) or num < -len(tokens):
662 return ''
679 return ''
663 else:
680 else:
664 return tokens[num]
681 return tokens[num]
665
682
666 def loadfunction(ui, extname, registrarobj):
683 def loadfunction(ui, extname, registrarobj):
667 """Load template function from specified registrarobj
684 """Load template function from specified registrarobj
668 """
685 """
669 for name, func in registrarobj._table.iteritems():
686 for name, func in registrarobj._table.iteritems():
670 funcs[name] = func
687 funcs[name] = func
671
688
672 # tell hggettext to extract docstrings from these functions:
689 # tell hggettext to extract docstrings from these functions:
673 i18nfunctions = funcs.values()
690 i18nfunctions = funcs.values()
@@ -1,4868 +1,4883 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 Filters bind as close as map operator:
137 Filters bind as close as map operator:
138
138
139 $ hg debugtemplate -r0 -v '{desc|splitlines % "{line}\n"}'
139 $ hg debugtemplate -r0 -v '{desc|splitlines % "{line}\n"}'
140 (template
140 (template
141 (%
141 (%
142 (|
142 (|
143 (symbol 'desc')
143 (symbol 'desc')
144 (symbol 'splitlines'))
144 (symbol 'splitlines'))
145 (template
145 (template
146 (symbol 'line')
146 (symbol 'line')
147 (string '\n'))))
147 (string '\n'))))
148 line 1
148 line 1
149 line 2
149 line 2
150
150
151 Keyword arguments:
151 Keyword arguments:
152
152
153 $ hg debugtemplate -r0 -v '{foo=bar|baz}'
153 $ hg debugtemplate -r0 -v '{foo=bar|baz}'
154 (template
154 (template
155 (keyvalue
155 (keyvalue
156 (symbol 'foo')
156 (symbol 'foo')
157 (|
157 (|
158 (symbol 'bar')
158 (symbol 'bar')
159 (symbol 'baz'))))
159 (symbol 'baz'))))
160 hg: parse error: can't use a key-value pair in this context
160 hg: parse error: can't use a key-value pair in this context
161 [255]
161 [255]
162
162
163 $ hg debugtemplate '{pad("foo", width=10, left=true)}\n'
163 $ hg debugtemplate '{pad("foo", width=10, left=true)}\n'
164 foo
164 foo
165
165
166 Call function which takes named arguments by filter syntax:
166 Call function which takes named arguments by filter syntax:
167
167
168 $ hg debugtemplate '{" "|separate}'
168 $ hg debugtemplate '{" "|separate}'
169 $ hg debugtemplate '{("not", "an", "argument", "list")|separate}'
169 $ hg debugtemplate '{("not", "an", "argument", "list")|separate}'
170 hg: parse error: unknown method 'list'
170 hg: parse error: unknown method 'list'
171 [255]
171 [255]
172
172
173 Second branch starting at nullrev:
173 Second branch starting at nullrev:
174
174
175 $ hg update null
175 $ hg update null
176 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
176 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
177 $ echo second > second
177 $ echo second > second
178 $ hg add second
178 $ hg add second
179 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
179 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
180 created new head
180 created new head
181
181
182 $ echo third > third
182 $ echo third > third
183 $ hg add third
183 $ hg add third
184 $ hg mv second fourth
184 $ hg mv second fourth
185 $ hg commit -m third -d "2020-01-01 10:01"
185 $ hg commit -m third -d "2020-01-01 10:01"
186
186
187 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
187 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
188 fourth (second)
188 fourth (second)
189 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
189 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
190 second -> fourth
190 second -> fourth
191 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
191 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
192 8 t
192 8 t
193 7 f
193 7 f
194
194
195 Working-directory revision has special identifiers, though they are still
195 Working-directory revision has special identifiers, though they are still
196 experimental:
196 experimental:
197
197
198 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
198 $ hg log -r 'wdir()' -T '{rev}:{node}\n'
199 2147483647:ffffffffffffffffffffffffffffffffffffffff
199 2147483647:ffffffffffffffffffffffffffffffffffffffff
200
200
201 Some keywords are invalid for working-directory revision, but they should
201 Some keywords are invalid for working-directory revision, but they should
202 never cause crash:
202 never cause crash:
203
203
204 $ hg log -r 'wdir()' -T '{manifest}\n'
204 $ hg log -r 'wdir()' -T '{manifest}\n'
205
205
206
206
207 Internal resources shouldn't be exposed (issue5699):
207 Internal resources shouldn't be exposed (issue5699):
208
208
209 $ hg log -r. -T '{cache}{ctx}{repo}{revcache}{templ}{ui}'
209 $ hg log -r. -T '{cache}{ctx}{repo}{revcache}{templ}{ui}'
210
210
211 Never crash on internal resource not available:
211 Never crash on internal resource not available:
212
212
213 $ hg --cwd .. debugtemplate '{"c0bebeef"|shortest}\n'
213 $ hg --cwd .. debugtemplate '{"c0bebeef"|shortest}\n'
214 abort: template resource not available: ctx
214 abort: template resource not available: ctx
215 [255]
215 [255]
216
216
217 $ hg config -T '{author}'
217 $ hg config -T '{author}'
218
218
219 Quoting for ui.logtemplate
219 Quoting for ui.logtemplate
220
220
221 $ hg tip --config "ui.logtemplate={rev}\n"
221 $ hg tip --config "ui.logtemplate={rev}\n"
222 8
222 8
223 $ hg tip --config "ui.logtemplate='{rev}\n'"
223 $ hg tip --config "ui.logtemplate='{rev}\n'"
224 8
224 8
225 $ hg tip --config 'ui.logtemplate="{rev}\n"'
225 $ hg tip --config 'ui.logtemplate="{rev}\n"'
226 8
226 8
227 $ hg tip --config 'ui.logtemplate=n{rev}\n'
227 $ hg tip --config 'ui.logtemplate=n{rev}\n'
228 n8
228 n8
229
229
230 Make sure user/global hgrc does not affect tests
230 Make sure user/global hgrc does not affect tests
231
231
232 $ echo '[ui]' > .hg/hgrc
232 $ echo '[ui]' > .hg/hgrc
233 $ echo 'logtemplate =' >> .hg/hgrc
233 $ echo 'logtemplate =' >> .hg/hgrc
234 $ echo 'style =' >> .hg/hgrc
234 $ echo 'style =' >> .hg/hgrc
235
235
236 Add some simple styles to settings
236 Add some simple styles to settings
237
237
238 $ cat <<'EOF' >> .hg/hgrc
238 $ cat <<'EOF' >> .hg/hgrc
239 > [templates]
239 > [templates]
240 > simple = "{rev}\n"
240 > simple = "{rev}\n"
241 > simple2 = {rev}\n
241 > simple2 = {rev}\n
242 > rev = "should not precede {rev} keyword\n"
242 > rev = "should not precede {rev} keyword\n"
243 > EOF
243 > EOF
244
244
245 $ hg log -l1 -Tsimple
245 $ hg log -l1 -Tsimple
246 8
246 8
247 $ hg log -l1 -Tsimple2
247 $ hg log -l1 -Tsimple2
248 8
248 8
249 $ hg log -l1 -Trev
249 $ hg log -l1 -Trev
250 should not precede 8 keyword
250 should not precede 8 keyword
251 $ hg log -l1 -T '{simple}'
251 $ hg log -l1 -T '{simple}'
252 8
252 8
253
253
254 Map file shouldn't see user templates:
254 Map file shouldn't see user templates:
255
255
256 $ cat <<EOF > tmpl
256 $ cat <<EOF > tmpl
257 > changeset = 'nothing expanded:{simple}\n'
257 > changeset = 'nothing expanded:{simple}\n'
258 > EOF
258 > EOF
259 $ hg log -l1 --style ./tmpl
259 $ hg log -l1 --style ./tmpl
260 nothing expanded:
260 nothing expanded:
261
261
262 Test templates and style maps in files:
262 Test templates and style maps in files:
263
263
264 $ echo "{rev}" > tmpl
264 $ echo "{rev}" > tmpl
265 $ hg log -l1 -T./tmpl
265 $ hg log -l1 -T./tmpl
266 8
266 8
267 $ hg log -l1 -Tblah/blah
267 $ hg log -l1 -Tblah/blah
268 blah/blah (no-eol)
268 blah/blah (no-eol)
269
269
270 $ printf 'changeset = "{rev}\\n"\n' > map-simple
270 $ printf 'changeset = "{rev}\\n"\n' > map-simple
271 $ hg log -l1 -T./map-simple
271 $ hg log -l1 -T./map-simple
272 8
272 8
273
273
274 a map file may have [templates] and [templatealias] sections:
274 a map file may have [templates] and [templatealias] sections:
275
275
276 $ cat <<'EOF' > map-simple
276 $ cat <<'EOF' > map-simple
277 > [templates]
277 > [templates]
278 > changeset = "{a}\n"
278 > changeset = "{a}\n"
279 > [templatealias]
279 > [templatealias]
280 > a = rev
280 > a = rev
281 > EOF
281 > EOF
282 $ hg log -l1 -T./map-simple
282 $ hg log -l1 -T./map-simple
283 8
283 8
284
284
285 so it can be included in hgrc
285 so it can be included in hgrc
286
286
287 $ cat <<EOF > myhgrc
287 $ cat <<EOF > myhgrc
288 > %include $HGRCPATH
288 > %include $HGRCPATH
289 > %include map-simple
289 > %include map-simple
290 > [templates]
290 > [templates]
291 > foo = "{changeset}"
291 > foo = "{changeset}"
292 > EOF
292 > EOF
293 $ HGRCPATH=./myhgrc hg log -l1 -Tfoo
293 $ HGRCPATH=./myhgrc hg log -l1 -Tfoo
294 8
294 8
295 $ HGRCPATH=./myhgrc hg log -l1 -T'{a}\n'
295 $ HGRCPATH=./myhgrc hg log -l1 -T'{a}\n'
296 8
296 8
297
297
298 Test template map inheritance
298 Test template map inheritance
299
299
300 $ echo "__base__ = map-cmdline.default" > map-simple
300 $ echo "__base__ = map-cmdline.default" > map-simple
301 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
301 $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple
302 $ hg log -l1 -T./map-simple
302 $ hg log -l1 -T./map-simple
303 changeset: ***8***
303 changeset: ***8***
304 tag: tip
304 tag: tip
305 user: test
305 user: test
306 date: Wed Jan 01 10:01:00 2020 +0000
306 date: Wed Jan 01 10:01:00 2020 +0000
307 summary: third
307 summary: third
308
308
309
309
310 Test docheader, docfooter and separator in template map
310 Test docheader, docfooter and separator in template map
311
311
312 $ cat <<'EOF' > map-myjson
312 $ cat <<'EOF' > map-myjson
313 > docheader = '\{\n'
313 > docheader = '\{\n'
314 > docfooter = '\n}\n'
314 > docfooter = '\n}\n'
315 > separator = ',\n'
315 > separator = ',\n'
316 > changeset = ' {dict(rev, node|short)|json}'
316 > changeset = ' {dict(rev, node|short)|json}'
317 > EOF
317 > EOF
318 $ hg log -l2 -T./map-myjson
318 $ hg log -l2 -T./map-myjson
319 {
319 {
320 {"node": "95c24699272e", "rev": 8},
320 {"node": "95c24699272e", "rev": 8},
321 {"node": "29114dbae42b", "rev": 7}
321 {"node": "29114dbae42b", "rev": 7}
322 }
322 }
323
323
324 Test docheader, docfooter and separator in [templates] section
324 Test docheader, docfooter and separator in [templates] section
325
325
326 $ cat <<'EOF' >> .hg/hgrc
326 $ cat <<'EOF' >> .hg/hgrc
327 > [templates]
327 > [templates]
328 > myjson = ' {dict(rev, node|short)|json}'
328 > myjson = ' {dict(rev, node|short)|json}'
329 > myjson:docheader = '\{\n'
329 > myjson:docheader = '\{\n'
330 > myjson:docfooter = '\n}\n'
330 > myjson:docfooter = '\n}\n'
331 > myjson:separator = ',\n'
331 > myjson:separator = ',\n'
332 > :docheader = 'should not be selected as a docheader for literal templates\n'
332 > :docheader = 'should not be selected as a docheader for literal templates\n'
333 > EOF
333 > EOF
334 $ hg log -l2 -Tmyjson
334 $ hg log -l2 -Tmyjson
335 {
335 {
336 {"node": "95c24699272e", "rev": 8},
336 {"node": "95c24699272e", "rev": 8},
337 {"node": "29114dbae42b", "rev": 7}
337 {"node": "29114dbae42b", "rev": 7}
338 }
338 }
339 $ hg log -l1 -T'{rev}\n'
339 $ hg log -l1 -T'{rev}\n'
340 8
340 8
341
341
342 Template should precede style option
342 Template should precede style option
343
343
344 $ hg log -l1 --style default -T '{rev}\n'
344 $ hg log -l1 --style default -T '{rev}\n'
345 8
345 8
346
346
347 Add a commit with empty description, to ensure that the templates
347 Add a commit with empty description, to ensure that the templates
348 below will omit the description line.
348 below will omit the description line.
349
349
350 $ echo c >> c
350 $ echo c >> c
351 $ hg add c
351 $ hg add c
352 $ hg commit -qm ' '
352 $ hg commit -qm ' '
353
353
354 Default style is like normal output. Phases style should be the same
354 Default style is like normal output. Phases style should be the same
355 as default style, except for extra phase lines.
355 as default style, except for extra phase lines.
356
356
357 $ hg log > log.out
357 $ hg log > log.out
358 $ hg log --style default > style.out
358 $ hg log --style default > style.out
359 $ cmp log.out style.out || diff -u log.out style.out
359 $ cmp log.out style.out || diff -u log.out style.out
360 $ hg log -T phases > phases.out
360 $ hg log -T phases > phases.out
361 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
361 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
362 +phase: draft
362 +phase: draft
363 +phase: draft
363 +phase: draft
364 +phase: draft
364 +phase: draft
365 +phase: draft
365 +phase: draft
366 +phase: draft
366 +phase: draft
367 +phase: draft
367 +phase: draft
368 +phase: draft
368 +phase: draft
369 +phase: draft
369 +phase: draft
370 +phase: draft
370 +phase: draft
371 +phase: draft
371 +phase: draft
372
372
373 $ hg log -v > log.out
373 $ hg log -v > log.out
374 $ hg log -v --style default > style.out
374 $ hg log -v --style default > style.out
375 $ cmp log.out style.out || diff -u log.out style.out
375 $ cmp log.out style.out || diff -u log.out style.out
376 $ hg log -v -T phases > phases.out
376 $ hg log -v -T phases > phases.out
377 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
377 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
378 +phase: draft
378 +phase: draft
379 +phase: draft
379 +phase: draft
380 +phase: draft
380 +phase: draft
381 +phase: draft
381 +phase: draft
382 +phase: draft
382 +phase: draft
383 +phase: draft
383 +phase: draft
384 +phase: draft
384 +phase: draft
385 +phase: draft
385 +phase: draft
386 +phase: draft
386 +phase: draft
387 +phase: draft
387 +phase: draft
388
388
389 $ hg log -q > log.out
389 $ hg log -q > log.out
390 $ hg log -q --style default > style.out
390 $ hg log -q --style default > style.out
391 $ cmp log.out style.out || diff -u log.out style.out
391 $ cmp log.out style.out || diff -u log.out style.out
392 $ hg log -q -T phases > phases.out
392 $ hg log -q -T phases > phases.out
393 $ cmp log.out phases.out || diff -u log.out phases.out
393 $ cmp log.out phases.out || diff -u log.out phases.out
394
394
395 $ hg log --debug > log.out
395 $ hg log --debug > log.out
396 $ hg log --debug --style default > style.out
396 $ hg log --debug --style default > style.out
397 $ cmp log.out style.out || diff -u log.out style.out
397 $ cmp log.out style.out || diff -u log.out style.out
398 $ hg log --debug -T phases > phases.out
398 $ hg log --debug -T phases > phases.out
399 $ cmp log.out phases.out || diff -u log.out phases.out
399 $ cmp log.out phases.out || diff -u log.out phases.out
400
400
401 Default style of working-directory revision should also be the same (but
401 Default style of working-directory revision should also be the same (but
402 date may change while running tests):
402 date may change while running tests):
403
403
404 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
404 $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out
405 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
405 $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out
406 $ cmp log.out style.out || diff -u log.out style.out
406 $ cmp log.out style.out || diff -u log.out style.out
407
407
408 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
408 $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out
409 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
409 $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out
410 $ cmp log.out style.out || diff -u log.out style.out
410 $ cmp log.out style.out || diff -u log.out style.out
411
411
412 $ hg log -r 'wdir()' -q > log.out
412 $ hg log -r 'wdir()' -q > log.out
413 $ hg log -r 'wdir()' -q --style default > style.out
413 $ hg log -r 'wdir()' -q --style default > style.out
414 $ cmp log.out style.out || diff -u log.out style.out
414 $ cmp log.out style.out || diff -u log.out style.out
415
415
416 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
416 $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out
417 $ hg log -r 'wdir()' --debug --style default \
417 $ hg log -r 'wdir()' --debug --style default \
418 > | sed 's|^date:.*|date:|' > style.out
418 > | sed 's|^date:.*|date:|' > style.out
419 $ cmp log.out style.out || diff -u log.out style.out
419 $ cmp log.out style.out || diff -u log.out style.out
420
420
421 Default style should also preserve color information (issue2866):
421 Default style should also preserve color information (issue2866):
422
422
423 $ cp $HGRCPATH $HGRCPATH-bak
423 $ cp $HGRCPATH $HGRCPATH-bak
424 $ cat <<EOF >> $HGRCPATH
424 $ cat <<EOF >> $HGRCPATH
425 > [extensions]
425 > [extensions]
426 > color=
426 > color=
427 > EOF
427 > EOF
428
428
429 $ hg --color=debug log > log.out
429 $ hg --color=debug log > log.out
430 $ hg --color=debug log --style default > style.out
430 $ hg --color=debug log --style default > style.out
431 $ cmp log.out style.out || diff -u log.out style.out
431 $ cmp log.out style.out || diff -u log.out style.out
432 $ hg --color=debug log -T phases > phases.out
432 $ hg --color=debug log -T phases > phases.out
433 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
433 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
434 +[log.phase|phase: draft]
434 +[log.phase|phase: draft]
435 +[log.phase|phase: draft]
435 +[log.phase|phase: draft]
436 +[log.phase|phase: draft]
436 +[log.phase|phase: draft]
437 +[log.phase|phase: draft]
437 +[log.phase|phase: draft]
438 +[log.phase|phase: draft]
438 +[log.phase|phase: draft]
439 +[log.phase|phase: draft]
439 +[log.phase|phase: draft]
440 +[log.phase|phase: draft]
440 +[log.phase|phase: draft]
441 +[log.phase|phase: draft]
441 +[log.phase|phase: draft]
442 +[log.phase|phase: draft]
442 +[log.phase|phase: draft]
443 +[log.phase|phase: draft]
443 +[log.phase|phase: draft]
444
444
445 $ hg --color=debug -v log > log.out
445 $ hg --color=debug -v log > log.out
446 $ hg --color=debug -v log --style default > style.out
446 $ hg --color=debug -v log --style default > style.out
447 $ cmp log.out style.out || diff -u log.out style.out
447 $ cmp log.out style.out || diff -u log.out style.out
448 $ hg --color=debug -v log -T phases > phases.out
448 $ hg --color=debug -v log -T phases > phases.out
449 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
449 $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@'
450 +[log.phase|phase: draft]
450 +[log.phase|phase: draft]
451 +[log.phase|phase: draft]
451 +[log.phase|phase: draft]
452 +[log.phase|phase: draft]
452 +[log.phase|phase: draft]
453 +[log.phase|phase: draft]
453 +[log.phase|phase: draft]
454 +[log.phase|phase: draft]
454 +[log.phase|phase: draft]
455 +[log.phase|phase: draft]
455 +[log.phase|phase: draft]
456 +[log.phase|phase: draft]
456 +[log.phase|phase: draft]
457 +[log.phase|phase: draft]
457 +[log.phase|phase: draft]
458 +[log.phase|phase: draft]
458 +[log.phase|phase: draft]
459 +[log.phase|phase: draft]
459 +[log.phase|phase: draft]
460
460
461 $ hg --color=debug -q log > log.out
461 $ hg --color=debug -q log > log.out
462 $ hg --color=debug -q log --style default > style.out
462 $ hg --color=debug -q log --style default > style.out
463 $ cmp log.out style.out || diff -u log.out style.out
463 $ cmp log.out style.out || diff -u log.out style.out
464 $ hg --color=debug -q log -T phases > phases.out
464 $ hg --color=debug -q log -T phases > phases.out
465 $ cmp log.out phases.out || diff -u log.out phases.out
465 $ cmp log.out phases.out || diff -u log.out phases.out
466
466
467 $ hg --color=debug --debug log > log.out
467 $ hg --color=debug --debug log > log.out
468 $ hg --color=debug --debug log --style default > style.out
468 $ hg --color=debug --debug log --style default > style.out
469 $ cmp log.out style.out || diff -u log.out style.out
469 $ cmp log.out style.out || diff -u log.out style.out
470 $ hg --color=debug --debug log -T phases > phases.out
470 $ hg --color=debug --debug log -T phases > phases.out
471 $ cmp log.out phases.out || diff -u log.out phases.out
471 $ cmp log.out phases.out || diff -u log.out phases.out
472
472
473 $ mv $HGRCPATH-bak $HGRCPATH
473 $ mv $HGRCPATH-bak $HGRCPATH
474
474
475 Remove commit with empty commit message, so as to not pollute further
475 Remove commit with empty commit message, so as to not pollute further
476 tests.
476 tests.
477
477
478 $ hg --config extensions.strip= strip -q .
478 $ hg --config extensions.strip= strip -q .
479
479
480 Revision with no copies (used to print a traceback):
480 Revision with no copies (used to print a traceback):
481
481
482 $ hg tip -v --template '\n'
482 $ hg tip -v --template '\n'
483
483
484
484
485 Compact style works:
485 Compact style works:
486
486
487 $ hg log -Tcompact
487 $ hg log -Tcompact
488 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
488 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
489 third
489 third
490
490
491 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
491 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
492 second
492 second
493
493
494 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
494 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
495 merge
495 merge
496
496
497 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
497 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
498 new head
498 new head
499
499
500 4 bbe44766e73d 1970-01-17 04:53 +0000 person
500 4 bbe44766e73d 1970-01-17 04:53 +0000 person
501 new branch
501 new branch
502
502
503 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
503 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
504 no user, no domain
504 no user, no domain
505
505
506 2 97054abb4ab8 1970-01-14 21:20 +0000 other
506 2 97054abb4ab8 1970-01-14 21:20 +0000 other
507 no person
507 no person
508
508
509 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
509 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
510 other 1
510 other 1
511
511
512 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
512 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
513 line 1
513 line 1
514
514
515
515
516 $ hg log -v --style compact
516 $ hg log -v --style compact
517 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
517 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
518 third
518 third
519
519
520 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
520 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
521 second
521 second
522
522
523 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
523 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
524 merge
524 merge
525
525
526 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
526 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
527 new head
527 new head
528
528
529 4 bbe44766e73d 1970-01-17 04:53 +0000 person
529 4 bbe44766e73d 1970-01-17 04:53 +0000 person
530 new branch
530 new branch
531
531
532 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
532 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
533 no user, no domain
533 no user, no domain
534
534
535 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
535 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
536 no person
536 no person
537
537
538 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
538 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
539 other 1
539 other 1
540 other 2
540 other 2
541
541
542 other 3
542 other 3
543
543
544 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
544 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
545 line 1
545 line 1
546 line 2
546 line 2
547
547
548
548
549 $ hg log --debug --style compact
549 $ hg log --debug --style compact
550 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
550 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
551 third
551 third
552
552
553 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
553 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
554 second
554 second
555
555
556 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
556 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
557 merge
557 merge
558
558
559 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
559 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
560 new head
560 new head
561
561
562 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
562 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
563 new branch
563 new branch
564
564
565 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
565 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
566 no user, no domain
566 no user, no domain
567
567
568 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
568 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
569 no person
569 no person
570
570
571 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
571 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
572 other 1
572 other 1
573 other 2
573 other 2
574
574
575 other 3
575 other 3
576
576
577 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
577 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
578 line 1
578 line 1
579 line 2
579 line 2
580
580
581
581
582 Test xml styles:
582 Test xml styles:
583
583
584 $ hg log --style xml -r 'not all()'
584 $ hg log --style xml -r 'not all()'
585 <?xml version="1.0"?>
585 <?xml version="1.0"?>
586 <log>
586 <log>
587 </log>
587 </log>
588
588
589 $ hg log --style xml
589 $ hg log --style xml
590 <?xml version="1.0"?>
590 <?xml version="1.0"?>
591 <log>
591 <log>
592 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
592 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
593 <tag>tip</tag>
593 <tag>tip</tag>
594 <author email="test">test</author>
594 <author email="test">test</author>
595 <date>2020-01-01T10:01:00+00:00</date>
595 <date>2020-01-01T10:01:00+00:00</date>
596 <msg xml:space="preserve">third</msg>
596 <msg xml:space="preserve">third</msg>
597 </logentry>
597 </logentry>
598 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
598 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
599 <parent revision="-1" node="0000000000000000000000000000000000000000" />
599 <parent revision="-1" node="0000000000000000000000000000000000000000" />
600 <author email="user@hostname">User Name</author>
600 <author email="user@hostname">User Name</author>
601 <date>1970-01-12T13:46:40+00:00</date>
601 <date>1970-01-12T13:46:40+00:00</date>
602 <msg xml:space="preserve">second</msg>
602 <msg xml:space="preserve">second</msg>
603 </logentry>
603 </logentry>
604 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
604 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
605 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
605 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
606 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
606 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
607 <author email="person">person</author>
607 <author email="person">person</author>
608 <date>1970-01-18T08:40:01+00:00</date>
608 <date>1970-01-18T08:40:01+00:00</date>
609 <msg xml:space="preserve">merge</msg>
609 <msg xml:space="preserve">merge</msg>
610 </logentry>
610 </logentry>
611 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
611 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
612 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
612 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
613 <author email="person">person</author>
613 <author email="person">person</author>
614 <date>1970-01-18T08:40:00+00:00</date>
614 <date>1970-01-18T08:40:00+00:00</date>
615 <msg xml:space="preserve">new head</msg>
615 <msg xml:space="preserve">new head</msg>
616 </logentry>
616 </logentry>
617 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
617 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
618 <branch>foo</branch>
618 <branch>foo</branch>
619 <author email="person">person</author>
619 <author email="person">person</author>
620 <date>1970-01-17T04:53:20+00:00</date>
620 <date>1970-01-17T04:53:20+00:00</date>
621 <msg xml:space="preserve">new branch</msg>
621 <msg xml:space="preserve">new branch</msg>
622 </logentry>
622 </logentry>
623 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
623 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
624 <author email="person">person</author>
624 <author email="person">person</author>
625 <date>1970-01-16T01:06:40+00:00</date>
625 <date>1970-01-16T01:06:40+00:00</date>
626 <msg xml:space="preserve">no user, no domain</msg>
626 <msg xml:space="preserve">no user, no domain</msg>
627 </logentry>
627 </logentry>
628 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
628 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
629 <author email="other@place">other</author>
629 <author email="other@place">other</author>
630 <date>1970-01-14T21:20:00+00:00</date>
630 <date>1970-01-14T21:20:00+00:00</date>
631 <msg xml:space="preserve">no person</msg>
631 <msg xml:space="preserve">no person</msg>
632 </logentry>
632 </logentry>
633 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
633 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
634 <author email="other@place">A. N. Other</author>
634 <author email="other@place">A. N. Other</author>
635 <date>1970-01-13T17:33:20+00:00</date>
635 <date>1970-01-13T17:33:20+00:00</date>
636 <msg xml:space="preserve">other 1
636 <msg xml:space="preserve">other 1
637 other 2
637 other 2
638
638
639 other 3</msg>
639 other 3</msg>
640 </logentry>
640 </logentry>
641 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
641 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
642 <author email="user@hostname">User Name</author>
642 <author email="user@hostname">User Name</author>
643 <date>1970-01-12T13:46:40+00:00</date>
643 <date>1970-01-12T13:46:40+00:00</date>
644 <msg xml:space="preserve">line 1
644 <msg xml:space="preserve">line 1
645 line 2</msg>
645 line 2</msg>
646 </logentry>
646 </logentry>
647 </log>
647 </log>
648
648
649 $ hg log -v --style xml
649 $ hg log -v --style xml
650 <?xml version="1.0"?>
650 <?xml version="1.0"?>
651 <log>
651 <log>
652 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
652 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
653 <tag>tip</tag>
653 <tag>tip</tag>
654 <author email="test">test</author>
654 <author email="test">test</author>
655 <date>2020-01-01T10:01:00+00:00</date>
655 <date>2020-01-01T10:01:00+00:00</date>
656 <msg xml:space="preserve">third</msg>
656 <msg xml:space="preserve">third</msg>
657 <paths>
657 <paths>
658 <path action="A">fourth</path>
658 <path action="A">fourth</path>
659 <path action="A">third</path>
659 <path action="A">third</path>
660 <path action="R">second</path>
660 <path action="R">second</path>
661 </paths>
661 </paths>
662 <copies>
662 <copies>
663 <copy source="second">fourth</copy>
663 <copy source="second">fourth</copy>
664 </copies>
664 </copies>
665 </logentry>
665 </logentry>
666 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
666 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
667 <parent revision="-1" node="0000000000000000000000000000000000000000" />
667 <parent revision="-1" node="0000000000000000000000000000000000000000" />
668 <author email="user@hostname">User Name</author>
668 <author email="user@hostname">User Name</author>
669 <date>1970-01-12T13:46:40+00:00</date>
669 <date>1970-01-12T13:46:40+00:00</date>
670 <msg xml:space="preserve">second</msg>
670 <msg xml:space="preserve">second</msg>
671 <paths>
671 <paths>
672 <path action="A">second</path>
672 <path action="A">second</path>
673 </paths>
673 </paths>
674 </logentry>
674 </logentry>
675 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
675 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
676 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
676 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
677 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
677 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
678 <author email="person">person</author>
678 <author email="person">person</author>
679 <date>1970-01-18T08:40:01+00:00</date>
679 <date>1970-01-18T08:40:01+00:00</date>
680 <msg xml:space="preserve">merge</msg>
680 <msg xml:space="preserve">merge</msg>
681 <paths>
681 <paths>
682 </paths>
682 </paths>
683 </logentry>
683 </logentry>
684 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
684 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
685 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
685 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
686 <author email="person">person</author>
686 <author email="person">person</author>
687 <date>1970-01-18T08:40:00+00:00</date>
687 <date>1970-01-18T08:40:00+00:00</date>
688 <msg xml:space="preserve">new head</msg>
688 <msg xml:space="preserve">new head</msg>
689 <paths>
689 <paths>
690 <path action="A">d</path>
690 <path action="A">d</path>
691 </paths>
691 </paths>
692 </logentry>
692 </logentry>
693 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
693 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
694 <branch>foo</branch>
694 <branch>foo</branch>
695 <author email="person">person</author>
695 <author email="person">person</author>
696 <date>1970-01-17T04:53:20+00:00</date>
696 <date>1970-01-17T04:53:20+00:00</date>
697 <msg xml:space="preserve">new branch</msg>
697 <msg xml:space="preserve">new branch</msg>
698 <paths>
698 <paths>
699 </paths>
699 </paths>
700 </logentry>
700 </logentry>
701 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
701 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
702 <author email="person">person</author>
702 <author email="person">person</author>
703 <date>1970-01-16T01:06:40+00:00</date>
703 <date>1970-01-16T01:06:40+00:00</date>
704 <msg xml:space="preserve">no user, no domain</msg>
704 <msg xml:space="preserve">no user, no domain</msg>
705 <paths>
705 <paths>
706 <path action="M">c</path>
706 <path action="M">c</path>
707 </paths>
707 </paths>
708 </logentry>
708 </logentry>
709 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
709 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
710 <author email="other@place">other</author>
710 <author email="other@place">other</author>
711 <date>1970-01-14T21:20:00+00:00</date>
711 <date>1970-01-14T21:20:00+00:00</date>
712 <msg xml:space="preserve">no person</msg>
712 <msg xml:space="preserve">no person</msg>
713 <paths>
713 <paths>
714 <path action="A">c</path>
714 <path action="A">c</path>
715 </paths>
715 </paths>
716 </logentry>
716 </logentry>
717 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
717 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
718 <author email="other@place">A. N. Other</author>
718 <author email="other@place">A. N. Other</author>
719 <date>1970-01-13T17:33:20+00:00</date>
719 <date>1970-01-13T17:33:20+00:00</date>
720 <msg xml:space="preserve">other 1
720 <msg xml:space="preserve">other 1
721 other 2
721 other 2
722
722
723 other 3</msg>
723 other 3</msg>
724 <paths>
724 <paths>
725 <path action="A">b</path>
725 <path action="A">b</path>
726 </paths>
726 </paths>
727 </logentry>
727 </logentry>
728 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
728 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
729 <author email="user@hostname">User Name</author>
729 <author email="user@hostname">User Name</author>
730 <date>1970-01-12T13:46:40+00:00</date>
730 <date>1970-01-12T13:46:40+00:00</date>
731 <msg xml:space="preserve">line 1
731 <msg xml:space="preserve">line 1
732 line 2</msg>
732 line 2</msg>
733 <paths>
733 <paths>
734 <path action="A">a</path>
734 <path action="A">a</path>
735 </paths>
735 </paths>
736 </logentry>
736 </logentry>
737 </log>
737 </log>
738
738
739 $ hg log --debug --style xml
739 $ hg log --debug --style xml
740 <?xml version="1.0"?>
740 <?xml version="1.0"?>
741 <log>
741 <log>
742 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
742 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
743 <tag>tip</tag>
743 <tag>tip</tag>
744 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
744 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
745 <parent revision="-1" node="0000000000000000000000000000000000000000" />
745 <parent revision="-1" node="0000000000000000000000000000000000000000" />
746 <author email="test">test</author>
746 <author email="test">test</author>
747 <date>2020-01-01T10:01:00+00:00</date>
747 <date>2020-01-01T10:01:00+00:00</date>
748 <msg xml:space="preserve">third</msg>
748 <msg xml:space="preserve">third</msg>
749 <paths>
749 <paths>
750 <path action="A">fourth</path>
750 <path action="A">fourth</path>
751 <path action="A">third</path>
751 <path action="A">third</path>
752 <path action="R">second</path>
752 <path action="R">second</path>
753 </paths>
753 </paths>
754 <copies>
754 <copies>
755 <copy source="second">fourth</copy>
755 <copy source="second">fourth</copy>
756 </copies>
756 </copies>
757 <extra key="branch">default</extra>
757 <extra key="branch">default</extra>
758 </logentry>
758 </logentry>
759 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
759 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
760 <parent revision="-1" node="0000000000000000000000000000000000000000" />
760 <parent revision="-1" node="0000000000000000000000000000000000000000" />
761 <parent revision="-1" node="0000000000000000000000000000000000000000" />
761 <parent revision="-1" node="0000000000000000000000000000000000000000" />
762 <author email="user@hostname">User Name</author>
762 <author email="user@hostname">User Name</author>
763 <date>1970-01-12T13:46:40+00:00</date>
763 <date>1970-01-12T13:46:40+00:00</date>
764 <msg xml:space="preserve">second</msg>
764 <msg xml:space="preserve">second</msg>
765 <paths>
765 <paths>
766 <path action="A">second</path>
766 <path action="A">second</path>
767 </paths>
767 </paths>
768 <extra key="branch">default</extra>
768 <extra key="branch">default</extra>
769 </logentry>
769 </logentry>
770 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
770 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
771 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
771 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
772 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
772 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
773 <author email="person">person</author>
773 <author email="person">person</author>
774 <date>1970-01-18T08:40:01+00:00</date>
774 <date>1970-01-18T08:40:01+00:00</date>
775 <msg xml:space="preserve">merge</msg>
775 <msg xml:space="preserve">merge</msg>
776 <paths>
776 <paths>
777 </paths>
777 </paths>
778 <extra key="branch">default</extra>
778 <extra key="branch">default</extra>
779 </logentry>
779 </logentry>
780 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
780 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
781 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
781 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
782 <parent revision="-1" node="0000000000000000000000000000000000000000" />
782 <parent revision="-1" node="0000000000000000000000000000000000000000" />
783 <author email="person">person</author>
783 <author email="person">person</author>
784 <date>1970-01-18T08:40:00+00:00</date>
784 <date>1970-01-18T08:40:00+00:00</date>
785 <msg xml:space="preserve">new head</msg>
785 <msg xml:space="preserve">new head</msg>
786 <paths>
786 <paths>
787 <path action="A">d</path>
787 <path action="A">d</path>
788 </paths>
788 </paths>
789 <extra key="branch">default</extra>
789 <extra key="branch">default</extra>
790 </logentry>
790 </logentry>
791 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
791 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
792 <branch>foo</branch>
792 <branch>foo</branch>
793 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
793 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
794 <parent revision="-1" node="0000000000000000000000000000000000000000" />
794 <parent revision="-1" node="0000000000000000000000000000000000000000" />
795 <author email="person">person</author>
795 <author email="person">person</author>
796 <date>1970-01-17T04:53:20+00:00</date>
796 <date>1970-01-17T04:53:20+00:00</date>
797 <msg xml:space="preserve">new branch</msg>
797 <msg xml:space="preserve">new branch</msg>
798 <paths>
798 <paths>
799 </paths>
799 </paths>
800 <extra key="branch">foo</extra>
800 <extra key="branch">foo</extra>
801 </logentry>
801 </logentry>
802 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
802 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
803 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
803 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
804 <parent revision="-1" node="0000000000000000000000000000000000000000" />
804 <parent revision="-1" node="0000000000000000000000000000000000000000" />
805 <author email="person">person</author>
805 <author email="person">person</author>
806 <date>1970-01-16T01:06:40+00:00</date>
806 <date>1970-01-16T01:06:40+00:00</date>
807 <msg xml:space="preserve">no user, no domain</msg>
807 <msg xml:space="preserve">no user, no domain</msg>
808 <paths>
808 <paths>
809 <path action="M">c</path>
809 <path action="M">c</path>
810 </paths>
810 </paths>
811 <extra key="branch">default</extra>
811 <extra key="branch">default</extra>
812 </logentry>
812 </logentry>
813 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
813 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
814 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
814 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
815 <parent revision="-1" node="0000000000000000000000000000000000000000" />
815 <parent revision="-1" node="0000000000000000000000000000000000000000" />
816 <author email="other@place">other</author>
816 <author email="other@place">other</author>
817 <date>1970-01-14T21:20:00+00:00</date>
817 <date>1970-01-14T21:20:00+00:00</date>
818 <msg xml:space="preserve">no person</msg>
818 <msg xml:space="preserve">no person</msg>
819 <paths>
819 <paths>
820 <path action="A">c</path>
820 <path action="A">c</path>
821 </paths>
821 </paths>
822 <extra key="branch">default</extra>
822 <extra key="branch">default</extra>
823 </logentry>
823 </logentry>
824 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
824 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
825 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
825 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
826 <parent revision="-1" node="0000000000000000000000000000000000000000" />
826 <parent revision="-1" node="0000000000000000000000000000000000000000" />
827 <author email="other@place">A. N. Other</author>
827 <author email="other@place">A. N. Other</author>
828 <date>1970-01-13T17:33:20+00:00</date>
828 <date>1970-01-13T17:33:20+00:00</date>
829 <msg xml:space="preserve">other 1
829 <msg xml:space="preserve">other 1
830 other 2
830 other 2
831
831
832 other 3</msg>
832 other 3</msg>
833 <paths>
833 <paths>
834 <path action="A">b</path>
834 <path action="A">b</path>
835 </paths>
835 </paths>
836 <extra key="branch">default</extra>
836 <extra key="branch">default</extra>
837 </logentry>
837 </logentry>
838 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
838 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
839 <parent revision="-1" node="0000000000000000000000000000000000000000" />
839 <parent revision="-1" node="0000000000000000000000000000000000000000" />
840 <parent revision="-1" node="0000000000000000000000000000000000000000" />
840 <parent revision="-1" node="0000000000000000000000000000000000000000" />
841 <author email="user@hostname">User Name</author>
841 <author email="user@hostname">User Name</author>
842 <date>1970-01-12T13:46:40+00:00</date>
842 <date>1970-01-12T13:46:40+00:00</date>
843 <msg xml:space="preserve">line 1
843 <msg xml:space="preserve">line 1
844 line 2</msg>
844 line 2</msg>
845 <paths>
845 <paths>
846 <path action="A">a</path>
846 <path action="A">a</path>
847 </paths>
847 </paths>
848 <extra key="branch">default</extra>
848 <extra key="branch">default</extra>
849 </logentry>
849 </logentry>
850 </log>
850 </log>
851
851
852
852
853 Test JSON style:
853 Test JSON style:
854
854
855 $ hg log -k nosuch -Tjson
855 $ hg log -k nosuch -Tjson
856 []
856 []
857
857
858 $ hg log -qr . -Tjson
858 $ hg log -qr . -Tjson
859 [
859 [
860 {
860 {
861 "rev": 8,
861 "rev": 8,
862 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
862 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
863 }
863 }
864 ]
864 ]
865
865
866 $ hg log -vpr . -Tjson --stat
866 $ hg log -vpr . -Tjson --stat
867 [
867 [
868 {
868 {
869 "rev": 8,
869 "rev": 8,
870 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
870 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
871 "branch": "default",
871 "branch": "default",
872 "phase": "draft",
872 "phase": "draft",
873 "user": "test",
873 "user": "test",
874 "date": [1577872860, 0],
874 "date": [1577872860, 0],
875 "desc": "third",
875 "desc": "third",
876 "bookmarks": [],
876 "bookmarks": [],
877 "tags": ["tip"],
877 "tags": ["tip"],
878 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
878 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
879 "files": ["fourth", "second", "third"],
879 "files": ["fourth", "second", "third"],
880 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
880 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
881 "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"
881 "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"
882 }
882 }
883 ]
883 ]
884
884
885 honor --git but not format-breaking diffopts
885 honor --git but not format-breaking diffopts
886 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
886 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
887 [
887 [
888 {
888 {
889 "rev": 8,
889 "rev": 8,
890 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
890 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
891 "branch": "default",
891 "branch": "default",
892 "phase": "draft",
892 "phase": "draft",
893 "user": "test",
893 "user": "test",
894 "date": [1577872860, 0],
894 "date": [1577872860, 0],
895 "desc": "third",
895 "desc": "third",
896 "bookmarks": [],
896 "bookmarks": [],
897 "tags": ["tip"],
897 "tags": ["tip"],
898 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
898 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
899 "files": ["fourth", "second", "third"],
899 "files": ["fourth", "second", "third"],
900 "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"
900 "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"
901 }
901 }
902 ]
902 ]
903
903
904 $ hg log -T json
904 $ hg log -T json
905 [
905 [
906 {
906 {
907 "rev": 8,
907 "rev": 8,
908 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
908 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
909 "branch": "default",
909 "branch": "default",
910 "phase": "draft",
910 "phase": "draft",
911 "user": "test",
911 "user": "test",
912 "date": [1577872860, 0],
912 "date": [1577872860, 0],
913 "desc": "third",
913 "desc": "third",
914 "bookmarks": [],
914 "bookmarks": [],
915 "tags": ["tip"],
915 "tags": ["tip"],
916 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
916 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
917 },
917 },
918 {
918 {
919 "rev": 7,
919 "rev": 7,
920 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
920 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
921 "branch": "default",
921 "branch": "default",
922 "phase": "draft",
922 "phase": "draft",
923 "user": "User Name <user@hostname>",
923 "user": "User Name <user@hostname>",
924 "date": [1000000, 0],
924 "date": [1000000, 0],
925 "desc": "second",
925 "desc": "second",
926 "bookmarks": [],
926 "bookmarks": [],
927 "tags": [],
927 "tags": [],
928 "parents": ["0000000000000000000000000000000000000000"]
928 "parents": ["0000000000000000000000000000000000000000"]
929 },
929 },
930 {
930 {
931 "rev": 6,
931 "rev": 6,
932 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
932 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
933 "branch": "default",
933 "branch": "default",
934 "phase": "draft",
934 "phase": "draft",
935 "user": "person",
935 "user": "person",
936 "date": [1500001, 0],
936 "date": [1500001, 0],
937 "desc": "merge",
937 "desc": "merge",
938 "bookmarks": [],
938 "bookmarks": [],
939 "tags": [],
939 "tags": [],
940 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
940 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
941 },
941 },
942 {
942 {
943 "rev": 5,
943 "rev": 5,
944 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
944 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
945 "branch": "default",
945 "branch": "default",
946 "phase": "draft",
946 "phase": "draft",
947 "user": "person",
947 "user": "person",
948 "date": [1500000, 0],
948 "date": [1500000, 0],
949 "desc": "new head",
949 "desc": "new head",
950 "bookmarks": [],
950 "bookmarks": [],
951 "tags": [],
951 "tags": [],
952 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
952 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
953 },
953 },
954 {
954 {
955 "rev": 4,
955 "rev": 4,
956 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
956 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
957 "branch": "foo",
957 "branch": "foo",
958 "phase": "draft",
958 "phase": "draft",
959 "user": "person",
959 "user": "person",
960 "date": [1400000, 0],
960 "date": [1400000, 0],
961 "desc": "new branch",
961 "desc": "new branch",
962 "bookmarks": [],
962 "bookmarks": [],
963 "tags": [],
963 "tags": [],
964 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
964 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
965 },
965 },
966 {
966 {
967 "rev": 3,
967 "rev": 3,
968 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
968 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
969 "branch": "default",
969 "branch": "default",
970 "phase": "draft",
970 "phase": "draft",
971 "user": "person",
971 "user": "person",
972 "date": [1300000, 0],
972 "date": [1300000, 0],
973 "desc": "no user, no domain",
973 "desc": "no user, no domain",
974 "bookmarks": [],
974 "bookmarks": [],
975 "tags": [],
975 "tags": [],
976 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
976 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
977 },
977 },
978 {
978 {
979 "rev": 2,
979 "rev": 2,
980 "node": "97054abb4ab824450e9164180baf491ae0078465",
980 "node": "97054abb4ab824450e9164180baf491ae0078465",
981 "branch": "default",
981 "branch": "default",
982 "phase": "draft",
982 "phase": "draft",
983 "user": "other@place",
983 "user": "other@place",
984 "date": [1200000, 0],
984 "date": [1200000, 0],
985 "desc": "no person",
985 "desc": "no person",
986 "bookmarks": [],
986 "bookmarks": [],
987 "tags": [],
987 "tags": [],
988 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
988 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
989 },
989 },
990 {
990 {
991 "rev": 1,
991 "rev": 1,
992 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
992 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
993 "branch": "default",
993 "branch": "default",
994 "phase": "draft",
994 "phase": "draft",
995 "user": "A. N. Other <other@place>",
995 "user": "A. N. Other <other@place>",
996 "date": [1100000, 0],
996 "date": [1100000, 0],
997 "desc": "other 1\nother 2\n\nother 3",
997 "desc": "other 1\nother 2\n\nother 3",
998 "bookmarks": [],
998 "bookmarks": [],
999 "tags": [],
999 "tags": [],
1000 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
1000 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
1001 },
1001 },
1002 {
1002 {
1003 "rev": 0,
1003 "rev": 0,
1004 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1004 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1005 "branch": "default",
1005 "branch": "default",
1006 "phase": "draft",
1006 "phase": "draft",
1007 "user": "User Name <user@hostname>",
1007 "user": "User Name <user@hostname>",
1008 "date": [1000000, 0],
1008 "date": [1000000, 0],
1009 "desc": "line 1\nline 2",
1009 "desc": "line 1\nline 2",
1010 "bookmarks": [],
1010 "bookmarks": [],
1011 "tags": [],
1011 "tags": [],
1012 "parents": ["0000000000000000000000000000000000000000"]
1012 "parents": ["0000000000000000000000000000000000000000"]
1013 }
1013 }
1014 ]
1014 ]
1015
1015
1016 $ hg heads -v -Tjson
1016 $ hg heads -v -Tjson
1017 [
1017 [
1018 {
1018 {
1019 "rev": 8,
1019 "rev": 8,
1020 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1020 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1021 "branch": "default",
1021 "branch": "default",
1022 "phase": "draft",
1022 "phase": "draft",
1023 "user": "test",
1023 "user": "test",
1024 "date": [1577872860, 0],
1024 "date": [1577872860, 0],
1025 "desc": "third",
1025 "desc": "third",
1026 "bookmarks": [],
1026 "bookmarks": [],
1027 "tags": ["tip"],
1027 "tags": ["tip"],
1028 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1028 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1029 "files": ["fourth", "second", "third"]
1029 "files": ["fourth", "second", "third"]
1030 },
1030 },
1031 {
1031 {
1032 "rev": 6,
1032 "rev": 6,
1033 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1033 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1034 "branch": "default",
1034 "branch": "default",
1035 "phase": "draft",
1035 "phase": "draft",
1036 "user": "person",
1036 "user": "person",
1037 "date": [1500001, 0],
1037 "date": [1500001, 0],
1038 "desc": "merge",
1038 "desc": "merge",
1039 "bookmarks": [],
1039 "bookmarks": [],
1040 "tags": [],
1040 "tags": [],
1041 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1041 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1042 "files": []
1042 "files": []
1043 },
1043 },
1044 {
1044 {
1045 "rev": 4,
1045 "rev": 4,
1046 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1046 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1047 "branch": "foo",
1047 "branch": "foo",
1048 "phase": "draft",
1048 "phase": "draft",
1049 "user": "person",
1049 "user": "person",
1050 "date": [1400000, 0],
1050 "date": [1400000, 0],
1051 "desc": "new branch",
1051 "desc": "new branch",
1052 "bookmarks": [],
1052 "bookmarks": [],
1053 "tags": [],
1053 "tags": [],
1054 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1054 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1055 "files": []
1055 "files": []
1056 }
1056 }
1057 ]
1057 ]
1058
1058
1059 $ hg log --debug -Tjson
1059 $ hg log --debug -Tjson
1060 [
1060 [
1061 {
1061 {
1062 "rev": 8,
1062 "rev": 8,
1063 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1063 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
1064 "branch": "default",
1064 "branch": "default",
1065 "phase": "draft",
1065 "phase": "draft",
1066 "user": "test",
1066 "user": "test",
1067 "date": [1577872860, 0],
1067 "date": [1577872860, 0],
1068 "desc": "third",
1068 "desc": "third",
1069 "bookmarks": [],
1069 "bookmarks": [],
1070 "tags": ["tip"],
1070 "tags": ["tip"],
1071 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1071 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
1072 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
1072 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
1073 "extra": {"branch": "default"},
1073 "extra": {"branch": "default"},
1074 "modified": [],
1074 "modified": [],
1075 "added": ["fourth", "third"],
1075 "added": ["fourth", "third"],
1076 "removed": ["second"]
1076 "removed": ["second"]
1077 },
1077 },
1078 {
1078 {
1079 "rev": 7,
1079 "rev": 7,
1080 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
1080 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
1081 "branch": "default",
1081 "branch": "default",
1082 "phase": "draft",
1082 "phase": "draft",
1083 "user": "User Name <user@hostname>",
1083 "user": "User Name <user@hostname>",
1084 "date": [1000000, 0],
1084 "date": [1000000, 0],
1085 "desc": "second",
1085 "desc": "second",
1086 "bookmarks": [],
1086 "bookmarks": [],
1087 "tags": [],
1087 "tags": [],
1088 "parents": ["0000000000000000000000000000000000000000"],
1088 "parents": ["0000000000000000000000000000000000000000"],
1089 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
1089 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
1090 "extra": {"branch": "default"},
1090 "extra": {"branch": "default"},
1091 "modified": [],
1091 "modified": [],
1092 "added": ["second"],
1092 "added": ["second"],
1093 "removed": []
1093 "removed": []
1094 },
1094 },
1095 {
1095 {
1096 "rev": 6,
1096 "rev": 6,
1097 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1097 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
1098 "branch": "default",
1098 "branch": "default",
1099 "phase": "draft",
1099 "phase": "draft",
1100 "user": "person",
1100 "user": "person",
1101 "date": [1500001, 0],
1101 "date": [1500001, 0],
1102 "desc": "merge",
1102 "desc": "merge",
1103 "bookmarks": [],
1103 "bookmarks": [],
1104 "tags": [],
1104 "tags": [],
1105 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1105 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
1106 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1106 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1107 "extra": {"branch": "default"},
1107 "extra": {"branch": "default"},
1108 "modified": [],
1108 "modified": [],
1109 "added": [],
1109 "added": [],
1110 "removed": []
1110 "removed": []
1111 },
1111 },
1112 {
1112 {
1113 "rev": 5,
1113 "rev": 5,
1114 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1114 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
1115 "branch": "default",
1115 "branch": "default",
1116 "phase": "draft",
1116 "phase": "draft",
1117 "user": "person",
1117 "user": "person",
1118 "date": [1500000, 0],
1118 "date": [1500000, 0],
1119 "desc": "new head",
1119 "desc": "new head",
1120 "bookmarks": [],
1120 "bookmarks": [],
1121 "tags": [],
1121 "tags": [],
1122 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1122 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1123 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1123 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
1124 "extra": {"branch": "default"},
1124 "extra": {"branch": "default"},
1125 "modified": [],
1125 "modified": [],
1126 "added": ["d"],
1126 "added": ["d"],
1127 "removed": []
1127 "removed": []
1128 },
1128 },
1129 {
1129 {
1130 "rev": 4,
1130 "rev": 4,
1131 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1131 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
1132 "branch": "foo",
1132 "branch": "foo",
1133 "phase": "draft",
1133 "phase": "draft",
1134 "user": "person",
1134 "user": "person",
1135 "date": [1400000, 0],
1135 "date": [1400000, 0],
1136 "desc": "new branch",
1136 "desc": "new branch",
1137 "bookmarks": [],
1137 "bookmarks": [],
1138 "tags": [],
1138 "tags": [],
1139 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1139 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
1140 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1140 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1141 "extra": {"branch": "foo"},
1141 "extra": {"branch": "foo"},
1142 "modified": [],
1142 "modified": [],
1143 "added": [],
1143 "added": [],
1144 "removed": []
1144 "removed": []
1145 },
1145 },
1146 {
1146 {
1147 "rev": 3,
1147 "rev": 3,
1148 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1148 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
1149 "branch": "default",
1149 "branch": "default",
1150 "phase": "draft",
1150 "phase": "draft",
1151 "user": "person",
1151 "user": "person",
1152 "date": [1300000, 0],
1152 "date": [1300000, 0],
1153 "desc": "no user, no domain",
1153 "desc": "no user, no domain",
1154 "bookmarks": [],
1154 "bookmarks": [],
1155 "tags": [],
1155 "tags": [],
1156 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1156 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
1157 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1157 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
1158 "extra": {"branch": "default"},
1158 "extra": {"branch": "default"},
1159 "modified": ["c"],
1159 "modified": ["c"],
1160 "added": [],
1160 "added": [],
1161 "removed": []
1161 "removed": []
1162 },
1162 },
1163 {
1163 {
1164 "rev": 2,
1164 "rev": 2,
1165 "node": "97054abb4ab824450e9164180baf491ae0078465",
1165 "node": "97054abb4ab824450e9164180baf491ae0078465",
1166 "branch": "default",
1166 "branch": "default",
1167 "phase": "draft",
1167 "phase": "draft",
1168 "user": "other@place",
1168 "user": "other@place",
1169 "date": [1200000, 0],
1169 "date": [1200000, 0],
1170 "desc": "no person",
1170 "desc": "no person",
1171 "bookmarks": [],
1171 "bookmarks": [],
1172 "tags": [],
1172 "tags": [],
1173 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1173 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
1174 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1174 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
1175 "extra": {"branch": "default"},
1175 "extra": {"branch": "default"},
1176 "modified": [],
1176 "modified": [],
1177 "added": ["c"],
1177 "added": ["c"],
1178 "removed": []
1178 "removed": []
1179 },
1179 },
1180 {
1180 {
1181 "rev": 1,
1181 "rev": 1,
1182 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1182 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
1183 "branch": "default",
1183 "branch": "default",
1184 "phase": "draft",
1184 "phase": "draft",
1185 "user": "A. N. Other <other@place>",
1185 "user": "A. N. Other <other@place>",
1186 "date": [1100000, 0],
1186 "date": [1100000, 0],
1187 "desc": "other 1\nother 2\n\nother 3",
1187 "desc": "other 1\nother 2\n\nother 3",
1188 "bookmarks": [],
1188 "bookmarks": [],
1189 "tags": [],
1189 "tags": [],
1190 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1190 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
1191 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1191 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
1192 "extra": {"branch": "default"},
1192 "extra": {"branch": "default"},
1193 "modified": [],
1193 "modified": [],
1194 "added": ["b"],
1194 "added": ["b"],
1195 "removed": []
1195 "removed": []
1196 },
1196 },
1197 {
1197 {
1198 "rev": 0,
1198 "rev": 0,
1199 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1199 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
1200 "branch": "default",
1200 "branch": "default",
1201 "phase": "draft",
1201 "phase": "draft",
1202 "user": "User Name <user@hostname>",
1202 "user": "User Name <user@hostname>",
1203 "date": [1000000, 0],
1203 "date": [1000000, 0],
1204 "desc": "line 1\nline 2",
1204 "desc": "line 1\nline 2",
1205 "bookmarks": [],
1205 "bookmarks": [],
1206 "tags": [],
1206 "tags": [],
1207 "parents": ["0000000000000000000000000000000000000000"],
1207 "parents": ["0000000000000000000000000000000000000000"],
1208 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1208 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
1209 "extra": {"branch": "default"},
1209 "extra": {"branch": "default"},
1210 "modified": [],
1210 "modified": [],
1211 "added": ["a"],
1211 "added": ["a"],
1212 "removed": []
1212 "removed": []
1213 }
1213 }
1214 ]
1214 ]
1215
1215
1216 Error if style not readable:
1216 Error if style not readable:
1217
1217
1218 #if unix-permissions no-root
1218 #if unix-permissions no-root
1219 $ touch q
1219 $ touch q
1220 $ chmod 0 q
1220 $ chmod 0 q
1221 $ hg log --style ./q
1221 $ hg log --style ./q
1222 abort: Permission denied: ./q
1222 abort: Permission denied: ./q
1223 [255]
1223 [255]
1224 #endif
1224 #endif
1225
1225
1226 Error if no style:
1226 Error if no style:
1227
1227
1228 $ hg log --style notexist
1228 $ hg log --style notexist
1229 abort: style 'notexist' not found
1229 abort: style 'notexist' not found
1230 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1230 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
1231 [255]
1231 [255]
1232
1232
1233 $ hg log -T list
1233 $ hg log -T list
1234 available styles: bisect, changelog, compact, default, phases, show, status, xml
1234 available styles: bisect, changelog, compact, default, phases, show, status, xml
1235 abort: specify a template
1235 abort: specify a template
1236 [255]
1236 [255]
1237
1237
1238 Error if style missing key:
1238 Error if style missing key:
1239
1239
1240 $ echo 'q = q' > t
1240 $ echo 'q = q' > t
1241 $ hg log --style ./t
1241 $ hg log --style ./t
1242 abort: "changeset" not in template map
1242 abort: "changeset" not in template map
1243 [255]
1243 [255]
1244
1244
1245 Error if style missing value:
1245 Error if style missing value:
1246
1246
1247 $ echo 'changeset =' > t
1247 $ echo 'changeset =' > t
1248 $ hg log --style t
1248 $ hg log --style t
1249 hg: parse error at t:1: missing value
1249 hg: parse error at t:1: missing value
1250 [255]
1250 [255]
1251
1251
1252 Error if include fails:
1252 Error if include fails:
1253
1253
1254 $ echo 'changeset = q' >> t
1254 $ echo 'changeset = q' >> t
1255 #if unix-permissions no-root
1255 #if unix-permissions no-root
1256 $ hg log --style ./t
1256 $ hg log --style ./t
1257 abort: template file ./q: Permission denied
1257 abort: template file ./q: Permission denied
1258 [255]
1258 [255]
1259 $ rm -f q
1259 $ rm -f q
1260 #endif
1260 #endif
1261
1261
1262 Include works:
1262 Include works:
1263
1263
1264 $ echo '{rev}' > q
1264 $ echo '{rev}' > q
1265 $ hg log --style ./t
1265 $ hg log --style ./t
1266 8
1266 8
1267 7
1267 7
1268 6
1268 6
1269 5
1269 5
1270 4
1270 4
1271 3
1271 3
1272 2
1272 2
1273 1
1273 1
1274 0
1274 0
1275
1275
1276 Check that recursive reference does not fall into RuntimeError (issue4758):
1276 Check that recursive reference does not fall into RuntimeError (issue4758):
1277
1277
1278 common mistake:
1278 common mistake:
1279
1279
1280 $ cat << EOF > issue4758
1280 $ cat << EOF > issue4758
1281 > changeset = '{changeset}\n'
1281 > changeset = '{changeset}\n'
1282 > EOF
1282 > EOF
1283 $ hg log --style ./issue4758
1283 $ hg log --style ./issue4758
1284 abort: recursive reference 'changeset' in template
1284 abort: recursive reference 'changeset' in template
1285 [255]
1285 [255]
1286
1286
1287 circular reference:
1287 circular reference:
1288
1288
1289 $ cat << EOF > issue4758
1289 $ cat << EOF > issue4758
1290 > changeset = '{foo}'
1290 > changeset = '{foo}'
1291 > foo = '{changeset}'
1291 > foo = '{changeset}'
1292 > EOF
1292 > EOF
1293 $ hg log --style ./issue4758
1293 $ hg log --style ./issue4758
1294 abort: recursive reference 'foo' in template
1294 abort: recursive reference 'foo' in template
1295 [255]
1295 [255]
1296
1296
1297 buildmap() -> gettemplate(), where no thunk was made:
1297 buildmap() -> gettemplate(), where no thunk was made:
1298
1298
1299 $ cat << EOF > issue4758
1299 $ cat << EOF > issue4758
1300 > changeset = '{files % changeset}\n'
1300 > changeset = '{files % changeset}\n'
1301 > EOF
1301 > EOF
1302 $ hg log --style ./issue4758
1302 $ hg log --style ./issue4758
1303 abort: recursive reference 'changeset' in template
1303 abort: recursive reference 'changeset' in template
1304 [255]
1304 [255]
1305
1305
1306 not a recursion if a keyword of the same name exists:
1306 not a recursion if a keyword of the same name exists:
1307
1307
1308 $ cat << EOF > issue4758
1308 $ cat << EOF > issue4758
1309 > changeset = '{tags % rev}'
1309 > changeset = '{tags % rev}'
1310 > rev = '{rev} {tag}\n'
1310 > rev = '{rev} {tag}\n'
1311 > EOF
1311 > EOF
1312 $ hg log --style ./issue4758 -r tip
1312 $ hg log --style ./issue4758 -r tip
1313 8 tip
1313 8 tip
1314
1314
1315 Check that {phase} works correctly on parents:
1315 Check that {phase} works correctly on parents:
1316
1316
1317 $ cat << EOF > parentphase
1317 $ cat << EOF > parentphase
1318 > changeset_debug = '{rev} ({phase}):{parents}\n'
1318 > changeset_debug = '{rev} ({phase}):{parents}\n'
1319 > parent = ' {rev} ({phase})'
1319 > parent = ' {rev} ({phase})'
1320 > EOF
1320 > EOF
1321 $ hg phase -r 5 --public
1321 $ hg phase -r 5 --public
1322 $ hg phase -r 7 --secret --force
1322 $ hg phase -r 7 --secret --force
1323 $ hg log --debug -G --style ./parentphase
1323 $ hg log --debug -G --style ./parentphase
1324 @ 8 (secret): 7 (secret) -1 (public)
1324 @ 8 (secret): 7 (secret) -1 (public)
1325 |
1325 |
1326 o 7 (secret): -1 (public) -1 (public)
1326 o 7 (secret): -1 (public) -1 (public)
1327
1327
1328 o 6 (draft): 5 (public) 4 (draft)
1328 o 6 (draft): 5 (public) 4 (draft)
1329 |\
1329 |\
1330 | o 5 (public): 3 (public) -1 (public)
1330 | o 5 (public): 3 (public) -1 (public)
1331 | |
1331 | |
1332 o | 4 (draft): 3 (public) -1 (public)
1332 o | 4 (draft): 3 (public) -1 (public)
1333 |/
1333 |/
1334 o 3 (public): 2 (public) -1 (public)
1334 o 3 (public): 2 (public) -1 (public)
1335 |
1335 |
1336 o 2 (public): 1 (public) -1 (public)
1336 o 2 (public): 1 (public) -1 (public)
1337 |
1337 |
1338 o 1 (public): 0 (public) -1 (public)
1338 o 1 (public): 0 (public) -1 (public)
1339 |
1339 |
1340 o 0 (public): -1 (public) -1 (public)
1340 o 0 (public): -1 (public) -1 (public)
1341
1341
1342
1342
1343 Missing non-standard names give no error (backward compatibility):
1343 Missing non-standard names give no error (backward compatibility):
1344
1344
1345 $ echo "changeset = '{c}'" > t
1345 $ echo "changeset = '{c}'" > t
1346 $ hg log --style ./t
1346 $ hg log --style ./t
1347
1347
1348 Defining non-standard name works:
1348 Defining non-standard name works:
1349
1349
1350 $ cat <<EOF > t
1350 $ cat <<EOF > t
1351 > changeset = '{c}'
1351 > changeset = '{c}'
1352 > c = q
1352 > c = q
1353 > EOF
1353 > EOF
1354 $ hg log --style ./t
1354 $ hg log --style ./t
1355 8
1355 8
1356 7
1356 7
1357 6
1357 6
1358 5
1358 5
1359 4
1359 4
1360 3
1360 3
1361 2
1361 2
1362 1
1362 1
1363 0
1363 0
1364
1364
1365 ui.style works:
1365 ui.style works:
1366
1366
1367 $ echo '[ui]' > .hg/hgrc
1367 $ echo '[ui]' > .hg/hgrc
1368 $ echo 'style = t' >> .hg/hgrc
1368 $ echo 'style = t' >> .hg/hgrc
1369 $ hg log
1369 $ hg log
1370 8
1370 8
1371 7
1371 7
1372 6
1372 6
1373 5
1373 5
1374 4
1374 4
1375 3
1375 3
1376 2
1376 2
1377 1
1377 1
1378 0
1378 0
1379
1379
1380
1380
1381 Issue338:
1381 Issue338:
1382
1382
1383 $ hg log --style=changelog > changelog
1383 $ hg log --style=changelog > changelog
1384
1384
1385 $ cat changelog
1385 $ cat changelog
1386 2020-01-01 test <test>
1386 2020-01-01 test <test>
1387
1387
1388 * fourth, second, third:
1388 * fourth, second, third:
1389 third
1389 third
1390 [95c24699272e] [tip]
1390 [95c24699272e] [tip]
1391
1391
1392 1970-01-12 User Name <user@hostname>
1392 1970-01-12 User Name <user@hostname>
1393
1393
1394 * second:
1394 * second:
1395 second
1395 second
1396 [29114dbae42b]
1396 [29114dbae42b]
1397
1397
1398 1970-01-18 person <person>
1398 1970-01-18 person <person>
1399
1399
1400 * merge
1400 * merge
1401 [d41e714fe50d]
1401 [d41e714fe50d]
1402
1402
1403 * d:
1403 * d:
1404 new head
1404 new head
1405 [13207e5a10d9]
1405 [13207e5a10d9]
1406
1406
1407 1970-01-17 person <person>
1407 1970-01-17 person <person>
1408
1408
1409 * new branch
1409 * new branch
1410 [bbe44766e73d] <foo>
1410 [bbe44766e73d] <foo>
1411
1411
1412 1970-01-16 person <person>
1412 1970-01-16 person <person>
1413
1413
1414 * c:
1414 * c:
1415 no user, no domain
1415 no user, no domain
1416 [10e46f2dcbf4]
1416 [10e46f2dcbf4]
1417
1417
1418 1970-01-14 other <other@place>
1418 1970-01-14 other <other@place>
1419
1419
1420 * c:
1420 * c:
1421 no person
1421 no person
1422 [97054abb4ab8]
1422 [97054abb4ab8]
1423
1423
1424 1970-01-13 A. N. Other <other@place>
1424 1970-01-13 A. N. Other <other@place>
1425
1425
1426 * b:
1426 * b:
1427 other 1 other 2
1427 other 1 other 2
1428
1428
1429 other 3
1429 other 3
1430 [b608e9d1a3f0]
1430 [b608e9d1a3f0]
1431
1431
1432 1970-01-12 User Name <user@hostname>
1432 1970-01-12 User Name <user@hostname>
1433
1433
1434 * a:
1434 * a:
1435 line 1 line 2
1435 line 1 line 2
1436 [1e4e1b8f71e0]
1436 [1e4e1b8f71e0]
1437
1437
1438
1438
1439 Issue2130: xml output for 'hg heads' is malformed
1439 Issue2130: xml output for 'hg heads' is malformed
1440
1440
1441 $ hg heads --style changelog
1441 $ hg heads --style changelog
1442 2020-01-01 test <test>
1442 2020-01-01 test <test>
1443
1443
1444 * fourth, second, third:
1444 * fourth, second, third:
1445 third
1445 third
1446 [95c24699272e] [tip]
1446 [95c24699272e] [tip]
1447
1447
1448 1970-01-18 person <person>
1448 1970-01-18 person <person>
1449
1449
1450 * merge
1450 * merge
1451 [d41e714fe50d]
1451 [d41e714fe50d]
1452
1452
1453 1970-01-17 person <person>
1453 1970-01-17 person <person>
1454
1454
1455 * new branch
1455 * new branch
1456 [bbe44766e73d] <foo>
1456 [bbe44766e73d] <foo>
1457
1457
1458
1458
1459 Keys work:
1459 Keys work:
1460
1460
1461 $ for key in author branch branches date desc file_adds file_dels file_mods \
1461 $ for key in author branch branches date desc file_adds file_dels file_mods \
1462 > file_copies file_copies_switch files \
1462 > file_copies file_copies_switch files \
1463 > manifest node parents rev tags diffstat extras \
1463 > manifest node parents rev tags diffstat extras \
1464 > p1rev p2rev p1node p2node; do
1464 > p1rev p2rev p1node p2node; do
1465 > for mode in '' --verbose --debug; do
1465 > for mode in '' --verbose --debug; do
1466 > hg log $mode --template "$key$mode: {$key}\n"
1466 > hg log $mode --template "$key$mode: {$key}\n"
1467 > done
1467 > done
1468 > done
1468 > done
1469 author: test
1469 author: test
1470 author: User Name <user@hostname>
1470 author: User Name <user@hostname>
1471 author: person
1471 author: person
1472 author: person
1472 author: person
1473 author: person
1473 author: person
1474 author: person
1474 author: person
1475 author: other@place
1475 author: other@place
1476 author: A. N. Other <other@place>
1476 author: A. N. Other <other@place>
1477 author: User Name <user@hostname>
1477 author: User Name <user@hostname>
1478 author--verbose: test
1478 author--verbose: test
1479 author--verbose: User Name <user@hostname>
1479 author--verbose: User Name <user@hostname>
1480 author--verbose: person
1480 author--verbose: person
1481 author--verbose: person
1481 author--verbose: person
1482 author--verbose: person
1482 author--verbose: person
1483 author--verbose: person
1483 author--verbose: person
1484 author--verbose: other@place
1484 author--verbose: other@place
1485 author--verbose: A. N. Other <other@place>
1485 author--verbose: A. N. Other <other@place>
1486 author--verbose: User Name <user@hostname>
1486 author--verbose: User Name <user@hostname>
1487 author--debug: test
1487 author--debug: test
1488 author--debug: User Name <user@hostname>
1488 author--debug: User Name <user@hostname>
1489 author--debug: person
1489 author--debug: person
1490 author--debug: person
1490 author--debug: person
1491 author--debug: person
1491 author--debug: person
1492 author--debug: person
1492 author--debug: person
1493 author--debug: other@place
1493 author--debug: other@place
1494 author--debug: A. N. Other <other@place>
1494 author--debug: A. N. Other <other@place>
1495 author--debug: User Name <user@hostname>
1495 author--debug: User Name <user@hostname>
1496 branch: default
1496 branch: default
1497 branch: default
1497 branch: default
1498 branch: default
1498 branch: default
1499 branch: default
1499 branch: default
1500 branch: foo
1500 branch: foo
1501 branch: default
1501 branch: default
1502 branch: default
1502 branch: default
1503 branch: default
1503 branch: default
1504 branch: default
1504 branch: default
1505 branch--verbose: default
1505 branch--verbose: default
1506 branch--verbose: default
1506 branch--verbose: default
1507 branch--verbose: default
1507 branch--verbose: default
1508 branch--verbose: default
1508 branch--verbose: default
1509 branch--verbose: foo
1509 branch--verbose: foo
1510 branch--verbose: default
1510 branch--verbose: default
1511 branch--verbose: default
1511 branch--verbose: default
1512 branch--verbose: default
1512 branch--verbose: default
1513 branch--verbose: default
1513 branch--verbose: default
1514 branch--debug: default
1514 branch--debug: default
1515 branch--debug: default
1515 branch--debug: default
1516 branch--debug: default
1516 branch--debug: default
1517 branch--debug: default
1517 branch--debug: default
1518 branch--debug: foo
1518 branch--debug: foo
1519 branch--debug: default
1519 branch--debug: default
1520 branch--debug: default
1520 branch--debug: default
1521 branch--debug: default
1521 branch--debug: default
1522 branch--debug: default
1522 branch--debug: default
1523 branches:
1523 branches:
1524 branches:
1524 branches:
1525 branches:
1525 branches:
1526 branches:
1526 branches:
1527 branches: foo
1527 branches: foo
1528 branches:
1528 branches:
1529 branches:
1529 branches:
1530 branches:
1530 branches:
1531 branches:
1531 branches:
1532 branches--verbose:
1532 branches--verbose:
1533 branches--verbose:
1533 branches--verbose:
1534 branches--verbose:
1534 branches--verbose:
1535 branches--verbose:
1535 branches--verbose:
1536 branches--verbose: foo
1536 branches--verbose: foo
1537 branches--verbose:
1537 branches--verbose:
1538 branches--verbose:
1538 branches--verbose:
1539 branches--verbose:
1539 branches--verbose:
1540 branches--verbose:
1540 branches--verbose:
1541 branches--debug:
1541 branches--debug:
1542 branches--debug:
1542 branches--debug:
1543 branches--debug:
1543 branches--debug:
1544 branches--debug:
1544 branches--debug:
1545 branches--debug: foo
1545 branches--debug: foo
1546 branches--debug:
1546 branches--debug:
1547 branches--debug:
1547 branches--debug:
1548 branches--debug:
1548 branches--debug:
1549 branches--debug:
1549 branches--debug:
1550 date: 1577872860.00
1550 date: 1577872860.00
1551 date: 1000000.00
1551 date: 1000000.00
1552 date: 1500001.00
1552 date: 1500001.00
1553 date: 1500000.00
1553 date: 1500000.00
1554 date: 1400000.00
1554 date: 1400000.00
1555 date: 1300000.00
1555 date: 1300000.00
1556 date: 1200000.00
1556 date: 1200000.00
1557 date: 1100000.00
1557 date: 1100000.00
1558 date: 1000000.00
1558 date: 1000000.00
1559 date--verbose: 1577872860.00
1559 date--verbose: 1577872860.00
1560 date--verbose: 1000000.00
1560 date--verbose: 1000000.00
1561 date--verbose: 1500001.00
1561 date--verbose: 1500001.00
1562 date--verbose: 1500000.00
1562 date--verbose: 1500000.00
1563 date--verbose: 1400000.00
1563 date--verbose: 1400000.00
1564 date--verbose: 1300000.00
1564 date--verbose: 1300000.00
1565 date--verbose: 1200000.00
1565 date--verbose: 1200000.00
1566 date--verbose: 1100000.00
1566 date--verbose: 1100000.00
1567 date--verbose: 1000000.00
1567 date--verbose: 1000000.00
1568 date--debug: 1577872860.00
1568 date--debug: 1577872860.00
1569 date--debug: 1000000.00
1569 date--debug: 1000000.00
1570 date--debug: 1500001.00
1570 date--debug: 1500001.00
1571 date--debug: 1500000.00
1571 date--debug: 1500000.00
1572 date--debug: 1400000.00
1572 date--debug: 1400000.00
1573 date--debug: 1300000.00
1573 date--debug: 1300000.00
1574 date--debug: 1200000.00
1574 date--debug: 1200000.00
1575 date--debug: 1100000.00
1575 date--debug: 1100000.00
1576 date--debug: 1000000.00
1576 date--debug: 1000000.00
1577 desc: third
1577 desc: third
1578 desc: second
1578 desc: second
1579 desc: merge
1579 desc: merge
1580 desc: new head
1580 desc: new head
1581 desc: new branch
1581 desc: new branch
1582 desc: no user, no domain
1582 desc: no user, no domain
1583 desc: no person
1583 desc: no person
1584 desc: other 1
1584 desc: other 1
1585 other 2
1585 other 2
1586
1586
1587 other 3
1587 other 3
1588 desc: line 1
1588 desc: line 1
1589 line 2
1589 line 2
1590 desc--verbose: third
1590 desc--verbose: third
1591 desc--verbose: second
1591 desc--verbose: second
1592 desc--verbose: merge
1592 desc--verbose: merge
1593 desc--verbose: new head
1593 desc--verbose: new head
1594 desc--verbose: new branch
1594 desc--verbose: new branch
1595 desc--verbose: no user, no domain
1595 desc--verbose: no user, no domain
1596 desc--verbose: no person
1596 desc--verbose: no person
1597 desc--verbose: other 1
1597 desc--verbose: other 1
1598 other 2
1598 other 2
1599
1599
1600 other 3
1600 other 3
1601 desc--verbose: line 1
1601 desc--verbose: line 1
1602 line 2
1602 line 2
1603 desc--debug: third
1603 desc--debug: third
1604 desc--debug: second
1604 desc--debug: second
1605 desc--debug: merge
1605 desc--debug: merge
1606 desc--debug: new head
1606 desc--debug: new head
1607 desc--debug: new branch
1607 desc--debug: new branch
1608 desc--debug: no user, no domain
1608 desc--debug: no user, no domain
1609 desc--debug: no person
1609 desc--debug: no person
1610 desc--debug: other 1
1610 desc--debug: other 1
1611 other 2
1611 other 2
1612
1612
1613 other 3
1613 other 3
1614 desc--debug: line 1
1614 desc--debug: line 1
1615 line 2
1615 line 2
1616 file_adds: fourth third
1616 file_adds: fourth third
1617 file_adds: second
1617 file_adds: second
1618 file_adds:
1618 file_adds:
1619 file_adds: d
1619 file_adds: d
1620 file_adds:
1620 file_adds:
1621 file_adds:
1621 file_adds:
1622 file_adds: c
1622 file_adds: c
1623 file_adds: b
1623 file_adds: b
1624 file_adds: a
1624 file_adds: a
1625 file_adds--verbose: fourth third
1625 file_adds--verbose: fourth third
1626 file_adds--verbose: second
1626 file_adds--verbose: second
1627 file_adds--verbose:
1627 file_adds--verbose:
1628 file_adds--verbose: d
1628 file_adds--verbose: d
1629 file_adds--verbose:
1629 file_adds--verbose:
1630 file_adds--verbose:
1630 file_adds--verbose:
1631 file_adds--verbose: c
1631 file_adds--verbose: c
1632 file_adds--verbose: b
1632 file_adds--verbose: b
1633 file_adds--verbose: a
1633 file_adds--verbose: a
1634 file_adds--debug: fourth third
1634 file_adds--debug: fourth third
1635 file_adds--debug: second
1635 file_adds--debug: second
1636 file_adds--debug:
1636 file_adds--debug:
1637 file_adds--debug: d
1637 file_adds--debug: d
1638 file_adds--debug:
1638 file_adds--debug:
1639 file_adds--debug:
1639 file_adds--debug:
1640 file_adds--debug: c
1640 file_adds--debug: c
1641 file_adds--debug: b
1641 file_adds--debug: b
1642 file_adds--debug: a
1642 file_adds--debug: a
1643 file_dels: second
1643 file_dels: second
1644 file_dels:
1644 file_dels:
1645 file_dels:
1645 file_dels:
1646 file_dels:
1646 file_dels:
1647 file_dels:
1647 file_dels:
1648 file_dels:
1648 file_dels:
1649 file_dels:
1649 file_dels:
1650 file_dels:
1650 file_dels:
1651 file_dels:
1651 file_dels:
1652 file_dels--verbose: second
1652 file_dels--verbose: second
1653 file_dels--verbose:
1653 file_dels--verbose:
1654 file_dels--verbose:
1654 file_dels--verbose:
1655 file_dels--verbose:
1655 file_dels--verbose:
1656 file_dels--verbose:
1656 file_dels--verbose:
1657 file_dels--verbose:
1657 file_dels--verbose:
1658 file_dels--verbose:
1658 file_dels--verbose:
1659 file_dels--verbose:
1659 file_dels--verbose:
1660 file_dels--verbose:
1660 file_dels--verbose:
1661 file_dels--debug: second
1661 file_dels--debug: second
1662 file_dels--debug:
1662 file_dels--debug:
1663 file_dels--debug:
1663 file_dels--debug:
1664 file_dels--debug:
1664 file_dels--debug:
1665 file_dels--debug:
1665 file_dels--debug:
1666 file_dels--debug:
1666 file_dels--debug:
1667 file_dels--debug:
1667 file_dels--debug:
1668 file_dels--debug:
1668 file_dels--debug:
1669 file_dels--debug:
1669 file_dels--debug:
1670 file_mods:
1670 file_mods:
1671 file_mods:
1671 file_mods:
1672 file_mods:
1672 file_mods:
1673 file_mods:
1673 file_mods:
1674 file_mods:
1674 file_mods:
1675 file_mods: c
1675 file_mods: c
1676 file_mods:
1676 file_mods:
1677 file_mods:
1677 file_mods:
1678 file_mods:
1678 file_mods:
1679 file_mods--verbose:
1679 file_mods--verbose:
1680 file_mods--verbose:
1680 file_mods--verbose:
1681 file_mods--verbose:
1681 file_mods--verbose:
1682 file_mods--verbose:
1682 file_mods--verbose:
1683 file_mods--verbose:
1683 file_mods--verbose:
1684 file_mods--verbose: c
1684 file_mods--verbose: c
1685 file_mods--verbose:
1685 file_mods--verbose:
1686 file_mods--verbose:
1686 file_mods--verbose:
1687 file_mods--verbose:
1687 file_mods--verbose:
1688 file_mods--debug:
1688 file_mods--debug:
1689 file_mods--debug:
1689 file_mods--debug:
1690 file_mods--debug:
1690 file_mods--debug:
1691 file_mods--debug:
1691 file_mods--debug:
1692 file_mods--debug:
1692 file_mods--debug:
1693 file_mods--debug: c
1693 file_mods--debug: c
1694 file_mods--debug:
1694 file_mods--debug:
1695 file_mods--debug:
1695 file_mods--debug:
1696 file_mods--debug:
1696 file_mods--debug:
1697 file_copies: fourth (second)
1697 file_copies: fourth (second)
1698 file_copies:
1698 file_copies:
1699 file_copies:
1699 file_copies:
1700 file_copies:
1700 file_copies:
1701 file_copies:
1701 file_copies:
1702 file_copies:
1702 file_copies:
1703 file_copies:
1703 file_copies:
1704 file_copies:
1704 file_copies:
1705 file_copies:
1705 file_copies:
1706 file_copies--verbose: fourth (second)
1706 file_copies--verbose: fourth (second)
1707 file_copies--verbose:
1707 file_copies--verbose:
1708 file_copies--verbose:
1708 file_copies--verbose:
1709 file_copies--verbose:
1709 file_copies--verbose:
1710 file_copies--verbose:
1710 file_copies--verbose:
1711 file_copies--verbose:
1711 file_copies--verbose:
1712 file_copies--verbose:
1712 file_copies--verbose:
1713 file_copies--verbose:
1713 file_copies--verbose:
1714 file_copies--verbose:
1714 file_copies--verbose:
1715 file_copies--debug: fourth (second)
1715 file_copies--debug: fourth (second)
1716 file_copies--debug:
1716 file_copies--debug:
1717 file_copies--debug:
1717 file_copies--debug:
1718 file_copies--debug:
1718 file_copies--debug:
1719 file_copies--debug:
1719 file_copies--debug:
1720 file_copies--debug:
1720 file_copies--debug:
1721 file_copies--debug:
1721 file_copies--debug:
1722 file_copies--debug:
1722 file_copies--debug:
1723 file_copies--debug:
1723 file_copies--debug:
1724 file_copies_switch:
1724 file_copies_switch:
1725 file_copies_switch:
1725 file_copies_switch:
1726 file_copies_switch:
1726 file_copies_switch:
1727 file_copies_switch:
1727 file_copies_switch:
1728 file_copies_switch:
1728 file_copies_switch:
1729 file_copies_switch:
1729 file_copies_switch:
1730 file_copies_switch:
1730 file_copies_switch:
1731 file_copies_switch:
1731 file_copies_switch:
1732 file_copies_switch:
1732 file_copies_switch:
1733 file_copies_switch--verbose:
1733 file_copies_switch--verbose:
1734 file_copies_switch--verbose:
1734 file_copies_switch--verbose:
1735 file_copies_switch--verbose:
1735 file_copies_switch--verbose:
1736 file_copies_switch--verbose:
1736 file_copies_switch--verbose:
1737 file_copies_switch--verbose:
1737 file_copies_switch--verbose:
1738 file_copies_switch--verbose:
1738 file_copies_switch--verbose:
1739 file_copies_switch--verbose:
1739 file_copies_switch--verbose:
1740 file_copies_switch--verbose:
1740 file_copies_switch--verbose:
1741 file_copies_switch--verbose:
1741 file_copies_switch--verbose:
1742 file_copies_switch--debug:
1742 file_copies_switch--debug:
1743 file_copies_switch--debug:
1743 file_copies_switch--debug:
1744 file_copies_switch--debug:
1744 file_copies_switch--debug:
1745 file_copies_switch--debug:
1745 file_copies_switch--debug:
1746 file_copies_switch--debug:
1746 file_copies_switch--debug:
1747 file_copies_switch--debug:
1747 file_copies_switch--debug:
1748 file_copies_switch--debug:
1748 file_copies_switch--debug:
1749 file_copies_switch--debug:
1749 file_copies_switch--debug:
1750 file_copies_switch--debug:
1750 file_copies_switch--debug:
1751 files: fourth second third
1751 files: fourth second third
1752 files: second
1752 files: second
1753 files:
1753 files:
1754 files: d
1754 files: d
1755 files:
1755 files:
1756 files: c
1756 files: c
1757 files: c
1757 files: c
1758 files: b
1758 files: b
1759 files: a
1759 files: a
1760 files--verbose: fourth second third
1760 files--verbose: fourth second third
1761 files--verbose: second
1761 files--verbose: second
1762 files--verbose:
1762 files--verbose:
1763 files--verbose: d
1763 files--verbose: d
1764 files--verbose:
1764 files--verbose:
1765 files--verbose: c
1765 files--verbose: c
1766 files--verbose: c
1766 files--verbose: c
1767 files--verbose: b
1767 files--verbose: b
1768 files--verbose: a
1768 files--verbose: a
1769 files--debug: fourth second third
1769 files--debug: fourth second third
1770 files--debug: second
1770 files--debug: second
1771 files--debug:
1771 files--debug:
1772 files--debug: d
1772 files--debug: d
1773 files--debug:
1773 files--debug:
1774 files--debug: c
1774 files--debug: c
1775 files--debug: c
1775 files--debug: c
1776 files--debug: b
1776 files--debug: b
1777 files--debug: a
1777 files--debug: a
1778 manifest: 6:94961b75a2da
1778 manifest: 6:94961b75a2da
1779 manifest: 5:f2dbc354b94e
1779 manifest: 5:f2dbc354b94e
1780 manifest: 4:4dc3def4f9b4
1780 manifest: 4:4dc3def4f9b4
1781 manifest: 4:4dc3def4f9b4
1781 manifest: 4:4dc3def4f9b4
1782 manifest: 3:cb5a1327723b
1782 manifest: 3:cb5a1327723b
1783 manifest: 3:cb5a1327723b
1783 manifest: 3:cb5a1327723b
1784 manifest: 2:6e0e82995c35
1784 manifest: 2:6e0e82995c35
1785 manifest: 1:4e8d705b1e53
1785 manifest: 1:4e8d705b1e53
1786 manifest: 0:a0c8bcbbb45c
1786 manifest: 0:a0c8bcbbb45c
1787 manifest--verbose: 6:94961b75a2da
1787 manifest--verbose: 6:94961b75a2da
1788 manifest--verbose: 5:f2dbc354b94e
1788 manifest--verbose: 5:f2dbc354b94e
1789 manifest--verbose: 4:4dc3def4f9b4
1789 manifest--verbose: 4:4dc3def4f9b4
1790 manifest--verbose: 4:4dc3def4f9b4
1790 manifest--verbose: 4:4dc3def4f9b4
1791 manifest--verbose: 3:cb5a1327723b
1791 manifest--verbose: 3:cb5a1327723b
1792 manifest--verbose: 3:cb5a1327723b
1792 manifest--verbose: 3:cb5a1327723b
1793 manifest--verbose: 2:6e0e82995c35
1793 manifest--verbose: 2:6e0e82995c35
1794 manifest--verbose: 1:4e8d705b1e53
1794 manifest--verbose: 1:4e8d705b1e53
1795 manifest--verbose: 0:a0c8bcbbb45c
1795 manifest--verbose: 0:a0c8bcbbb45c
1796 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1796 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1797 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1797 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1798 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1798 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1799 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1799 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1800 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1800 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1801 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1801 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1802 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1802 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1803 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1803 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1804 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1804 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1805 node: 95c24699272ef57d062b8bccc32c878bf841784a
1805 node: 95c24699272ef57d062b8bccc32c878bf841784a
1806 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1806 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1807 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1807 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1808 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1808 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1809 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1809 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1810 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1810 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1811 node: 97054abb4ab824450e9164180baf491ae0078465
1811 node: 97054abb4ab824450e9164180baf491ae0078465
1812 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1812 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1813 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1813 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1814 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1814 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1815 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1815 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1816 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1816 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1817 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1817 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1818 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1818 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1819 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1819 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1820 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1820 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1821 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1821 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1822 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1822 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1823 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1823 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1824 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1824 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1825 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1825 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1826 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1826 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1827 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1827 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1828 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1828 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1829 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1829 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1830 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1830 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1831 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1831 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1832 parents:
1832 parents:
1833 parents: -1:000000000000
1833 parents: -1:000000000000
1834 parents: 5:13207e5a10d9 4:bbe44766e73d
1834 parents: 5:13207e5a10d9 4:bbe44766e73d
1835 parents: 3:10e46f2dcbf4
1835 parents: 3:10e46f2dcbf4
1836 parents:
1836 parents:
1837 parents:
1837 parents:
1838 parents:
1838 parents:
1839 parents:
1839 parents:
1840 parents:
1840 parents:
1841 parents--verbose:
1841 parents--verbose:
1842 parents--verbose: -1:000000000000
1842 parents--verbose: -1:000000000000
1843 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1843 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1844 parents--verbose: 3:10e46f2dcbf4
1844 parents--verbose: 3:10e46f2dcbf4
1845 parents--verbose:
1845 parents--verbose:
1846 parents--verbose:
1846 parents--verbose:
1847 parents--verbose:
1847 parents--verbose:
1848 parents--verbose:
1848 parents--verbose:
1849 parents--verbose:
1849 parents--verbose:
1850 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1850 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1851 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1851 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1852 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1852 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1853 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1853 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1854 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1854 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1855 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1855 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1856 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1856 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1857 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1857 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1858 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1858 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1859 rev: 8
1859 rev: 8
1860 rev: 7
1860 rev: 7
1861 rev: 6
1861 rev: 6
1862 rev: 5
1862 rev: 5
1863 rev: 4
1863 rev: 4
1864 rev: 3
1864 rev: 3
1865 rev: 2
1865 rev: 2
1866 rev: 1
1866 rev: 1
1867 rev: 0
1867 rev: 0
1868 rev--verbose: 8
1868 rev--verbose: 8
1869 rev--verbose: 7
1869 rev--verbose: 7
1870 rev--verbose: 6
1870 rev--verbose: 6
1871 rev--verbose: 5
1871 rev--verbose: 5
1872 rev--verbose: 4
1872 rev--verbose: 4
1873 rev--verbose: 3
1873 rev--verbose: 3
1874 rev--verbose: 2
1874 rev--verbose: 2
1875 rev--verbose: 1
1875 rev--verbose: 1
1876 rev--verbose: 0
1876 rev--verbose: 0
1877 rev--debug: 8
1877 rev--debug: 8
1878 rev--debug: 7
1878 rev--debug: 7
1879 rev--debug: 6
1879 rev--debug: 6
1880 rev--debug: 5
1880 rev--debug: 5
1881 rev--debug: 4
1881 rev--debug: 4
1882 rev--debug: 3
1882 rev--debug: 3
1883 rev--debug: 2
1883 rev--debug: 2
1884 rev--debug: 1
1884 rev--debug: 1
1885 rev--debug: 0
1885 rev--debug: 0
1886 tags: tip
1886 tags: tip
1887 tags:
1887 tags:
1888 tags:
1888 tags:
1889 tags:
1889 tags:
1890 tags:
1890 tags:
1891 tags:
1891 tags:
1892 tags:
1892 tags:
1893 tags:
1893 tags:
1894 tags:
1894 tags:
1895 tags--verbose: tip
1895 tags--verbose: tip
1896 tags--verbose:
1896 tags--verbose:
1897 tags--verbose:
1897 tags--verbose:
1898 tags--verbose:
1898 tags--verbose:
1899 tags--verbose:
1899 tags--verbose:
1900 tags--verbose:
1900 tags--verbose:
1901 tags--verbose:
1901 tags--verbose:
1902 tags--verbose:
1902 tags--verbose:
1903 tags--verbose:
1903 tags--verbose:
1904 tags--debug: tip
1904 tags--debug: tip
1905 tags--debug:
1905 tags--debug:
1906 tags--debug:
1906 tags--debug:
1907 tags--debug:
1907 tags--debug:
1908 tags--debug:
1908 tags--debug:
1909 tags--debug:
1909 tags--debug:
1910 tags--debug:
1910 tags--debug:
1911 tags--debug:
1911 tags--debug:
1912 tags--debug:
1912 tags--debug:
1913 diffstat: 3: +2/-1
1913 diffstat: 3: +2/-1
1914 diffstat: 1: +1/-0
1914 diffstat: 1: +1/-0
1915 diffstat: 0: +0/-0
1915 diffstat: 0: +0/-0
1916 diffstat: 1: +1/-0
1916 diffstat: 1: +1/-0
1917 diffstat: 0: +0/-0
1917 diffstat: 0: +0/-0
1918 diffstat: 1: +1/-0
1918 diffstat: 1: +1/-0
1919 diffstat: 1: +4/-0
1919 diffstat: 1: +4/-0
1920 diffstat: 1: +2/-0
1920 diffstat: 1: +2/-0
1921 diffstat: 1: +1/-0
1921 diffstat: 1: +1/-0
1922 diffstat--verbose: 3: +2/-1
1922 diffstat--verbose: 3: +2/-1
1923 diffstat--verbose: 1: +1/-0
1923 diffstat--verbose: 1: +1/-0
1924 diffstat--verbose: 0: +0/-0
1924 diffstat--verbose: 0: +0/-0
1925 diffstat--verbose: 1: +1/-0
1925 diffstat--verbose: 1: +1/-0
1926 diffstat--verbose: 0: +0/-0
1926 diffstat--verbose: 0: +0/-0
1927 diffstat--verbose: 1: +1/-0
1927 diffstat--verbose: 1: +1/-0
1928 diffstat--verbose: 1: +4/-0
1928 diffstat--verbose: 1: +4/-0
1929 diffstat--verbose: 1: +2/-0
1929 diffstat--verbose: 1: +2/-0
1930 diffstat--verbose: 1: +1/-0
1930 diffstat--verbose: 1: +1/-0
1931 diffstat--debug: 3: +2/-1
1931 diffstat--debug: 3: +2/-1
1932 diffstat--debug: 1: +1/-0
1932 diffstat--debug: 1: +1/-0
1933 diffstat--debug: 0: +0/-0
1933 diffstat--debug: 0: +0/-0
1934 diffstat--debug: 1: +1/-0
1934 diffstat--debug: 1: +1/-0
1935 diffstat--debug: 0: +0/-0
1935 diffstat--debug: 0: +0/-0
1936 diffstat--debug: 1: +1/-0
1936 diffstat--debug: 1: +1/-0
1937 diffstat--debug: 1: +4/-0
1937 diffstat--debug: 1: +4/-0
1938 diffstat--debug: 1: +2/-0
1938 diffstat--debug: 1: +2/-0
1939 diffstat--debug: 1: +1/-0
1939 diffstat--debug: 1: +1/-0
1940 extras: branch=default
1940 extras: branch=default
1941 extras: branch=default
1941 extras: branch=default
1942 extras: branch=default
1942 extras: branch=default
1943 extras: branch=default
1943 extras: branch=default
1944 extras: branch=foo
1944 extras: branch=foo
1945 extras: branch=default
1945 extras: branch=default
1946 extras: branch=default
1946 extras: branch=default
1947 extras: branch=default
1947 extras: branch=default
1948 extras: branch=default
1948 extras: branch=default
1949 extras--verbose: branch=default
1949 extras--verbose: branch=default
1950 extras--verbose: branch=default
1950 extras--verbose: branch=default
1951 extras--verbose: branch=default
1951 extras--verbose: branch=default
1952 extras--verbose: branch=default
1952 extras--verbose: branch=default
1953 extras--verbose: branch=foo
1953 extras--verbose: branch=foo
1954 extras--verbose: branch=default
1954 extras--verbose: branch=default
1955 extras--verbose: branch=default
1955 extras--verbose: branch=default
1956 extras--verbose: branch=default
1956 extras--verbose: branch=default
1957 extras--verbose: branch=default
1957 extras--verbose: branch=default
1958 extras--debug: branch=default
1958 extras--debug: branch=default
1959 extras--debug: branch=default
1959 extras--debug: branch=default
1960 extras--debug: branch=default
1960 extras--debug: branch=default
1961 extras--debug: branch=default
1961 extras--debug: branch=default
1962 extras--debug: branch=foo
1962 extras--debug: branch=foo
1963 extras--debug: branch=default
1963 extras--debug: branch=default
1964 extras--debug: branch=default
1964 extras--debug: branch=default
1965 extras--debug: branch=default
1965 extras--debug: branch=default
1966 extras--debug: branch=default
1966 extras--debug: branch=default
1967 p1rev: 7
1967 p1rev: 7
1968 p1rev: -1
1968 p1rev: -1
1969 p1rev: 5
1969 p1rev: 5
1970 p1rev: 3
1970 p1rev: 3
1971 p1rev: 3
1971 p1rev: 3
1972 p1rev: 2
1972 p1rev: 2
1973 p1rev: 1
1973 p1rev: 1
1974 p1rev: 0
1974 p1rev: 0
1975 p1rev: -1
1975 p1rev: -1
1976 p1rev--verbose: 7
1976 p1rev--verbose: 7
1977 p1rev--verbose: -1
1977 p1rev--verbose: -1
1978 p1rev--verbose: 5
1978 p1rev--verbose: 5
1979 p1rev--verbose: 3
1979 p1rev--verbose: 3
1980 p1rev--verbose: 3
1980 p1rev--verbose: 3
1981 p1rev--verbose: 2
1981 p1rev--verbose: 2
1982 p1rev--verbose: 1
1982 p1rev--verbose: 1
1983 p1rev--verbose: 0
1983 p1rev--verbose: 0
1984 p1rev--verbose: -1
1984 p1rev--verbose: -1
1985 p1rev--debug: 7
1985 p1rev--debug: 7
1986 p1rev--debug: -1
1986 p1rev--debug: -1
1987 p1rev--debug: 5
1987 p1rev--debug: 5
1988 p1rev--debug: 3
1988 p1rev--debug: 3
1989 p1rev--debug: 3
1989 p1rev--debug: 3
1990 p1rev--debug: 2
1990 p1rev--debug: 2
1991 p1rev--debug: 1
1991 p1rev--debug: 1
1992 p1rev--debug: 0
1992 p1rev--debug: 0
1993 p1rev--debug: -1
1993 p1rev--debug: -1
1994 p2rev: -1
1994 p2rev: -1
1995 p2rev: -1
1995 p2rev: -1
1996 p2rev: 4
1996 p2rev: 4
1997 p2rev: -1
1997 p2rev: -1
1998 p2rev: -1
1998 p2rev: -1
1999 p2rev: -1
1999 p2rev: -1
2000 p2rev: -1
2000 p2rev: -1
2001 p2rev: -1
2001 p2rev: -1
2002 p2rev: -1
2002 p2rev: -1
2003 p2rev--verbose: -1
2003 p2rev--verbose: -1
2004 p2rev--verbose: -1
2004 p2rev--verbose: -1
2005 p2rev--verbose: 4
2005 p2rev--verbose: 4
2006 p2rev--verbose: -1
2006 p2rev--verbose: -1
2007 p2rev--verbose: -1
2007 p2rev--verbose: -1
2008 p2rev--verbose: -1
2008 p2rev--verbose: -1
2009 p2rev--verbose: -1
2009 p2rev--verbose: -1
2010 p2rev--verbose: -1
2010 p2rev--verbose: -1
2011 p2rev--verbose: -1
2011 p2rev--verbose: -1
2012 p2rev--debug: -1
2012 p2rev--debug: -1
2013 p2rev--debug: -1
2013 p2rev--debug: -1
2014 p2rev--debug: 4
2014 p2rev--debug: 4
2015 p2rev--debug: -1
2015 p2rev--debug: -1
2016 p2rev--debug: -1
2016 p2rev--debug: -1
2017 p2rev--debug: -1
2017 p2rev--debug: -1
2018 p2rev--debug: -1
2018 p2rev--debug: -1
2019 p2rev--debug: -1
2019 p2rev--debug: -1
2020 p2rev--debug: -1
2020 p2rev--debug: -1
2021 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2021 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2022 p1node: 0000000000000000000000000000000000000000
2022 p1node: 0000000000000000000000000000000000000000
2023 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
2023 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
2024 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2024 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2025 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2025 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2026 p1node: 97054abb4ab824450e9164180baf491ae0078465
2026 p1node: 97054abb4ab824450e9164180baf491ae0078465
2027 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2027 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2028 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
2028 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
2029 p1node: 0000000000000000000000000000000000000000
2029 p1node: 0000000000000000000000000000000000000000
2030 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2030 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2031 p1node--verbose: 0000000000000000000000000000000000000000
2031 p1node--verbose: 0000000000000000000000000000000000000000
2032 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
2032 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
2033 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2033 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2034 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2034 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2035 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
2035 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
2036 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2036 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2037 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
2037 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
2038 p1node--verbose: 0000000000000000000000000000000000000000
2038 p1node--verbose: 0000000000000000000000000000000000000000
2039 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2039 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
2040 p1node--debug: 0000000000000000000000000000000000000000
2040 p1node--debug: 0000000000000000000000000000000000000000
2041 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
2041 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
2042 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2042 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2043 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2043 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
2044 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
2044 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
2045 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2045 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2046 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
2046 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
2047 p1node--debug: 0000000000000000000000000000000000000000
2047 p1node--debug: 0000000000000000000000000000000000000000
2048 p2node: 0000000000000000000000000000000000000000
2048 p2node: 0000000000000000000000000000000000000000
2049 p2node: 0000000000000000000000000000000000000000
2049 p2node: 0000000000000000000000000000000000000000
2050 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2050 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2051 p2node: 0000000000000000000000000000000000000000
2051 p2node: 0000000000000000000000000000000000000000
2052 p2node: 0000000000000000000000000000000000000000
2052 p2node: 0000000000000000000000000000000000000000
2053 p2node: 0000000000000000000000000000000000000000
2053 p2node: 0000000000000000000000000000000000000000
2054 p2node: 0000000000000000000000000000000000000000
2054 p2node: 0000000000000000000000000000000000000000
2055 p2node: 0000000000000000000000000000000000000000
2055 p2node: 0000000000000000000000000000000000000000
2056 p2node: 0000000000000000000000000000000000000000
2056 p2node: 0000000000000000000000000000000000000000
2057 p2node--verbose: 0000000000000000000000000000000000000000
2057 p2node--verbose: 0000000000000000000000000000000000000000
2058 p2node--verbose: 0000000000000000000000000000000000000000
2058 p2node--verbose: 0000000000000000000000000000000000000000
2059 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2059 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2060 p2node--verbose: 0000000000000000000000000000000000000000
2060 p2node--verbose: 0000000000000000000000000000000000000000
2061 p2node--verbose: 0000000000000000000000000000000000000000
2061 p2node--verbose: 0000000000000000000000000000000000000000
2062 p2node--verbose: 0000000000000000000000000000000000000000
2062 p2node--verbose: 0000000000000000000000000000000000000000
2063 p2node--verbose: 0000000000000000000000000000000000000000
2063 p2node--verbose: 0000000000000000000000000000000000000000
2064 p2node--verbose: 0000000000000000000000000000000000000000
2064 p2node--verbose: 0000000000000000000000000000000000000000
2065 p2node--verbose: 0000000000000000000000000000000000000000
2065 p2node--verbose: 0000000000000000000000000000000000000000
2066 p2node--debug: 0000000000000000000000000000000000000000
2066 p2node--debug: 0000000000000000000000000000000000000000
2067 p2node--debug: 0000000000000000000000000000000000000000
2067 p2node--debug: 0000000000000000000000000000000000000000
2068 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2068 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
2069 p2node--debug: 0000000000000000000000000000000000000000
2069 p2node--debug: 0000000000000000000000000000000000000000
2070 p2node--debug: 0000000000000000000000000000000000000000
2070 p2node--debug: 0000000000000000000000000000000000000000
2071 p2node--debug: 0000000000000000000000000000000000000000
2071 p2node--debug: 0000000000000000000000000000000000000000
2072 p2node--debug: 0000000000000000000000000000000000000000
2072 p2node--debug: 0000000000000000000000000000000000000000
2073 p2node--debug: 0000000000000000000000000000000000000000
2073 p2node--debug: 0000000000000000000000000000000000000000
2074 p2node--debug: 0000000000000000000000000000000000000000
2074 p2node--debug: 0000000000000000000000000000000000000000
2075
2075
2076 Filters work:
2076 Filters work:
2077
2077
2078 $ hg log --template '{author|domain}\n'
2078 $ hg log --template '{author|domain}\n'
2079
2079
2080 hostname
2080 hostname
2081
2081
2082
2082
2083
2083
2084
2084
2085 place
2085 place
2086 place
2086 place
2087 hostname
2087 hostname
2088
2088
2089 $ hg log --template '{author|person}\n'
2089 $ hg log --template '{author|person}\n'
2090 test
2090 test
2091 User Name
2091 User Name
2092 person
2092 person
2093 person
2093 person
2094 person
2094 person
2095 person
2095 person
2096 other
2096 other
2097 A. N. Other
2097 A. N. Other
2098 User Name
2098 User Name
2099
2099
2100 $ hg log --template '{author|user}\n'
2100 $ hg log --template '{author|user}\n'
2101 test
2101 test
2102 user
2102 user
2103 person
2103 person
2104 person
2104 person
2105 person
2105 person
2106 person
2106 person
2107 other
2107 other
2108 other
2108 other
2109 user
2109 user
2110
2110
2111 $ hg log --template '{date|date}\n'
2111 $ hg log --template '{date|date}\n'
2112 Wed Jan 01 10:01:00 2020 +0000
2112 Wed Jan 01 10:01:00 2020 +0000
2113 Mon Jan 12 13:46:40 1970 +0000
2113 Mon Jan 12 13:46:40 1970 +0000
2114 Sun Jan 18 08:40:01 1970 +0000
2114 Sun Jan 18 08:40:01 1970 +0000
2115 Sun Jan 18 08:40:00 1970 +0000
2115 Sun Jan 18 08:40:00 1970 +0000
2116 Sat Jan 17 04:53:20 1970 +0000
2116 Sat Jan 17 04:53:20 1970 +0000
2117 Fri Jan 16 01:06:40 1970 +0000
2117 Fri Jan 16 01:06:40 1970 +0000
2118 Wed Jan 14 21:20:00 1970 +0000
2118 Wed Jan 14 21:20:00 1970 +0000
2119 Tue Jan 13 17:33:20 1970 +0000
2119 Tue Jan 13 17:33:20 1970 +0000
2120 Mon Jan 12 13:46:40 1970 +0000
2120 Mon Jan 12 13:46:40 1970 +0000
2121
2121
2122 $ hg log --template '{date|isodate}\n'
2122 $ hg log --template '{date|isodate}\n'
2123 2020-01-01 10:01 +0000
2123 2020-01-01 10:01 +0000
2124 1970-01-12 13:46 +0000
2124 1970-01-12 13:46 +0000
2125 1970-01-18 08:40 +0000
2125 1970-01-18 08:40 +0000
2126 1970-01-18 08:40 +0000
2126 1970-01-18 08:40 +0000
2127 1970-01-17 04:53 +0000
2127 1970-01-17 04:53 +0000
2128 1970-01-16 01:06 +0000
2128 1970-01-16 01:06 +0000
2129 1970-01-14 21:20 +0000
2129 1970-01-14 21:20 +0000
2130 1970-01-13 17:33 +0000
2130 1970-01-13 17:33 +0000
2131 1970-01-12 13:46 +0000
2131 1970-01-12 13:46 +0000
2132
2132
2133 $ hg log --template '{date|isodatesec}\n'
2133 $ hg log --template '{date|isodatesec}\n'
2134 2020-01-01 10:01:00 +0000
2134 2020-01-01 10:01:00 +0000
2135 1970-01-12 13:46:40 +0000
2135 1970-01-12 13:46:40 +0000
2136 1970-01-18 08:40:01 +0000
2136 1970-01-18 08:40:01 +0000
2137 1970-01-18 08:40:00 +0000
2137 1970-01-18 08:40:00 +0000
2138 1970-01-17 04:53:20 +0000
2138 1970-01-17 04:53:20 +0000
2139 1970-01-16 01:06:40 +0000
2139 1970-01-16 01:06:40 +0000
2140 1970-01-14 21:20:00 +0000
2140 1970-01-14 21:20:00 +0000
2141 1970-01-13 17:33:20 +0000
2141 1970-01-13 17:33:20 +0000
2142 1970-01-12 13:46:40 +0000
2142 1970-01-12 13:46:40 +0000
2143
2143
2144 $ hg log --template '{date|rfc822date}\n'
2144 $ hg log --template '{date|rfc822date}\n'
2145 Wed, 01 Jan 2020 10:01:00 +0000
2145 Wed, 01 Jan 2020 10:01:00 +0000
2146 Mon, 12 Jan 1970 13:46:40 +0000
2146 Mon, 12 Jan 1970 13:46:40 +0000
2147 Sun, 18 Jan 1970 08:40:01 +0000
2147 Sun, 18 Jan 1970 08:40:01 +0000
2148 Sun, 18 Jan 1970 08:40:00 +0000
2148 Sun, 18 Jan 1970 08:40:00 +0000
2149 Sat, 17 Jan 1970 04:53:20 +0000
2149 Sat, 17 Jan 1970 04:53:20 +0000
2150 Fri, 16 Jan 1970 01:06:40 +0000
2150 Fri, 16 Jan 1970 01:06:40 +0000
2151 Wed, 14 Jan 1970 21:20:00 +0000
2151 Wed, 14 Jan 1970 21:20:00 +0000
2152 Tue, 13 Jan 1970 17:33:20 +0000
2152 Tue, 13 Jan 1970 17:33:20 +0000
2153 Mon, 12 Jan 1970 13:46:40 +0000
2153 Mon, 12 Jan 1970 13:46:40 +0000
2154
2154
2155 $ hg log --template '{desc|firstline}\n'
2155 $ hg log --template '{desc|firstline}\n'
2156 third
2156 third
2157 second
2157 second
2158 merge
2158 merge
2159 new head
2159 new head
2160 new branch
2160 new branch
2161 no user, no domain
2161 no user, no domain
2162 no person
2162 no person
2163 other 1
2163 other 1
2164 line 1
2164 line 1
2165
2165
2166 $ hg log --template '{node|short}\n'
2166 $ hg log --template '{node|short}\n'
2167 95c24699272e
2167 95c24699272e
2168 29114dbae42b
2168 29114dbae42b
2169 d41e714fe50d
2169 d41e714fe50d
2170 13207e5a10d9
2170 13207e5a10d9
2171 bbe44766e73d
2171 bbe44766e73d
2172 10e46f2dcbf4
2172 10e46f2dcbf4
2173 97054abb4ab8
2173 97054abb4ab8
2174 b608e9d1a3f0
2174 b608e9d1a3f0
2175 1e4e1b8f71e0
2175 1e4e1b8f71e0
2176
2176
2177 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
2177 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
2178 <changeset author="test"/>
2178 <changeset author="test"/>
2179 <changeset author="User Name &lt;user@hostname&gt;"/>
2179 <changeset author="User Name &lt;user@hostname&gt;"/>
2180 <changeset author="person"/>
2180 <changeset author="person"/>
2181 <changeset author="person"/>
2181 <changeset author="person"/>
2182 <changeset author="person"/>
2182 <changeset author="person"/>
2183 <changeset author="person"/>
2183 <changeset author="person"/>
2184 <changeset author="other@place"/>
2184 <changeset author="other@place"/>
2185 <changeset author="A. N. Other &lt;other@place&gt;"/>
2185 <changeset author="A. N. Other &lt;other@place&gt;"/>
2186 <changeset author="User Name &lt;user@hostname&gt;"/>
2186 <changeset author="User Name &lt;user@hostname&gt;"/>
2187
2187
2188 $ hg log --template '{rev}: {children}\n'
2188 $ hg log --template '{rev}: {children}\n'
2189 8:
2189 8:
2190 7: 8:95c24699272e
2190 7: 8:95c24699272e
2191 6:
2191 6:
2192 5: 6:d41e714fe50d
2192 5: 6:d41e714fe50d
2193 4: 6:d41e714fe50d
2193 4: 6:d41e714fe50d
2194 3: 4:bbe44766e73d 5:13207e5a10d9
2194 3: 4:bbe44766e73d 5:13207e5a10d9
2195 2: 3:10e46f2dcbf4
2195 2: 3:10e46f2dcbf4
2196 1: 2:97054abb4ab8
2196 1: 2:97054abb4ab8
2197 0: 1:b608e9d1a3f0
2197 0: 1:b608e9d1a3f0
2198
2198
2199 Formatnode filter works:
2199 Formatnode filter works:
2200
2200
2201 $ hg -q log -r 0 --template '{node|formatnode}\n'
2201 $ hg -q log -r 0 --template '{node|formatnode}\n'
2202 1e4e1b8f71e0
2202 1e4e1b8f71e0
2203
2203
2204 $ hg log -r 0 --template '{node|formatnode}\n'
2204 $ hg log -r 0 --template '{node|formatnode}\n'
2205 1e4e1b8f71e0
2205 1e4e1b8f71e0
2206
2206
2207 $ hg -v log -r 0 --template '{node|formatnode}\n'
2207 $ hg -v log -r 0 --template '{node|formatnode}\n'
2208 1e4e1b8f71e0
2208 1e4e1b8f71e0
2209
2209
2210 $ hg --debug log -r 0 --template '{node|formatnode}\n'
2210 $ hg --debug log -r 0 --template '{node|formatnode}\n'
2211 1e4e1b8f71e05681d422154f5421e385fec3454f
2211 1e4e1b8f71e05681d422154f5421e385fec3454f
2212
2212
2213 Age filter:
2213 Age filter:
2214
2214
2215 $ hg init unstable-hash
2215 $ hg init unstable-hash
2216 $ cd unstable-hash
2216 $ cd unstable-hash
2217 $ hg log --template '{date|age}\n' > /dev/null || exit 1
2217 $ hg log --template '{date|age}\n' > /dev/null || exit 1
2218
2218
2219 >>> from __future__ import absolute_import
2219 >>> from __future__ import absolute_import
2220 >>> import datetime
2220 >>> import datetime
2221 >>> fp = open('a', 'wb')
2221 >>> fp = open('a', 'wb')
2222 >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
2222 >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
2223 >>> fp.write(b'%d-%d-%d 00:00' % (n.year, n.month, n.day)) and None
2223 >>> fp.write(b'%d-%d-%d 00:00' % (n.year, n.month, n.day)) and None
2224 >>> fp.close()
2224 >>> fp.close()
2225 $ hg add a
2225 $ hg add a
2226 $ hg commit -m future -d "`cat a`"
2226 $ hg commit -m future -d "`cat a`"
2227
2227
2228 $ hg log -l1 --template '{date|age}\n'
2228 $ hg log -l1 --template '{date|age}\n'
2229 7 years from now
2229 7 years from now
2230
2230
2231 $ cd ..
2231 $ cd ..
2232 $ rm -rf unstable-hash
2232 $ rm -rf unstable-hash
2233
2233
2234 Filename filters:
2234 Filename filters:
2235
2235
2236 $ hg debugtemplate '{"foo/bar"|basename}|{"foo/"|basename}|{"foo"|basename}|\n'
2236 $ hg debugtemplate '{"foo/bar"|basename}|{"foo/"|basename}|{"foo"|basename}|\n'
2237 bar||foo|
2237 bar||foo|
2238 $ hg debugtemplate '{"foo/bar"|dirname}|{"foo/"|dirname}|{"foo"|dirname}|\n'
2238 $ hg debugtemplate '{"foo/bar"|dirname}|{"foo/"|dirname}|{"foo"|dirname}|\n'
2239 foo|foo||
2239 foo|foo||
2240 $ hg debugtemplate '{"foo/bar"|stripdir}|{"foo/"|stripdir}|{"foo"|stripdir}|\n'
2240 $ hg debugtemplate '{"foo/bar"|stripdir}|{"foo/"|stripdir}|{"foo"|stripdir}|\n'
2241 foo|foo|foo|
2241 foo|foo|foo|
2242
2242
2243 Add a dummy commit to make up for the instability of the above:
2243 Add a dummy commit to make up for the instability of the above:
2244
2244
2245 $ echo a > a
2245 $ echo a > a
2246 $ hg add a
2246 $ hg add a
2247 $ hg ci -m future
2247 $ hg ci -m future
2248
2248
2249 Count filter:
2249 Count filter:
2250
2250
2251 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2251 $ hg log -l1 --template '{node|count} {node|short|count}\n'
2252 40 12
2252 40 12
2253
2253
2254 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2254 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
2255 0 1 4
2255 0 1 4
2256
2256
2257 $ hg log -G --template '{rev}: children: {children|count}, \
2257 $ hg log -G --template '{rev}: children: {children|count}, \
2258 > tags: {tags|count}, file_adds: {file_adds|count}, \
2258 > tags: {tags|count}, file_adds: {file_adds|count}, \
2259 > ancestors: {revset("ancestors(%s)", rev)|count}'
2259 > ancestors: {revset("ancestors(%s)", rev)|count}'
2260 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2260 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
2261 |
2261 |
2262 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2262 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
2263 |
2263 |
2264 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2264 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
2265
2265
2266 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2266 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
2267 |\
2267 |\
2268 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2268 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
2269 | |
2269 | |
2270 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2270 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
2271 |/
2271 |/
2272 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2272 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
2273 |
2273 |
2274 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2274 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
2275 |
2275 |
2276 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2276 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
2277 |
2277 |
2278 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2278 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
2279
2279
2280
2280
2281 $ hg log -l1 -T '{termwidth|count}\n'
2281 $ hg log -l1 -T '{termwidth|count}\n'
2282 hg: parse error: not countable
2282 hg: parse error: not countable
2283 (template filter 'count' is not compatible with keyword 'termwidth')
2283 (template filter 'count' is not compatible with keyword 'termwidth')
2284 [255]
2284 [255]
2285
2285
2286 Upper/lower filters:
2286 Upper/lower filters:
2287
2287
2288 $ hg log -r0 --template '{branch|upper}\n'
2288 $ hg log -r0 --template '{branch|upper}\n'
2289 DEFAULT
2289 DEFAULT
2290 $ hg log -r0 --template '{author|lower}\n'
2290 $ hg log -r0 --template '{author|lower}\n'
2291 user name <user@hostname>
2291 user name <user@hostname>
2292 $ hg log -r0 --template '{date|upper}\n'
2292 $ hg log -r0 --template '{date|upper}\n'
2293 1000000.00
2293 1000000.00
2294
2294
2295 Add a commit that does all possible modifications at once
2295 Add a commit that does all possible modifications at once
2296
2296
2297 $ echo modify >> third
2297 $ echo modify >> third
2298 $ touch b
2298 $ touch b
2299 $ hg add b
2299 $ hg add b
2300 $ hg mv fourth fifth
2300 $ hg mv fourth fifth
2301 $ hg rm a
2301 $ hg rm a
2302 $ hg ci -m "Modify, add, remove, rename"
2302 $ hg ci -m "Modify, add, remove, rename"
2303
2303
2304 Check the status template
2304 Check the status template
2305
2305
2306 $ cat <<EOF >> $HGRCPATH
2306 $ cat <<EOF >> $HGRCPATH
2307 > [extensions]
2307 > [extensions]
2308 > color=
2308 > color=
2309 > EOF
2309 > EOF
2310
2310
2311 $ hg log -T status -r 10
2311 $ hg log -T status -r 10
2312 changeset: 10:0f9759ec227a
2312 changeset: 10:0f9759ec227a
2313 tag: tip
2313 tag: tip
2314 user: test
2314 user: test
2315 date: Thu Jan 01 00:00:00 1970 +0000
2315 date: Thu Jan 01 00:00:00 1970 +0000
2316 summary: Modify, add, remove, rename
2316 summary: Modify, add, remove, rename
2317 files:
2317 files:
2318 M third
2318 M third
2319 A b
2319 A b
2320 A fifth
2320 A fifth
2321 R a
2321 R a
2322 R fourth
2322 R fourth
2323
2323
2324 $ hg log -T status -C -r 10
2324 $ hg log -T status -C -r 10
2325 changeset: 10:0f9759ec227a
2325 changeset: 10:0f9759ec227a
2326 tag: tip
2326 tag: tip
2327 user: test
2327 user: test
2328 date: Thu Jan 01 00:00:00 1970 +0000
2328 date: Thu Jan 01 00:00:00 1970 +0000
2329 summary: Modify, add, remove, rename
2329 summary: Modify, add, remove, rename
2330 files:
2330 files:
2331 M third
2331 M third
2332 A b
2332 A b
2333 A fifth
2333 A fifth
2334 fourth
2334 fourth
2335 R a
2335 R a
2336 R fourth
2336 R fourth
2337
2337
2338 $ hg log -T status -C -r 10 -v
2338 $ hg log -T status -C -r 10 -v
2339 changeset: 10:0f9759ec227a
2339 changeset: 10:0f9759ec227a
2340 tag: tip
2340 tag: tip
2341 user: test
2341 user: test
2342 date: Thu Jan 01 00:00:00 1970 +0000
2342 date: Thu Jan 01 00:00:00 1970 +0000
2343 description:
2343 description:
2344 Modify, add, remove, rename
2344 Modify, add, remove, rename
2345
2345
2346 files:
2346 files:
2347 M third
2347 M third
2348 A b
2348 A b
2349 A fifth
2349 A fifth
2350 fourth
2350 fourth
2351 R a
2351 R a
2352 R fourth
2352 R fourth
2353
2353
2354 $ hg log -T status -C -r 10 --debug
2354 $ hg log -T status -C -r 10 --debug
2355 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2355 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2356 tag: tip
2356 tag: tip
2357 phase: secret
2357 phase: secret
2358 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2358 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2359 parent: -1:0000000000000000000000000000000000000000
2359 parent: -1:0000000000000000000000000000000000000000
2360 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2360 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2361 user: test
2361 user: test
2362 date: Thu Jan 01 00:00:00 1970 +0000
2362 date: Thu Jan 01 00:00:00 1970 +0000
2363 extra: branch=default
2363 extra: branch=default
2364 description:
2364 description:
2365 Modify, add, remove, rename
2365 Modify, add, remove, rename
2366
2366
2367 files:
2367 files:
2368 M third
2368 M third
2369 A b
2369 A b
2370 A fifth
2370 A fifth
2371 fourth
2371 fourth
2372 R a
2372 R a
2373 R fourth
2373 R fourth
2374
2374
2375 $ hg log -T status -C -r 10 --quiet
2375 $ hg log -T status -C -r 10 --quiet
2376 10:0f9759ec227a
2376 10:0f9759ec227a
2377 $ hg --color=debug log -T status -r 10
2377 $ hg --color=debug log -T status -r 10
2378 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2378 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2379 [log.tag|tag: tip]
2379 [log.tag|tag: tip]
2380 [log.user|user: test]
2380 [log.user|user: test]
2381 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2381 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2382 [log.summary|summary: Modify, add, remove, rename]
2382 [log.summary|summary: Modify, add, remove, rename]
2383 [ui.note log.files|files:]
2383 [ui.note log.files|files:]
2384 [status.modified|M third]
2384 [status.modified|M third]
2385 [status.added|A b]
2385 [status.added|A b]
2386 [status.added|A fifth]
2386 [status.added|A fifth]
2387 [status.removed|R a]
2387 [status.removed|R a]
2388 [status.removed|R fourth]
2388 [status.removed|R fourth]
2389
2389
2390 $ hg --color=debug log -T status -C -r 10
2390 $ hg --color=debug log -T status -C -r 10
2391 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2391 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2392 [log.tag|tag: tip]
2392 [log.tag|tag: tip]
2393 [log.user|user: test]
2393 [log.user|user: test]
2394 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2394 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2395 [log.summary|summary: Modify, add, remove, rename]
2395 [log.summary|summary: Modify, add, remove, rename]
2396 [ui.note log.files|files:]
2396 [ui.note log.files|files:]
2397 [status.modified|M third]
2397 [status.modified|M third]
2398 [status.added|A b]
2398 [status.added|A b]
2399 [status.added|A fifth]
2399 [status.added|A fifth]
2400 [status.copied| fourth]
2400 [status.copied| fourth]
2401 [status.removed|R a]
2401 [status.removed|R a]
2402 [status.removed|R fourth]
2402 [status.removed|R fourth]
2403
2403
2404 $ hg --color=debug log -T status -C -r 10 -v
2404 $ hg --color=debug log -T status -C -r 10 -v
2405 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2405 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2406 [log.tag|tag: tip]
2406 [log.tag|tag: tip]
2407 [log.user|user: test]
2407 [log.user|user: test]
2408 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2408 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2409 [ui.note log.description|description:]
2409 [ui.note log.description|description:]
2410 [ui.note log.description|Modify, add, remove, rename]
2410 [ui.note log.description|Modify, add, remove, rename]
2411
2411
2412 [ui.note log.files|files:]
2412 [ui.note log.files|files:]
2413 [status.modified|M third]
2413 [status.modified|M third]
2414 [status.added|A b]
2414 [status.added|A b]
2415 [status.added|A fifth]
2415 [status.added|A fifth]
2416 [status.copied| fourth]
2416 [status.copied| fourth]
2417 [status.removed|R a]
2417 [status.removed|R a]
2418 [status.removed|R fourth]
2418 [status.removed|R fourth]
2419
2419
2420 $ hg --color=debug log -T status -C -r 10 --debug
2420 $ hg --color=debug log -T status -C -r 10 --debug
2421 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2421 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2422 [log.tag|tag: tip]
2422 [log.tag|tag: tip]
2423 [log.phase|phase: secret]
2423 [log.phase|phase: secret]
2424 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2424 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2425 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2425 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2426 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2426 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2427 [log.user|user: test]
2427 [log.user|user: test]
2428 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2428 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2429 [ui.debug log.extra|extra: branch=default]
2429 [ui.debug log.extra|extra: branch=default]
2430 [ui.note log.description|description:]
2430 [ui.note log.description|description:]
2431 [ui.note log.description|Modify, add, remove, rename]
2431 [ui.note log.description|Modify, add, remove, rename]
2432
2432
2433 [ui.note log.files|files:]
2433 [ui.note log.files|files:]
2434 [status.modified|M third]
2434 [status.modified|M third]
2435 [status.added|A b]
2435 [status.added|A b]
2436 [status.added|A fifth]
2436 [status.added|A fifth]
2437 [status.copied| fourth]
2437 [status.copied| fourth]
2438 [status.removed|R a]
2438 [status.removed|R a]
2439 [status.removed|R fourth]
2439 [status.removed|R fourth]
2440
2440
2441 $ hg --color=debug log -T status -C -r 10 --quiet
2441 $ hg --color=debug log -T status -C -r 10 --quiet
2442 [log.node|10:0f9759ec227a]
2442 [log.node|10:0f9759ec227a]
2443
2443
2444 Check the bisect template
2444 Check the bisect template
2445
2445
2446 $ hg bisect -g 1
2446 $ hg bisect -g 1
2447 $ hg bisect -b 3 --noupdate
2447 $ hg bisect -b 3 --noupdate
2448 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2448 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2449 $ hg log -T bisect -r 0:4
2449 $ hg log -T bisect -r 0:4
2450 changeset: 0:1e4e1b8f71e0
2450 changeset: 0:1e4e1b8f71e0
2451 bisect: good (implicit)
2451 bisect: good (implicit)
2452 user: User Name <user@hostname>
2452 user: User Name <user@hostname>
2453 date: Mon Jan 12 13:46:40 1970 +0000
2453 date: Mon Jan 12 13:46:40 1970 +0000
2454 summary: line 1
2454 summary: line 1
2455
2455
2456 changeset: 1:b608e9d1a3f0
2456 changeset: 1:b608e9d1a3f0
2457 bisect: good
2457 bisect: good
2458 user: A. N. Other <other@place>
2458 user: A. N. Other <other@place>
2459 date: Tue Jan 13 17:33:20 1970 +0000
2459 date: Tue Jan 13 17:33:20 1970 +0000
2460 summary: other 1
2460 summary: other 1
2461
2461
2462 changeset: 2:97054abb4ab8
2462 changeset: 2:97054abb4ab8
2463 bisect: untested
2463 bisect: untested
2464 user: other@place
2464 user: other@place
2465 date: Wed Jan 14 21:20:00 1970 +0000
2465 date: Wed Jan 14 21:20:00 1970 +0000
2466 summary: no person
2466 summary: no person
2467
2467
2468 changeset: 3:10e46f2dcbf4
2468 changeset: 3:10e46f2dcbf4
2469 bisect: bad
2469 bisect: bad
2470 user: person
2470 user: person
2471 date: Fri Jan 16 01:06:40 1970 +0000
2471 date: Fri Jan 16 01:06:40 1970 +0000
2472 summary: no user, no domain
2472 summary: no user, no domain
2473
2473
2474 changeset: 4:bbe44766e73d
2474 changeset: 4:bbe44766e73d
2475 bisect: bad (implicit)
2475 bisect: bad (implicit)
2476 branch: foo
2476 branch: foo
2477 user: person
2477 user: person
2478 date: Sat Jan 17 04:53:20 1970 +0000
2478 date: Sat Jan 17 04:53:20 1970 +0000
2479 summary: new branch
2479 summary: new branch
2480
2480
2481 $ hg log --debug -T bisect -r 0:4
2481 $ hg log --debug -T bisect -r 0:4
2482 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2482 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2483 bisect: good (implicit)
2483 bisect: good (implicit)
2484 phase: public
2484 phase: public
2485 parent: -1:0000000000000000000000000000000000000000
2485 parent: -1:0000000000000000000000000000000000000000
2486 parent: -1:0000000000000000000000000000000000000000
2486 parent: -1:0000000000000000000000000000000000000000
2487 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2487 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2488 user: User Name <user@hostname>
2488 user: User Name <user@hostname>
2489 date: Mon Jan 12 13:46:40 1970 +0000
2489 date: Mon Jan 12 13:46:40 1970 +0000
2490 files+: a
2490 files+: a
2491 extra: branch=default
2491 extra: branch=default
2492 description:
2492 description:
2493 line 1
2493 line 1
2494 line 2
2494 line 2
2495
2495
2496
2496
2497 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2497 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2498 bisect: good
2498 bisect: good
2499 phase: public
2499 phase: public
2500 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2500 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2501 parent: -1:0000000000000000000000000000000000000000
2501 parent: -1:0000000000000000000000000000000000000000
2502 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2502 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2503 user: A. N. Other <other@place>
2503 user: A. N. Other <other@place>
2504 date: Tue Jan 13 17:33:20 1970 +0000
2504 date: Tue Jan 13 17:33:20 1970 +0000
2505 files+: b
2505 files+: b
2506 extra: branch=default
2506 extra: branch=default
2507 description:
2507 description:
2508 other 1
2508 other 1
2509 other 2
2509 other 2
2510
2510
2511 other 3
2511 other 3
2512
2512
2513
2513
2514 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2514 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2515 bisect: untested
2515 bisect: untested
2516 phase: public
2516 phase: public
2517 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2517 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2518 parent: -1:0000000000000000000000000000000000000000
2518 parent: -1:0000000000000000000000000000000000000000
2519 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2519 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2520 user: other@place
2520 user: other@place
2521 date: Wed Jan 14 21:20:00 1970 +0000
2521 date: Wed Jan 14 21:20:00 1970 +0000
2522 files+: c
2522 files+: c
2523 extra: branch=default
2523 extra: branch=default
2524 description:
2524 description:
2525 no person
2525 no person
2526
2526
2527
2527
2528 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2528 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2529 bisect: bad
2529 bisect: bad
2530 phase: public
2530 phase: public
2531 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2531 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2532 parent: -1:0000000000000000000000000000000000000000
2532 parent: -1:0000000000000000000000000000000000000000
2533 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2533 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2534 user: person
2534 user: person
2535 date: Fri Jan 16 01:06:40 1970 +0000
2535 date: Fri Jan 16 01:06:40 1970 +0000
2536 files: c
2536 files: c
2537 extra: branch=default
2537 extra: branch=default
2538 description:
2538 description:
2539 no user, no domain
2539 no user, no domain
2540
2540
2541
2541
2542 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2542 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2543 bisect: bad (implicit)
2543 bisect: bad (implicit)
2544 branch: foo
2544 branch: foo
2545 phase: draft
2545 phase: draft
2546 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2546 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2547 parent: -1:0000000000000000000000000000000000000000
2547 parent: -1:0000000000000000000000000000000000000000
2548 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2548 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2549 user: person
2549 user: person
2550 date: Sat Jan 17 04:53:20 1970 +0000
2550 date: Sat Jan 17 04:53:20 1970 +0000
2551 extra: branch=foo
2551 extra: branch=foo
2552 description:
2552 description:
2553 new branch
2553 new branch
2554
2554
2555
2555
2556 $ hg log -v -T bisect -r 0:4
2556 $ hg log -v -T bisect -r 0:4
2557 changeset: 0:1e4e1b8f71e0
2557 changeset: 0:1e4e1b8f71e0
2558 bisect: good (implicit)
2558 bisect: good (implicit)
2559 user: User Name <user@hostname>
2559 user: User Name <user@hostname>
2560 date: Mon Jan 12 13:46:40 1970 +0000
2560 date: Mon Jan 12 13:46:40 1970 +0000
2561 files: a
2561 files: a
2562 description:
2562 description:
2563 line 1
2563 line 1
2564 line 2
2564 line 2
2565
2565
2566
2566
2567 changeset: 1:b608e9d1a3f0
2567 changeset: 1:b608e9d1a3f0
2568 bisect: good
2568 bisect: good
2569 user: A. N. Other <other@place>
2569 user: A. N. Other <other@place>
2570 date: Tue Jan 13 17:33:20 1970 +0000
2570 date: Tue Jan 13 17:33:20 1970 +0000
2571 files: b
2571 files: b
2572 description:
2572 description:
2573 other 1
2573 other 1
2574 other 2
2574 other 2
2575
2575
2576 other 3
2576 other 3
2577
2577
2578
2578
2579 changeset: 2:97054abb4ab8
2579 changeset: 2:97054abb4ab8
2580 bisect: untested
2580 bisect: untested
2581 user: other@place
2581 user: other@place
2582 date: Wed Jan 14 21:20:00 1970 +0000
2582 date: Wed Jan 14 21:20:00 1970 +0000
2583 files: c
2583 files: c
2584 description:
2584 description:
2585 no person
2585 no person
2586
2586
2587
2587
2588 changeset: 3:10e46f2dcbf4
2588 changeset: 3:10e46f2dcbf4
2589 bisect: bad
2589 bisect: bad
2590 user: person
2590 user: person
2591 date: Fri Jan 16 01:06:40 1970 +0000
2591 date: Fri Jan 16 01:06:40 1970 +0000
2592 files: c
2592 files: c
2593 description:
2593 description:
2594 no user, no domain
2594 no user, no domain
2595
2595
2596
2596
2597 changeset: 4:bbe44766e73d
2597 changeset: 4:bbe44766e73d
2598 bisect: bad (implicit)
2598 bisect: bad (implicit)
2599 branch: foo
2599 branch: foo
2600 user: person
2600 user: person
2601 date: Sat Jan 17 04:53:20 1970 +0000
2601 date: Sat Jan 17 04:53:20 1970 +0000
2602 description:
2602 description:
2603 new branch
2603 new branch
2604
2604
2605
2605
2606 $ hg --color=debug log -T bisect -r 0:4
2606 $ hg --color=debug log -T bisect -r 0:4
2607 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2607 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2608 [log.bisect bisect.good|bisect: good (implicit)]
2608 [log.bisect bisect.good|bisect: good (implicit)]
2609 [log.user|user: User Name <user@hostname>]
2609 [log.user|user: User Name <user@hostname>]
2610 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2610 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2611 [log.summary|summary: line 1]
2611 [log.summary|summary: line 1]
2612
2612
2613 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2613 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2614 [log.bisect bisect.good|bisect: good]
2614 [log.bisect bisect.good|bisect: good]
2615 [log.user|user: A. N. Other <other@place>]
2615 [log.user|user: A. N. Other <other@place>]
2616 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2616 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2617 [log.summary|summary: other 1]
2617 [log.summary|summary: other 1]
2618
2618
2619 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2619 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2620 [log.bisect bisect.untested|bisect: untested]
2620 [log.bisect bisect.untested|bisect: untested]
2621 [log.user|user: other@place]
2621 [log.user|user: other@place]
2622 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2622 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2623 [log.summary|summary: no person]
2623 [log.summary|summary: no person]
2624
2624
2625 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2625 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2626 [log.bisect bisect.bad|bisect: bad]
2626 [log.bisect bisect.bad|bisect: bad]
2627 [log.user|user: person]
2627 [log.user|user: person]
2628 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2628 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2629 [log.summary|summary: no user, no domain]
2629 [log.summary|summary: no user, no domain]
2630
2630
2631 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2631 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2632 [log.bisect bisect.bad|bisect: bad (implicit)]
2632 [log.bisect bisect.bad|bisect: bad (implicit)]
2633 [log.branch|branch: foo]
2633 [log.branch|branch: foo]
2634 [log.user|user: person]
2634 [log.user|user: person]
2635 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2635 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2636 [log.summary|summary: new branch]
2636 [log.summary|summary: new branch]
2637
2637
2638 $ hg --color=debug log --debug -T bisect -r 0:4
2638 $ hg --color=debug log --debug -T bisect -r 0:4
2639 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2639 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2640 [log.bisect bisect.good|bisect: good (implicit)]
2640 [log.bisect bisect.good|bisect: good (implicit)]
2641 [log.phase|phase: public]
2641 [log.phase|phase: public]
2642 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2642 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2643 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2643 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2644 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2644 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2645 [log.user|user: User Name <user@hostname>]
2645 [log.user|user: User Name <user@hostname>]
2646 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2646 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2647 [ui.debug log.files|files+: a]
2647 [ui.debug log.files|files+: a]
2648 [ui.debug log.extra|extra: branch=default]
2648 [ui.debug log.extra|extra: branch=default]
2649 [ui.note log.description|description:]
2649 [ui.note log.description|description:]
2650 [ui.note log.description|line 1
2650 [ui.note log.description|line 1
2651 line 2]
2651 line 2]
2652
2652
2653
2653
2654 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2654 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2655 [log.bisect bisect.good|bisect: good]
2655 [log.bisect bisect.good|bisect: good]
2656 [log.phase|phase: public]
2656 [log.phase|phase: public]
2657 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2657 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2658 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2658 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2659 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2659 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2660 [log.user|user: A. N. Other <other@place>]
2660 [log.user|user: A. N. Other <other@place>]
2661 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2661 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2662 [ui.debug log.files|files+: b]
2662 [ui.debug log.files|files+: b]
2663 [ui.debug log.extra|extra: branch=default]
2663 [ui.debug log.extra|extra: branch=default]
2664 [ui.note log.description|description:]
2664 [ui.note log.description|description:]
2665 [ui.note log.description|other 1
2665 [ui.note log.description|other 1
2666 other 2
2666 other 2
2667
2667
2668 other 3]
2668 other 3]
2669
2669
2670
2670
2671 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2671 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2672 [log.bisect bisect.untested|bisect: untested]
2672 [log.bisect bisect.untested|bisect: untested]
2673 [log.phase|phase: public]
2673 [log.phase|phase: public]
2674 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2674 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2675 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2675 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2676 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2676 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2677 [log.user|user: other@place]
2677 [log.user|user: other@place]
2678 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2678 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2679 [ui.debug log.files|files+: c]
2679 [ui.debug log.files|files+: c]
2680 [ui.debug log.extra|extra: branch=default]
2680 [ui.debug log.extra|extra: branch=default]
2681 [ui.note log.description|description:]
2681 [ui.note log.description|description:]
2682 [ui.note log.description|no person]
2682 [ui.note log.description|no person]
2683
2683
2684
2684
2685 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2685 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2686 [log.bisect bisect.bad|bisect: bad]
2686 [log.bisect bisect.bad|bisect: bad]
2687 [log.phase|phase: public]
2687 [log.phase|phase: public]
2688 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2688 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2689 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2689 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2690 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2690 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2691 [log.user|user: person]
2691 [log.user|user: person]
2692 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2692 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2693 [ui.debug log.files|files: c]
2693 [ui.debug log.files|files: c]
2694 [ui.debug log.extra|extra: branch=default]
2694 [ui.debug log.extra|extra: branch=default]
2695 [ui.note log.description|description:]
2695 [ui.note log.description|description:]
2696 [ui.note log.description|no user, no domain]
2696 [ui.note log.description|no user, no domain]
2697
2697
2698
2698
2699 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2699 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2700 [log.bisect bisect.bad|bisect: bad (implicit)]
2700 [log.bisect bisect.bad|bisect: bad (implicit)]
2701 [log.branch|branch: foo]
2701 [log.branch|branch: foo]
2702 [log.phase|phase: draft]
2702 [log.phase|phase: draft]
2703 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2703 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2704 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2704 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2705 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2705 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2706 [log.user|user: person]
2706 [log.user|user: person]
2707 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2707 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2708 [ui.debug log.extra|extra: branch=foo]
2708 [ui.debug log.extra|extra: branch=foo]
2709 [ui.note log.description|description:]
2709 [ui.note log.description|description:]
2710 [ui.note log.description|new branch]
2710 [ui.note log.description|new branch]
2711
2711
2712
2712
2713 $ hg --color=debug log -v -T bisect -r 0:4
2713 $ hg --color=debug log -v -T bisect -r 0:4
2714 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2714 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2715 [log.bisect bisect.good|bisect: good (implicit)]
2715 [log.bisect bisect.good|bisect: good (implicit)]
2716 [log.user|user: User Name <user@hostname>]
2716 [log.user|user: User Name <user@hostname>]
2717 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2717 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2718 [ui.note log.files|files: a]
2718 [ui.note log.files|files: a]
2719 [ui.note log.description|description:]
2719 [ui.note log.description|description:]
2720 [ui.note log.description|line 1
2720 [ui.note log.description|line 1
2721 line 2]
2721 line 2]
2722
2722
2723
2723
2724 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2724 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2725 [log.bisect bisect.good|bisect: good]
2725 [log.bisect bisect.good|bisect: good]
2726 [log.user|user: A. N. Other <other@place>]
2726 [log.user|user: A. N. Other <other@place>]
2727 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2727 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2728 [ui.note log.files|files: b]
2728 [ui.note log.files|files: b]
2729 [ui.note log.description|description:]
2729 [ui.note log.description|description:]
2730 [ui.note log.description|other 1
2730 [ui.note log.description|other 1
2731 other 2
2731 other 2
2732
2732
2733 other 3]
2733 other 3]
2734
2734
2735
2735
2736 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2736 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2737 [log.bisect bisect.untested|bisect: untested]
2737 [log.bisect bisect.untested|bisect: untested]
2738 [log.user|user: other@place]
2738 [log.user|user: other@place]
2739 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2739 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2740 [ui.note log.files|files: c]
2740 [ui.note log.files|files: c]
2741 [ui.note log.description|description:]
2741 [ui.note log.description|description:]
2742 [ui.note log.description|no person]
2742 [ui.note log.description|no person]
2743
2743
2744
2744
2745 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2745 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2746 [log.bisect bisect.bad|bisect: bad]
2746 [log.bisect bisect.bad|bisect: bad]
2747 [log.user|user: person]
2747 [log.user|user: person]
2748 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2748 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2749 [ui.note log.files|files: c]
2749 [ui.note log.files|files: c]
2750 [ui.note log.description|description:]
2750 [ui.note log.description|description:]
2751 [ui.note log.description|no user, no domain]
2751 [ui.note log.description|no user, no domain]
2752
2752
2753
2753
2754 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2754 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2755 [log.bisect bisect.bad|bisect: bad (implicit)]
2755 [log.bisect bisect.bad|bisect: bad (implicit)]
2756 [log.branch|branch: foo]
2756 [log.branch|branch: foo]
2757 [log.user|user: person]
2757 [log.user|user: person]
2758 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2758 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2759 [ui.note log.description|description:]
2759 [ui.note log.description|description:]
2760 [ui.note log.description|new branch]
2760 [ui.note log.description|new branch]
2761
2761
2762
2762
2763 $ hg bisect --reset
2763 $ hg bisect --reset
2764
2764
2765 Error on syntax:
2765 Error on syntax:
2766
2766
2767 $ echo 'x = "f' >> t
2767 $ echo 'x = "f' >> t
2768 $ hg log
2768 $ hg log
2769 hg: parse error at t:3: unmatched quotes
2769 hg: parse error at t:3: unmatched quotes
2770 [255]
2770 [255]
2771
2771
2772 $ hg log -T '{date'
2772 $ hg log -T '{date'
2773 hg: parse error at 1: unterminated template expansion
2773 hg: parse error at 1: unterminated template expansion
2774 ({date
2774 ({date
2775 ^ here)
2775 ^ here)
2776 [255]
2776 [255]
2777 $ hg log -T '{date(}'
2777 $ hg log -T '{date(}'
2778 hg: parse error at 6: not a prefix: end
2778 hg: parse error at 6: not a prefix: end
2779 ({date(}
2779 ({date(}
2780 ^ here)
2780 ^ here)
2781 [255]
2781 [255]
2782 $ hg log -T '{date)}'
2782 $ hg log -T '{date)}'
2783 hg: parse error at 5: invalid token
2783 hg: parse error at 5: invalid token
2784 ({date)}
2784 ({date)}
2785 ^ here)
2785 ^ here)
2786 [255]
2786 [255]
2787 $ hg log -T '{date date}'
2787 $ hg log -T '{date date}'
2788 hg: parse error at 6: invalid token
2788 hg: parse error at 6: invalid token
2789 ({date date}
2789 ({date date}
2790 ^ here)
2790 ^ here)
2791 [255]
2791 [255]
2792
2792
2793 $ hg log -T '{}'
2793 $ hg log -T '{}'
2794 hg: parse error at 1: not a prefix: end
2794 hg: parse error at 1: not a prefix: end
2795 ({}
2795 ({}
2796 ^ here)
2796 ^ here)
2797 [255]
2797 [255]
2798 $ hg debugtemplate -v '{()}'
2798 $ hg debugtemplate -v '{()}'
2799 (template
2799 (template
2800 (group
2800 (group
2801 None))
2801 None))
2802 hg: parse error: missing argument
2802 hg: parse error: missing argument
2803 [255]
2803 [255]
2804
2804
2805 Behind the scenes, this would throw TypeError without intype=bytes
2805 Behind the scenes, this would throw TypeError without intype=bytes
2806
2806
2807 $ hg log -l 3 --template '{date|obfuscate}\n'
2807 $ hg log -l 3 --template '{date|obfuscate}\n'
2808 &#48;&#46;&#48;&#48;
2808 &#48;&#46;&#48;&#48;
2809 &#48;&#46;&#48;&#48;
2809 &#48;&#46;&#48;&#48;
2810 &#49;&#53;&#55;&#55;&#56;&#55;&#50;&#56;&#54;&#48;&#46;&#48;&#48;
2810 &#49;&#53;&#55;&#55;&#56;&#55;&#50;&#56;&#54;&#48;&#46;&#48;&#48;
2811
2811
2812 Behind the scenes, this will throw a ValueError
2812 Behind the scenes, this will throw a ValueError
2813
2813
2814 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2814 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2815 hg: parse error: invalid date: 'Modify, add, remove, rename'
2815 hg: parse error: invalid date: 'Modify, add, remove, rename'
2816 (template filter 'shortdate' is not compatible with keyword 'desc')
2816 (template filter 'shortdate' is not compatible with keyword 'desc')
2817 [255]
2817 [255]
2818
2818
2819 Behind the scenes, this would throw AttributeError without intype=bytes
2819 Behind the scenes, this would throw AttributeError without intype=bytes
2820
2820
2821 $ hg log -l 3 --template 'line: {date|escape}\n'
2821 $ hg log -l 3 --template 'line: {date|escape}\n'
2822 line: 0.00
2822 line: 0.00
2823 line: 0.00
2823 line: 0.00
2824 line: 1577872860.00
2824 line: 1577872860.00
2825
2825
2826 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2826 $ hg log -l 3 --template 'line: {extras|localdate}\n'
2827 hg: parse error: localdate expects a date information
2827 hg: parse error: localdate expects a date information
2828 [255]
2828 [255]
2829
2829
2830 Behind the scenes, this will throw ValueError
2830 Behind the scenes, this will throw ValueError
2831
2831
2832 $ hg tip --template '{author|email|date}\n'
2832 $ hg tip --template '{author|email|date}\n'
2833 hg: parse error: date expects a date information
2833 hg: parse error: date expects a date information
2834 [255]
2834 [255]
2835
2835
2836 $ hg tip -T '{author|email|shortdate}\n'
2836 $ hg tip -T '{author|email|shortdate}\n'
2837 hg: parse error: invalid date: 'test'
2837 hg: parse error: invalid date: 'test'
2838 (template filter 'shortdate' is not compatible with keyword 'author')
2838 (template filter 'shortdate' is not compatible with keyword 'author')
2839 [255]
2839 [255]
2840
2840
2841 $ hg tip -T '{get(extras, "branch")|shortdate}\n'
2841 $ hg tip -T '{get(extras, "branch")|shortdate}\n'
2842 hg: parse error: invalid date: 'default'
2842 hg: parse error: invalid date: 'default'
2843 (incompatible use of template filter 'shortdate')
2843 (incompatible use of template filter 'shortdate')
2844 [255]
2844 [255]
2845
2845
2846 Error in nested template:
2846 Error in nested template:
2847
2847
2848 $ hg log -T '{"date'
2848 $ hg log -T '{"date'
2849 hg: parse error at 2: unterminated string
2849 hg: parse error at 2: unterminated string
2850 ({"date
2850 ({"date
2851 ^ here)
2851 ^ here)
2852 [255]
2852 [255]
2853
2853
2854 $ hg log -T '{"foo{date|?}"}'
2854 $ hg log -T '{"foo{date|?}"}'
2855 hg: parse error at 11: syntax error
2855 hg: parse error at 11: syntax error
2856 ({"foo{date|?}"}
2856 ({"foo{date|?}"}
2857 ^ here)
2857 ^ here)
2858 [255]
2858 [255]
2859
2859
2860 Thrown an error if a template function doesn't exist
2860 Thrown an error if a template function doesn't exist
2861
2861
2862 $ hg tip --template '{foo()}\n'
2862 $ hg tip --template '{foo()}\n'
2863 hg: parse error: unknown function 'foo'
2863 hg: parse error: unknown function 'foo'
2864 [255]
2864 [255]
2865
2865
2866 Pass generator object created by template function to filter
2866 Pass generator object created by template function to filter
2867
2867
2868 $ hg log -l 1 --template '{if(author, author)|user}\n'
2868 $ hg log -l 1 --template '{if(author, author)|user}\n'
2869 test
2869 test
2870
2870
2871 Test index keyword:
2871 Test index keyword:
2872
2872
2873 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
2873 $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
2874 10 0:a 1:b 2:fifth 3:fourth 4:third
2874 10 0:a 1:b 2:fifth 3:fourth 4:third
2875 11 0:a
2875 11 0:a
2876
2876
2877 $ hg branches -T '{index} {branch}\n'
2877 $ hg branches -T '{index} {branch}\n'
2878 0 default
2878 0 default
2879 1 foo
2879 1 foo
2880
2880
2881 Test diff function:
2881 Test diff function:
2882
2882
2883 $ hg diff -c 8
2883 $ hg diff -c 8
2884 diff -r 29114dbae42b -r 95c24699272e fourth
2884 diff -r 29114dbae42b -r 95c24699272e fourth
2885 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2885 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2886 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2886 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2887 @@ -0,0 +1,1 @@
2887 @@ -0,0 +1,1 @@
2888 +second
2888 +second
2889 diff -r 29114dbae42b -r 95c24699272e second
2889 diff -r 29114dbae42b -r 95c24699272e second
2890 --- a/second Mon Jan 12 13:46:40 1970 +0000
2890 --- a/second Mon Jan 12 13:46:40 1970 +0000
2891 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2891 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2892 @@ -1,1 +0,0 @@
2892 @@ -1,1 +0,0 @@
2893 -second
2893 -second
2894 diff -r 29114dbae42b -r 95c24699272e third
2894 diff -r 29114dbae42b -r 95c24699272e third
2895 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2895 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2896 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2896 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2897 @@ -0,0 +1,1 @@
2897 @@ -0,0 +1,1 @@
2898 +third
2898 +third
2899
2899
2900 $ hg log -r 8 -T "{diff()}"
2900 $ hg log -r 8 -T "{diff()}"
2901 diff -r 29114dbae42b -r 95c24699272e fourth
2901 diff -r 29114dbae42b -r 95c24699272e fourth
2902 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2902 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2903 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2903 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2904 @@ -0,0 +1,1 @@
2904 @@ -0,0 +1,1 @@
2905 +second
2905 +second
2906 diff -r 29114dbae42b -r 95c24699272e second
2906 diff -r 29114dbae42b -r 95c24699272e second
2907 --- a/second Mon Jan 12 13:46:40 1970 +0000
2907 --- a/second Mon Jan 12 13:46:40 1970 +0000
2908 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2908 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2909 @@ -1,1 +0,0 @@
2909 @@ -1,1 +0,0 @@
2910 -second
2910 -second
2911 diff -r 29114dbae42b -r 95c24699272e third
2911 diff -r 29114dbae42b -r 95c24699272e third
2912 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2912 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2913 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2913 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2914 @@ -0,0 +1,1 @@
2914 @@ -0,0 +1,1 @@
2915 +third
2915 +third
2916
2916
2917 $ hg log -r 8 -T "{diff('glob:f*')}"
2917 $ hg log -r 8 -T "{diff('glob:f*')}"
2918 diff -r 29114dbae42b -r 95c24699272e fourth
2918 diff -r 29114dbae42b -r 95c24699272e fourth
2919 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2919 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2920 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2920 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2921 @@ -0,0 +1,1 @@
2921 @@ -0,0 +1,1 @@
2922 +second
2922 +second
2923
2923
2924 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2924 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2925 diff -r 29114dbae42b -r 95c24699272e second
2925 diff -r 29114dbae42b -r 95c24699272e second
2926 --- a/second Mon Jan 12 13:46:40 1970 +0000
2926 --- a/second Mon Jan 12 13:46:40 1970 +0000
2927 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2927 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2928 @@ -1,1 +0,0 @@
2928 @@ -1,1 +0,0 @@
2929 -second
2929 -second
2930 diff -r 29114dbae42b -r 95c24699272e third
2930 diff -r 29114dbae42b -r 95c24699272e third
2931 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2931 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2932 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2932 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2933 @@ -0,0 +1,1 @@
2933 @@ -0,0 +1,1 @@
2934 +third
2934 +third
2935
2935
2936 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2936 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2937 diff -r 29114dbae42b -r 95c24699272e fourth
2937 diff -r 29114dbae42b -r 95c24699272e fourth
2938 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2938 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2939 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2939 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2940 @@ -0,0 +1,1 @@
2940 @@ -0,0 +1,1 @@
2941 +second
2941 +second
2942
2942
2943 ui verbosity:
2943 ui verbosity:
2944
2944
2945 $ hg log -l1 -T '{verbosity}\n'
2945 $ hg log -l1 -T '{verbosity}\n'
2946
2946
2947 $ hg log -l1 -T '{verbosity}\n' --debug
2947 $ hg log -l1 -T '{verbosity}\n' --debug
2948 debug
2948 debug
2949 $ hg log -l1 -T '{verbosity}\n' --quiet
2949 $ hg log -l1 -T '{verbosity}\n' --quiet
2950 quiet
2950 quiet
2951 $ hg log -l1 -T '{verbosity}\n' --verbose
2951 $ hg log -l1 -T '{verbosity}\n' --verbose
2952 verbose
2952 verbose
2953
2953
2954 $ cd ..
2954 $ cd ..
2955
2955
2956
2956
2957 latesttag:
2957 latesttag:
2958
2958
2959 $ hg init latesttag
2959 $ hg init latesttag
2960 $ cd latesttag
2960 $ cd latesttag
2961
2961
2962 $ echo a > file
2962 $ echo a > file
2963 $ hg ci -Am a -d '0 0'
2963 $ hg ci -Am a -d '0 0'
2964 adding file
2964 adding file
2965
2965
2966 $ echo b >> file
2966 $ echo b >> file
2967 $ hg ci -m b -d '1 0'
2967 $ hg ci -m b -d '1 0'
2968
2968
2969 $ echo c >> head1
2969 $ echo c >> head1
2970 $ hg ci -Am h1c -d '2 0'
2970 $ hg ci -Am h1c -d '2 0'
2971 adding head1
2971 adding head1
2972
2972
2973 $ hg update -q 1
2973 $ hg update -q 1
2974 $ echo d >> head2
2974 $ echo d >> head2
2975 $ hg ci -Am h2d -d '3 0'
2975 $ hg ci -Am h2d -d '3 0'
2976 adding head2
2976 adding head2
2977 created new head
2977 created new head
2978
2978
2979 $ echo e >> head2
2979 $ echo e >> head2
2980 $ hg ci -m h2e -d '4 0'
2980 $ hg ci -m h2e -d '4 0'
2981
2981
2982 $ hg merge -q
2982 $ hg merge -q
2983 $ hg ci -m merge -d '5 -3600'
2983 $ hg ci -m merge -d '5 -3600'
2984
2984
2985 No tag set:
2985 No tag set:
2986
2986
2987 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2987 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
2988 @ 5: null+5
2988 @ 5: null+5
2989 |\
2989 |\
2990 | o 4: null+4
2990 | o 4: null+4
2991 | |
2991 | |
2992 | o 3: null+3
2992 | o 3: null+3
2993 | |
2993 | |
2994 o | 2: null+3
2994 o | 2: null+3
2995 |/
2995 |/
2996 o 1: null+2
2996 o 1: null+2
2997 |
2997 |
2998 o 0: null+1
2998 o 0: null+1
2999
2999
3000
3000
3001 One common tag: longest path wins for {latesttagdistance}:
3001 One common tag: longest path wins for {latesttagdistance}:
3002
3002
3003 $ hg tag -r 1 -m t1 -d '6 0' t1
3003 $ hg tag -r 1 -m t1 -d '6 0' t1
3004 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3004 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3005 @ 6: t1+4
3005 @ 6: t1+4
3006 |
3006 |
3007 o 5: t1+3
3007 o 5: t1+3
3008 |\
3008 |\
3009 | o 4: t1+2
3009 | o 4: t1+2
3010 | |
3010 | |
3011 | o 3: t1+1
3011 | o 3: t1+1
3012 | |
3012 | |
3013 o | 2: t1+1
3013 o | 2: t1+1
3014 |/
3014 |/
3015 o 1: t1+0
3015 o 1: t1+0
3016 |
3016 |
3017 o 0: null+1
3017 o 0: null+1
3018
3018
3019
3019
3020 One ancestor tag: closest wins:
3020 One ancestor tag: closest wins:
3021
3021
3022 $ hg tag -r 2 -m t2 -d '7 0' t2
3022 $ hg tag -r 2 -m t2 -d '7 0' t2
3023 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3023 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3024 @ 7: t2+3
3024 @ 7: t2+3
3025 |
3025 |
3026 o 6: t2+2
3026 o 6: t2+2
3027 |
3027 |
3028 o 5: t2+1
3028 o 5: t2+1
3029 |\
3029 |\
3030 | o 4: t1+2
3030 | o 4: t1+2
3031 | |
3031 | |
3032 | o 3: t1+1
3032 | o 3: t1+1
3033 | |
3033 | |
3034 o | 2: t2+0
3034 o | 2: t2+0
3035 |/
3035 |/
3036 o 1: t1+0
3036 o 1: t1+0
3037 |
3037 |
3038 o 0: null+1
3038 o 0: null+1
3039
3039
3040
3040
3041 Two branch tags: more recent wins if same number of changes:
3041 Two branch tags: more recent wins if same number of changes:
3042
3042
3043 $ hg tag -r 3 -m t3 -d '8 0' t3
3043 $ hg tag -r 3 -m t3 -d '8 0' t3
3044 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3044 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3045 @ 8: t3+5
3045 @ 8: t3+5
3046 |
3046 |
3047 o 7: t3+4
3047 o 7: t3+4
3048 |
3048 |
3049 o 6: t3+3
3049 o 6: t3+3
3050 |
3050 |
3051 o 5: t3+2
3051 o 5: t3+2
3052 |\
3052 |\
3053 | o 4: t3+1
3053 | o 4: t3+1
3054 | |
3054 | |
3055 | o 3: t3+0
3055 | o 3: t3+0
3056 | |
3056 | |
3057 o | 2: t2+0
3057 o | 2: t2+0
3058 |/
3058 |/
3059 o 1: t1+0
3059 o 1: t1+0
3060 |
3060 |
3061 o 0: null+1
3061 o 0: null+1
3062
3062
3063
3063
3064 Two branch tags: fewest changes wins:
3064 Two branch tags: fewest changes wins:
3065
3065
3066 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
3066 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
3067 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3067 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3068 @ 9: t4+5,6
3068 @ 9: t4+5,6
3069 |
3069 |
3070 o 8: t4+4,5
3070 o 8: t4+4,5
3071 |
3071 |
3072 o 7: t4+3,4
3072 o 7: t4+3,4
3073 |
3073 |
3074 o 6: t4+2,3
3074 o 6: t4+2,3
3075 |
3075 |
3076 o 5: t4+1,2
3076 o 5: t4+1,2
3077 |\
3077 |\
3078 | o 4: t4+0,0
3078 | o 4: t4+0,0
3079 | |
3079 | |
3080 | o 3: t3+0,0
3080 | o 3: t3+0,0
3081 | |
3081 | |
3082 o | 2: t2+0,0
3082 o | 2: t2+0,0
3083 |/
3083 |/
3084 o 1: t1+0,0
3084 o 1: t1+0,0
3085 |
3085 |
3086 o 0: null+1,1
3086 o 0: null+1,1
3087
3087
3088
3088
3089 Merged tag overrides:
3089 Merged tag overrides:
3090
3090
3091 $ hg tag -r 5 -m t5 -d '9 0' t5
3091 $ hg tag -r 5 -m t5 -d '9 0' t5
3092 $ hg tag -r 3 -m at3 -d '10 0' at3
3092 $ hg tag -r 3 -m at3 -d '10 0' at3
3093 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3093 $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
3094 @ 11: t5+6
3094 @ 11: t5+6
3095 |
3095 |
3096 o 10: t5+5
3096 o 10: t5+5
3097 |
3097 |
3098 o 9: t5+4
3098 o 9: t5+4
3099 |
3099 |
3100 o 8: t5+3
3100 o 8: t5+3
3101 |
3101 |
3102 o 7: t5+2
3102 o 7: t5+2
3103 |
3103 |
3104 o 6: t5+1
3104 o 6: t5+1
3105 |
3105 |
3106 o 5: t5+0
3106 o 5: t5+0
3107 |\
3107 |\
3108 | o 4: t4+0
3108 | o 4: t4+0
3109 | |
3109 | |
3110 | o 3: at3:t3+0
3110 | o 3: at3:t3+0
3111 | |
3111 | |
3112 o | 2: t2+0
3112 o | 2: t2+0
3113 |/
3113 |/
3114 o 1: t1+0
3114 o 1: t1+0
3115 |
3115 |
3116 o 0: null+1
3116 o 0: null+1
3117
3117
3118
3118
3119 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3119 $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
3120 @ 11: t5+6,6
3120 @ 11: t5+6,6
3121 |
3121 |
3122 o 10: t5+5,5
3122 o 10: t5+5,5
3123 |
3123 |
3124 o 9: t5+4,4
3124 o 9: t5+4,4
3125 |
3125 |
3126 o 8: t5+3,3
3126 o 8: t5+3,3
3127 |
3127 |
3128 o 7: t5+2,2
3128 o 7: t5+2,2
3129 |
3129 |
3130 o 6: t5+1,1
3130 o 6: t5+1,1
3131 |
3131 |
3132 o 5: t5+0,0
3132 o 5: t5+0,0
3133 |\
3133 |\
3134 | o 4: t4+0,0
3134 | o 4: t4+0,0
3135 | |
3135 | |
3136 | o 3: at3+0,0 t3+0,0
3136 | o 3: at3+0,0 t3+0,0
3137 | |
3137 | |
3138 o | 2: t2+0,0
3138 o | 2: t2+0,0
3139 |/
3139 |/
3140 o 1: t1+0,0
3140 o 1: t1+0,0
3141 |
3141 |
3142 o 0: null+1,1
3142 o 0: null+1,1
3143
3143
3144
3144
3145 $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
3145 $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
3146 @ 11: t3, C: 9, D: 8
3146 @ 11: t3, C: 9, D: 8
3147 |
3147 |
3148 o 10: t3, C: 8, D: 7
3148 o 10: t3, C: 8, D: 7
3149 |
3149 |
3150 o 9: t3, C: 7, D: 6
3150 o 9: t3, C: 7, D: 6
3151 |
3151 |
3152 o 8: t3, C: 6, D: 5
3152 o 8: t3, C: 6, D: 5
3153 |
3153 |
3154 o 7: t3, C: 5, D: 4
3154 o 7: t3, C: 5, D: 4
3155 |
3155 |
3156 o 6: t3, C: 4, D: 3
3156 o 6: t3, C: 4, D: 3
3157 |
3157 |
3158 o 5: t3, C: 3, D: 2
3158 o 5: t3, C: 3, D: 2
3159 |\
3159 |\
3160 | o 4: t3, C: 1, D: 1
3160 | o 4: t3, C: 1, D: 1
3161 | |
3161 | |
3162 | o 3: t3, C: 0, D: 0
3162 | o 3: t3, C: 0, D: 0
3163 | |
3163 | |
3164 o | 2: t1, C: 1, D: 1
3164 o | 2: t1, C: 1, D: 1
3165 |/
3165 |/
3166 o 1: t1, C: 0, D: 0
3166 o 1: t1, C: 0, D: 0
3167 |
3167 |
3168 o 0: null, C: 1, D: 1
3168 o 0: null, C: 1, D: 1
3169
3169
3170
3170
3171 $ cd ..
3171 $ cd ..
3172
3172
3173
3173
3174 Style path expansion: issue1948 - ui.style option doesn't work on OSX
3174 Style path expansion: issue1948 - ui.style option doesn't work on OSX
3175 if it is a relative path
3175 if it is a relative path
3176
3176
3177 $ mkdir -p home/styles
3177 $ mkdir -p home/styles
3178
3178
3179 $ cat > home/styles/teststyle <<EOF
3179 $ cat > home/styles/teststyle <<EOF
3180 > changeset = 'test {rev}:{node|short}\n'
3180 > changeset = 'test {rev}:{node|short}\n'
3181 > EOF
3181 > EOF
3182
3182
3183 $ HOME=`pwd`/home; export HOME
3183 $ HOME=`pwd`/home; export HOME
3184
3184
3185 $ cat > latesttag/.hg/hgrc <<EOF
3185 $ cat > latesttag/.hg/hgrc <<EOF
3186 > [ui]
3186 > [ui]
3187 > style = ~/styles/teststyle
3187 > style = ~/styles/teststyle
3188 > EOF
3188 > EOF
3189
3189
3190 $ hg -R latesttag tip
3190 $ hg -R latesttag tip
3191 test 11:97e5943b523a
3191 test 11:97e5943b523a
3192
3192
3193 Test recursive showlist template (issue1989):
3193 Test recursive showlist template (issue1989):
3194
3194
3195 $ cat > style1989 <<EOF
3195 $ cat > style1989 <<EOF
3196 > changeset = '{file_mods}{manifest}{extras}'
3196 > changeset = '{file_mods}{manifest}{extras}'
3197 > file_mod = 'M|{author|person}\n'
3197 > file_mod = 'M|{author|person}\n'
3198 > manifest = '{rev},{author}\n'
3198 > manifest = '{rev},{author}\n'
3199 > extra = '{key}: {author}\n'
3199 > extra = '{key}: {author}\n'
3200 > EOF
3200 > EOF
3201
3201
3202 $ hg -R latesttag log -r tip --style=style1989
3202 $ hg -R latesttag log -r tip --style=style1989
3203 M|test
3203 M|test
3204 11,test
3204 11,test
3205 branch: test
3205 branch: test
3206
3206
3207 Test new-style inline templating:
3207 Test new-style inline templating:
3208
3208
3209 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
3209 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
3210 modified files: .hgtags
3210 modified files: .hgtags
3211
3211
3212
3212
3213 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
3213 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
3214 hg: parse error: keyword 'rev' is not iterable of mappings
3214 hg: parse error: keyword 'rev' is not iterable of mappings
3215 [255]
3215 [255]
3216 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
3216 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
3217 hg: parse error: None is not iterable of mappings
3217 hg: parse error: None is not iterable of mappings
3218 [255]
3218 [255]
3219 $ hg log -R latesttag -r tip -T '{extras % "{key}\n" % "{key}\n"}'
3219 $ hg log -R latesttag -r tip -T '{extras % "{key}\n" % "{key}\n"}'
3220 hg: parse error: list of strings is not mappable
3220 hg: parse error: list of strings is not mappable
3221 [255]
3221 [255]
3222
3222
3223 Test new-style inline templating of non-list/dict type:
3223 Test new-style inline templating of non-list/dict type:
3224
3224
3225 $ hg log -R latesttag -r tip -T '{manifest}\n'
3225 $ hg log -R latesttag -r tip -T '{manifest}\n'
3226 11:2bc6e9006ce2
3226 11:2bc6e9006ce2
3227 $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n'
3227 $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n'
3228 string length: 15
3228 string length: 15
3229 $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
3229 $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
3230 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
3230 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
3231
3231
3232 $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}'
3232 $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}'
3233 branch: default
3233 branch: default
3234 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}'
3234 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}'
3235 hg: parse error: None is not iterable of mappings
3235 hg: parse error: None is not iterable of mappings
3236 [255]
3236 [255]
3237 $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}'
3237 $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}'
3238 branch: default
3238 branch: default
3239 $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}'
3239 $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}'
3240 0:ce3cec86e6c2
3240 0:ce3cec86e6c2
3241 $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}'
3241 $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}'
3242 9:fbc7cd862e9c
3242 9:fbc7cd862e9c
3243
3243
3244 Test manifest/get() can be join()-ed as before, though it's silly:
3244 Test manifest/get() can be join()-ed as before, though it's silly:
3245
3245
3246 $ hg log -R latesttag -r tip -T '{join(manifest, "")}\n'
3246 $ hg log -R latesttag -r tip -T '{join(manifest, "")}\n'
3247 11:2bc6e9006ce2
3247 11:2bc6e9006ce2
3248 $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), "")}\n'
3248 $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), "")}\n'
3249 default
3249 default
3250
3250
3251 Test min/max of integers
3251 Test min/max of integers
3252
3252
3253 $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'
3253 $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'
3254 9
3254 9
3255 $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
3255 $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
3256 10
3256 10
3257
3257
3258 Test min/max over map operation:
3258 Test min/max over map operation:
3259
3259
3260 $ hg log -R latesttag -r3 -T '{min(tags % "{tag}")}\n'
3260 $ hg log -R latesttag -r3 -T '{min(tags % "{tag}")}\n'
3261 at3
3261 at3
3262 $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n'
3262 $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n'
3263 t3
3263 t3
3264
3264
3265 Test min/max of if() result
3265 Test min/max of if() result
3266
3266
3267 $ cd latesttag
3267 $ cd latesttag
3268 $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n'
3268 $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n'
3269 9
3269 9
3270 $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n'
3270 $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n'
3271 10
3271 10
3272 $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n'
3272 $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n'
3273 9
3273 9
3274 $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n'
3274 $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n'
3275 10
3275 10
3276 $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n'
3276 $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n'
3277 9
3277 9
3278 $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n'
3278 $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n'
3279 10
3279 10
3280 $ cd ..
3280 $ cd ..
3281
3281
3282 Test laziness of if() then/else clause
3282 Test laziness of if() then/else clause
3283
3283
3284 $ hg debugtemplate '{count(0)}'
3284 $ hg debugtemplate '{count(0)}'
3285 hg: parse error: not countable
3285 hg: parse error: not countable
3286 (incompatible use of template filter 'count')
3286 (incompatible use of template filter 'count')
3287 [255]
3287 [255]
3288 $ hg debugtemplate '{if(true, "", count(0))}'
3288 $ hg debugtemplate '{if(true, "", count(0))}'
3289 $ hg debugtemplate '{if(false, count(0), "")}'
3289 $ hg debugtemplate '{if(false, count(0), "")}'
3290 $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}'
3290 $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}'
3291 $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}'
3291 $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}'
3292 $ hg debugtemplate '{ifeq(0, 0, "", count(0))}'
3292 $ hg debugtemplate '{ifeq(0, 0, "", count(0))}'
3293 $ hg debugtemplate '{ifeq(0, 1, count(0), "")}'
3293 $ hg debugtemplate '{ifeq(0, 1, count(0), "")}'
3294
3294
3295 Test dot operator precedence:
3295 Test dot operator precedence:
3296
3296
3297 $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n'
3297 $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n'
3298 (template
3298 (template
3299 (|
3299 (|
3300 (.
3300 (.
3301 (symbol 'manifest')
3301 (symbol 'manifest')
3302 (symbol 'node'))
3302 (symbol 'node'))
3303 (symbol 'short'))
3303 (symbol 'short'))
3304 (string '\n'))
3304 (string '\n'))
3305 89f4071fec70
3305 89f4071fec70
3306
3306
3307 (the following examples are invalid, but seem natural in parsing POV)
3307 (the following examples are invalid, but seem natural in parsing POV)
3308
3308
3309 $ hg debugtemplate -R latesttag -r0 -v '{foo|bar.baz}\n' 2> /dev/null
3309 $ hg debugtemplate -R latesttag -r0 -v '{foo|bar.baz}\n' 2> /dev/null
3310 (template
3310 (template
3311 (|
3311 (|
3312 (symbol 'foo')
3312 (symbol 'foo')
3313 (.
3313 (.
3314 (symbol 'bar')
3314 (symbol 'bar')
3315 (symbol 'baz')))
3315 (symbol 'baz')))
3316 (string '\n'))
3316 (string '\n'))
3317 [255]
3317 [255]
3318 $ hg debugtemplate -R latesttag -r0 -v '{foo.bar()}\n' 2> /dev/null
3318 $ hg debugtemplate -R latesttag -r0 -v '{foo.bar()}\n' 2> /dev/null
3319 (template
3319 (template
3320 (.
3320 (.
3321 (symbol 'foo')
3321 (symbol 'foo')
3322 (func
3322 (func
3323 (symbol 'bar')
3323 (symbol 'bar')
3324 None))
3324 None))
3325 (string '\n'))
3325 (string '\n'))
3326 [255]
3326 [255]
3327
3327
3328 Test evaluation of dot operator:
3328 Test evaluation of dot operator:
3329
3329
3330 $ hg log -R latesttag -l1 -T '{min(revset("0:9")).node}\n'
3330 $ hg log -R latesttag -l1 -T '{min(revset("0:9")).node}\n'
3331 ce3cec86e6c26bd9bdfc590a6b92abc9680f1796
3331 ce3cec86e6c26bd9bdfc590a6b92abc9680f1796
3332 $ hg log -R latesttag -r0 -T '{extras.branch}\n'
3332 $ hg log -R latesttag -r0 -T '{extras.branch}\n'
3333 default
3333 default
3334
3334
3335 $ hg log -R latesttag -l1 -T '{author.invalid}\n'
3335 $ hg log -R latesttag -l1 -T '{author.invalid}\n'
3336 hg: parse error: keyword 'author' has no member
3336 hg: parse error: keyword 'author' has no member
3337 [255]
3337 [255]
3338 $ hg log -R latesttag -l1 -T '{min("abc").invalid}\n'
3338 $ hg log -R latesttag -l1 -T '{min("abc").invalid}\n'
3339 hg: parse error: 'a' has no member
3339 hg: parse error: 'a' has no member
3340 [255]
3340 [255]
3341
3341
3342 Test the sub function of templating for expansion:
3342 Test the sub function of templating for expansion:
3343
3343
3344 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
3344 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
3345 xx
3345 xx
3346
3346
3347 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
3347 $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
3348 hg: parse error: sub got an invalid pattern: [
3348 hg: parse error: sub got an invalid pattern: [
3349 [255]
3349 [255]
3350 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
3350 $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
3351 hg: parse error: sub got an invalid replacement: \1
3351 hg: parse error: sub got an invalid replacement: \1
3352 [255]
3352 [255]
3353
3353
3354 Test the strip function with chars specified:
3354 Test the strip function with chars specified:
3355
3355
3356 $ hg log -R latesttag --template '{desc}\n'
3356 $ hg log -R latesttag --template '{desc}\n'
3357 at3
3357 at3
3358 t5
3358 t5
3359 t4
3359 t4
3360 t3
3360 t3
3361 t2
3361 t2
3362 t1
3362 t1
3363 merge
3363 merge
3364 h2e
3364 h2e
3365 h2d
3365 h2d
3366 h1c
3366 h1c
3367 b
3367 b
3368 a
3368 a
3369
3369
3370 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
3370 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
3371 at3
3371 at3
3372 5
3372 5
3373 4
3373 4
3374 3
3374 3
3375 2
3375 2
3376 1
3376 1
3377 merg
3377 merg
3378 h2
3378 h2
3379 h2d
3379 h2d
3380 h1c
3380 h1c
3381 b
3381 b
3382 a
3382 a
3383
3383
3384 Test date format:
3384 Test date format:
3385
3385
3386 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3386 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
3387 date: 70 01 01 10 +0000
3387 date: 70 01 01 10 +0000
3388 date: 70 01 01 09 +0000
3388 date: 70 01 01 09 +0000
3389 date: 70 01 01 04 +0000
3389 date: 70 01 01 04 +0000
3390 date: 70 01 01 08 +0000
3390 date: 70 01 01 08 +0000
3391 date: 70 01 01 07 +0000
3391 date: 70 01 01 07 +0000
3392 date: 70 01 01 06 +0000
3392 date: 70 01 01 06 +0000
3393 date: 70 01 01 05 +0100
3393 date: 70 01 01 05 +0100
3394 date: 70 01 01 04 +0000
3394 date: 70 01 01 04 +0000
3395 date: 70 01 01 03 +0000
3395 date: 70 01 01 03 +0000
3396 date: 70 01 01 02 +0000
3396 date: 70 01 01 02 +0000
3397 date: 70 01 01 01 +0000
3397 date: 70 01 01 01 +0000
3398 date: 70 01 01 00 +0000
3398 date: 70 01 01 00 +0000
3399
3399
3400 Test invalid date:
3400 Test invalid date:
3401
3401
3402 $ hg log -R latesttag -T '{date(rev)}\n'
3402 $ hg log -R latesttag -T '{date(rev)}\n'
3403 hg: parse error: date expects a date information
3403 hg: parse error: date expects a date information
3404 [255]
3404 [255]
3405
3405
3406 Test integer literal:
3406 Test integer literal:
3407
3407
3408 $ hg debugtemplate -v '{(0)}\n'
3408 $ hg debugtemplate -v '{(0)}\n'
3409 (template
3409 (template
3410 (group
3410 (group
3411 (integer '0'))
3411 (integer '0'))
3412 (string '\n'))
3412 (string '\n'))
3413 0
3413 0
3414 $ hg debugtemplate -v '{(123)}\n'
3414 $ hg debugtemplate -v '{(123)}\n'
3415 (template
3415 (template
3416 (group
3416 (group
3417 (integer '123'))
3417 (integer '123'))
3418 (string '\n'))
3418 (string '\n'))
3419 123
3419 123
3420 $ hg debugtemplate -v '{(-4)}\n'
3420 $ hg debugtemplate -v '{(-4)}\n'
3421 (template
3421 (template
3422 (group
3422 (group
3423 (negate
3423 (negate
3424 (integer '4')))
3424 (integer '4')))
3425 (string '\n'))
3425 (string '\n'))
3426 -4
3426 -4
3427 $ hg debugtemplate '{(-)}\n'
3427 $ hg debugtemplate '{(-)}\n'
3428 hg: parse error at 3: not a prefix: )
3428 hg: parse error at 3: not a prefix: )
3429 ({(-)}\n
3429 ({(-)}\n
3430 ^ here)
3430 ^ here)
3431 [255]
3431 [255]
3432 $ hg debugtemplate '{(-a)}\n'
3432 $ hg debugtemplate '{(-a)}\n'
3433 hg: parse error: negation needs an integer argument
3433 hg: parse error: negation needs an integer argument
3434 [255]
3434 [255]
3435
3435
3436 top-level integer literal is interpreted as symbol (i.e. variable name):
3436 top-level integer literal is interpreted as symbol (i.e. variable name):
3437
3437
3438 $ hg debugtemplate -D 1=one -v '{1}\n'
3438 $ hg debugtemplate -D 1=one -v '{1}\n'
3439 (template
3439 (template
3440 (integer '1')
3440 (integer '1')
3441 (string '\n'))
3441 (string '\n'))
3442 one
3442 one
3443 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3443 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
3444 (template
3444 (template
3445 (func
3445 (func
3446 (symbol 'if')
3446 (symbol 'if')
3447 (list
3447 (list
3448 (string 't')
3448 (string 't')
3449 (template
3449 (template
3450 (integer '1'))))
3450 (integer '1'))))
3451 (string '\n'))
3451 (string '\n'))
3452 one
3452 one
3453 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3453 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
3454 (template
3454 (template
3455 (|
3455 (|
3456 (integer '1')
3456 (integer '1')
3457 (symbol 'stringify'))
3457 (symbol 'stringify'))
3458 (string '\n'))
3458 (string '\n'))
3459 one
3459 one
3460
3460
3461 unless explicit symbol is expected:
3461 unless explicit symbol is expected:
3462
3462
3463 $ hg log -Ra -r0 -T '{desc|1}\n'
3463 $ hg log -Ra -r0 -T '{desc|1}\n'
3464 hg: parse error: expected a symbol, got 'integer'
3464 hg: parse error: expected a symbol, got 'integer'
3465 [255]
3465 [255]
3466 $ hg log -Ra -r0 -T '{1()}\n'
3466 $ hg log -Ra -r0 -T '{1()}\n'
3467 hg: parse error: expected a symbol, got 'integer'
3467 hg: parse error: expected a symbol, got 'integer'
3468 [255]
3468 [255]
3469
3469
3470 Test string literal:
3470 Test string literal:
3471
3471
3472 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3472 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
3473 (template
3473 (template
3474 (string 'string with no template fragment')
3474 (string 'string with no template fragment')
3475 (string '\n'))
3475 (string '\n'))
3476 string with no template fragment
3476 string with no template fragment
3477 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3477 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
3478 (template
3478 (template
3479 (template
3479 (template
3480 (string 'template: ')
3480 (string 'template: ')
3481 (symbol 'rev'))
3481 (symbol 'rev'))
3482 (string '\n'))
3482 (string '\n'))
3483 template: 0
3483 template: 0
3484 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3484 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
3485 (template
3485 (template
3486 (string 'rawstring: {rev}')
3486 (string 'rawstring: {rev}')
3487 (string '\n'))
3487 (string '\n'))
3488 rawstring: {rev}
3488 rawstring: {rev}
3489 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3489 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
3490 (template
3490 (template
3491 (%
3491 (%
3492 (symbol 'files')
3492 (symbol 'files')
3493 (string 'rawstring: {file}'))
3493 (string 'rawstring: {file}'))
3494 (string '\n'))
3494 (string '\n'))
3495 rawstring: {file}
3495 rawstring: {file}
3496
3496
3497 Test string escaping:
3497 Test string escaping:
3498
3498
3499 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3499 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3500 >
3500 >
3501 <>\n<[>
3501 <>\n<[>
3502 <>\n<]>
3502 <>\n<]>
3503 <>\n<
3503 <>\n<
3504
3504
3505 $ hg log -R latesttag -r 0 \
3505 $ hg log -R latesttag -r 0 \
3506 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3506 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3507 >
3507 >
3508 <>\n<[>
3508 <>\n<[>
3509 <>\n<]>
3509 <>\n<]>
3510 <>\n<
3510 <>\n<
3511
3511
3512 $ hg log -R latesttag -r 0 -T esc \
3512 $ hg log -R latesttag -r 0 -T esc \
3513 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3513 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3514 >
3514 >
3515 <>\n<[>
3515 <>\n<[>
3516 <>\n<]>
3516 <>\n<]>
3517 <>\n<
3517 <>\n<
3518
3518
3519 $ cat <<'EOF' > esctmpl
3519 $ cat <<'EOF' > esctmpl
3520 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3520 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
3521 > EOF
3521 > EOF
3522 $ hg log -R latesttag -r 0 --style ./esctmpl
3522 $ hg log -R latesttag -r 0 --style ./esctmpl
3523 >
3523 >
3524 <>\n<[>
3524 <>\n<[>
3525 <>\n<]>
3525 <>\n<]>
3526 <>\n<
3526 <>\n<
3527
3527
3528 Test string escaping of quotes:
3528 Test string escaping of quotes:
3529
3529
3530 $ hg log -Ra -r0 -T '{"\""}\n'
3530 $ hg log -Ra -r0 -T '{"\""}\n'
3531 "
3531 "
3532 $ hg log -Ra -r0 -T '{"\\\""}\n'
3532 $ hg log -Ra -r0 -T '{"\\\""}\n'
3533 \"
3533 \"
3534 $ hg log -Ra -r0 -T '{r"\""}\n'
3534 $ hg log -Ra -r0 -T '{r"\""}\n'
3535 \"
3535 \"
3536 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3536 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3537 \\\"
3537 \\\"
3538
3538
3539
3539
3540 $ hg log -Ra -r0 -T '{"\""}\n'
3540 $ hg log -Ra -r0 -T '{"\""}\n'
3541 "
3541 "
3542 $ hg log -Ra -r0 -T '{"\\\""}\n'
3542 $ hg log -Ra -r0 -T '{"\\\""}\n'
3543 \"
3543 \"
3544 $ hg log -Ra -r0 -T '{r"\""}\n'
3544 $ hg log -Ra -r0 -T '{r"\""}\n'
3545 \"
3545 \"
3546 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3546 $ hg log -Ra -r0 -T '{r"\\\""}\n'
3547 \\\"
3547 \\\"
3548
3548
3549 Test exception in quoted template. single backslash before quotation mark is
3549 Test exception in quoted template. single backslash before quotation mark is
3550 stripped before parsing:
3550 stripped before parsing:
3551
3551
3552 $ cat <<'EOF' > escquotetmpl
3552 $ cat <<'EOF' > escquotetmpl
3553 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3553 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
3554 > EOF
3554 > EOF
3555 $ cd latesttag
3555 $ cd latesttag
3556 $ hg log -r 2 --style ../escquotetmpl
3556 $ hg log -r 2 --style ../escquotetmpl
3557 " \" \" \\" head1
3557 " \" \" \\" head1
3558
3558
3559 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3559 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
3560 valid
3560 valid
3561 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3561 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
3562 valid
3562 valid
3563
3563
3564 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3564 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
3565 _evalifliteral() templates (issue4733):
3565 _evalifliteral() templates (issue4733):
3566
3566
3567 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3567 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
3568 "2
3568 "2
3569 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3569 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
3570 "2
3570 "2
3571 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3571 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
3572 "2
3572 "2
3573
3573
3574 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3574 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
3575 \"
3575 \"
3576 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3576 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
3577 \"
3577 \"
3578 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3578 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3579 \"
3579 \"
3580
3580
3581 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3581 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
3582 \\\"
3582 \\\"
3583 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3583 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
3584 \\\"
3584 \\\"
3585 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3585 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
3586 \\\"
3586 \\\"
3587
3587
3588 escaped single quotes and errors:
3588 escaped single quotes and errors:
3589
3589
3590 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3590 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
3591 foo
3591 foo
3592 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3592 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
3593 foo
3593 foo
3594 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3594 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
3595 hg: parse error at 21: unterminated string
3595 hg: parse error at 21: unterminated string
3596 ({if(rev, "{if(rev, \")}")}\n
3596 ({if(rev, "{if(rev, \")}")}\n
3597 ^ here)
3597 ^ here)
3598 [255]
3598 [255]
3599 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3599 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
3600 hg: parse error: trailing \ in string
3600 hg: parse error: trailing \ in string
3601 [255]
3601 [255]
3602 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3602 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
3603 hg: parse error: trailing \ in string
3603 hg: parse error: trailing \ in string
3604 [255]
3604 [255]
3605
3605
3606 $ cd ..
3606 $ cd ..
3607
3607
3608 Test leading backslashes:
3608 Test leading backslashes:
3609
3609
3610 $ cd latesttag
3610 $ cd latesttag
3611 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3611 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
3612 {rev} {file}
3612 {rev} {file}
3613 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3613 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
3614 \2 \head1
3614 \2 \head1
3615 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3615 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
3616 \{rev} \{file}
3616 \{rev} \{file}
3617 $ cd ..
3617 $ cd ..
3618
3618
3619 Test leading backslashes in "if" expression (issue4714):
3619 Test leading backslashes in "if" expression (issue4714):
3620
3620
3621 $ cd latesttag
3621 $ cd latesttag
3622 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3622 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
3623 {rev} \{rev}
3623 {rev} \{rev}
3624 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3624 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
3625 \2 \\{rev}
3625 \2 \\{rev}
3626 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3626 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
3627 \{rev} \\\{rev}
3627 \{rev} \\\{rev}
3628 $ cd ..
3628 $ cd ..
3629
3629
3630 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3630 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
3631
3631
3632 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3632 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
3633 \x6e
3633 \x6e
3634 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3634 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
3635 \x5c\x786e
3635 \x5c\x786e
3636 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3636 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
3637 \x6e
3637 \x6e
3638 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3638 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
3639 \x5c\x786e
3639 \x5c\x786e
3640
3640
3641 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3641 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
3642 \x6e
3642 \x6e
3643 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3643 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
3644 \x5c\x786e
3644 \x5c\x786e
3645 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3645 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
3646 \x6e
3646 \x6e
3647 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3647 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
3648 \x5c\x786e
3648 \x5c\x786e
3649
3649
3650 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3650 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
3651 fourth
3651 fourth
3652 second
3652 second
3653 third
3653 third
3654 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3654 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
3655 fourth\nsecond\nthird
3655 fourth\nsecond\nthird
3656
3656
3657 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3657 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
3658 <p>
3658 <p>
3659 1st
3659 1st
3660 </p>
3660 </p>
3661 <p>
3661 <p>
3662 2nd
3662 2nd
3663 </p>
3663 </p>
3664 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3664 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
3665 <p>
3665 <p>
3666 1st\n\n2nd
3666 1st\n\n2nd
3667 </p>
3667 </p>
3668 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3668 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
3669 1st
3669 1st
3670
3670
3671 2nd
3671 2nd
3672
3672
3673 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3673 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
3674 o perso
3674 o perso
3675 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3675 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
3676 no person
3676 no person
3677 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3677 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
3678 o perso
3678 o perso
3679 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3679 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
3680 no perso
3680 no perso
3681
3681
3682 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3682 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
3683 -o perso-
3683 -o perso-
3684 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3684 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
3685 no person
3685 no person
3686 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3686 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
3687 \x2do perso\x2d
3687 \x2do perso\x2d
3688 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3688 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
3689 -o perso-
3689 -o perso-
3690 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3690 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
3691 \x2do perso\x6e
3691 \x2do perso\x6e
3692
3692
3693 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3693 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
3694 fourth
3694 fourth
3695 second
3695 second
3696 third
3696 third
3697
3697
3698 Test string escaping in nested expression:
3698 Test string escaping in nested expression:
3699
3699
3700 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3700 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
3701 fourth\x6esecond\x6ethird
3701 fourth\x6esecond\x6ethird
3702 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3702 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
3703 fourth\x6esecond\x6ethird
3703 fourth\x6esecond\x6ethird
3704
3704
3705 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3705 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
3706 fourth\x6esecond\x6ethird
3706 fourth\x6esecond\x6ethird
3707 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3707 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
3708 fourth\x5c\x786esecond\x5c\x786ethird
3708 fourth\x5c\x786esecond\x5c\x786ethird
3709
3709
3710 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3710 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
3711 3:\x6eo user, \x6eo domai\x6e
3711 3:\x6eo user, \x6eo domai\x6e
3712 4:\x5c\x786eew bra\x5c\x786ech
3712 4:\x5c\x786eew bra\x5c\x786ech
3713
3713
3714 Test quotes in nested expression are evaluated just like a $(command)
3714 Test quotes in nested expression are evaluated just like a $(command)
3715 substitution in POSIX shells:
3715 substitution in POSIX shells:
3716
3716
3717 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3717 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
3718 8:95c24699272e
3718 8:95c24699272e
3719 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3719 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
3720 {8} "95c24699272e"
3720 {8} "95c24699272e"
3721
3721
3722 Test recursive evaluation:
3722 Test recursive evaluation:
3723
3723
3724 $ hg init r
3724 $ hg init r
3725 $ cd r
3725 $ cd r
3726 $ echo a > a
3726 $ echo a > a
3727 $ hg ci -Am '{rev}'
3727 $ hg ci -Am '{rev}'
3728 adding a
3728 adding a
3729 $ hg log -r 0 --template '{if(rev, desc)}\n'
3729 $ hg log -r 0 --template '{if(rev, desc)}\n'
3730 {rev}
3730 {rev}
3731 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3731 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
3732 test 0
3732 test 0
3733
3733
3734 $ hg branch -q 'text.{rev}'
3734 $ hg branch -q 'text.{rev}'
3735 $ echo aa >> aa
3735 $ echo aa >> aa
3736 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3736 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3737
3737
3738 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3738 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3739 {node|short}desc to
3739 {node|short}desc to
3740 text.{rev}be wrapped
3740 text.{rev}be wrapped
3741 text.{rev}desc to be
3741 text.{rev}desc to be
3742 text.{rev}wrapped (no-eol)
3742 text.{rev}wrapped (no-eol)
3743 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3743 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3744 bcc7ff960b8e:desc to
3744 bcc7ff960b8e:desc to
3745 text.1:be wrapped
3745 text.1:be wrapped
3746 text.1:desc to be
3746 text.1:desc to be
3747 text.1:wrapped (no-eol)
3747 text.1:wrapped (no-eol)
3748 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3748 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
3749 hg: parse error: fill expects an integer width
3749 hg: parse error: fill expects an integer width
3750 [255]
3750 [255]
3751
3751
3752 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3752 $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}'
3753 bcc7ff960b8e:desc to be
3753 bcc7ff960b8e:desc to be
3754 termwidth.1:wrapped desc
3754 termwidth.1:wrapped desc
3755 termwidth.1:to be wrapped (no-eol)
3755 termwidth.1:to be wrapped (no-eol)
3756
3756
3757 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3757 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3758 {node|short} (no-eol)
3758 {node|short} (no-eol)
3759 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3759 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3760 bcc-ff---b-e (no-eol)
3760 bcc-ff---b-e (no-eol)
3761
3761
3762 $ cat >> .hg/hgrc <<EOF
3762 $ cat >> .hg/hgrc <<EOF
3763 > [extensions]
3763 > [extensions]
3764 > color=
3764 > color=
3765 > [color]
3765 > [color]
3766 > mode=ansi
3766 > mode=ansi
3767 > text.{rev} = red
3767 > text.{rev} = red
3768 > text.1 = green
3768 > text.1 = green
3769 > EOF
3769 > EOF
3770 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3770 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3771 \x1b[0;31mtext\x1b[0m (esc)
3771 \x1b[0;31mtext\x1b[0m (esc)
3772 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3772 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3773 \x1b[0;32mtext\x1b[0m (esc)
3773 \x1b[0;32mtext\x1b[0m (esc)
3774
3774
3775 color effect can be specified without quoting:
3775 color effect can be specified without quoting:
3776
3776
3777 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3777 $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
3778 \x1b[0;31mtext\x1b[0m (esc)
3778 \x1b[0;31mtext\x1b[0m (esc)
3779
3779
3780 color effects can be nested (issue5413)
3780 color effects can be nested (issue5413)
3781
3781
3782 $ hg debugtemplate --color=always \
3782 $ hg debugtemplate --color=always \
3783 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3783 > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
3784 \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)
3784 \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)
3785
3785
3786 pad() should interact well with color codes (issue5416)
3786 pad() should interact well with color codes (issue5416)
3787
3787
3788 $ hg debugtemplate --color=always \
3788 $ hg debugtemplate --color=always \
3789 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3789 > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
3790 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3790 \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
3791
3791
3792 label should be no-op if color is disabled:
3792 label should be no-op if color is disabled:
3793
3793
3794 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3794 $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
3795 text
3795 text
3796 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3796 $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
3797 text
3797 text
3798
3798
3799 Test branches inside if statement:
3799 Test branches inside if statement:
3800
3800
3801 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3801 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3802 no
3802 no
3803
3803
3804 Test dict constructor:
3804 Test dict constructor:
3805
3805
3806 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3806 $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
3807 y=f7769ec2ab97 x=0
3807 y=f7769ec2ab97 x=0
3808 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3808 $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
3809 x=0
3809 x=0
3810 y=f7769ec2ab97
3810 y=f7769ec2ab97
3811 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3811 $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
3812 {"x": 0, "y": "f7769ec2ab97"}
3812 {"x": 0, "y": "f7769ec2ab97"}
3813 $ hg log -r 0 -T '{dict()|json}\n'
3813 $ hg log -r 0 -T '{dict()|json}\n'
3814 {}
3814 {}
3815
3815
3816 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3816 $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
3817 rev=0 node=f7769ec2ab97
3817 rev=0 node=f7769ec2ab97
3818 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3818 $ hg log -r 0 -T '{dict(rev, node|short)}\n'
3819 rev=0 node=f7769ec2ab97
3819 rev=0 node=f7769ec2ab97
3820
3820
3821 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3821 $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
3822 hg: parse error: duplicated dict key 'rev' inferred
3822 hg: parse error: duplicated dict key 'rev' inferred
3823 [255]
3823 [255]
3824 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3824 $ hg log -r 0 -T '{dict(node, node|short)}\n'
3825 hg: parse error: duplicated dict key 'node' inferred
3825 hg: parse error: duplicated dict key 'node' inferred
3826 [255]
3826 [255]
3827 $ hg log -r 0 -T '{dict(1 + 2)}'
3827 $ hg log -r 0 -T '{dict(1 + 2)}'
3828 hg: parse error: dict key cannot be inferred
3828 hg: parse error: dict key cannot be inferred
3829 [255]
3829 [255]
3830
3830
3831 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3831 $ hg log -r 0 -T '{dict(x=rev, x=node)}'
3832 hg: parse error: dict got multiple values for keyword argument 'x'
3832 hg: parse error: dict got multiple values for keyword argument 'x'
3833 [255]
3833 [255]
3834
3834
3835 Test get function:
3835 Test get function:
3836
3836
3837 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3837 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3838 default
3838 default
3839 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3839 $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
3840 default
3840 default
3841 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3841 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3842 hg: parse error: get() expects a dict as first argument
3842 hg: parse error: get() expects a dict as first argument
3843 [255]
3843 [255]
3844
3844
3845 Test json filter applied to hybrid object:
3845 Test json filter applied to hybrid object:
3846
3846
3847 $ hg log -r0 -T '{files|json}\n'
3847 $ hg log -r0 -T '{files|json}\n'
3848 ["a"]
3848 ["a"]
3849 $ hg log -r0 -T '{extras|json}\n'
3849 $ hg log -r0 -T '{extras|json}\n'
3850 {"branch": "default"}
3850 {"branch": "default"}
3851
3851
3852 Test json filter applied to map result:
3852 Test json filter applied to map result:
3853
3853
3854 $ hg log -r0 -T '{json(extras % "{key}")}\n'
3854 $ hg log -r0 -T '{json(extras % "{key}")}\n'
3855 ["branch"]
3855 ["branch"]
3856
3856
3857 Test localdate(date, tz) function:
3857 Test localdate(date, tz) function:
3858
3858
3859 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3859 $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
3860 1970-01-01 09:00 +0900
3860 1970-01-01 09:00 +0900
3861 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3861 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
3862 1970-01-01 00:00 +0000
3862 1970-01-01 00:00 +0000
3863 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3863 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
3864 hg: parse error: localdate expects a timezone
3864 hg: parse error: localdate expects a timezone
3865 [255]
3865 [255]
3866 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3866 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
3867 1970-01-01 02:00 +0200
3867 1970-01-01 02:00 +0200
3868 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3868 $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
3869 1970-01-01 00:00 +0000
3869 1970-01-01 00:00 +0000
3870 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3870 $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
3871 1970-01-01 00:00 +0000
3871 1970-01-01 00:00 +0000
3872 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3872 $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
3873 hg: parse error: localdate expects a timezone
3873 hg: parse error: localdate expects a timezone
3874 [255]
3874 [255]
3875 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3875 $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
3876 hg: parse error: localdate expects a timezone
3876 hg: parse error: localdate expects a timezone
3877 [255]
3877 [255]
3878
3878
3879 Test shortest(node) function:
3879 Test shortest(node) function:
3880
3880
3881 $ echo b > b
3881 $ echo b > b
3882 $ hg ci -qAm b
3882 $ hg ci -qAm b
3883 $ hg log --template '{shortest(node)}\n'
3883 $ hg log --template '{shortest(node)}\n'
3884 e777
3884 e777
3885 bcc7
3885 bcc7
3886 f776
3886 f776
3887 $ hg log --template '{shortest(node, 10)}\n'
3887 $ hg log --template '{shortest(node, 10)}\n'
3888 e777603221
3888 e777603221
3889 bcc7ff960b
3889 bcc7ff960b
3890 f7769ec2ab
3890 f7769ec2ab
3891 $ hg log --template '{node|shortest}\n' -l1
3891 $ hg log --template '{node|shortest}\n' -l1
3892 e777
3892 e777
3893
3893
3894 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3894 $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
3895 f7769ec2ab
3895 f7769ec2ab
3896 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3896 $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
3897 hg: parse error: shortest() expects an integer minlength
3897 hg: parse error: shortest() expects an integer minlength
3898 [255]
3898 [255]
3899
3899
3900 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3900 $ hg log -r 'wdir()' -T '{node|shortest}\n'
3901 ffff
3901 ffff
3902
3902
3903 $ hg log --template '{shortest("f")}\n' -l1
3904 f
3905
3906 $ hg log --template '{shortest("0123456789012345678901234567890123456789")}\n' -l1
3907 0123456789012345678901234567890123456789
3908
3909 $ hg log --template '{shortest("01234567890123456789012345678901234567890123456789")}\n' -l1
3910 01234567890123456789012345678901234567890123456789
3911
3912 $ hg log --template '{shortest("not a hex string")}\n' -l1
3913 not a hex string
3914
3915 $ hg log --template '{shortest("not a hex string, but it'\''s 40 bytes long")}\n' -l1
3916 not a hex string, but it's 40 bytes long
3917
3903 $ cd ..
3918 $ cd ..
3904
3919
3905 Test shortest(node) with the repo having short hash collision:
3920 Test shortest(node) with the repo having short hash collision:
3906
3921
3907 $ hg init hashcollision
3922 $ hg init hashcollision
3908 $ cd hashcollision
3923 $ cd hashcollision
3909 $ cat <<EOF >> .hg/hgrc
3924 $ cat <<EOF >> .hg/hgrc
3910 > [experimental]
3925 > [experimental]
3911 > evolution.createmarkers=True
3926 > evolution.createmarkers=True
3912 > EOF
3927 > EOF
3913 $ echo 0 > a
3928 $ echo 0 > a
3914 $ hg ci -qAm 0
3929 $ hg ci -qAm 0
3915 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3930 $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
3916 > hg up -q 0
3931 > hg up -q 0
3917 > echo $i > a
3932 > echo $i > a
3918 > hg ci -qm $i
3933 > hg ci -qm $i
3919 > done
3934 > done
3920 $ hg up -q null
3935 $ hg up -q null
3921 $ hg log -r0: -T '{rev}:{node}\n'
3936 $ hg log -r0: -T '{rev}:{node}\n'
3922 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3937 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
3923 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3938 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
3924 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3939 2:11407b3f1b9c3e76a79c1ec5373924df096f0499
3925 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3940 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
3926 4:10776689e627b465361ad5c296a20a487e153ca4
3941 4:10776689e627b465361ad5c296a20a487e153ca4
3927 5:a00be79088084cb3aff086ab799f8790e01a976b
3942 5:a00be79088084cb3aff086ab799f8790e01a976b
3928 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3943 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
3929 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3944 7:a0457b3450b8e1b778f1163b31a435802987fe5d
3930 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3945 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
3931 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3946 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
3932 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3947 10:c562ddd9c94164376c20b86b0b4991636a3bf84f
3933 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3948 $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
3934 obsoleted 1 changesets
3949 obsoleted 1 changesets
3935 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3950 $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
3936 obsoleted 1 changesets
3951 obsoleted 1 changesets
3937 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3952 $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
3938 obsoleted 1 changesets
3953 obsoleted 1 changesets
3939
3954
3940 nodes starting with '11' (we don't have the revision number '11' though)
3955 nodes starting with '11' (we don't have the revision number '11' though)
3941
3956
3942 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3957 $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
3943 1:1142
3958 1:1142
3944 2:1140
3959 2:1140
3945 3:11d
3960 3:11d
3946
3961
3947 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3962 '5:a00' is hidden, but still we have two nodes starting with 'a0'
3948
3963
3949 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3964 $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
3950 6:a0b
3965 6:a0b
3951 7:a04
3966 7:a04
3952
3967
3953 node '10' conflicts with the revision number '10' even if it is hidden
3968 node '10' conflicts with the revision number '10' even if it is hidden
3954 (we could exclude hidden revision numbers, but currently we don't)
3969 (we could exclude hidden revision numbers, but currently we don't)
3955
3970
3956 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3971 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
3957 4:107
3972 4:107
3958 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3973 $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
3959 4:107
3974 4:107
3960
3975
3961 node 'c562' should be unique if the other 'c562' nodes are hidden
3976 node 'c562' should be unique if the other 'c562' nodes are hidden
3962 (but we don't try the slow path to filter out hidden nodes for now)
3977 (but we don't try the slow path to filter out hidden nodes for now)
3963
3978
3964 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
3979 $ hg log -r 8 -T '{rev}:{node|shortest}\n'
3965 8:c5625
3980 8:c5625
3966 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
3981 $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
3967 8:c5625
3982 8:c5625
3968 9:c5623
3983 9:c5623
3969 10:c562d
3984 10:c562d
3970
3985
3971 $ cd ..
3986 $ cd ..
3972
3987
3973 Test pad function
3988 Test pad function
3974
3989
3975 $ cd r
3990 $ cd r
3976
3991
3977 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3992 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3978 2 test
3993 2 test
3979 1 {node|short}
3994 1 {node|short}
3980 0 test
3995 0 test
3981
3996
3982 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3997 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3983 2 test
3998 2 test
3984 1 {node|short}
3999 1 {node|short}
3985 0 test
4000 0 test
3986
4001
3987 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
4002 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3988 2------------------- test
4003 2------------------- test
3989 1------------------- {node|short}
4004 1------------------- {node|short}
3990 0------------------- test
4005 0------------------- test
3991
4006
3992 Test template string in pad function
4007 Test template string in pad function
3993
4008
3994 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
4009 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3995 {0} test
4010 {0} test
3996
4011
3997 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
4012 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3998 \{rev} test
4013 \{rev} test
3999
4014
4000 Test width argument passed to pad function
4015 Test width argument passed to pad function
4001
4016
4002 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
4017 $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
4003 0 test
4018 0 test
4004 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
4019 $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
4005 hg: parse error: pad() expects an integer width
4020 hg: parse error: pad() expects an integer width
4006 [255]
4021 [255]
4007
4022
4008 Test invalid fillchar passed to pad function
4023 Test invalid fillchar passed to pad function
4009
4024
4010 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
4025 $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
4011 hg: parse error: pad() expects a single fill character
4026 hg: parse error: pad() expects a single fill character
4012 [255]
4027 [255]
4013 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
4028 $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
4014 hg: parse error: pad() expects a single fill character
4029 hg: parse error: pad() expects a single fill character
4015 [255]
4030 [255]
4016
4031
4017 Test boolean argument passed to pad function
4032 Test boolean argument passed to pad function
4018
4033
4019 no crash
4034 no crash
4020
4035
4021 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
4036 $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
4022 ---------0
4037 ---------0
4023
4038
4024 string/literal
4039 string/literal
4025
4040
4026 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
4041 $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
4027 ---------0
4042 ---------0
4028 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
4043 $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
4029 0---------
4044 0---------
4030 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
4045 $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
4031 0---------
4046 0---------
4032
4047
4033 unknown keyword is evaluated to ''
4048 unknown keyword is evaluated to ''
4034
4049
4035 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
4050 $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
4036 0---------
4051 0---------
4037
4052
4038 Test separate function
4053 Test separate function
4039
4054
4040 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
4055 $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
4041 a-b-c
4056 a-b-c
4042 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
4057 $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
4043 0:f7769ec2ab97 test default
4058 0:f7769ec2ab97 test default
4044 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
4059 $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
4045 a \x1b[0;31mb\x1b[0m c d (esc)
4060 a \x1b[0;31mb\x1b[0m c d (esc)
4046
4061
4047 Test boolean expression/literal passed to if function
4062 Test boolean expression/literal passed to if function
4048
4063
4049 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
4064 $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
4050 rev 0 is True
4065 rev 0 is True
4051 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
4066 $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
4052 literal 0 is True as well
4067 literal 0 is True as well
4053 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
4068 $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
4054 empty string is False
4069 empty string is False
4055 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
4070 $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
4056 empty list is False
4071 empty list is False
4057 $ hg log -r 0 -T '{if(true, "true is True")}\n'
4072 $ hg log -r 0 -T '{if(true, "true is True")}\n'
4058 true is True
4073 true is True
4059 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
4074 $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
4060 false is False
4075 false is False
4061 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
4076 $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
4062 non-empty string is True
4077 non-empty string is True
4063
4078
4064 Test ifcontains function
4079 Test ifcontains function
4065
4080
4066 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
4081 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
4067 2 is in the string
4082 2 is in the string
4068 1 is not
4083 1 is not
4069 0 is in the string
4084 0 is in the string
4070
4085
4071 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
4086 $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
4072 2 is in the string
4087 2 is in the string
4073 1 is not
4088 1 is not
4074 0 is in the string
4089 0 is in the string
4075
4090
4076 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
4091 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
4077 2 did not add a
4092 2 did not add a
4078 1 did not add a
4093 1 did not add a
4079 0 added a
4094 0 added a
4080
4095
4081 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
4096 $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
4082 2 is parent of 1
4097 2 is parent of 1
4083 1
4098 1
4084 0
4099 0
4085
4100
4086 Test revset function
4101 Test revset function
4087
4102
4088 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
4103 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
4089 2 current rev
4104 2 current rev
4090 1 not current rev
4105 1 not current rev
4091 0 not current rev
4106 0 not current rev
4092
4107
4093 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
4108 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
4094 2 match rev
4109 2 match rev
4095 1 match rev
4110 1 match rev
4096 0 not match rev
4111 0 not match rev
4097
4112
4098 $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1
4113 $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1
4099 type not match
4114 type not match
4100
4115
4101 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
4116 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
4102 2 Parents: 1
4117 2 Parents: 1
4103 1 Parents: 0
4118 1 Parents: 0
4104 0 Parents:
4119 0 Parents:
4105
4120
4106 $ cat >> .hg/hgrc <<EOF
4121 $ cat >> .hg/hgrc <<EOF
4107 > [revsetalias]
4122 > [revsetalias]
4108 > myparents(\$1) = parents(\$1)
4123 > myparents(\$1) = parents(\$1)
4109 > EOF
4124 > EOF
4110 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
4125 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
4111 2 Parents: 1
4126 2 Parents: 1
4112 1 Parents: 0
4127 1 Parents: 0
4113 0 Parents:
4128 0 Parents:
4114
4129
4115 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
4130 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
4116 Rev: 2
4131 Rev: 2
4117 Ancestor: 0
4132 Ancestor: 0
4118 Ancestor: 1
4133 Ancestor: 1
4119 Ancestor: 2
4134 Ancestor: 2
4120
4135
4121 Rev: 1
4136 Rev: 1
4122 Ancestor: 0
4137 Ancestor: 0
4123 Ancestor: 1
4138 Ancestor: 1
4124
4139
4125 Rev: 0
4140 Rev: 0
4126 Ancestor: 0
4141 Ancestor: 0
4127
4142
4128 $ hg log --template '{revset("TIP"|lower)}\n' -l1
4143 $ hg log --template '{revset("TIP"|lower)}\n' -l1
4129 2
4144 2
4130
4145
4131 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
4146 $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
4132 2
4147 2
4133
4148
4134 a list template is evaluated for each item of revset/parents
4149 a list template is evaluated for each item of revset/parents
4135
4150
4136 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
4151 $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
4137 2 p: 1:bcc7ff960b8e
4152 2 p: 1:bcc7ff960b8e
4138 1 p: 0:f7769ec2ab97
4153 1 p: 0:f7769ec2ab97
4139 0 p:
4154 0 p:
4140
4155
4141 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
4156 $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
4142 2 p: 1:bcc7ff960b8e -1:000000000000
4157 2 p: 1:bcc7ff960b8e -1:000000000000
4143 1 p: 0:f7769ec2ab97 -1:000000000000
4158 1 p: 0:f7769ec2ab97 -1:000000000000
4144 0 p: -1:000000000000 -1:000000000000
4159 0 p: -1:000000000000 -1:000000000000
4145
4160
4146 therefore, 'revcache' should be recreated for each rev
4161 therefore, 'revcache' should be recreated for each rev
4147
4162
4148 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
4163 $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
4149 2 aa b
4164 2 aa b
4150 p
4165 p
4151 1
4166 1
4152 p a
4167 p a
4153 0 a
4168 0 a
4154 p
4169 p
4155
4170
4156 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
4171 $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
4157 2 aa b
4172 2 aa b
4158 p
4173 p
4159 1
4174 1
4160 p a
4175 p a
4161 0 a
4176 0 a
4162 p
4177 p
4163
4178
4164 a revset item must be evaluated as an integer revision, not an offset from tip
4179 a revset item must be evaluated as an integer revision, not an offset from tip
4165
4180
4166 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
4181 $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
4167 -1:000000000000
4182 -1:000000000000
4168 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
4183 $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
4169 -1:000000000000
4184 -1:000000000000
4170
4185
4171 join() should pick '{rev}' from revset items:
4186 join() should pick '{rev}' from revset items:
4172
4187
4173 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
4188 $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
4174 4, 5
4189 4, 5
4175
4190
4176 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
4191 on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
4177 default. join() should agree with the default formatting:
4192 default. join() should agree with the default formatting:
4178
4193
4179 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
4194 $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
4180 5:13207e5a10d9, 4:bbe44766e73d
4195 5:13207e5a10d9, 4:bbe44766e73d
4181
4196
4182 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
4197 $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
4183 5:13207e5a10d9fd28ec424934298e176197f2c67f,
4198 5:13207e5a10d9fd28ec424934298e176197f2c67f,
4184 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
4199 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
4185
4200
4186 Invalid arguments passed to revset()
4201 Invalid arguments passed to revset()
4187
4202
4188 $ hg log -T '{revset("%whatever", 0)}\n'
4203 $ hg log -T '{revset("%whatever", 0)}\n'
4189 hg: parse error: unexpected revspec format character w
4204 hg: parse error: unexpected revspec format character w
4190 [255]
4205 [255]
4191 $ hg log -T '{revset("%lwhatever", files)}\n'
4206 $ hg log -T '{revset("%lwhatever", files)}\n'
4192 hg: parse error: unexpected revspec format character w
4207 hg: parse error: unexpected revspec format character w
4193 [255]
4208 [255]
4194 $ hg log -T '{revset("%s %s", 0)}\n'
4209 $ hg log -T '{revset("%s %s", 0)}\n'
4195 hg: parse error: missing argument for revspec
4210 hg: parse error: missing argument for revspec
4196 [255]
4211 [255]
4197 $ hg log -T '{revset("", 0)}\n'
4212 $ hg log -T '{revset("", 0)}\n'
4198 hg: parse error: too many revspec arguments specified
4213 hg: parse error: too many revspec arguments specified
4199 [255]
4214 [255]
4200 $ hg log -T '{revset("%s", 0, 1)}\n'
4215 $ hg log -T '{revset("%s", 0, 1)}\n'
4201 hg: parse error: too many revspec arguments specified
4216 hg: parse error: too many revspec arguments specified
4202 [255]
4217 [255]
4203 $ hg log -T '{revset("%", 0)}\n'
4218 $ hg log -T '{revset("%", 0)}\n'
4204 hg: parse error: incomplete revspec format character
4219 hg: parse error: incomplete revspec format character
4205 [255]
4220 [255]
4206 $ hg log -T '{revset("%l", 0)}\n'
4221 $ hg log -T '{revset("%l", 0)}\n'
4207 hg: parse error: incomplete revspec format character
4222 hg: parse error: incomplete revspec format character
4208 [255]
4223 [255]
4209 $ hg log -T '{revset("%d", 'foo')}\n'
4224 $ hg log -T '{revset("%d", 'foo')}\n'
4210 hg: parse error: invalid argument for revspec
4225 hg: parse error: invalid argument for revspec
4211 [255]
4226 [255]
4212 $ hg log -T '{revset("%ld", files)}\n'
4227 $ hg log -T '{revset("%ld", files)}\n'
4213 hg: parse error: invalid argument for revspec
4228 hg: parse error: invalid argument for revspec
4214 [255]
4229 [255]
4215 $ hg log -T '{revset("%ls", 0)}\n'
4230 $ hg log -T '{revset("%ls", 0)}\n'
4216 hg: parse error: invalid argument for revspec
4231 hg: parse error: invalid argument for revspec
4217 [255]
4232 [255]
4218 $ hg log -T '{revset("%b", 'foo')}\n'
4233 $ hg log -T '{revset("%b", 'foo')}\n'
4219 hg: parse error: invalid argument for revspec
4234 hg: parse error: invalid argument for revspec
4220 [255]
4235 [255]
4221 $ hg log -T '{revset("%lb", files)}\n'
4236 $ hg log -T '{revset("%lb", files)}\n'
4222 hg: parse error: invalid argument for revspec
4237 hg: parse error: invalid argument for revspec
4223 [255]
4238 [255]
4224 $ hg log -T '{revset("%r", 0)}\n'
4239 $ hg log -T '{revset("%r", 0)}\n'
4225 hg: parse error: invalid argument for revspec
4240 hg: parse error: invalid argument for revspec
4226 [255]
4241 [255]
4227
4242
4228 Test 'originalnode'
4243 Test 'originalnode'
4229
4244
4230 $ hg log -r 1 -T '{revset("null") % "{node|short} {originalnode|short}"}\n'
4245 $ hg log -r 1 -T '{revset("null") % "{node|short} {originalnode|short}"}\n'
4231 000000000000 bcc7ff960b8e
4246 000000000000 bcc7ff960b8e
4232 $ hg log -r 0 -T '{manifest % "{node} {originalnode}"}\n'
4247 $ hg log -r 0 -T '{manifest % "{node} {originalnode}"}\n'
4233 a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 f7769ec2ab975ad19684098ad1ffd9b81ecc71a1
4248 a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 f7769ec2ab975ad19684098ad1ffd9b81ecc71a1
4234
4249
4235 Test files function
4250 Test files function
4236
4251
4237 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
4252 $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
4238 2
4253 2
4239 a
4254 a
4240 aa
4255 aa
4241 b
4256 b
4242 1
4257 1
4243 a
4258 a
4244 0
4259 0
4245 a
4260 a
4246
4261
4247 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
4262 $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
4248 2
4263 2
4249 aa
4264 aa
4250 1
4265 1
4251
4266
4252 0
4267 0
4253
4268
4254
4269
4255 Test relpath function
4270 Test relpath function
4256
4271
4257 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
4272 $ hg log -r0 -T '{files % "{file|relpath}\n"}'
4258 a
4273 a
4259 $ cd ..
4274 $ cd ..
4260 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
4275 $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
4261 r/a
4276 r/a
4262 $ cd r
4277 $ cd r
4263
4278
4264 Test active bookmark templating
4279 Test active bookmark templating
4265
4280
4266 $ hg book foo
4281 $ hg book foo
4267 $ hg book bar
4282 $ hg book bar
4268 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
4283 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
4269 2 bar* foo
4284 2 bar* foo
4270 1
4285 1
4271 0
4286 0
4272 $ hg log --template "{rev} {activebookmark}\n"
4287 $ hg log --template "{rev} {activebookmark}\n"
4273 2 bar
4288 2 bar
4274 1
4289 1
4275 0
4290 0
4276 $ hg bookmarks --inactive bar
4291 $ hg bookmarks --inactive bar
4277 $ hg log --template "{rev} {activebookmark}\n"
4292 $ hg log --template "{rev} {activebookmark}\n"
4278 2
4293 2
4279 1
4294 1
4280 0
4295 0
4281 $ hg book -r1 baz
4296 $ hg book -r1 baz
4282 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
4297 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
4283 2 bar foo
4298 2 bar foo
4284 1 baz
4299 1 baz
4285 0
4300 0
4286 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
4301 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
4287 2 t
4302 2 t
4288 1 f
4303 1 f
4289 0 f
4304 0 f
4290
4305
4291 Test namespaces dict
4306 Test namespaces dict
4292
4307
4293 $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n'
4308 $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n'
4294 2
4309 2
4295 bookmarks color=bookmark builtin=True
4310 bookmarks color=bookmark builtin=True
4296 bar,foo
4311 bar,foo
4297 tags color=tag builtin=True
4312 tags color=tag builtin=True
4298 tip
4313 tip
4299 branches color=branch builtin=True
4314 branches color=branch builtin=True
4300 text.{rev}
4315 text.{rev}
4301 revnames color=revname builtin=False
4316 revnames color=revname builtin=False
4302 r2
4317 r2
4303
4318
4304 1
4319 1
4305 bookmarks color=bookmark builtin=True
4320 bookmarks color=bookmark builtin=True
4306 baz
4321 baz
4307 tags color=tag builtin=True
4322 tags color=tag builtin=True
4308
4323
4309 branches color=branch builtin=True
4324 branches color=branch builtin=True
4310 text.{rev}
4325 text.{rev}
4311 revnames color=revname builtin=False
4326 revnames color=revname builtin=False
4312 r1
4327 r1
4313
4328
4314 0
4329 0
4315 bookmarks color=bookmark builtin=True
4330 bookmarks color=bookmark builtin=True
4316
4331
4317 tags color=tag builtin=True
4332 tags color=tag builtin=True
4318
4333
4319 branches color=branch builtin=True
4334 branches color=branch builtin=True
4320 default
4335 default
4321 revnames color=revname builtin=False
4336 revnames color=revname builtin=False
4322 r0
4337 r0
4323
4338
4324 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
4339 $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}'
4325 bookmarks: bar foo
4340 bookmarks: bar foo
4326 tags: tip
4341 tags: tip
4327 branches: text.{rev}
4342 branches: text.{rev}
4328 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
4343 $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}'
4329 bookmarks:
4344 bookmarks:
4330 bar
4345 bar
4331 foo
4346 foo
4332 tags:
4347 tags:
4333 tip
4348 tip
4334 branches:
4349 branches:
4335 text.{rev}
4350 text.{rev}
4336 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
4351 $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}'
4337 bar
4352 bar
4338 foo
4353 foo
4339 $ hg log -r2 -T '{namespaces.bookmarks % "{bookmark}\n"}'
4354 $ hg log -r2 -T '{namespaces.bookmarks % "{bookmark}\n"}'
4340 bar
4355 bar
4341 foo
4356 foo
4342
4357
4343 Test stringify on sub expressions
4358 Test stringify on sub expressions
4344
4359
4345 $ cd ..
4360 $ cd ..
4346 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
4361 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
4347 fourth, second, third
4362 fourth, second, third
4348 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
4363 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
4349 abc
4364 abc
4350
4365
4351 Test splitlines
4366 Test splitlines
4352
4367
4353 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
4368 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
4354 @ foo Modify, add, remove, rename
4369 @ foo Modify, add, remove, rename
4355 |
4370 |
4356 o foo future
4371 o foo future
4357 |
4372 |
4358 o foo third
4373 o foo third
4359 |
4374 |
4360 o foo second
4375 o foo second
4361
4376
4362 o foo merge
4377 o foo merge
4363 |\
4378 |\
4364 | o foo new head
4379 | o foo new head
4365 | |
4380 | |
4366 o | foo new branch
4381 o | foo new branch
4367 |/
4382 |/
4368 o foo no user, no domain
4383 o foo no user, no domain
4369 |
4384 |
4370 o foo no person
4385 o foo no person
4371 |
4386 |
4372 o foo other 1
4387 o foo other 1
4373 | foo other 2
4388 | foo other 2
4374 | foo
4389 | foo
4375 | foo other 3
4390 | foo other 3
4376 o foo line 1
4391 o foo line 1
4377 foo line 2
4392 foo line 2
4378
4393
4379 $ hg log -R a -r0 -T '{desc|splitlines}\n'
4394 $ hg log -R a -r0 -T '{desc|splitlines}\n'
4380 line 1 line 2
4395 line 1 line 2
4381 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
4396 $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
4382 line 1|line 2
4397 line 1|line 2
4383
4398
4384 Test startswith
4399 Test startswith
4385 $ hg log -Gv -R a --template "{startswith(desc)}"
4400 $ hg log -Gv -R a --template "{startswith(desc)}"
4386 hg: parse error: startswith expects two arguments
4401 hg: parse error: startswith expects two arguments
4387 [255]
4402 [255]
4388
4403
4389 $ hg log -Gv -R a --template "{startswith('line', desc)}"
4404 $ hg log -Gv -R a --template "{startswith('line', desc)}"
4390 @
4405 @
4391 |
4406 |
4392 o
4407 o
4393 |
4408 |
4394 o
4409 o
4395 |
4410 |
4396 o
4411 o
4397
4412
4398 o
4413 o
4399 |\
4414 |\
4400 | o
4415 | o
4401 | |
4416 | |
4402 o |
4417 o |
4403 |/
4418 |/
4404 o
4419 o
4405 |
4420 |
4406 o
4421 o
4407 |
4422 |
4408 o
4423 o
4409 |
4424 |
4410 o line 1
4425 o line 1
4411 line 2
4426 line 2
4412
4427
4413 Test bad template with better error message
4428 Test bad template with better error message
4414
4429
4415 $ hg log -Gv -R a --template '{desc|user()}'
4430 $ hg log -Gv -R a --template '{desc|user()}'
4416 hg: parse error: expected a symbol, got 'func'
4431 hg: parse error: expected a symbol, got 'func'
4417 [255]
4432 [255]
4418
4433
4419 Test word function (including index out of bounds graceful failure)
4434 Test word function (including index out of bounds graceful failure)
4420
4435
4421 $ hg log -Gv -R a --template "{word('1', desc)}"
4436 $ hg log -Gv -R a --template "{word('1', desc)}"
4422 @ add,
4437 @ add,
4423 |
4438 |
4424 o
4439 o
4425 |
4440 |
4426 o
4441 o
4427 |
4442 |
4428 o
4443 o
4429
4444
4430 o
4445 o
4431 |\
4446 |\
4432 | o head
4447 | o head
4433 | |
4448 | |
4434 o | branch
4449 o | branch
4435 |/
4450 |/
4436 o user,
4451 o user,
4437 |
4452 |
4438 o person
4453 o person
4439 |
4454 |
4440 o 1
4455 o 1
4441 |
4456 |
4442 o 1
4457 o 1
4443
4458
4444
4459
4445 Test word third parameter used as splitter
4460 Test word third parameter used as splitter
4446
4461
4447 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
4462 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
4448 @ M
4463 @ M
4449 |
4464 |
4450 o future
4465 o future
4451 |
4466 |
4452 o third
4467 o third
4453 |
4468 |
4454 o sec
4469 o sec
4455
4470
4456 o merge
4471 o merge
4457 |\
4472 |\
4458 | o new head
4473 | o new head
4459 | |
4474 | |
4460 o | new branch
4475 o | new branch
4461 |/
4476 |/
4462 o n
4477 o n
4463 |
4478 |
4464 o n
4479 o n
4465 |
4480 |
4466 o
4481 o
4467 |
4482 |
4468 o line 1
4483 o line 1
4469 line 2
4484 line 2
4470
4485
4471 Test word error messages for not enough and too many arguments
4486 Test word error messages for not enough and too many arguments
4472
4487
4473 $ hg log -Gv -R a --template "{word('0')}"
4488 $ hg log -Gv -R a --template "{word('0')}"
4474 hg: parse error: word expects two or three arguments, got 1
4489 hg: parse error: word expects two or three arguments, got 1
4475 [255]
4490 [255]
4476
4491
4477 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4492 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
4478 hg: parse error: word expects two or three arguments, got 7
4493 hg: parse error: word expects two or three arguments, got 7
4479 [255]
4494 [255]
4480
4495
4481 Test word for integer literal
4496 Test word for integer literal
4482
4497
4483 $ hg log -R a --template "{word(2, desc)}\n" -r0
4498 $ hg log -R a --template "{word(2, desc)}\n" -r0
4484 line
4499 line
4485
4500
4486 Test word for invalid numbers
4501 Test word for invalid numbers
4487
4502
4488 $ hg log -Gv -R a --template "{word('a', desc)}"
4503 $ hg log -Gv -R a --template "{word('a', desc)}"
4489 hg: parse error: word expects an integer index
4504 hg: parse error: word expects an integer index
4490 [255]
4505 [255]
4491
4506
4492 Test word for out of range
4507 Test word for out of range
4493
4508
4494 $ hg log -R a --template "{word(10000, desc)}"
4509 $ hg log -R a --template "{word(10000, desc)}"
4495 $ hg log -R a --template "{word(-10000, desc)}"
4510 $ hg log -R a --template "{word(-10000, desc)}"
4496
4511
4497 Test indent and not adding to empty lines
4512 Test indent and not adding to empty lines
4498
4513
4499 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4514 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
4500 -----
4515 -----
4501 > line 1
4516 > line 1
4502 >> line 2
4517 >> line 2
4503 -----
4518 -----
4504 > other 1
4519 > other 1
4505 >> other 2
4520 >> other 2
4506
4521
4507 >> other 3
4522 >> other 3
4508
4523
4509 Test with non-strings like dates
4524 Test with non-strings like dates
4510
4525
4511 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4526 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
4512 1200000.00
4527 1200000.00
4513 1300000.00
4528 1300000.00
4514
4529
4515 Test broken string escapes:
4530 Test broken string escapes:
4516
4531
4517 $ hg log -T "bogus\\" -R a
4532 $ hg log -T "bogus\\" -R a
4518 hg: parse error: trailing \ in string
4533 hg: parse error: trailing \ in string
4519 [255]
4534 [255]
4520 $ hg log -T "\\xy" -R a
4535 $ hg log -T "\\xy" -R a
4521 hg: parse error: invalid \x escape* (glob)
4536 hg: parse error: invalid \x escape* (glob)
4522 [255]
4537 [255]
4523
4538
4524 json filter should escape HTML tags so that the output can be embedded in hgweb:
4539 json filter should escape HTML tags so that the output can be embedded in hgweb:
4525
4540
4526 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4541 $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
4527 "\u003cfoo@example.org\u003e"
4542 "\u003cfoo@example.org\u003e"
4528
4543
4529 Templater supports aliases of symbol and func() styles:
4544 Templater supports aliases of symbol and func() styles:
4530
4545
4531 $ hg clone -q a aliases
4546 $ hg clone -q a aliases
4532 $ cd aliases
4547 $ cd aliases
4533 $ cat <<EOF >> .hg/hgrc
4548 $ cat <<EOF >> .hg/hgrc
4534 > [templatealias]
4549 > [templatealias]
4535 > r = rev
4550 > r = rev
4536 > rn = "{r}:{node|short}"
4551 > rn = "{r}:{node|short}"
4537 > status(c, files) = files % "{c} {file}\n"
4552 > status(c, files) = files % "{c} {file}\n"
4538 > utcdate(d) = localdate(d, "UTC")
4553 > utcdate(d) = localdate(d, "UTC")
4539 > EOF
4554 > EOF
4540
4555
4541 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4556 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
4542 (template
4557 (template
4543 (symbol 'rn')
4558 (symbol 'rn')
4544 (string ' ')
4559 (string ' ')
4545 (|
4560 (|
4546 (func
4561 (func
4547 (symbol 'utcdate')
4562 (symbol 'utcdate')
4548 (symbol 'date'))
4563 (symbol 'date'))
4549 (symbol 'isodate'))
4564 (symbol 'isodate'))
4550 (string '\n'))
4565 (string '\n'))
4551 * expanded:
4566 * expanded:
4552 (template
4567 (template
4553 (template
4568 (template
4554 (symbol 'rev')
4569 (symbol 'rev')
4555 (string ':')
4570 (string ':')
4556 (|
4571 (|
4557 (symbol 'node')
4572 (symbol 'node')
4558 (symbol 'short')))
4573 (symbol 'short')))
4559 (string ' ')
4574 (string ' ')
4560 (|
4575 (|
4561 (func
4576 (func
4562 (symbol 'localdate')
4577 (symbol 'localdate')
4563 (list
4578 (list
4564 (symbol 'date')
4579 (symbol 'date')
4565 (string 'UTC')))
4580 (string 'UTC')))
4566 (symbol 'isodate'))
4581 (symbol 'isodate'))
4567 (string '\n'))
4582 (string '\n'))
4568 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4583 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4569
4584
4570 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4585 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
4571 (template
4586 (template
4572 (func
4587 (func
4573 (symbol 'status')
4588 (symbol 'status')
4574 (list
4589 (list
4575 (string 'A')
4590 (string 'A')
4576 (symbol 'file_adds'))))
4591 (symbol 'file_adds'))))
4577 * expanded:
4592 * expanded:
4578 (template
4593 (template
4579 (%
4594 (%
4580 (symbol 'file_adds')
4595 (symbol 'file_adds')
4581 (template
4596 (template
4582 (string 'A')
4597 (string 'A')
4583 (string ' ')
4598 (string ' ')
4584 (symbol 'file')
4599 (symbol 'file')
4585 (string '\n'))))
4600 (string '\n'))))
4586 A a
4601 A a
4587
4602
4588 A unary function alias can be called as a filter:
4603 A unary function alias can be called as a filter:
4589
4604
4590 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4605 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
4591 (template
4606 (template
4592 (|
4607 (|
4593 (|
4608 (|
4594 (symbol 'date')
4609 (symbol 'date')
4595 (symbol 'utcdate'))
4610 (symbol 'utcdate'))
4596 (symbol 'isodate'))
4611 (symbol 'isodate'))
4597 (string '\n'))
4612 (string '\n'))
4598 * expanded:
4613 * expanded:
4599 (template
4614 (template
4600 (|
4615 (|
4601 (func
4616 (func
4602 (symbol 'localdate')
4617 (symbol 'localdate')
4603 (list
4618 (list
4604 (symbol 'date')
4619 (symbol 'date')
4605 (string 'UTC')))
4620 (string 'UTC')))
4606 (symbol 'isodate'))
4621 (symbol 'isodate'))
4607 (string '\n'))
4622 (string '\n'))
4608 1970-01-12 13:46 +0000
4623 1970-01-12 13:46 +0000
4609
4624
4610 Aliases should be applied only to command arguments and templates in hgrc.
4625 Aliases should be applied only to command arguments and templates in hgrc.
4611 Otherwise, our stock styles and web templates could be corrupted:
4626 Otherwise, our stock styles and web templates could be corrupted:
4612
4627
4613 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4628 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
4614 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4629 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4615
4630
4616 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4631 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
4617 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4632 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
4618
4633
4619 $ cat <<EOF > tmpl
4634 $ cat <<EOF > tmpl
4620 > changeset = 'nothing expanded:{rn}\n'
4635 > changeset = 'nothing expanded:{rn}\n'
4621 > EOF
4636 > EOF
4622 $ hg log -r0 --style ./tmpl
4637 $ hg log -r0 --style ./tmpl
4623 nothing expanded:
4638 nothing expanded:
4624
4639
4625 Aliases in formatter:
4640 Aliases in formatter:
4626
4641
4627 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4642 $ hg branches -T '{pad(branch, 7)} {rn}\n'
4628 default 6:d41e714fe50d
4643 default 6:d41e714fe50d
4629 foo 4:bbe44766e73d
4644 foo 4:bbe44766e73d
4630
4645
4631 Aliases should honor HGPLAIN:
4646 Aliases should honor HGPLAIN:
4632
4647
4633 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4648 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
4634 nothing expanded:
4649 nothing expanded:
4635 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4650 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
4636 0:1e4e1b8f71e0
4651 0:1e4e1b8f71e0
4637
4652
4638 Unparsable alias:
4653 Unparsable alias:
4639
4654
4640 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4655 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
4641 (template
4656 (template
4642 (symbol 'bad'))
4657 (symbol 'bad'))
4643 abort: bad definition of template alias "bad": at 2: not a prefix: end
4658 abort: bad definition of template alias "bad": at 2: not a prefix: end
4644 [255]
4659 [255]
4645 $ hg log --config templatealias.bad='x(' -T '{bad}'
4660 $ hg log --config templatealias.bad='x(' -T '{bad}'
4646 abort: bad definition of template alias "bad": at 2: not a prefix: end
4661 abort: bad definition of template alias "bad": at 2: not a prefix: end
4647 [255]
4662 [255]
4648
4663
4649 $ cd ..
4664 $ cd ..
4650
4665
4651 Set up repository for non-ascii encoding tests:
4666 Set up repository for non-ascii encoding tests:
4652
4667
4653 $ hg init nonascii
4668 $ hg init nonascii
4654 $ cd nonascii
4669 $ cd nonascii
4655 $ $PYTHON <<EOF
4670 $ $PYTHON <<EOF
4656 > open('latin1', 'wb').write(b'\xe9')
4671 > open('latin1', 'wb').write(b'\xe9')
4657 > open('utf-8', 'wb').write(b'\xc3\xa9')
4672 > open('utf-8', 'wb').write(b'\xc3\xa9')
4658 > EOF
4673 > EOF
4659 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4674 $ HGENCODING=utf-8 hg branch -q `cat utf-8`
4660 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4675 $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
4661
4676
4662 json filter should try round-trip conversion to utf-8:
4677 json filter should try round-trip conversion to utf-8:
4663
4678
4664 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4679 $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
4665 "\u00e9"
4680 "\u00e9"
4666 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4681 $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
4667 "non-ascii branch: \u00e9"
4682 "non-ascii branch: \u00e9"
4668
4683
4669 json filter takes input as utf-8b:
4684 json filter takes input as utf-8b:
4670
4685
4671 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4686 $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
4672 "\u00e9"
4687 "\u00e9"
4673 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4688 $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
4674 "\udce9"
4689 "\udce9"
4675
4690
4676 utf8 filter:
4691 utf8 filter:
4677
4692
4678 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4693 $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
4679 round-trip: c3a9
4694 round-trip: c3a9
4680 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4695 $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
4681 decoded: c3a9
4696 decoded: c3a9
4682 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4697 $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
4683 abort: decoding near * (glob)
4698 abort: decoding near * (glob)
4684 [255]
4699 [255]
4685 $ hg log -T "coerced to string: {rev|utf8}\n" -r0
4700 $ hg log -T "coerced to string: {rev|utf8}\n" -r0
4686 coerced to string: 0
4701 coerced to string: 0
4687
4702
4688 pad width:
4703 pad width:
4689
4704
4690 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4705 $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
4691 \xc3\xa9- (esc)
4706 \xc3\xa9- (esc)
4692
4707
4693 $ cd ..
4708 $ cd ..
4694
4709
4695 Test that template function in extension is registered as expected
4710 Test that template function in extension is registered as expected
4696
4711
4697 $ cd a
4712 $ cd a
4698
4713
4699 $ cat <<EOF > $TESTTMP/customfunc.py
4714 $ cat <<EOF > $TESTTMP/customfunc.py
4700 > from mercurial import registrar
4715 > from mercurial import registrar
4701 >
4716 >
4702 > templatefunc = registrar.templatefunc()
4717 > templatefunc = registrar.templatefunc()
4703 >
4718 >
4704 > @templatefunc(b'custom()')
4719 > @templatefunc(b'custom()')
4705 > def custom(context, mapping, args):
4720 > def custom(context, mapping, args):
4706 > return b'custom'
4721 > return b'custom'
4707 > EOF
4722 > EOF
4708 $ cat <<EOF > .hg/hgrc
4723 $ cat <<EOF > .hg/hgrc
4709 > [extensions]
4724 > [extensions]
4710 > customfunc = $TESTTMP/customfunc.py
4725 > customfunc = $TESTTMP/customfunc.py
4711 > EOF
4726 > EOF
4712
4727
4713 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4728 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
4714 custom
4729 custom
4715
4730
4716 $ cd ..
4731 $ cd ..
4717
4732
4718 Test 'graphwidth' in 'hg log' on various topologies. The key here is that the
4733 Test 'graphwidth' in 'hg log' on various topologies. The key here is that the
4719 printed graphwidths 3, 5, 7, etc. should all line up in their respective
4734 printed graphwidths 3, 5, 7, etc. should all line up in their respective
4720 columns. We don't care about other aspects of the graph rendering here.
4735 columns. We don't care about other aspects of the graph rendering here.
4721
4736
4722 $ hg init graphwidth
4737 $ hg init graphwidth
4723 $ cd graphwidth
4738 $ cd graphwidth
4724
4739
4725 $ wrappabletext="a a a a a a a a a a a a"
4740 $ wrappabletext="a a a a a a a a a a a a"
4726
4741
4727 $ printf "first\n" > file
4742 $ printf "first\n" > file
4728 $ hg add file
4743 $ hg add file
4729 $ hg commit -m "$wrappabletext"
4744 $ hg commit -m "$wrappabletext"
4730
4745
4731 $ printf "first\nsecond\n" > file
4746 $ printf "first\nsecond\n" > file
4732 $ hg commit -m "$wrappabletext"
4747 $ hg commit -m "$wrappabletext"
4733
4748
4734 $ hg checkout 0
4749 $ hg checkout 0
4735 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4750 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4736 $ printf "third\nfirst\n" > file
4751 $ printf "third\nfirst\n" > file
4737 $ hg commit -m "$wrappabletext"
4752 $ hg commit -m "$wrappabletext"
4738 created new head
4753 created new head
4739
4754
4740 $ hg merge
4755 $ hg merge
4741 merging file
4756 merging file
4742 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
4757 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
4743 (branch merge, don't forget to commit)
4758 (branch merge, don't forget to commit)
4744
4759
4745 $ hg log --graph -T "{graphwidth}"
4760 $ hg log --graph -T "{graphwidth}"
4746 @ 3
4761 @ 3
4747 |
4762 |
4748 | @ 5
4763 | @ 5
4749 |/
4764 |/
4750 o 3
4765 o 3
4751
4766
4752 $ hg commit -m "$wrappabletext"
4767 $ hg commit -m "$wrappabletext"
4753
4768
4754 $ hg log --graph -T "{graphwidth}"
4769 $ hg log --graph -T "{graphwidth}"
4755 @ 5
4770 @ 5
4756 |\
4771 |\
4757 | o 5
4772 | o 5
4758 | |
4773 | |
4759 o | 5
4774 o | 5
4760 |/
4775 |/
4761 o 3
4776 o 3
4762
4777
4763
4778
4764 $ hg checkout 0
4779 $ hg checkout 0
4765 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4780 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4766 $ printf "third\nfirst\nsecond\n" > file
4781 $ printf "third\nfirst\nsecond\n" > file
4767 $ hg commit -m "$wrappabletext"
4782 $ hg commit -m "$wrappabletext"
4768 created new head
4783 created new head
4769
4784
4770 $ hg log --graph -T "{graphwidth}"
4785 $ hg log --graph -T "{graphwidth}"
4771 @ 3
4786 @ 3
4772 |
4787 |
4773 | o 7
4788 | o 7
4774 | |\
4789 | |\
4775 +---o 7
4790 +---o 7
4776 | |
4791 | |
4777 | o 5
4792 | o 5
4778 |/
4793 |/
4779 o 3
4794 o 3
4780
4795
4781
4796
4782 $ hg log --graph -T "{graphwidth}" -r 3
4797 $ hg log --graph -T "{graphwidth}" -r 3
4783 o 5
4798 o 5
4784 |\
4799 |\
4785 ~ ~
4800 ~ ~
4786
4801
4787 $ hg log --graph -T "{graphwidth}" -r 1
4802 $ hg log --graph -T "{graphwidth}" -r 1
4788 o 3
4803 o 3
4789 |
4804 |
4790 ~
4805 ~
4791
4806
4792 $ hg merge
4807 $ hg merge
4793 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4808 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4794 (branch merge, don't forget to commit)
4809 (branch merge, don't forget to commit)
4795 $ hg commit -m "$wrappabletext"
4810 $ hg commit -m "$wrappabletext"
4796
4811
4797 $ printf "seventh\n" >> file
4812 $ printf "seventh\n" >> file
4798 $ hg commit -m "$wrappabletext"
4813 $ hg commit -m "$wrappabletext"
4799
4814
4800 $ hg log --graph -T "{graphwidth}"
4815 $ hg log --graph -T "{graphwidth}"
4801 @ 3
4816 @ 3
4802 |
4817 |
4803 o 5
4818 o 5
4804 |\
4819 |\
4805 | o 5
4820 | o 5
4806 | |
4821 | |
4807 o | 7
4822 o | 7
4808 |\ \
4823 |\ \
4809 | o | 7
4824 | o | 7
4810 | |/
4825 | |/
4811 o / 5
4826 o / 5
4812 |/
4827 |/
4813 o 3
4828 o 3
4814
4829
4815
4830
4816 The point of graphwidth is to allow wrapping that accounts for the space taken
4831 The point of graphwidth is to allow wrapping that accounts for the space taken
4817 by the graph.
4832 by the graph.
4818
4833
4819 $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}"
4834 $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}"
4820 @ a a a a
4835 @ a a a a
4821 | a a a a
4836 | a a a a
4822 | a a a a
4837 | a a a a
4823 o a a a
4838 o a a a
4824 |\ a a a
4839 |\ a a a
4825 | | a a a
4840 | | a a a
4826 | | a a a
4841 | | a a a
4827 | o a a a
4842 | o a a a
4828 | | a a a
4843 | | a a a
4829 | | a a a
4844 | | a a a
4830 | | a a a
4845 | | a a a
4831 o | a a
4846 o | a a
4832 |\ \ a a
4847 |\ \ a a
4833 | | | a a
4848 | | | a a
4834 | | | a a
4849 | | | a a
4835 | | | a a
4850 | | | a a
4836 | | | a a
4851 | | | a a
4837 | o | a a
4852 | o | a a
4838 | |/ a a
4853 | |/ a a
4839 | | a a
4854 | | a a
4840 | | a a
4855 | | a a
4841 | | a a
4856 | | a a
4842 | | a a
4857 | | a a
4843 o | a a a
4858 o | a a a
4844 |/ a a a
4859 |/ a a a
4845 | a a a
4860 | a a a
4846 | a a a
4861 | a a a
4847 o a a a a
4862 o a a a a
4848 a a a a
4863 a a a a
4849 a a a a
4864 a a a a
4850
4865
4851 Something tricky happens when there are elided nodes; the next drawn row of
4866 Something tricky happens when there are elided nodes; the next drawn row of
4852 edges can be more than one column wider, but the graph width only increases by
4867 edges can be more than one column wider, but the graph width only increases by
4853 one column. The remaining columns are added in between the nodes.
4868 one column. The remaining columns are added in between the nodes.
4854
4869
4855 $ hg log --graph -T "{graphwidth}" -r "0|2|4|5"
4870 $ hg log --graph -T "{graphwidth}" -r "0|2|4|5"
4856 o 5
4871 o 5
4857 |\
4872 |\
4858 | \
4873 | \
4859 | :\
4874 | :\
4860 o : : 7
4875 o : : 7
4861 :/ /
4876 :/ /
4862 : o 5
4877 : o 5
4863 :/
4878 :/
4864 o 3
4879 o 3
4865
4880
4866
4881
4867 $ cd ..
4882 $ cd ..
4868
4883
General Comments 0
You need to be logged in to leave comments. Login now