##// END OF EJS Templates
formatter: add support for parts map of [templates] section...
Yuya Nishihara -
r32952:61b60b28 default
parent child Browse files
Show More
@@ -1,518 +1,523 b''
1 # formatter.py - generic output formatting for mercurial
1 # formatter.py - generic output formatting for mercurial
2 #
2 #
3 # Copyright 2012 Matt Mackall <mpm@selenic.com>
3 # Copyright 2012 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """Generic output formatting for Mercurial
8 """Generic output formatting for Mercurial
9
9
10 The formatter provides API to show data in various ways. The following
10 The formatter provides API to show data in various ways. The following
11 functions should be used in place of ui.write():
11 functions should be used in place of ui.write():
12
12
13 - fm.write() for unconditional output
13 - fm.write() for unconditional output
14 - fm.condwrite() to show some extra data conditionally in plain output
14 - fm.condwrite() to show some extra data conditionally in plain output
15 - fm.context() to provide changectx to template output
15 - fm.context() to provide changectx to template output
16 - fm.data() to provide extra data to JSON or template output
16 - fm.data() to provide extra data to JSON or template output
17 - fm.plain() to show raw text that isn't provided to JSON or template output
17 - fm.plain() to show raw text that isn't provided to JSON or template output
18
18
19 To show structured data (e.g. date tuples, dicts, lists), apply fm.format*()
19 To show structured data (e.g. date tuples, dicts, lists), apply fm.format*()
20 beforehand so the data is converted to the appropriate data type. Use
20 beforehand so the data is converted to the appropriate data type. Use
21 fm.isplain() if you need to convert or format data conditionally which isn't
21 fm.isplain() if you need to convert or format data conditionally which isn't
22 supported by the formatter API.
22 supported by the formatter API.
23
23
24 To build nested structure (i.e. a list of dicts), use fm.nested().
24 To build nested structure (i.e. a list of dicts), use fm.nested().
25
25
26 See also https://www.mercurial-scm.org/wiki/GenericTemplatingPlan
26 See also https://www.mercurial-scm.org/wiki/GenericTemplatingPlan
27
27
28 fm.condwrite() vs 'if cond:':
28 fm.condwrite() vs 'if cond:':
29
29
30 In most cases, use fm.condwrite() so users can selectively show the data
30 In most cases, use fm.condwrite() so users can selectively show the data
31 in template output. If it's costly to build data, use plain 'if cond:' with
31 in template output. If it's costly to build data, use plain 'if cond:' with
32 fm.write().
32 fm.write().
33
33
34 fm.nested() vs fm.formatdict() (or fm.formatlist()):
34 fm.nested() vs fm.formatdict() (or fm.formatlist()):
35
35
36 fm.nested() should be used to form a tree structure (a list of dicts of
36 fm.nested() should be used to form a tree structure (a list of dicts of
37 lists of dicts...) which can be accessed through template keywords, e.g.
37 lists of dicts...) which can be accessed through template keywords, e.g.
38 "{foo % "{bar % {...}} {baz % {...}}"}". On the other hand, fm.formatdict()
38 "{foo % "{bar % {...}} {baz % {...}}"}". On the other hand, fm.formatdict()
39 exports a dict-type object to template, which can be accessed by e.g.
39 exports a dict-type object to template, which can be accessed by e.g.
40 "{get(foo, key)}" function.
40 "{get(foo, key)}" function.
41
41
42 Doctest helper:
42 Doctest helper:
43
43
44 >>> def show(fn, verbose=False, **opts):
44 >>> def show(fn, verbose=False, **opts):
45 ... import sys
45 ... import sys
46 ... from . import ui as uimod
46 ... from . import ui as uimod
47 ... ui = uimod.ui()
47 ... ui = uimod.ui()
48 ... ui.fout = sys.stdout # redirect to doctest
48 ... ui.fout = sys.stdout # redirect to doctest
49 ... ui.verbose = verbose
49 ... ui.verbose = verbose
50 ... return fn(ui, ui.formatter(fn.__name__, opts))
50 ... return fn(ui, ui.formatter(fn.__name__, opts))
51
51
52 Basic example:
52 Basic example:
53
53
54 >>> def files(ui, fm):
54 >>> def files(ui, fm):
55 ... files = [('foo', 123, (0, 0)), ('bar', 456, (1, 0))]
55 ... files = [('foo', 123, (0, 0)), ('bar', 456, (1, 0))]
56 ... for f in files:
56 ... for f in files:
57 ... fm.startitem()
57 ... fm.startitem()
58 ... fm.write('path', '%s', f[0])
58 ... fm.write('path', '%s', f[0])
59 ... fm.condwrite(ui.verbose, 'date', ' %s',
59 ... fm.condwrite(ui.verbose, 'date', ' %s',
60 ... fm.formatdate(f[2], '%Y-%m-%d %H:%M:%S'))
60 ... fm.formatdate(f[2], '%Y-%m-%d %H:%M:%S'))
61 ... fm.data(size=f[1])
61 ... fm.data(size=f[1])
62 ... fm.plain('\\n')
62 ... fm.plain('\\n')
63 ... fm.end()
63 ... fm.end()
64 >>> show(files)
64 >>> show(files)
65 foo
65 foo
66 bar
66 bar
67 >>> show(files, verbose=True)
67 >>> show(files, verbose=True)
68 foo 1970-01-01 00:00:00
68 foo 1970-01-01 00:00:00
69 bar 1970-01-01 00:00:01
69 bar 1970-01-01 00:00:01
70 >>> show(files, template='json')
70 >>> show(files, template='json')
71 [
71 [
72 {
72 {
73 "date": [0, 0],
73 "date": [0, 0],
74 "path": "foo",
74 "path": "foo",
75 "size": 123
75 "size": 123
76 },
76 },
77 {
77 {
78 "date": [1, 0],
78 "date": [1, 0],
79 "path": "bar",
79 "path": "bar",
80 "size": 456
80 "size": 456
81 }
81 }
82 ]
82 ]
83 >>> show(files, template='path: {path}\\ndate: {date|rfc3339date}\\n')
83 >>> show(files, template='path: {path}\\ndate: {date|rfc3339date}\\n')
84 path: foo
84 path: foo
85 date: 1970-01-01T00:00:00+00:00
85 date: 1970-01-01T00:00:00+00:00
86 path: bar
86 path: bar
87 date: 1970-01-01T00:00:01+00:00
87 date: 1970-01-01T00:00:01+00:00
88
88
89 Nested example:
89 Nested example:
90
90
91 >>> def subrepos(ui, fm):
91 >>> def subrepos(ui, fm):
92 ... fm.startitem()
92 ... fm.startitem()
93 ... fm.write('repo', '[%s]\\n', 'baz')
93 ... fm.write('repo', '[%s]\\n', 'baz')
94 ... files(ui, fm.nested('files'))
94 ... files(ui, fm.nested('files'))
95 ... fm.end()
95 ... fm.end()
96 >>> show(subrepos)
96 >>> show(subrepos)
97 [baz]
97 [baz]
98 foo
98 foo
99 bar
99 bar
100 >>> show(subrepos, template='{repo}: {join(files % "{path}", ", ")}\\n')
100 >>> show(subrepos, template='{repo}: {join(files % "{path}", ", ")}\\n')
101 baz: foo, bar
101 baz: foo, bar
102 """
102 """
103
103
104 from __future__ import absolute_import
104 from __future__ import absolute_import
105
105
106 import collections
106 import collections
107 import contextlib
107 import contextlib
108 import itertools
108 import itertools
109 import os
109 import os
110
110
111 from .i18n import _
111 from .i18n import _
112 from .node import (
112 from .node import (
113 hex,
113 hex,
114 short,
114 short,
115 )
115 )
116
116
117 from . import (
117 from . import (
118 error,
118 error,
119 pycompat,
119 pycompat,
120 templatefilters,
120 templatefilters,
121 templatekw,
121 templatekw,
122 templater,
122 templater,
123 util,
123 util,
124 )
124 )
125
125
126 pickle = util.pickle
126 pickle = util.pickle
127
127
128 class _nullconverter(object):
128 class _nullconverter(object):
129 '''convert non-primitive data types to be processed by formatter'''
129 '''convert non-primitive data types to be processed by formatter'''
130 @staticmethod
130 @staticmethod
131 def formatdate(date, fmt):
131 def formatdate(date, fmt):
132 '''convert date tuple to appropriate format'''
132 '''convert date tuple to appropriate format'''
133 return date
133 return date
134 @staticmethod
134 @staticmethod
135 def formatdict(data, key, value, fmt, sep):
135 def formatdict(data, key, value, fmt, sep):
136 '''convert dict or key-value pairs to appropriate dict format'''
136 '''convert dict or key-value pairs to appropriate dict format'''
137 # use plain dict instead of util.sortdict so that data can be
137 # use plain dict instead of util.sortdict so that data can be
138 # serialized as a builtin dict in pickle output
138 # serialized as a builtin dict in pickle output
139 return dict(data)
139 return dict(data)
140 @staticmethod
140 @staticmethod
141 def formatlist(data, name, fmt, sep):
141 def formatlist(data, name, fmt, sep):
142 '''convert iterable to appropriate list format'''
142 '''convert iterable to appropriate list format'''
143 return list(data)
143 return list(data)
144
144
145 class baseformatter(object):
145 class baseformatter(object):
146 def __init__(self, ui, topic, opts, converter):
146 def __init__(self, ui, topic, opts, converter):
147 self._ui = ui
147 self._ui = ui
148 self._topic = topic
148 self._topic = topic
149 self._style = opts.get("style")
149 self._style = opts.get("style")
150 self._template = opts.get("template")
150 self._template = opts.get("template")
151 self._converter = converter
151 self._converter = converter
152 self._item = None
152 self._item = None
153 # function to convert node to string suitable for this output
153 # function to convert node to string suitable for this output
154 self.hexfunc = hex
154 self.hexfunc = hex
155 def __enter__(self):
155 def __enter__(self):
156 return self
156 return self
157 def __exit__(self, exctype, excvalue, traceback):
157 def __exit__(self, exctype, excvalue, traceback):
158 if exctype is None:
158 if exctype is None:
159 self.end()
159 self.end()
160 def _showitem(self):
160 def _showitem(self):
161 '''show a formatted item once all data is collected'''
161 '''show a formatted item once all data is collected'''
162 pass
162 pass
163 def startitem(self):
163 def startitem(self):
164 '''begin an item in the format list'''
164 '''begin an item in the format list'''
165 if self._item is not None:
165 if self._item is not None:
166 self._showitem()
166 self._showitem()
167 self._item = {}
167 self._item = {}
168 def formatdate(self, date, fmt='%a %b %d %H:%M:%S %Y %1%2'):
168 def formatdate(self, date, fmt='%a %b %d %H:%M:%S %Y %1%2'):
169 '''convert date tuple to appropriate format'''
169 '''convert date tuple to appropriate format'''
170 return self._converter.formatdate(date, fmt)
170 return self._converter.formatdate(date, fmt)
171 def formatdict(self, data, key='key', value='value', fmt='%s=%s', sep=' '):
171 def formatdict(self, data, key='key', value='value', fmt='%s=%s', sep=' '):
172 '''convert dict or key-value pairs to appropriate dict format'''
172 '''convert dict or key-value pairs to appropriate dict format'''
173 return self._converter.formatdict(data, key, value, fmt, sep)
173 return self._converter.formatdict(data, key, value, fmt, sep)
174 def formatlist(self, data, name, fmt='%s', sep=' '):
174 def formatlist(self, data, name, fmt='%s', sep=' '):
175 '''convert iterable to appropriate list format'''
175 '''convert iterable to appropriate list format'''
176 # name is mandatory argument for now, but it could be optional if
176 # name is mandatory argument for now, but it could be optional if
177 # we have default template keyword, e.g. {item}
177 # we have default template keyword, e.g. {item}
178 return self._converter.formatlist(data, name, fmt, sep)
178 return self._converter.formatlist(data, name, fmt, sep)
179 def context(self, **ctxs):
179 def context(self, **ctxs):
180 '''insert context objects to be used to render template keywords'''
180 '''insert context objects to be used to render template keywords'''
181 pass
181 pass
182 def data(self, **data):
182 def data(self, **data):
183 '''insert data into item that's not shown in default output'''
183 '''insert data into item that's not shown in default output'''
184 data = pycompat.byteskwargs(data)
184 data = pycompat.byteskwargs(data)
185 self._item.update(data)
185 self._item.update(data)
186 def write(self, fields, deftext, *fielddata, **opts):
186 def write(self, fields, deftext, *fielddata, **opts):
187 '''do default text output while assigning data to item'''
187 '''do default text output while assigning data to item'''
188 fieldkeys = fields.split()
188 fieldkeys = fields.split()
189 assert len(fieldkeys) == len(fielddata)
189 assert len(fieldkeys) == len(fielddata)
190 self._item.update(zip(fieldkeys, fielddata))
190 self._item.update(zip(fieldkeys, fielddata))
191 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
191 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
192 '''do conditional write (primarily for plain formatter)'''
192 '''do conditional write (primarily for plain formatter)'''
193 fieldkeys = fields.split()
193 fieldkeys = fields.split()
194 assert len(fieldkeys) == len(fielddata)
194 assert len(fieldkeys) == len(fielddata)
195 self._item.update(zip(fieldkeys, fielddata))
195 self._item.update(zip(fieldkeys, fielddata))
196 def plain(self, text, **opts):
196 def plain(self, text, **opts):
197 '''show raw text for non-templated mode'''
197 '''show raw text for non-templated mode'''
198 pass
198 pass
199 def isplain(self):
199 def isplain(self):
200 '''check for plain formatter usage'''
200 '''check for plain formatter usage'''
201 return False
201 return False
202 def nested(self, field):
202 def nested(self, field):
203 '''sub formatter to store nested data in the specified field'''
203 '''sub formatter to store nested data in the specified field'''
204 self._item[field] = data = []
204 self._item[field] = data = []
205 return _nestedformatter(self._ui, self._converter, data)
205 return _nestedformatter(self._ui, self._converter, data)
206 def end(self):
206 def end(self):
207 '''end output for the formatter'''
207 '''end output for the formatter'''
208 if self._item is not None:
208 if self._item is not None:
209 self._showitem()
209 self._showitem()
210
210
211 def nullformatter(ui, topic):
211 def nullformatter(ui, topic):
212 '''formatter that prints nothing'''
212 '''formatter that prints nothing'''
213 return baseformatter(ui, topic, opts={}, converter=_nullconverter)
213 return baseformatter(ui, topic, opts={}, converter=_nullconverter)
214
214
215 class _nestedformatter(baseformatter):
215 class _nestedformatter(baseformatter):
216 '''build sub items and store them in the parent formatter'''
216 '''build sub items and store them in the parent formatter'''
217 def __init__(self, ui, converter, data):
217 def __init__(self, ui, converter, data):
218 baseformatter.__init__(self, ui, topic='', opts={}, converter=converter)
218 baseformatter.__init__(self, ui, topic='', opts={}, converter=converter)
219 self._data = data
219 self._data = data
220 def _showitem(self):
220 def _showitem(self):
221 self._data.append(self._item)
221 self._data.append(self._item)
222
222
223 def _iteritems(data):
223 def _iteritems(data):
224 '''iterate key-value pairs in stable order'''
224 '''iterate key-value pairs in stable order'''
225 if isinstance(data, dict):
225 if isinstance(data, dict):
226 return sorted(data.iteritems())
226 return sorted(data.iteritems())
227 return data
227 return data
228
228
229 class _plainconverter(object):
229 class _plainconverter(object):
230 '''convert non-primitive data types to text'''
230 '''convert non-primitive data types to text'''
231 @staticmethod
231 @staticmethod
232 def formatdate(date, fmt):
232 def formatdate(date, fmt):
233 '''stringify date tuple in the given format'''
233 '''stringify date tuple in the given format'''
234 return util.datestr(date, fmt)
234 return util.datestr(date, fmt)
235 @staticmethod
235 @staticmethod
236 def formatdict(data, key, value, fmt, sep):
236 def formatdict(data, key, value, fmt, sep):
237 '''stringify key-value pairs separated by sep'''
237 '''stringify key-value pairs separated by sep'''
238 return sep.join(fmt % (k, v) for k, v in _iteritems(data))
238 return sep.join(fmt % (k, v) for k, v in _iteritems(data))
239 @staticmethod
239 @staticmethod
240 def formatlist(data, name, fmt, sep):
240 def formatlist(data, name, fmt, sep):
241 '''stringify iterable separated by sep'''
241 '''stringify iterable separated by sep'''
242 return sep.join(fmt % e for e in data)
242 return sep.join(fmt % e for e in data)
243
243
244 class plainformatter(baseformatter):
244 class plainformatter(baseformatter):
245 '''the default text output scheme'''
245 '''the default text output scheme'''
246 def __init__(self, ui, out, topic, opts):
246 def __init__(self, ui, out, topic, opts):
247 baseformatter.__init__(self, ui, topic, opts, _plainconverter)
247 baseformatter.__init__(self, ui, topic, opts, _plainconverter)
248 if ui.debugflag:
248 if ui.debugflag:
249 self.hexfunc = hex
249 self.hexfunc = hex
250 else:
250 else:
251 self.hexfunc = short
251 self.hexfunc = short
252 if ui is out:
252 if ui is out:
253 self._write = ui.write
253 self._write = ui.write
254 else:
254 else:
255 self._write = lambda s, **opts: out.write(s)
255 self._write = lambda s, **opts: out.write(s)
256 def startitem(self):
256 def startitem(self):
257 pass
257 pass
258 def data(self, **data):
258 def data(self, **data):
259 pass
259 pass
260 def write(self, fields, deftext, *fielddata, **opts):
260 def write(self, fields, deftext, *fielddata, **opts):
261 self._write(deftext % fielddata, **opts)
261 self._write(deftext % fielddata, **opts)
262 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
262 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
263 '''do conditional write'''
263 '''do conditional write'''
264 if cond:
264 if cond:
265 self._write(deftext % fielddata, **opts)
265 self._write(deftext % fielddata, **opts)
266 def plain(self, text, **opts):
266 def plain(self, text, **opts):
267 self._write(text, **opts)
267 self._write(text, **opts)
268 def isplain(self):
268 def isplain(self):
269 return True
269 return True
270 def nested(self, field):
270 def nested(self, field):
271 # nested data will be directly written to ui
271 # nested data will be directly written to ui
272 return self
272 return self
273 def end(self):
273 def end(self):
274 pass
274 pass
275
275
276 class debugformatter(baseformatter):
276 class debugformatter(baseformatter):
277 def __init__(self, ui, out, topic, opts):
277 def __init__(self, ui, out, topic, opts):
278 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
278 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
279 self._out = out
279 self._out = out
280 self._out.write("%s = [\n" % self._topic)
280 self._out.write("%s = [\n" % self._topic)
281 def _showitem(self):
281 def _showitem(self):
282 self._out.write(" " + repr(self._item) + ",\n")
282 self._out.write(" " + repr(self._item) + ",\n")
283 def end(self):
283 def end(self):
284 baseformatter.end(self)
284 baseformatter.end(self)
285 self._out.write("]\n")
285 self._out.write("]\n")
286
286
287 class pickleformatter(baseformatter):
287 class pickleformatter(baseformatter):
288 def __init__(self, ui, out, topic, opts):
288 def __init__(self, ui, out, topic, opts):
289 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
289 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
290 self._out = out
290 self._out = out
291 self._data = []
291 self._data = []
292 def _showitem(self):
292 def _showitem(self):
293 self._data.append(self._item)
293 self._data.append(self._item)
294 def end(self):
294 def end(self):
295 baseformatter.end(self)
295 baseformatter.end(self)
296 self._out.write(pickle.dumps(self._data))
296 self._out.write(pickle.dumps(self._data))
297
297
298 class jsonformatter(baseformatter):
298 class jsonformatter(baseformatter):
299 def __init__(self, ui, out, topic, opts):
299 def __init__(self, ui, out, topic, opts):
300 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
300 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
301 self._out = out
301 self._out = out
302 self._out.write("[")
302 self._out.write("[")
303 self._first = True
303 self._first = True
304 def _showitem(self):
304 def _showitem(self):
305 if self._first:
305 if self._first:
306 self._first = False
306 self._first = False
307 else:
307 else:
308 self._out.write(",")
308 self._out.write(",")
309
309
310 self._out.write("\n {\n")
310 self._out.write("\n {\n")
311 first = True
311 first = True
312 for k, v in sorted(self._item.items()):
312 for k, v in sorted(self._item.items()):
313 if first:
313 if first:
314 first = False
314 first = False
315 else:
315 else:
316 self._out.write(",\n")
316 self._out.write(",\n")
317 u = templatefilters.json(v, paranoid=False)
317 u = templatefilters.json(v, paranoid=False)
318 self._out.write(' "%s": %s' % (k, u))
318 self._out.write(' "%s": %s' % (k, u))
319 self._out.write("\n }")
319 self._out.write("\n }")
320 def end(self):
320 def end(self):
321 baseformatter.end(self)
321 baseformatter.end(self)
322 self._out.write("\n]\n")
322 self._out.write("\n]\n")
323
323
324 class _templateconverter(object):
324 class _templateconverter(object):
325 '''convert non-primitive data types to be processed by templater'''
325 '''convert non-primitive data types to be processed by templater'''
326 @staticmethod
326 @staticmethod
327 def formatdate(date, fmt):
327 def formatdate(date, fmt):
328 '''return date tuple'''
328 '''return date tuple'''
329 return date
329 return date
330 @staticmethod
330 @staticmethod
331 def formatdict(data, key, value, fmt, sep):
331 def formatdict(data, key, value, fmt, sep):
332 '''build object that can be evaluated as either plain string or dict'''
332 '''build object that can be evaluated as either plain string or dict'''
333 data = util.sortdict(_iteritems(data))
333 data = util.sortdict(_iteritems(data))
334 def f():
334 def f():
335 yield _plainconverter.formatdict(data, key, value, fmt, sep)
335 yield _plainconverter.formatdict(data, key, value, fmt, sep)
336 return templatekw.hybriddict(data, key=key, value=value, fmt=fmt,
336 return templatekw.hybriddict(data, key=key, value=value, fmt=fmt,
337 gen=f())
337 gen=f())
338 @staticmethod
338 @staticmethod
339 def formatlist(data, name, fmt, sep):
339 def formatlist(data, name, fmt, sep):
340 '''build object that can be evaluated as either plain string or list'''
340 '''build object that can be evaluated as either plain string or list'''
341 data = list(data)
341 data = list(data)
342 def f():
342 def f():
343 yield _plainconverter.formatlist(data, name, fmt, sep)
343 yield _plainconverter.formatlist(data, name, fmt, sep)
344 return templatekw.hybridlist(data, name=name, fmt=fmt, gen=f())
344 return templatekw.hybridlist(data, name=name, fmt=fmt, gen=f())
345
345
346 class templateformatter(baseformatter):
346 class templateformatter(baseformatter):
347 def __init__(self, ui, out, topic, opts):
347 def __init__(self, ui, out, topic, opts):
348 baseformatter.__init__(self, ui, topic, opts, _templateconverter)
348 baseformatter.__init__(self, ui, topic, opts, _templateconverter)
349 self._out = out
349 self._out = out
350 spec = lookuptemplate(ui, topic, opts.get('template', ''))
350 spec = lookuptemplate(ui, topic, opts.get('template', ''))
351 self._tref = spec.ref
351 self._tref = spec.ref
352 self._t = loadtemplater(ui, spec, cache=templatekw.defaulttempl)
352 self._t = loadtemplater(ui, spec, cache=templatekw.defaulttempl)
353 self._parts = templatepartsmap(spec, self._t,
353 self._parts = templatepartsmap(spec, self._t,
354 ['docheader', 'docfooter', 'separator'])
354 ['docheader', 'docfooter', 'separator'])
355 self._counter = itertools.count()
355 self._counter = itertools.count()
356 self._cache = {} # for templatekw/funcs to store reusable data
356 self._cache = {} # for templatekw/funcs to store reusable data
357 self._renderitem('docheader', {})
357 self._renderitem('docheader', {})
358
358
359 def context(self, **ctxs):
359 def context(self, **ctxs):
360 '''insert context objects to be used to render template keywords'''
360 '''insert context objects to be used to render template keywords'''
361 ctxs = pycompat.byteskwargs(ctxs)
361 ctxs = pycompat.byteskwargs(ctxs)
362 assert all(k == 'ctx' for k in ctxs)
362 assert all(k == 'ctx' for k in ctxs)
363 self._item.update(ctxs)
363 self._item.update(ctxs)
364
364
365 def _showitem(self):
365 def _showitem(self):
366 item = self._item.copy()
366 item = self._item.copy()
367 item['index'] = index = next(self._counter)
367 item['index'] = index = next(self._counter)
368 if index > 0:
368 if index > 0:
369 self._renderitem('separator', {})
369 self._renderitem('separator', {})
370 self._renderitem(self._tref, item)
370 self._renderitem(self._tref, item)
371
371
372 def _renderitem(self, part, item):
372 def _renderitem(self, part, item):
373 if part not in self._parts:
373 if part not in self._parts:
374 return
374 return
375 ref = self._parts[part]
375 ref = self._parts[part]
376
376
377 # TODO: add support for filectx. probably each template keyword or
377 # TODO: add support for filectx. probably each template keyword or
378 # function will have to declare dependent resources. e.g.
378 # function will have to declare dependent resources. e.g.
379 # @templatekeyword(..., requires=('ctx',))
379 # @templatekeyword(..., requires=('ctx',))
380 props = {}
380 props = {}
381 if 'ctx' in item:
381 if 'ctx' in item:
382 props.update(templatekw.keywords)
382 props.update(templatekw.keywords)
383 # explicitly-defined fields precede templatekw
383 # explicitly-defined fields precede templatekw
384 props.update(item)
384 props.update(item)
385 if 'ctx' in item:
385 if 'ctx' in item:
386 # but template resources must be always available
386 # but template resources must be always available
387 props['templ'] = self._t
387 props['templ'] = self._t
388 props['repo'] = props['ctx'].repo()
388 props['repo'] = props['ctx'].repo()
389 props['revcache'] = {}
389 props['revcache'] = {}
390 props = pycompat.strkwargs(props)
390 props = pycompat.strkwargs(props)
391 g = self._t(ref, ui=self._ui, cache=self._cache, **props)
391 g = self._t(ref, ui=self._ui, cache=self._cache, **props)
392 self._out.write(templater.stringify(g))
392 self._out.write(templater.stringify(g))
393
393
394 def end(self):
394 def end(self):
395 baseformatter.end(self)
395 baseformatter.end(self)
396 self._renderitem('docfooter', {})
396 self._renderitem('docfooter', {})
397
397
398 templatespec = collections.namedtuple(r'templatespec',
398 templatespec = collections.namedtuple(r'templatespec',
399 r'ref tmpl mapfile')
399 r'ref tmpl mapfile')
400
400
401 def lookuptemplate(ui, topic, tmpl):
401 def lookuptemplate(ui, topic, tmpl):
402 """Find the template matching the given -T/--template spec 'tmpl'
402 """Find the template matching the given -T/--template spec 'tmpl'
403
403
404 'tmpl' can be any of the following:
404 'tmpl' can be any of the following:
405
405
406 - a literal template (e.g. '{rev}')
406 - a literal template (e.g. '{rev}')
407 - a map-file name or path (e.g. 'changelog')
407 - a map-file name or path (e.g. 'changelog')
408 - a reference to [templates] in config file
408 - a reference to [templates] in config file
409 - a path to raw template file
409 - a path to raw template file
410
410
411 A map file defines a stand-alone template environment. If a map file
411 A map file defines a stand-alone template environment. If a map file
412 selected, all templates defined in the file will be loaded, and the
412 selected, all templates defined in the file will be loaded, and the
413 template matching the given topic will be rendered. No aliases will be
413 template matching the given topic will be rendered. No aliases will be
414 loaded from user config.
414 loaded from user config.
415
415
416 If no map file selected, all templates in [templates] section will be
416 If no map file selected, all templates in [templates] section will be
417 available as well as aliases in [templatealias].
417 available as well as aliases in [templatealias].
418 """
418 """
419
419
420 # looks like a literal template?
420 # looks like a literal template?
421 if '{' in tmpl:
421 if '{' in tmpl:
422 return templatespec('', tmpl, None)
422 return templatespec('', tmpl, None)
423
423
424 # perhaps a stock style?
424 # perhaps a stock style?
425 if not os.path.split(tmpl)[0]:
425 if not os.path.split(tmpl)[0]:
426 mapname = (templater.templatepath('map-cmdline.' + tmpl)
426 mapname = (templater.templatepath('map-cmdline.' + tmpl)
427 or templater.templatepath(tmpl))
427 or templater.templatepath(tmpl))
428 if mapname and os.path.isfile(mapname):
428 if mapname and os.path.isfile(mapname):
429 return templatespec(topic, None, mapname)
429 return templatespec(topic, None, mapname)
430
430
431 # perhaps it's a reference to [templates]
431 # perhaps it's a reference to [templates]
432 if ui.config('templates', tmpl):
432 if ui.config('templates', tmpl):
433 return templatespec(tmpl, None, None)
433 return templatespec(tmpl, None, None)
434
434
435 if tmpl == 'list':
435 if tmpl == 'list':
436 ui.write(_("available styles: %s\n") % templater.stylelist())
436 ui.write(_("available styles: %s\n") % templater.stylelist())
437 raise error.Abort(_("specify a template"))
437 raise error.Abort(_("specify a template"))
438
438
439 # perhaps it's a path to a map or a template
439 # perhaps it's a path to a map or a template
440 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
440 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
441 # is it a mapfile for a style?
441 # is it a mapfile for a style?
442 if os.path.basename(tmpl).startswith("map-"):
442 if os.path.basename(tmpl).startswith("map-"):
443 return templatespec(topic, None, os.path.realpath(tmpl))
443 return templatespec(topic, None, os.path.realpath(tmpl))
444 with util.posixfile(tmpl, 'rb') as f:
444 with util.posixfile(tmpl, 'rb') as f:
445 tmpl = f.read()
445 tmpl = f.read()
446 return templatespec('', tmpl, None)
446 return templatespec('', tmpl, None)
447
447
448 # constant string?
448 # constant string?
449 return templatespec('', tmpl, None)
449 return templatespec('', tmpl, None)
450
450
451 def templatepartsmap(spec, t, partnames):
451 def templatepartsmap(spec, t, partnames):
452 """Create a mapping of {part: ref}"""
452 """Create a mapping of {part: ref}"""
453 partsmap = {spec.ref: spec.ref} # initial ref must exist in t
453 partsmap = {spec.ref: spec.ref} # initial ref must exist in t
454 if spec.mapfile:
454 if spec.mapfile:
455 partsmap.update((p, p) for p in partnames if p in t)
455 partsmap.update((p, p) for p in partnames if p in t)
456 elif spec.ref:
457 for part in partnames:
458 ref = '%s:%s' % (spec.ref, part) # select config sub-section
459 if ref in t:
460 partsmap[part] = ref
456 return partsmap
461 return partsmap
457
462
458 def loadtemplater(ui, spec, cache=None):
463 def loadtemplater(ui, spec, cache=None):
459 """Create a templater from either a literal template or loading from
464 """Create a templater from either a literal template or loading from
460 a map file"""
465 a map file"""
461 assert not (spec.tmpl and spec.mapfile)
466 assert not (spec.tmpl and spec.mapfile)
462 if spec.mapfile:
467 if spec.mapfile:
463 return templater.templater.frommapfile(spec.mapfile, cache=cache)
468 return templater.templater.frommapfile(spec.mapfile, cache=cache)
464 return maketemplater(ui, spec.tmpl, cache=cache)
469 return maketemplater(ui, spec.tmpl, cache=cache)
465
470
466 def maketemplater(ui, tmpl, cache=None):
471 def maketemplater(ui, tmpl, cache=None):
467 """Create a templater from a string template 'tmpl'"""
472 """Create a templater from a string template 'tmpl'"""
468 aliases = ui.configitems('templatealias')
473 aliases = ui.configitems('templatealias')
469 t = templater.templater(cache=cache, aliases=aliases)
474 t = templater.templater(cache=cache, aliases=aliases)
470 t.cache.update((k, templater.unquotestring(v))
475 t.cache.update((k, templater.unquotestring(v))
471 for k, v in ui.configitems('templates'))
476 for k, v in ui.configitems('templates'))
472 if tmpl:
477 if tmpl:
473 t.cache[''] = tmpl
478 t.cache[''] = tmpl
474 return t
479 return t
475
480
476 def formatter(ui, out, topic, opts):
481 def formatter(ui, out, topic, opts):
477 template = opts.get("template", "")
482 template = opts.get("template", "")
478 if template == "json":
483 if template == "json":
479 return jsonformatter(ui, out, topic, opts)
484 return jsonformatter(ui, out, topic, opts)
480 elif template == "pickle":
485 elif template == "pickle":
481 return pickleformatter(ui, out, topic, opts)
486 return pickleformatter(ui, out, topic, opts)
482 elif template == "debug":
487 elif template == "debug":
483 return debugformatter(ui, out, topic, opts)
488 return debugformatter(ui, out, topic, opts)
484 elif template != "":
489 elif template != "":
485 return templateformatter(ui, out, topic, opts)
490 return templateformatter(ui, out, topic, opts)
486 # developer config: ui.formatdebug
491 # developer config: ui.formatdebug
487 elif ui.configbool('ui', 'formatdebug'):
492 elif ui.configbool('ui', 'formatdebug'):
488 return debugformatter(ui, out, topic, opts)
493 return debugformatter(ui, out, topic, opts)
489 # deprecated config: ui.formatjson
494 # deprecated config: ui.formatjson
490 elif ui.configbool('ui', 'formatjson'):
495 elif ui.configbool('ui', 'formatjson'):
491 return jsonformatter(ui, out, topic, opts)
496 return jsonformatter(ui, out, topic, opts)
492 return plainformatter(ui, out, topic, opts)
497 return plainformatter(ui, out, topic, opts)
493
498
494 @contextlib.contextmanager
499 @contextlib.contextmanager
495 def openformatter(ui, filename, topic, opts):
500 def openformatter(ui, filename, topic, opts):
496 """Create a formatter that writes outputs to the specified file
501 """Create a formatter that writes outputs to the specified file
497
502
498 Must be invoked using the 'with' statement.
503 Must be invoked using the 'with' statement.
499 """
504 """
500 with util.posixfile(filename, 'wb') as out:
505 with util.posixfile(filename, 'wb') as out:
501 with formatter(ui, out, topic, opts) as fm:
506 with formatter(ui, out, topic, opts) as fm:
502 yield fm
507 yield fm
503
508
504 @contextlib.contextmanager
509 @contextlib.contextmanager
505 def _neverending(fm):
510 def _neverending(fm):
506 yield fm
511 yield fm
507
512
508 def maybereopen(fm, filename, opts):
513 def maybereopen(fm, filename, opts):
509 """Create a formatter backed by file if filename specified, else return
514 """Create a formatter backed by file if filename specified, else return
510 the given formatter
515 the given formatter
511
516
512 Must be invoked using the 'with' statement. This will never call fm.end()
517 Must be invoked using the 'with' statement. This will never call fm.end()
513 of the given formatter.
518 of the given formatter.
514 """
519 """
515 if filename:
520 if filename:
516 return openformatter(fm._ui, filename, fm._topic, opts)
521 return openformatter(fm._ui, filename, fm._topic, opts)
517 else:
522 else:
518 return _neverending(fm)
523 return _neverending(fm)
@@ -1,200 +1,209 b''
1 Mercurial allows you to customize output of commands through
1 Mercurial allows you to customize output of commands through
2 templates. You can either pass in a template or select an existing
2 templates. You can either pass in a template or select an existing
3 template-style from the command line, via the --template option.
3 template-style from the command line, via the --template option.
4
4
5 You can customize output for any "log-like" command: log,
5 You can customize output for any "log-like" command: log,
6 outgoing, incoming, tip, parents, and heads.
6 outgoing, incoming, tip, parents, and heads.
7
7
8 Some built-in styles are packaged with Mercurial. These can be listed
8 Some built-in styles are packaged with Mercurial. These can be listed
9 with :hg:`log --template list`. Example usage::
9 with :hg:`log --template list`. Example usage::
10
10
11 $ hg log -r1.0::1.1 --template changelog
11 $ hg log -r1.0::1.1 --template changelog
12
12
13 A template is a piece of text, with markup to invoke variable
13 A template is a piece of text, with markup to invoke variable
14 expansion::
14 expansion::
15
15
16 $ hg log -r1 --template "{node}\n"
16 $ hg log -r1 --template "{node}\n"
17 b56ce7b07c52de7d5fd79fb89701ea538af65746
17 b56ce7b07c52de7d5fd79fb89701ea538af65746
18
18
19 Keywords
19 Keywords
20 ========
20 ========
21
21
22 Strings in curly braces are called keywords. The availability of
22 Strings in curly braces are called keywords. The availability of
23 keywords depends on the exact context of the templater. These
23 keywords depends on the exact context of the templater. These
24 keywords are usually available for templating a log-like command:
24 keywords are usually available for templating a log-like command:
25
25
26 .. keywordsmarker
26 .. keywordsmarker
27
27
28 The "date" keyword does not produce human-readable output. If you
28 The "date" keyword does not produce human-readable output. If you
29 want to use a date in your output, you can use a filter to process
29 want to use a date in your output, you can use a filter to process
30 it. Filters are functions which return a string based on the input
30 it. Filters are functions which return a string based on the input
31 variable. Be sure to use the stringify filter first when you're
31 variable. Be sure to use the stringify filter first when you're
32 applying a string-input filter to a list-like input variable.
32 applying a string-input filter to a list-like input variable.
33 You can also use a chain of filters to get the desired output::
33 You can also use a chain of filters to get the desired output::
34
34
35 $ hg tip --template "{date|isodate}\n"
35 $ hg tip --template "{date|isodate}\n"
36 2008-08-21 18:22 +0000
36 2008-08-21 18:22 +0000
37
37
38 Filters
38 Filters
39 =======
39 =======
40
40
41 List of filters:
41 List of filters:
42
42
43 .. filtersmarker
43 .. filtersmarker
44
44
45 Note that a filter is nothing more than a function call, i.e.
45 Note that a filter is nothing more than a function call, i.e.
46 ``expr|filter`` is equivalent to ``filter(expr)``.
46 ``expr|filter`` is equivalent to ``filter(expr)``.
47
47
48 Functions
48 Functions
49 =========
49 =========
50
50
51 In addition to filters, there are some basic built-in functions:
51 In addition to filters, there are some basic built-in functions:
52
52
53 .. functionsmarker
53 .. functionsmarker
54
54
55 Operators
55 Operators
56 =========
56 =========
57
57
58 We provide a limited set of infix arithmetic operations on integers::
58 We provide a limited set of infix arithmetic operations on integers::
59
59
60 + for addition
60 + for addition
61 - for subtraction
61 - for subtraction
62 * for multiplication
62 * for multiplication
63 / for floor division (division rounded to integer nearest -infinity)
63 / for floor division (division rounded to integer nearest -infinity)
64
64
65 Division fulfills the law x = x / y + mod(x, y).
65 Division fulfills the law x = x / y + mod(x, y).
66
66
67 Also, for any expression that returns a list, there is a list operator::
67 Also, for any expression that returns a list, there is a list operator::
68
68
69 expr % "{template}"
69 expr % "{template}"
70
70
71 As seen in the above example, ``{template}`` is interpreted as a template.
71 As seen in the above example, ``{template}`` is interpreted as a template.
72 To prevent it from being interpreted, you can use an escape character ``\{``
72 To prevent it from being interpreted, you can use an escape character ``\{``
73 or a raw string prefix, ``r'...'``.
73 or a raw string prefix, ``r'...'``.
74
74
75 Aliases
75 Aliases
76 =======
76 =======
77
77
78 New keywords and functions can be defined in the ``templatealias`` section of
78 New keywords and functions can be defined in the ``templatealias`` section of
79 a Mercurial configuration file::
79 a Mercurial configuration file::
80
80
81 <alias> = <definition>
81 <alias> = <definition>
82
82
83 Arguments of the form `a1`, `a2`, etc. are substituted from the alias into
83 Arguments of the form `a1`, `a2`, etc. are substituted from the alias into
84 the definition.
84 the definition.
85
85
86 For example,
86 For example,
87
87
88 ::
88 ::
89
89
90 [templatealias]
90 [templatealias]
91 r = rev
91 r = rev
92 rn = "{r}:{node|short}"
92 rn = "{r}:{node|short}"
93 leftpad(s, w) = pad(s, w, ' ', True)
93 leftpad(s, w) = pad(s, w, ' ', True)
94
94
95 defines two symbol aliases, ``r`` and ``rn``, and a function alias
95 defines two symbol aliases, ``r`` and ``rn``, and a function alias
96 ``leftpad()``.
96 ``leftpad()``.
97
97
98 It's also possible to specify complete template strings, using the
98 It's also possible to specify complete template strings, using the
99 ``templates`` section. The syntax used is the general template string syntax.
99 ``templates`` section. The syntax used is the general template string syntax.
100
100
101 For example,
101 For example,
102
102
103 ::
103 ::
104
104
105 [templates]
105 [templates]
106 nodedate = "{node|short}: {date(date, "%Y-%m-%d")}\n"
106 nodedate = "{node|short}: {date(date, "%Y-%m-%d")}\n"
107
107
108 defines a template, ``nodedate``, which can be called like::
108 defines a template, ``nodedate``, which can be called like::
109
109
110 $ hg log -r . -Tnodedate
110 $ hg log -r . -Tnodedate
111
111
112 A template defined in ``templates`` section can also be referenced from
112 A template defined in ``templates`` section can also be referenced from
113 another template::
113 another template::
114
114
115 $ hg log -r . -T "{rev} {nodedate}"
115 $ hg log -r . -T "{rev} {nodedate}"
116
116
117 but be aware that the keywords cannot be overridden by templates. For example,
117 but be aware that the keywords cannot be overridden by templates. For example,
118 a template defined as ``templates.rev`` cannot be referenced as ``{rev}``.
118 a template defined as ``templates.rev`` cannot be referenced as ``{rev}``.
119
119
120 A template defined in ``templates`` section may have sub templates which
121 are inserted before/after/between items::
122
123 [templates]
124 myjson = ' {dict(rev, node|short)|json}'
125 myjson:docheader = '\{\n'
126 myjson:docfooter = '\n}\n'
127 myjson:separator = ',\n'
128
120 Examples
129 Examples
121 ========
130 ========
122
131
123 Some sample command line templates:
132 Some sample command line templates:
124
133
125 - Format lists, e.g. files::
134 - Format lists, e.g. files::
126
135
127 $ hg log -r 0 --template "files:\n{files % ' {file}\n'}"
136 $ hg log -r 0 --template "files:\n{files % ' {file}\n'}"
128
137
129 - Join the list of files with a ", "::
138 - Join the list of files with a ", "::
130
139
131 $ hg log -r 0 --template "files: {join(files, ', ')}\n"
140 $ hg log -r 0 --template "files: {join(files, ', ')}\n"
132
141
133 - Join the list of files ending with ".py" with a ", "::
142 - Join the list of files ending with ".py" with a ", "::
134
143
135 $ hg log -r 0 --template "pythonfiles: {join(files('**.py'), ', ')}\n"
144 $ hg log -r 0 --template "pythonfiles: {join(files('**.py'), ', ')}\n"
136
145
137 - Separate non-empty arguments by a " "::
146 - Separate non-empty arguments by a " "::
138
147
139 $ hg log -r 0 --template "{separate(' ', node, bookmarks, tags}\n"
148 $ hg log -r 0 --template "{separate(' ', node, bookmarks, tags}\n"
140
149
141 - Modify each line of a commit description::
150 - Modify each line of a commit description::
142
151
143 $ hg log --template "{splitlines(desc) % '**** {line}\n'}"
152 $ hg log --template "{splitlines(desc) % '**** {line}\n'}"
144
153
145 - Format date::
154 - Format date::
146
155
147 $ hg log -r 0 --template "{date(date, '%Y')}\n"
156 $ hg log -r 0 --template "{date(date, '%Y')}\n"
148
157
149 - Display date in UTC::
158 - Display date in UTC::
150
159
151 $ hg log -r 0 --template "{localdate(date, 'UTC')|date}\n"
160 $ hg log -r 0 --template "{localdate(date, 'UTC')|date}\n"
152
161
153 - Output the description set to a fill-width of 30::
162 - Output the description set to a fill-width of 30::
154
163
155 $ hg log -r 0 --template "{fill(desc, 30)}"
164 $ hg log -r 0 --template "{fill(desc, 30)}"
156
165
157 - Use a conditional to test for the default branch::
166 - Use a conditional to test for the default branch::
158
167
159 $ hg log -r 0 --template "{ifeq(branch, 'default', 'on the main branch',
168 $ hg log -r 0 --template "{ifeq(branch, 'default', 'on the main branch',
160 'on branch {branch}')}\n"
169 'on branch {branch}')}\n"
161
170
162 - Append a newline if not empty::
171 - Append a newline if not empty::
163
172
164 $ hg tip --template "{if(author, '{author}\n')}"
173 $ hg tip --template "{if(author, '{author}\n')}"
165
174
166 - Label the output for use with the color extension::
175 - Label the output for use with the color extension::
167
176
168 $ hg log -r 0 --template "{label('changeset.{phase}', node|short)}\n"
177 $ hg log -r 0 --template "{label('changeset.{phase}', node|short)}\n"
169
178
170 - Invert the firstline filter, i.e. everything but the first line::
179 - Invert the firstline filter, i.e. everything but the first line::
171
180
172 $ hg log -r 0 --template "{sub(r'^.*\n?\n?', '', desc)}\n"
181 $ hg log -r 0 --template "{sub(r'^.*\n?\n?', '', desc)}\n"
173
182
174 - Display the contents of the 'extra' field, one per line::
183 - Display the contents of the 'extra' field, one per line::
175
184
176 $ hg log -r 0 --template "{join(extras, '\n')}\n"
185 $ hg log -r 0 --template "{join(extras, '\n')}\n"
177
186
178 - Mark the active bookmark with '*'::
187 - Mark the active bookmark with '*'::
179
188
180 $ hg log --template "{bookmarks % '{bookmark}{ifeq(bookmark, active, '*')} '}\n"
189 $ hg log --template "{bookmarks % '{bookmark}{ifeq(bookmark, active, '*')} '}\n"
181
190
182 - Find the previous release candidate tag, the distance and changes since the tag::
191 - Find the previous release candidate tag, the distance and changes since the tag::
183
192
184 $ hg log -r . --template "{latesttag('re:^.*-rc$') % '{tag}, {changes}, {distance}'}\n"
193 $ hg log -r . --template "{latesttag('re:^.*-rc$') % '{tag}, {changes}, {distance}'}\n"
185
194
186 - Mark the working copy parent with '@'::
195 - Mark the working copy parent with '@'::
187
196
188 $ hg log --template "{ifcontains(rev, revset('.'), '@')}\n"
197 $ hg log --template "{ifcontains(rev, revset('.'), '@')}\n"
189
198
190 - Show details of parent revisions::
199 - Show details of parent revisions::
191
200
192 $ hg log --template "{revset('parents(%d)', rev) % '{desc|firstline}\n'}"
201 $ hg log --template "{revset('parents(%d)', rev) % '{desc|firstline}\n'}"
193
202
194 - Show only commit descriptions that start with "template"::
203 - Show only commit descriptions that start with "template"::
195
204
196 $ hg log --template "{startswith('template', firstline(desc))}\n"
205 $ hg log --template "{startswith('template', firstline(desc))}\n"
197
206
198 - Print the first word of each line of a commit message::
207 - Print the first word of each line of a commit message::
199
208
200 $ hg log --template "{word(0, desc)}\n"
209 $ hg log --template "{word(0, desc)}\n"
@@ -1,741 +1,766 b''
1 $ hg init a
1 $ hg init a
2 $ cd a
2 $ cd a
3
3
4 Verify checking branch of nullrev before the cache is created doesnt crash
4 Verify checking branch of nullrev before the cache is created doesnt crash
5 $ hg log -r 'branch(.)' -T '{branch}\n'
5 $ hg log -r 'branch(.)' -T '{branch}\n'
6
6
7 Basic test
7 Basic test
8 $ echo 'root' >root
8 $ echo 'root' >root
9 $ hg add root
9 $ hg add root
10 $ hg commit -d '0 0' -m "Adding root node"
10 $ hg commit -d '0 0' -m "Adding root node"
11
11
12 $ echo 'a' >a
12 $ echo 'a' >a
13 $ hg add a
13 $ hg add a
14 $ hg branch a
14 $ hg branch a
15 marked working directory as branch a
15 marked working directory as branch a
16 (branches are permanent and global, did you want a bookmark?)
16 (branches are permanent and global, did you want a bookmark?)
17 $ hg commit -d '1 0' -m "Adding a branch"
17 $ hg commit -d '1 0' -m "Adding a branch"
18
18
19 $ hg branch q
19 $ hg branch q
20 marked working directory as branch q
20 marked working directory as branch q
21 $ echo 'aa' >a
21 $ echo 'aa' >a
22 $ hg branch -C
22 $ hg branch -C
23 reset working directory to branch a
23 reset working directory to branch a
24 $ hg commit -d '2 0' -m "Adding to a branch"
24 $ hg commit -d '2 0' -m "Adding to a branch"
25
25
26 $ hg update -C 0
26 $ hg update -C 0
27 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
27 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
28 $ echo 'b' >b
28 $ echo 'b' >b
29 $ hg add b
29 $ hg add b
30 $ hg branch b
30 $ hg branch b
31 marked working directory as branch b
31 marked working directory as branch b
32 $ hg commit -d '2 0' -m "Adding b branch"
32 $ hg commit -d '2 0' -m "Adding b branch"
33
33
34 $ echo 'bh1' >bh1
34 $ echo 'bh1' >bh1
35 $ hg add bh1
35 $ hg add bh1
36 $ hg commit -d '3 0' -m "Adding b branch head 1"
36 $ hg commit -d '3 0' -m "Adding b branch head 1"
37
37
38 $ hg update -C 2
38 $ hg update -C 2
39 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
39 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
40 $ echo 'bh2' >bh2
40 $ echo 'bh2' >bh2
41 $ hg add bh2
41 $ hg add bh2
42 $ hg commit -d '4 0' -m "Adding b branch head 2"
42 $ hg commit -d '4 0' -m "Adding b branch head 2"
43
43
44 $ echo 'c' >c
44 $ echo 'c' >c
45 $ hg add c
45 $ hg add c
46 $ hg branch c
46 $ hg branch c
47 marked working directory as branch c
47 marked working directory as branch c
48 $ hg commit -d '5 0' -m "Adding c branch"
48 $ hg commit -d '5 0' -m "Adding c branch"
49
49
50 reserved names
50 reserved names
51
51
52 $ hg branch tip
52 $ hg branch tip
53 abort: the name 'tip' is reserved
53 abort: the name 'tip' is reserved
54 [255]
54 [255]
55 $ hg branch null
55 $ hg branch null
56 abort: the name 'null' is reserved
56 abort: the name 'null' is reserved
57 [255]
57 [255]
58 $ hg branch .
58 $ hg branch .
59 abort: the name '.' is reserved
59 abort: the name '.' is reserved
60 [255]
60 [255]
61
61
62 invalid characters
62 invalid characters
63
63
64 $ hg branch 'foo:bar'
64 $ hg branch 'foo:bar'
65 abort: ':' cannot be used in a name
65 abort: ':' cannot be used in a name
66 [255]
66 [255]
67
67
68 $ hg branch 'foo
68 $ hg branch 'foo
69 > bar'
69 > bar'
70 abort: '\n' cannot be used in a name
70 abort: '\n' cannot be used in a name
71 [255]
71 [255]
72
72
73 trailing or leading spaces should be stripped before testing duplicates
73 trailing or leading spaces should be stripped before testing duplicates
74
74
75 $ hg branch 'b '
75 $ hg branch 'b '
76 abort: a branch of the same name already exists
76 abort: a branch of the same name already exists
77 (use 'hg update' to switch to it)
77 (use 'hg update' to switch to it)
78 [255]
78 [255]
79
79
80 $ hg branch ' b'
80 $ hg branch ' b'
81 abort: a branch of the same name already exists
81 abort: a branch of the same name already exists
82 (use 'hg update' to switch to it)
82 (use 'hg update' to switch to it)
83 [255]
83 [255]
84
84
85 verify update will accept invalid legacy branch names
85 verify update will accept invalid legacy branch names
86
86
87 $ hg init test-invalid-branch-name
87 $ hg init test-invalid-branch-name
88 $ cd test-invalid-branch-name
88 $ cd test-invalid-branch-name
89 $ hg pull -u "$TESTDIR"/bundles/test-invalid-branch-name.hg
89 $ hg pull -u "$TESTDIR"/bundles/test-invalid-branch-name.hg
90 pulling from *test-invalid-branch-name.hg (glob)
90 pulling from *test-invalid-branch-name.hg (glob)
91 requesting all changes
91 requesting all changes
92 adding changesets
92 adding changesets
93 adding manifests
93 adding manifests
94 adding file changes
94 adding file changes
95 added 3 changesets with 3 changes to 2 files
95 added 3 changesets with 3 changes to 2 files
96 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
97
97
98 $ hg update '"colon:test"'
98 $ hg update '"colon:test"'
99 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 $ cd ..
100 $ cd ..
101
101
102 $ echo 'd' >d
102 $ echo 'd' >d
103 $ hg add d
103 $ hg add d
104 $ hg branch 'a branch name much longer than the default justification used by branches'
104 $ hg branch 'a branch name much longer than the default justification used by branches'
105 marked working directory as branch a branch name much longer than the default justification used by branches
105 marked working directory as branch a branch name much longer than the default justification used by branches
106 $ hg commit -d '6 0' -m "Adding d branch"
106 $ hg commit -d '6 0' -m "Adding d branch"
107
107
108 $ hg branches
108 $ hg branches
109 a branch name much longer than the default justification used by branches 7:10ff5895aa57
109 a branch name much longer than the default justification used by branches 7:10ff5895aa57
110 b 4:aee39cd168d0
110 b 4:aee39cd168d0
111 c 6:589736a22561 (inactive)
111 c 6:589736a22561 (inactive)
112 a 5:d8cbc61dbaa6 (inactive)
112 a 5:d8cbc61dbaa6 (inactive)
113 default 0:19709c5a4e75 (inactive)
113 default 0:19709c5a4e75 (inactive)
114
114
115 -------
115 -------
116
116
117 $ hg branches -a
117 $ hg branches -a
118 a branch name much longer than the default justification used by branches 7:10ff5895aa57
118 a branch name much longer than the default justification used by branches 7:10ff5895aa57
119 b 4:aee39cd168d0
119 b 4:aee39cd168d0
120
120
121 --- Branch a
121 --- Branch a
122
122
123 $ hg log -b a
123 $ hg log -b a
124 changeset: 5:d8cbc61dbaa6
124 changeset: 5:d8cbc61dbaa6
125 branch: a
125 branch: a
126 parent: 2:881fe2b92ad0
126 parent: 2:881fe2b92ad0
127 user: test
127 user: test
128 date: Thu Jan 01 00:00:04 1970 +0000
128 date: Thu Jan 01 00:00:04 1970 +0000
129 summary: Adding b branch head 2
129 summary: Adding b branch head 2
130
130
131 changeset: 2:881fe2b92ad0
131 changeset: 2:881fe2b92ad0
132 branch: a
132 branch: a
133 user: test
133 user: test
134 date: Thu Jan 01 00:00:02 1970 +0000
134 date: Thu Jan 01 00:00:02 1970 +0000
135 summary: Adding to a branch
135 summary: Adding to a branch
136
136
137 changeset: 1:dd6b440dd85a
137 changeset: 1:dd6b440dd85a
138 branch: a
138 branch: a
139 user: test
139 user: test
140 date: Thu Jan 01 00:00:01 1970 +0000
140 date: Thu Jan 01 00:00:01 1970 +0000
141 summary: Adding a branch
141 summary: Adding a branch
142
142
143
143
144 ---- Branch b
144 ---- Branch b
145
145
146 $ hg log -b b
146 $ hg log -b b
147 changeset: 4:aee39cd168d0
147 changeset: 4:aee39cd168d0
148 branch: b
148 branch: b
149 user: test
149 user: test
150 date: Thu Jan 01 00:00:03 1970 +0000
150 date: Thu Jan 01 00:00:03 1970 +0000
151 summary: Adding b branch head 1
151 summary: Adding b branch head 1
152
152
153 changeset: 3:ac22033332d1
153 changeset: 3:ac22033332d1
154 branch: b
154 branch: b
155 parent: 0:19709c5a4e75
155 parent: 0:19709c5a4e75
156 user: test
156 user: test
157 date: Thu Jan 01 00:00:02 1970 +0000
157 date: Thu Jan 01 00:00:02 1970 +0000
158 summary: Adding b branch
158 summary: Adding b branch
159
159
160
160
161 ---- going to test branch closing
161 ---- going to test branch closing
162
162
163 $ hg branches
163 $ hg branches
164 a branch name much longer than the default justification used by branches 7:10ff5895aa57
164 a branch name much longer than the default justification used by branches 7:10ff5895aa57
165 b 4:aee39cd168d0
165 b 4:aee39cd168d0
166 c 6:589736a22561 (inactive)
166 c 6:589736a22561 (inactive)
167 a 5:d8cbc61dbaa6 (inactive)
167 a 5:d8cbc61dbaa6 (inactive)
168 default 0:19709c5a4e75 (inactive)
168 default 0:19709c5a4e75 (inactive)
169 $ hg up -C b
169 $ hg up -C b
170 2 files updated, 0 files merged, 4 files removed, 0 files unresolved
170 2 files updated, 0 files merged, 4 files removed, 0 files unresolved
171 $ echo 'xxx1' >> b
171 $ echo 'xxx1' >> b
172 $ hg commit -d '7 0' -m 'adding cset to branch b'
172 $ hg commit -d '7 0' -m 'adding cset to branch b'
173 $ hg up -C aee39cd168d0
173 $ hg up -C aee39cd168d0
174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 $ echo 'xxx2' >> b
175 $ echo 'xxx2' >> b
176 $ hg commit -d '8 0' -m 'adding head to branch b'
176 $ hg commit -d '8 0' -m 'adding head to branch b'
177 created new head
177 created new head
178 $ echo 'xxx3' >> b
178 $ echo 'xxx3' >> b
179 $ hg commit -d '9 0' -m 'adding another cset to branch b'
179 $ hg commit -d '9 0' -m 'adding another cset to branch b'
180 $ hg branches
180 $ hg branches
181 b 10:bfbe841b666e
181 b 10:bfbe841b666e
182 a branch name much longer than the default justification used by branches 7:10ff5895aa57
182 a branch name much longer than the default justification used by branches 7:10ff5895aa57
183 c 6:589736a22561 (inactive)
183 c 6:589736a22561 (inactive)
184 a 5:d8cbc61dbaa6 (inactive)
184 a 5:d8cbc61dbaa6 (inactive)
185 default 0:19709c5a4e75 (inactive)
185 default 0:19709c5a4e75 (inactive)
186 $ hg heads --closed
186 $ hg heads --closed
187 changeset: 10:bfbe841b666e
187 changeset: 10:bfbe841b666e
188 branch: b
188 branch: b
189 tag: tip
189 tag: tip
190 user: test
190 user: test
191 date: Thu Jan 01 00:00:09 1970 +0000
191 date: Thu Jan 01 00:00:09 1970 +0000
192 summary: adding another cset to branch b
192 summary: adding another cset to branch b
193
193
194 changeset: 8:eebb944467c9
194 changeset: 8:eebb944467c9
195 branch: b
195 branch: b
196 parent: 4:aee39cd168d0
196 parent: 4:aee39cd168d0
197 user: test
197 user: test
198 date: Thu Jan 01 00:00:07 1970 +0000
198 date: Thu Jan 01 00:00:07 1970 +0000
199 summary: adding cset to branch b
199 summary: adding cset to branch b
200
200
201 changeset: 7:10ff5895aa57
201 changeset: 7:10ff5895aa57
202 branch: a branch name much longer than the default justification used by branches
202 branch: a branch name much longer than the default justification used by branches
203 user: test
203 user: test
204 date: Thu Jan 01 00:00:06 1970 +0000
204 date: Thu Jan 01 00:00:06 1970 +0000
205 summary: Adding d branch
205 summary: Adding d branch
206
206
207 changeset: 6:589736a22561
207 changeset: 6:589736a22561
208 branch: c
208 branch: c
209 user: test
209 user: test
210 date: Thu Jan 01 00:00:05 1970 +0000
210 date: Thu Jan 01 00:00:05 1970 +0000
211 summary: Adding c branch
211 summary: Adding c branch
212
212
213 changeset: 5:d8cbc61dbaa6
213 changeset: 5:d8cbc61dbaa6
214 branch: a
214 branch: a
215 parent: 2:881fe2b92ad0
215 parent: 2:881fe2b92ad0
216 user: test
216 user: test
217 date: Thu Jan 01 00:00:04 1970 +0000
217 date: Thu Jan 01 00:00:04 1970 +0000
218 summary: Adding b branch head 2
218 summary: Adding b branch head 2
219
219
220 changeset: 0:19709c5a4e75
220 changeset: 0:19709c5a4e75
221 user: test
221 user: test
222 date: Thu Jan 01 00:00:00 1970 +0000
222 date: Thu Jan 01 00:00:00 1970 +0000
223 summary: Adding root node
223 summary: Adding root node
224
224
225 $ hg heads
225 $ hg heads
226 changeset: 10:bfbe841b666e
226 changeset: 10:bfbe841b666e
227 branch: b
227 branch: b
228 tag: tip
228 tag: tip
229 user: test
229 user: test
230 date: Thu Jan 01 00:00:09 1970 +0000
230 date: Thu Jan 01 00:00:09 1970 +0000
231 summary: adding another cset to branch b
231 summary: adding another cset to branch b
232
232
233 changeset: 8:eebb944467c9
233 changeset: 8:eebb944467c9
234 branch: b
234 branch: b
235 parent: 4:aee39cd168d0
235 parent: 4:aee39cd168d0
236 user: test
236 user: test
237 date: Thu Jan 01 00:00:07 1970 +0000
237 date: Thu Jan 01 00:00:07 1970 +0000
238 summary: adding cset to branch b
238 summary: adding cset to branch b
239
239
240 changeset: 7:10ff5895aa57
240 changeset: 7:10ff5895aa57
241 branch: a branch name much longer than the default justification used by branches
241 branch: a branch name much longer than the default justification used by branches
242 user: test
242 user: test
243 date: Thu Jan 01 00:00:06 1970 +0000
243 date: Thu Jan 01 00:00:06 1970 +0000
244 summary: Adding d branch
244 summary: Adding d branch
245
245
246 changeset: 6:589736a22561
246 changeset: 6:589736a22561
247 branch: c
247 branch: c
248 user: test
248 user: test
249 date: Thu Jan 01 00:00:05 1970 +0000
249 date: Thu Jan 01 00:00:05 1970 +0000
250 summary: Adding c branch
250 summary: Adding c branch
251
251
252 changeset: 5:d8cbc61dbaa6
252 changeset: 5:d8cbc61dbaa6
253 branch: a
253 branch: a
254 parent: 2:881fe2b92ad0
254 parent: 2:881fe2b92ad0
255 user: test
255 user: test
256 date: Thu Jan 01 00:00:04 1970 +0000
256 date: Thu Jan 01 00:00:04 1970 +0000
257 summary: Adding b branch head 2
257 summary: Adding b branch head 2
258
258
259 changeset: 0:19709c5a4e75
259 changeset: 0:19709c5a4e75
260 user: test
260 user: test
261 date: Thu Jan 01 00:00:00 1970 +0000
261 date: Thu Jan 01 00:00:00 1970 +0000
262 summary: Adding root node
262 summary: Adding root node
263
263
264 $ hg commit -d '9 0' --close-branch -m 'prune bad branch'
264 $ hg commit -d '9 0' --close-branch -m 'prune bad branch'
265 $ hg branches -a
265 $ hg branches -a
266 b 8:eebb944467c9
266 b 8:eebb944467c9
267 a branch name much longer than the default justification used by branches 7:10ff5895aa57
267 a branch name much longer than the default justification used by branches 7:10ff5895aa57
268 $ hg up -C b
268 $ hg up -C b
269 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
270 $ hg commit -d '9 0' --close-branch -m 'close this part branch too'
270 $ hg commit -d '9 0' --close-branch -m 'close this part branch too'
271 $ hg commit -d '9 0' --close-branch -m 're-closing this branch'
271 $ hg commit -d '9 0' --close-branch -m 're-closing this branch'
272 abort: can only close branch heads
272 abort: can only close branch heads
273 [255]
273 [255]
274
274
275 $ hg log -r tip --debug
275 $ hg log -r tip --debug
276 changeset: 12:e3d49c0575d8fc2cb1cd6859c747c14f5f6d499f
276 changeset: 12:e3d49c0575d8fc2cb1cd6859c747c14f5f6d499f
277 branch: b
277 branch: b
278 tag: tip
278 tag: tip
279 phase: draft
279 phase: draft
280 parent: 8:eebb944467c9fb9651ed232aeaf31b3c0a7fc6c1
280 parent: 8:eebb944467c9fb9651ed232aeaf31b3c0a7fc6c1
281 parent: -1:0000000000000000000000000000000000000000
281 parent: -1:0000000000000000000000000000000000000000
282 manifest: 8:6f9ed32d2b310e391a4f107d5f0f071df785bfee
282 manifest: 8:6f9ed32d2b310e391a4f107d5f0f071df785bfee
283 user: test
283 user: test
284 date: Thu Jan 01 00:00:09 1970 +0000
284 date: Thu Jan 01 00:00:09 1970 +0000
285 extra: branch=b
285 extra: branch=b
286 extra: close=1
286 extra: close=1
287 description:
287 description:
288 close this part branch too
288 close this part branch too
289
289
290
290
291 --- b branch should be inactive
291 --- b branch should be inactive
292
292
293 $ hg branches
293 $ hg branches
294 a branch name much longer than the default justification used by branches 7:10ff5895aa57
294 a branch name much longer than the default justification used by branches 7:10ff5895aa57
295 c 6:589736a22561 (inactive)
295 c 6:589736a22561 (inactive)
296 a 5:d8cbc61dbaa6 (inactive)
296 a 5:d8cbc61dbaa6 (inactive)
297 default 0:19709c5a4e75 (inactive)
297 default 0:19709c5a4e75 (inactive)
298 $ hg branches -c
298 $ hg branches -c
299 a branch name much longer than the default justification used by branches 7:10ff5895aa57
299 a branch name much longer than the default justification used by branches 7:10ff5895aa57
300 b 12:e3d49c0575d8 (closed)
300 b 12:e3d49c0575d8 (closed)
301 c 6:589736a22561 (inactive)
301 c 6:589736a22561 (inactive)
302 a 5:d8cbc61dbaa6 (inactive)
302 a 5:d8cbc61dbaa6 (inactive)
303 default 0:19709c5a4e75 (inactive)
303 default 0:19709c5a4e75 (inactive)
304 $ hg branches -a
304 $ hg branches -a
305 a branch name much longer than the default justification used by branches 7:10ff5895aa57
305 a branch name much longer than the default justification used by branches 7:10ff5895aa57
306 $ hg branches -q
306 $ hg branches -q
307 a branch name much longer than the default justification used by branches
307 a branch name much longer than the default justification used by branches
308 c
308 c
309 a
309 a
310 default
310 default
311 $ hg heads b
311 $ hg heads b
312 no open branch heads found on branches b
312 no open branch heads found on branches b
313 [1]
313 [1]
314 $ hg heads --closed b
314 $ hg heads --closed b
315 changeset: 12:e3d49c0575d8
315 changeset: 12:e3d49c0575d8
316 branch: b
316 branch: b
317 tag: tip
317 tag: tip
318 parent: 8:eebb944467c9
318 parent: 8:eebb944467c9
319 user: test
319 user: test
320 date: Thu Jan 01 00:00:09 1970 +0000
320 date: Thu Jan 01 00:00:09 1970 +0000
321 summary: close this part branch too
321 summary: close this part branch too
322
322
323 changeset: 11:d3f163457ebf
323 changeset: 11:d3f163457ebf
324 branch: b
324 branch: b
325 user: test
325 user: test
326 date: Thu Jan 01 00:00:09 1970 +0000
326 date: Thu Jan 01 00:00:09 1970 +0000
327 summary: prune bad branch
327 summary: prune bad branch
328
328
329 $ echo 'xxx4' >> b
329 $ echo 'xxx4' >> b
330 $ hg commit -d '9 0' -m 'reopen branch with a change'
330 $ hg commit -d '9 0' -m 'reopen branch with a change'
331 reopening closed branch head 12
331 reopening closed branch head 12
332
332
333 --- branch b is back in action
333 --- branch b is back in action
334
334
335 $ hg branches -a
335 $ hg branches -a
336 b 13:e23b5505d1ad
336 b 13:e23b5505d1ad
337 a branch name much longer than the default justification used by branches 7:10ff5895aa57
337 a branch name much longer than the default justification used by branches 7:10ff5895aa57
338
338
339 ---- test heads listings
339 ---- test heads listings
340
340
341 $ hg heads
341 $ hg heads
342 changeset: 13:e23b5505d1ad
342 changeset: 13:e23b5505d1ad
343 branch: b
343 branch: b
344 tag: tip
344 tag: tip
345 user: test
345 user: test
346 date: Thu Jan 01 00:00:09 1970 +0000
346 date: Thu Jan 01 00:00:09 1970 +0000
347 summary: reopen branch with a change
347 summary: reopen branch with a change
348
348
349 changeset: 7:10ff5895aa57
349 changeset: 7:10ff5895aa57
350 branch: a branch name much longer than the default justification used by branches
350 branch: a branch name much longer than the default justification used by branches
351 user: test
351 user: test
352 date: Thu Jan 01 00:00:06 1970 +0000
352 date: Thu Jan 01 00:00:06 1970 +0000
353 summary: Adding d branch
353 summary: Adding d branch
354
354
355 changeset: 6:589736a22561
355 changeset: 6:589736a22561
356 branch: c
356 branch: c
357 user: test
357 user: test
358 date: Thu Jan 01 00:00:05 1970 +0000
358 date: Thu Jan 01 00:00:05 1970 +0000
359 summary: Adding c branch
359 summary: Adding c branch
360
360
361 changeset: 5:d8cbc61dbaa6
361 changeset: 5:d8cbc61dbaa6
362 branch: a
362 branch: a
363 parent: 2:881fe2b92ad0
363 parent: 2:881fe2b92ad0
364 user: test
364 user: test
365 date: Thu Jan 01 00:00:04 1970 +0000
365 date: Thu Jan 01 00:00:04 1970 +0000
366 summary: Adding b branch head 2
366 summary: Adding b branch head 2
367
367
368 changeset: 0:19709c5a4e75
368 changeset: 0:19709c5a4e75
369 user: test
369 user: test
370 date: Thu Jan 01 00:00:00 1970 +0000
370 date: Thu Jan 01 00:00:00 1970 +0000
371 summary: Adding root node
371 summary: Adding root node
372
372
373
373
374 branch default
374 branch default
375
375
376 $ hg heads default
376 $ hg heads default
377 changeset: 0:19709c5a4e75
377 changeset: 0:19709c5a4e75
378 user: test
378 user: test
379 date: Thu Jan 01 00:00:00 1970 +0000
379 date: Thu Jan 01 00:00:00 1970 +0000
380 summary: Adding root node
380 summary: Adding root node
381
381
382
382
383 branch a
383 branch a
384
384
385 $ hg heads a
385 $ hg heads a
386 changeset: 5:d8cbc61dbaa6
386 changeset: 5:d8cbc61dbaa6
387 branch: a
387 branch: a
388 parent: 2:881fe2b92ad0
388 parent: 2:881fe2b92ad0
389 user: test
389 user: test
390 date: Thu Jan 01 00:00:04 1970 +0000
390 date: Thu Jan 01 00:00:04 1970 +0000
391 summary: Adding b branch head 2
391 summary: Adding b branch head 2
392
392
393 $ hg heads --active a
393 $ hg heads --active a
394 no open branch heads found on branches a
394 no open branch heads found on branches a
395 [1]
395 [1]
396
396
397 branch b
397 branch b
398
398
399 $ hg heads b
399 $ hg heads b
400 changeset: 13:e23b5505d1ad
400 changeset: 13:e23b5505d1ad
401 branch: b
401 branch: b
402 tag: tip
402 tag: tip
403 user: test
403 user: test
404 date: Thu Jan 01 00:00:09 1970 +0000
404 date: Thu Jan 01 00:00:09 1970 +0000
405 summary: reopen branch with a change
405 summary: reopen branch with a change
406
406
407 $ hg heads --closed b
407 $ hg heads --closed b
408 changeset: 13:e23b5505d1ad
408 changeset: 13:e23b5505d1ad
409 branch: b
409 branch: b
410 tag: tip
410 tag: tip
411 user: test
411 user: test
412 date: Thu Jan 01 00:00:09 1970 +0000
412 date: Thu Jan 01 00:00:09 1970 +0000
413 summary: reopen branch with a change
413 summary: reopen branch with a change
414
414
415 changeset: 11:d3f163457ebf
415 changeset: 11:d3f163457ebf
416 branch: b
416 branch: b
417 user: test
417 user: test
418 date: Thu Jan 01 00:00:09 1970 +0000
418 date: Thu Jan 01 00:00:09 1970 +0000
419 summary: prune bad branch
419 summary: prune bad branch
420
420
421 default branch colors:
421 default branch colors:
422
422
423 $ cat <<EOF >> $HGRCPATH
423 $ cat <<EOF >> $HGRCPATH
424 > [extensions]
424 > [extensions]
425 > color =
425 > color =
426 > [color]
426 > [color]
427 > mode = ansi
427 > mode = ansi
428 > EOF
428 > EOF
429
429
430 $ hg up -C c
430 $ hg up -C c
431 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
431 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
432 $ hg commit -d '9 0' --close-branch -m 'reclosing this branch'
432 $ hg commit -d '9 0' --close-branch -m 'reclosing this branch'
433 $ hg up -C b
433 $ hg up -C b
434 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
434 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
435 $ hg branches --color=always
435 $ hg branches --color=always
436 \x1b[0;32mb\x1b[0m\x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
436 \x1b[0;32mb\x1b[0m\x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
437 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;33m 7:10ff5895aa57\x1b[0m (esc)
437 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;33m 7:10ff5895aa57\x1b[0m (esc)
438 \x1b[0;0ma\x1b[0m\x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
438 \x1b[0;0ma\x1b[0m\x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
439 \x1b[0;0mdefault\x1b[0m\x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
439 \x1b[0;0mdefault\x1b[0m\x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
440
440
441 default closed branch color:
441 default closed branch color:
442
442
443 $ hg branches --color=always --closed
443 $ hg branches --color=always --closed
444 \x1b[0;32mb\x1b[0m\x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
444 \x1b[0;32mb\x1b[0m\x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
445 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;33m 7:10ff5895aa57\x1b[0m (esc)
445 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;33m 7:10ff5895aa57\x1b[0m (esc)
446 \x1b[0;30;1mc\x1b[0m\x1b[0;33m 14:f894c25619d3\x1b[0m (closed) (esc)
446 \x1b[0;30;1mc\x1b[0m\x1b[0;33m 14:f894c25619d3\x1b[0m (closed) (esc)
447 \x1b[0;0ma\x1b[0m\x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
447 \x1b[0;0ma\x1b[0m\x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
448 \x1b[0;0mdefault\x1b[0m\x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
448 \x1b[0;0mdefault\x1b[0m\x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
449
449
450 $ cat <<EOF >> $HGRCPATH
450 $ cat <<EOF >> $HGRCPATH
451 > [extensions]
451 > [extensions]
452 > color =
452 > color =
453 > [color]
453 > [color]
454 > branches.active = green
454 > branches.active = green
455 > branches.closed = blue
455 > branches.closed = blue
456 > branches.current = red
456 > branches.current = red
457 > branches.inactive = magenta
457 > branches.inactive = magenta
458 > log.changeset = cyan
458 > log.changeset = cyan
459 > EOF
459 > EOF
460
460
461 custom branch colors:
461 custom branch colors:
462
462
463 $ hg branches --color=always
463 $ hg branches --color=always
464 \x1b[0;31mb\x1b[0m\x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
464 \x1b[0;31mb\x1b[0m\x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
465 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;36m 7:10ff5895aa57\x1b[0m (esc)
465 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;36m 7:10ff5895aa57\x1b[0m (esc)
466 \x1b[0;35ma\x1b[0m\x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
466 \x1b[0;35ma\x1b[0m\x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
467 \x1b[0;35mdefault\x1b[0m\x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
467 \x1b[0;35mdefault\x1b[0m\x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
468
468
469 custom closed branch color:
469 custom closed branch color:
470
470
471 $ hg branches --color=always --closed
471 $ hg branches --color=always --closed
472 \x1b[0;31mb\x1b[0m\x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
472 \x1b[0;31mb\x1b[0m\x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
473 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;36m 7:10ff5895aa57\x1b[0m (esc)
473 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;36m 7:10ff5895aa57\x1b[0m (esc)
474 \x1b[0;34mc\x1b[0m\x1b[0;36m 14:f894c25619d3\x1b[0m (closed) (esc)
474 \x1b[0;34mc\x1b[0m\x1b[0;36m 14:f894c25619d3\x1b[0m (closed) (esc)
475 \x1b[0;35ma\x1b[0m\x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
475 \x1b[0;35ma\x1b[0m\x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
476 \x1b[0;35mdefault\x1b[0m\x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
476 \x1b[0;35mdefault\x1b[0m\x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
477
477
478 template output:
478 template output:
479
479
480 $ hg branches -Tjson --closed
480 $ hg branches -Tjson --closed
481 [
481 [
482 {
482 {
483 "active": true,
483 "active": true,
484 "branch": "b",
484 "branch": "b",
485 "closed": false,
485 "closed": false,
486 "current": true,
486 "current": true,
487 "node": "e23b5505d1ad24aab6f84fd8c7cb8cd8e5e93be0",
487 "node": "e23b5505d1ad24aab6f84fd8c7cb8cd8e5e93be0",
488 "rev": 13
488 "rev": 13
489 },
489 },
490 {
490 {
491 "active": true,
491 "active": true,
492 "branch": "a branch name much longer than the default justification used by branches",
492 "branch": "a branch name much longer than the default justification used by branches",
493 "closed": false,
493 "closed": false,
494 "current": false,
494 "current": false,
495 "node": "10ff5895aa5793bd378da574af8cec8ea408d831",
495 "node": "10ff5895aa5793bd378da574af8cec8ea408d831",
496 "rev": 7
496 "rev": 7
497 },
497 },
498 {
498 {
499 "active": false,
499 "active": false,
500 "branch": "c",
500 "branch": "c",
501 "closed": true,
501 "closed": true,
502 "current": false,
502 "current": false,
503 "node": "f894c25619d3f1484639d81be950e0a07bc6f1f6",
503 "node": "f894c25619d3f1484639d81be950e0a07bc6f1f6",
504 "rev": 14
504 "rev": 14
505 },
505 },
506 {
506 {
507 "active": false,
507 "active": false,
508 "branch": "a",
508 "branch": "a",
509 "closed": false,
509 "closed": false,
510 "current": false,
510 "current": false,
511 "node": "d8cbc61dbaa6dc817175d1e301eecb863f280832",
511 "node": "d8cbc61dbaa6dc817175d1e301eecb863f280832",
512 "rev": 5
512 "rev": 5
513 },
513 },
514 {
514 {
515 "active": false,
515 "active": false,
516 "branch": "default",
516 "branch": "default",
517 "closed": false,
517 "closed": false,
518 "current": false,
518 "current": false,
519 "node": "19709c5a4e75bf938f8e349aff97438539bb729e",
519 "node": "19709c5a4e75bf938f8e349aff97438539bb729e",
520 "rev": 0
520 "rev": 0
521 }
521 }
522 ]
522 ]
523
523
524 $ hg branches --closed -T '{if(closed, "{branch}\n")}'
524 $ hg branches --closed -T '{if(closed, "{branch}\n")}'
525 c
525 c
526
526
527 $ hg branches -T '{word(0, branch)}: {desc|firstline}\n'
527 $ hg branches -T '{word(0, branch)}: {desc|firstline}\n'
528 b: reopen branch with a change
528 b: reopen branch with a change
529 a: Adding d branch
529 a: Adding d branch
530 a: Adding b branch head 2
530 a: Adding b branch head 2
531 default: Adding root node
531 default: Adding root node
532
532
533 $ cat <<'EOF' > "$TESTTMP/map-myjson"
533 $ cat <<'EOF' > "$TESTTMP/map-myjson"
534 > docheader = '\{\n'
534 > docheader = '\{\n'
535 > docfooter = '\n}\n'
535 > docfooter = '\n}\n'
536 > separator = ',\n'
536 > separator = ',\n'
537 > branches = ' {dict(branch, node|short)|json}'
537 > branches = ' {dict(branch, node|short)|json}'
538 > EOF
538 > EOF
539 $ hg branches -T "$TESTTMP/map-myjson"
539 $ hg branches -T "$TESTTMP/map-myjson"
540 {
540 {
541 {"branch": "b", "node": "e23b5505d1ad"},
541 {"branch": "b", "node": "e23b5505d1ad"},
542 {"branch": "a branch *", "node": "10ff5895aa57"}, (glob)
542 {"branch": "a branch *", "node": "10ff5895aa57"}, (glob)
543 {"branch": "a", "node": "d8cbc61dbaa6"},
543 {"branch": "a", "node": "d8cbc61dbaa6"},
544 {"branch": "default", "node": "19709c5a4e75"}
544 {"branch": "default", "node": "19709c5a4e75"}
545 }
545 }
546
546
547 $ cat <<'EOF' >> .hg/hgrc
548 > [templates]
549 > myjson = ' {dict(branch, node|short)|json}'
550 > myjson:docheader = '\{\n'
551 > myjson:docfooter = '\n}\n'
552 > myjson:separator = ',\n'
553 > EOF
554 $ hg branches -T myjson
555 {
556 {"branch": "b", "node": "e23b5505d1ad"},
557 {"branch": "a branch *", "node": "10ff5895aa57"}, (glob)
558 {"branch": "a", "node": "d8cbc61dbaa6"},
559 {"branch": "default", "node": "19709c5a4e75"}
560 }
561
562 $ cat <<'EOF' >> .hg/hgrc
563 > [templates]
564 > :docheader = 'should not be selected as a docheader for literal templates\n'
565 > EOF
566 $ hg branches -T '{branch}\n'
567 b
568 a branch name much longer than the default justification used by branches
569 a
570 default
571
547 Tests of revision branch name caching
572 Tests of revision branch name caching
548
573
549 We rev branch cache is updated automatically. In these tests we use a trick to
574 We rev branch cache is updated automatically. In these tests we use a trick to
550 trigger rebuilds. We remove the branch head cache and run 'hg head' to cause a
575 trigger rebuilds. We remove the branch head cache and run 'hg head' to cause a
551 rebuild that also will populate the rev branch cache.
576 rebuild that also will populate the rev branch cache.
552
577
553 revision branch cache is created when building the branch head cache
578 revision branch cache is created when building the branch head cache
554 $ rm -rf .hg/cache; hg head a -T '{rev}\n'
579 $ rm -rf .hg/cache; hg head a -T '{rev}\n'
555 5
580 5
556 $ f --hexdump --size .hg/cache/rbc-*
581 $ f --hexdump --size .hg/cache/rbc-*
557 .hg/cache/rbc-names-v1: size=87
582 .hg/cache/rbc-names-v1: size=87
558 0000: 64 65 66 61 75 6c 74 00 61 00 62 00 63 00 61 20 |default.a.b.c.a |
583 0000: 64 65 66 61 75 6c 74 00 61 00 62 00 63 00 61 20 |default.a.b.c.a |
559 0010: 62 72 61 6e 63 68 20 6e 61 6d 65 20 6d 75 63 68 |branch name much|
584 0010: 62 72 61 6e 63 68 20 6e 61 6d 65 20 6d 75 63 68 |branch name much|
560 0020: 20 6c 6f 6e 67 65 72 20 74 68 61 6e 20 74 68 65 | longer than the|
585 0020: 20 6c 6f 6e 67 65 72 20 74 68 61 6e 20 74 68 65 | longer than the|
561 0030: 20 64 65 66 61 75 6c 74 20 6a 75 73 74 69 66 69 | default justifi|
586 0030: 20 64 65 66 61 75 6c 74 20 6a 75 73 74 69 66 69 | default justifi|
562 0040: 63 61 74 69 6f 6e 20 75 73 65 64 20 62 79 20 62 |cation used by b|
587 0040: 63 61 74 69 6f 6e 20 75 73 65 64 20 62 79 20 62 |cation used by b|
563 0050: 72 61 6e 63 68 65 73 |ranches|
588 0050: 72 61 6e 63 68 65 73 |ranches|
564 .hg/cache/rbc-revs-v1: size=120
589 .hg/cache/rbc-revs-v1: size=120
565 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
590 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
566 0010: 88 1f e2 b9 00 00 00 01 ac 22 03 33 00 00 00 02 |.........".3....|
591 0010: 88 1f e2 b9 00 00 00 01 ac 22 03 33 00 00 00 02 |.........".3....|
567 0020: ae e3 9c d1 00 00 00 02 d8 cb c6 1d 00 00 00 01 |................|
592 0020: ae e3 9c d1 00 00 00 02 d8 cb c6 1d 00 00 00 01 |................|
568 0030: 58 97 36 a2 00 00 00 03 10 ff 58 95 00 00 00 04 |X.6.......X.....|
593 0030: 58 97 36 a2 00 00 00 03 10 ff 58 95 00 00 00 04 |X.6.......X.....|
569 0040: ee bb 94 44 00 00 00 02 5f 40 61 bb 00 00 00 02 |...D...._@a.....|
594 0040: ee bb 94 44 00 00 00 02 5f 40 61 bb 00 00 00 02 |...D...._@a.....|
570 0050: bf be 84 1b 00 00 00 02 d3 f1 63 45 80 00 00 02 |..........cE....|
595 0050: bf be 84 1b 00 00 00 02 d3 f1 63 45 80 00 00 02 |..........cE....|
571 0060: e3 d4 9c 05 80 00 00 02 e2 3b 55 05 00 00 00 02 |.........;U.....|
596 0060: e3 d4 9c 05 80 00 00 02 e2 3b 55 05 00 00 00 02 |.........;U.....|
572 0070: f8 94 c2 56 80 00 00 03 |...V....|
597 0070: f8 94 c2 56 80 00 00 03 |...V....|
573
598
574 no errors when revbranchcache is not writable
599 no errors when revbranchcache is not writable
575
600
576 $ echo >> .hg/cache/rbc-revs-v1
601 $ echo >> .hg/cache/rbc-revs-v1
577 $ mv .hg/cache/rbc-revs-v1 .hg/cache/rbc-revs-v1_
602 $ mv .hg/cache/rbc-revs-v1 .hg/cache/rbc-revs-v1_
578 $ mkdir .hg/cache/rbc-revs-v1
603 $ mkdir .hg/cache/rbc-revs-v1
579 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n'
604 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n'
580 5
605 5
581 $ rmdir .hg/cache/rbc-revs-v1
606 $ rmdir .hg/cache/rbc-revs-v1
582 $ mv .hg/cache/rbc-revs-v1_ .hg/cache/rbc-revs-v1
607 $ mv .hg/cache/rbc-revs-v1_ .hg/cache/rbc-revs-v1
583
608
584 no errors when wlock cannot be acquired
609 no errors when wlock cannot be acquired
585
610
586 #if unix-permissions
611 #if unix-permissions
587 $ mv .hg/cache/rbc-revs-v1 .hg/cache/rbc-revs-v1_
612 $ mv .hg/cache/rbc-revs-v1 .hg/cache/rbc-revs-v1_
588 $ rm -f .hg/cache/branch*
613 $ rm -f .hg/cache/branch*
589 $ chmod 555 .hg
614 $ chmod 555 .hg
590 $ hg head a -T '{rev}\n'
615 $ hg head a -T '{rev}\n'
591 5
616 5
592 $ chmod 755 .hg
617 $ chmod 755 .hg
593 $ mv .hg/cache/rbc-revs-v1_ .hg/cache/rbc-revs-v1
618 $ mv .hg/cache/rbc-revs-v1_ .hg/cache/rbc-revs-v1
594 #endif
619 #endif
595
620
596 recovery from invalid cache revs file with trailing data
621 recovery from invalid cache revs file with trailing data
597 $ echo >> .hg/cache/rbc-revs-v1
622 $ echo >> .hg/cache/rbc-revs-v1
598 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
623 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
599 5
624 5
600 truncating cache/rbc-revs-v1 to 120
625 truncating cache/rbc-revs-v1 to 120
601 $ f --size .hg/cache/rbc-revs*
626 $ f --size .hg/cache/rbc-revs*
602 .hg/cache/rbc-revs-v1: size=120
627 .hg/cache/rbc-revs-v1: size=120
603 recovery from invalid cache file with partial last record
628 recovery from invalid cache file with partial last record
604 $ mv .hg/cache/rbc-revs-v1 .
629 $ mv .hg/cache/rbc-revs-v1 .
605 $ f -qDB 119 rbc-revs-v1 > .hg/cache/rbc-revs-v1
630 $ f -qDB 119 rbc-revs-v1 > .hg/cache/rbc-revs-v1
606 $ f --size .hg/cache/rbc-revs*
631 $ f --size .hg/cache/rbc-revs*
607 .hg/cache/rbc-revs-v1: size=119
632 .hg/cache/rbc-revs-v1: size=119
608 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
633 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
609 5
634 5
610 truncating cache/rbc-revs-v1 to 112
635 truncating cache/rbc-revs-v1 to 112
611 $ f --size .hg/cache/rbc-revs*
636 $ f --size .hg/cache/rbc-revs*
612 .hg/cache/rbc-revs-v1: size=120
637 .hg/cache/rbc-revs-v1: size=120
613 recovery from invalid cache file with missing record - no truncation
638 recovery from invalid cache file with missing record - no truncation
614 $ mv .hg/cache/rbc-revs-v1 .
639 $ mv .hg/cache/rbc-revs-v1 .
615 $ f -qDB 112 rbc-revs-v1 > .hg/cache/rbc-revs-v1
640 $ f -qDB 112 rbc-revs-v1 > .hg/cache/rbc-revs-v1
616 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
641 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
617 5
642 5
618 $ f --size .hg/cache/rbc-revs*
643 $ f --size .hg/cache/rbc-revs*
619 .hg/cache/rbc-revs-v1: size=120
644 .hg/cache/rbc-revs-v1: size=120
620 recovery from invalid cache file with some bad records
645 recovery from invalid cache file with some bad records
621 $ mv .hg/cache/rbc-revs-v1 .
646 $ mv .hg/cache/rbc-revs-v1 .
622 $ f -qDB 8 rbc-revs-v1 > .hg/cache/rbc-revs-v1
647 $ f -qDB 8 rbc-revs-v1 > .hg/cache/rbc-revs-v1
623 $ f --size .hg/cache/rbc-revs*
648 $ f --size .hg/cache/rbc-revs*
624 .hg/cache/rbc-revs-v1: size=8
649 .hg/cache/rbc-revs-v1: size=8
625 $ f -qDB 112 rbc-revs-v1 >> .hg/cache/rbc-revs-v1
650 $ f -qDB 112 rbc-revs-v1 >> .hg/cache/rbc-revs-v1
626 $ f --size .hg/cache/rbc-revs*
651 $ f --size .hg/cache/rbc-revs*
627 .hg/cache/rbc-revs-v1: size=120
652 .hg/cache/rbc-revs-v1: size=120
628 $ hg log -r 'branch(.)' -T '{rev} ' --debug
653 $ hg log -r 'branch(.)' -T '{rev} ' --debug
629 history modification detected - truncating revision branch cache to revision 13
654 history modification detected - truncating revision branch cache to revision 13
630 history modification detected - truncating revision branch cache to revision 1
655 history modification detected - truncating revision branch cache to revision 1
631 3 4 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 8
656 3 4 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 8
632 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
657 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
633 5
658 5
634 truncating cache/rbc-revs-v1 to 104
659 truncating cache/rbc-revs-v1 to 104
635 $ f --size --hexdump --bytes=16 .hg/cache/rbc-revs*
660 $ f --size --hexdump --bytes=16 .hg/cache/rbc-revs*
636 .hg/cache/rbc-revs-v1: size=120
661 .hg/cache/rbc-revs-v1: size=120
637 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
662 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
638 cache is updated when committing
663 cache is updated when committing
639 $ hg branch i-will-regret-this
664 $ hg branch i-will-regret-this
640 marked working directory as branch i-will-regret-this
665 marked working directory as branch i-will-regret-this
641 $ hg ci -m regrets
666 $ hg ci -m regrets
642 $ f --size .hg/cache/rbc-*
667 $ f --size .hg/cache/rbc-*
643 .hg/cache/rbc-names-v1: size=106
668 .hg/cache/rbc-names-v1: size=106
644 .hg/cache/rbc-revs-v1: size=128
669 .hg/cache/rbc-revs-v1: size=128
645 update after rollback - the cache will be correct but rbc-names will will still
670 update after rollback - the cache will be correct but rbc-names will will still
646 contain the branch name even though it no longer is used
671 contain the branch name even though it no longer is used
647 $ hg up -qr '.^'
672 $ hg up -qr '.^'
648 $ hg rollback -qf
673 $ hg rollback -qf
649 $ f --size --hexdump .hg/cache/rbc-*
674 $ f --size --hexdump .hg/cache/rbc-*
650 .hg/cache/rbc-names-v1: size=106
675 .hg/cache/rbc-names-v1: size=106
651 0000: 64 65 66 61 75 6c 74 00 61 00 62 00 63 00 61 20 |default.a.b.c.a |
676 0000: 64 65 66 61 75 6c 74 00 61 00 62 00 63 00 61 20 |default.a.b.c.a |
652 0010: 62 72 61 6e 63 68 20 6e 61 6d 65 20 6d 75 63 68 |branch name much|
677 0010: 62 72 61 6e 63 68 20 6e 61 6d 65 20 6d 75 63 68 |branch name much|
653 0020: 20 6c 6f 6e 67 65 72 20 74 68 61 6e 20 74 68 65 | longer than the|
678 0020: 20 6c 6f 6e 67 65 72 20 74 68 61 6e 20 74 68 65 | longer than the|
654 0030: 20 64 65 66 61 75 6c 74 20 6a 75 73 74 69 66 69 | default justifi|
679 0030: 20 64 65 66 61 75 6c 74 20 6a 75 73 74 69 66 69 | default justifi|
655 0040: 63 61 74 69 6f 6e 20 75 73 65 64 20 62 79 20 62 |cation used by b|
680 0040: 63 61 74 69 6f 6e 20 75 73 65 64 20 62 79 20 62 |cation used by b|
656 0050: 72 61 6e 63 68 65 73 00 69 2d 77 69 6c 6c 2d 72 |ranches.i-will-r|
681 0050: 72 61 6e 63 68 65 73 00 69 2d 77 69 6c 6c 2d 72 |ranches.i-will-r|
657 0060: 65 67 72 65 74 2d 74 68 69 73 |egret-this|
682 0060: 65 67 72 65 74 2d 74 68 69 73 |egret-this|
658 .hg/cache/rbc-revs-v1: size=120
683 .hg/cache/rbc-revs-v1: size=120
659 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
684 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
660 0010: 88 1f e2 b9 00 00 00 01 ac 22 03 33 00 00 00 02 |.........".3....|
685 0010: 88 1f e2 b9 00 00 00 01 ac 22 03 33 00 00 00 02 |.........".3....|
661 0020: ae e3 9c d1 00 00 00 02 d8 cb c6 1d 00 00 00 01 |................|
686 0020: ae e3 9c d1 00 00 00 02 d8 cb c6 1d 00 00 00 01 |................|
662 0030: 58 97 36 a2 00 00 00 03 10 ff 58 95 00 00 00 04 |X.6.......X.....|
687 0030: 58 97 36 a2 00 00 00 03 10 ff 58 95 00 00 00 04 |X.6.......X.....|
663 0040: ee bb 94 44 00 00 00 02 5f 40 61 bb 00 00 00 02 |...D...._@a.....|
688 0040: ee bb 94 44 00 00 00 02 5f 40 61 bb 00 00 00 02 |...D...._@a.....|
664 0050: bf be 84 1b 00 00 00 02 d3 f1 63 45 80 00 00 02 |..........cE....|
689 0050: bf be 84 1b 00 00 00 02 d3 f1 63 45 80 00 00 02 |..........cE....|
665 0060: e3 d4 9c 05 80 00 00 02 e2 3b 55 05 00 00 00 02 |.........;U.....|
690 0060: e3 d4 9c 05 80 00 00 02 e2 3b 55 05 00 00 00 02 |.........;U.....|
666 0070: f8 94 c2 56 80 00 00 03 |...V....|
691 0070: f8 94 c2 56 80 00 00 03 |...V....|
667 cache is updated/truncated when stripping - it is thus very hard to get in a
692 cache is updated/truncated when stripping - it is thus very hard to get in a
668 situation where the cache is out of sync and the hash check detects it
693 situation where the cache is out of sync and the hash check detects it
669 $ hg --config extensions.strip= strip -r tip --nob
694 $ hg --config extensions.strip= strip -r tip --nob
670 $ f --size .hg/cache/rbc-revs*
695 $ f --size .hg/cache/rbc-revs*
671 .hg/cache/rbc-revs-v1: size=112
696 .hg/cache/rbc-revs-v1: size=112
672
697
673 cache is rebuilt when corruption is detected
698 cache is rebuilt when corruption is detected
674 $ echo > .hg/cache/rbc-names-v1
699 $ echo > .hg/cache/rbc-names-v1
675 $ hg log -r '5:&branch(.)' -T '{rev} ' --debug
700 $ hg log -r '5:&branch(.)' -T '{rev} ' --debug
676 referenced branch names not found - rebuilding revision branch cache from scratch
701 referenced branch names not found - rebuilding revision branch cache from scratch
677 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 40
702 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 40
678 $ f --size --hexdump .hg/cache/rbc-*
703 $ f --size --hexdump .hg/cache/rbc-*
679 .hg/cache/rbc-names-v1: size=79
704 .hg/cache/rbc-names-v1: size=79
680 0000: 62 00 61 00 63 00 61 20 62 72 61 6e 63 68 20 6e |b.a.c.a branch n|
705 0000: 62 00 61 00 63 00 61 20 62 72 61 6e 63 68 20 6e |b.a.c.a branch n|
681 0010: 61 6d 65 20 6d 75 63 68 20 6c 6f 6e 67 65 72 20 |ame much longer |
706 0010: 61 6d 65 20 6d 75 63 68 20 6c 6f 6e 67 65 72 20 |ame much longer |
682 0020: 74 68 61 6e 20 74 68 65 20 64 65 66 61 75 6c 74 |than the default|
707 0020: 74 68 61 6e 20 74 68 65 20 64 65 66 61 75 6c 74 |than the default|
683 0030: 20 6a 75 73 74 69 66 69 63 61 74 69 6f 6e 20 75 | justification u|
708 0030: 20 6a 75 73 74 69 66 69 63 61 74 69 6f 6e 20 75 | justification u|
684 0040: 73 65 64 20 62 79 20 62 72 61 6e 63 68 65 73 |sed by branches|
709 0040: 73 65 64 20 62 79 20 62 72 61 6e 63 68 65 73 |sed by branches|
685 .hg/cache/rbc-revs-v1: size=112
710 .hg/cache/rbc-revs-v1: size=112
686 0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
711 0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
687 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
712 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
688 0020: 00 00 00 00 00 00 00 00 d8 cb c6 1d 00 00 00 01 |................|
713 0020: 00 00 00 00 00 00 00 00 d8 cb c6 1d 00 00 00 01 |................|
689 0030: 58 97 36 a2 00 00 00 02 10 ff 58 95 00 00 00 03 |X.6.......X.....|
714 0030: 58 97 36 a2 00 00 00 02 10 ff 58 95 00 00 00 03 |X.6.......X.....|
690 0040: ee bb 94 44 00 00 00 00 5f 40 61 bb 00 00 00 00 |...D...._@a.....|
715 0040: ee bb 94 44 00 00 00 00 5f 40 61 bb 00 00 00 00 |...D...._@a.....|
691 0050: bf be 84 1b 00 00 00 00 d3 f1 63 45 80 00 00 00 |..........cE....|
716 0050: bf be 84 1b 00 00 00 00 d3 f1 63 45 80 00 00 00 |..........cE....|
692 0060: e3 d4 9c 05 80 00 00 00 e2 3b 55 05 00 00 00 00 |.........;U.....|
717 0060: e3 d4 9c 05 80 00 00 00 e2 3b 55 05 00 00 00 00 |.........;U.....|
693
718
694 Test that cache files are created and grows correctly:
719 Test that cache files are created and grows correctly:
695
720
696 $ rm .hg/cache/rbc*
721 $ rm .hg/cache/rbc*
697 $ hg log -r "5 & branch(5)" -T "{rev}\n"
722 $ hg log -r "5 & branch(5)" -T "{rev}\n"
698 5
723 5
699 $ f --size --hexdump .hg/cache/rbc-*
724 $ f --size --hexdump .hg/cache/rbc-*
700 .hg/cache/rbc-names-v1: size=1
725 .hg/cache/rbc-names-v1: size=1
701 0000: 61 |a|
726 0000: 61 |a|
702 .hg/cache/rbc-revs-v1: size=112
727 .hg/cache/rbc-revs-v1: size=112
703 0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
728 0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
704 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
729 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
705 0020: 00 00 00 00 00 00 00 00 d8 cb c6 1d 00 00 00 00 |................|
730 0020: 00 00 00 00 00 00 00 00 d8 cb c6 1d 00 00 00 00 |................|
706 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
731 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
707 0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
732 0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
708 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
733 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
709 0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
734 0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
710
735
711 $ cd ..
736 $ cd ..
712
737
713 Test for multiple incorrect branch cache entries:
738 Test for multiple incorrect branch cache entries:
714
739
715 $ hg init b
740 $ hg init b
716 $ cd b
741 $ cd b
717 $ touch f
742 $ touch f
718 $ hg ci -Aqmf
743 $ hg ci -Aqmf
719 $ echo >> f
744 $ echo >> f
720 $ hg ci -Amf
745 $ hg ci -Amf
721 $ hg branch -q branch
746 $ hg branch -q branch
722 $ hg ci -Amf
747 $ hg ci -Amf
723
748
724 $ f --size --hexdump .hg/cache/rbc-*
749 $ f --size --hexdump .hg/cache/rbc-*
725 .hg/cache/rbc-names-v1: size=14
750 .hg/cache/rbc-names-v1: size=14
726 0000: 64 65 66 61 75 6c 74 00 62 72 61 6e 63 68 |default.branch|
751 0000: 64 65 66 61 75 6c 74 00 62 72 61 6e 63 68 |default.branch|
727 .hg/cache/rbc-revs-v1: size=24
752 .hg/cache/rbc-revs-v1: size=24
728 0000: 66 e5 f5 aa 00 00 00 00 fa 4c 04 e5 00 00 00 00 |f........L......|
753 0000: 66 e5 f5 aa 00 00 00 00 fa 4c 04 e5 00 00 00 00 |f........L......|
729 0010: 56 46 78 69 00 00 00 01 |VFxi....|
754 0010: 56 46 78 69 00 00 00 01 |VFxi....|
730 $ : > .hg/cache/rbc-revs-v1
755 $ : > .hg/cache/rbc-revs-v1
731
756
732 No superfluous rebuilding of cache:
757 No superfluous rebuilding of cache:
733 $ hg log -r "branch(null)&branch(branch)" --debug
758 $ hg log -r "branch(null)&branch(branch)" --debug
734 $ f --size --hexdump .hg/cache/rbc-*
759 $ f --size --hexdump .hg/cache/rbc-*
735 .hg/cache/rbc-names-v1: size=14
760 .hg/cache/rbc-names-v1: size=14
736 0000: 64 65 66 61 75 6c 74 00 62 72 61 6e 63 68 |default.branch|
761 0000: 64 65 66 61 75 6c 74 00 62 72 61 6e 63 68 |default.branch|
737 .hg/cache/rbc-revs-v1: size=24
762 .hg/cache/rbc-revs-v1: size=24
738 0000: 66 e5 f5 aa 00 00 00 00 fa 4c 04 e5 00 00 00 00 |f........L......|
763 0000: 66 e5 f5 aa 00 00 00 00 fa 4c 04 e5 00 00 00 00 |f........L......|
739 0010: 56 46 78 69 00 00 00 01 |VFxi....|
764 0010: 56 46 78 69 00 00 00 01 |VFxi....|
740
765
741 $ cd ..
766 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now