##// END OF EJS Templates
templatekw: make experimental {peerpaths} return a single-level dict (BC)...
Yuya Nishihara -
r34539:ac38e889 default
parent child Browse files
Show More
@@ -1,892 +1,887 b''
1 # templatekw.py - common changeset template keywords
1 # templatekw.py - common changeset template keywords
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from .i18n import _
10 from .i18n import _
11 from .node import (
11 from .node import (
12 hex,
12 hex,
13 nullid,
13 nullid,
14 )
14 )
15
15
16 from . import (
16 from . import (
17 encoding,
17 encoding,
18 error,
18 error,
19 hbisect,
19 hbisect,
20 obsutil,
20 obsutil,
21 patch,
21 patch,
22 pycompat,
22 pycompat,
23 registrar,
23 registrar,
24 scmutil,
24 scmutil,
25 util,
25 util,
26 )
26 )
27
27
28 class _hybrid(object):
28 class _hybrid(object):
29 """Wrapper for list or dict to support legacy template
29 """Wrapper for list or dict to support legacy template
30
30
31 This class allows us to handle both:
31 This class allows us to handle both:
32 - "{files}" (legacy command-line-specific list hack) and
32 - "{files}" (legacy command-line-specific list hack) and
33 - "{files % '{file}\n'}" (hgweb-style with inlining and function support)
33 - "{files % '{file}\n'}" (hgweb-style with inlining and function support)
34 and to access raw values:
34 and to access raw values:
35 - "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
35 - "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
36 - "{get(extras, key)}"
36 - "{get(extras, key)}"
37 - "{files|json}"
37 - "{files|json}"
38 """
38 """
39
39
40 def __init__(self, gen, values, makemap, joinfmt):
40 def __init__(self, gen, values, makemap, joinfmt):
41 if gen is not None:
41 if gen is not None:
42 self.gen = gen # generator or function returning generator
42 self.gen = gen # generator or function returning generator
43 self._values = values
43 self._values = values
44 self._makemap = makemap
44 self._makemap = makemap
45 self.joinfmt = joinfmt
45 self.joinfmt = joinfmt
46 def gen(self):
46 def gen(self):
47 """Default generator to stringify this as {join(self, ' ')}"""
47 """Default generator to stringify this as {join(self, ' ')}"""
48 for i, x in enumerate(self._values):
48 for i, x in enumerate(self._values):
49 if i > 0:
49 if i > 0:
50 yield ' '
50 yield ' '
51 yield self.joinfmt(x)
51 yield self.joinfmt(x)
52 def itermaps(self):
52 def itermaps(self):
53 makemap = self._makemap
53 makemap = self._makemap
54 for x in self._values:
54 for x in self._values:
55 yield makemap(x)
55 yield makemap(x)
56 def __contains__(self, x):
56 def __contains__(self, x):
57 return x in self._values
57 return x in self._values
58 def __getitem__(self, key):
58 def __getitem__(self, key):
59 return self._values[key]
59 return self._values[key]
60 def __len__(self):
60 def __len__(self):
61 return len(self._values)
61 return len(self._values)
62 def __iter__(self):
62 def __iter__(self):
63 return iter(self._values)
63 return iter(self._values)
64 def __getattr__(self, name):
64 def __getattr__(self, name):
65 if name not in ('get', 'items', 'iteritems', 'iterkeys', 'itervalues',
65 if name not in ('get', 'items', 'iteritems', 'iterkeys', 'itervalues',
66 'keys', 'values'):
66 'keys', 'values'):
67 raise AttributeError(name)
67 raise AttributeError(name)
68 return getattr(self._values, name)
68 return getattr(self._values, name)
69
69
70 class _mappable(object):
70 class _mappable(object):
71 """Wrapper for non-list/dict object to support map operation
71 """Wrapper for non-list/dict object to support map operation
72
72
73 This class allows us to handle both:
73 This class allows us to handle both:
74 - "{manifest}"
74 - "{manifest}"
75 - "{manifest % '{rev}:{node}'}"
75 - "{manifest % '{rev}:{node}'}"
76 - "{manifest.rev}"
76 - "{manifest.rev}"
77
77
78 Unlike a _hybrid, this does not simulate the behavior of the underling
78 Unlike a _hybrid, this does not simulate the behavior of the underling
79 value. Use unwrapvalue() or unwraphybrid() to obtain the inner object.
79 value. Use unwrapvalue() or unwraphybrid() to obtain the inner object.
80 """
80 """
81
81
82 def __init__(self, gen, key, value, makemap):
82 def __init__(self, gen, key, value, makemap):
83 if gen is not None:
83 if gen is not None:
84 self.gen = gen # generator or function returning generator
84 self.gen = gen # generator or function returning generator
85 self._key = key
85 self._key = key
86 self._value = value # may be generator of strings
86 self._value = value # may be generator of strings
87 self._makemap = makemap
87 self._makemap = makemap
88
88
89 def gen(self):
89 def gen(self):
90 yield pycompat.bytestr(self._value)
90 yield pycompat.bytestr(self._value)
91
91
92 def tomap(self):
92 def tomap(self):
93 return self._makemap(self._key)
93 return self._makemap(self._key)
94
94
95 def itermaps(self):
95 def itermaps(self):
96 yield self.tomap()
96 yield self.tomap()
97
97
98 def hybriddict(data, key='key', value='value', fmt='%s=%s', gen=None):
98 def hybriddict(data, key='key', value='value', fmt='%s=%s', gen=None):
99 """Wrap data to support both dict-like and string-like operations"""
99 """Wrap data to support both dict-like and string-like operations"""
100 return _hybrid(gen, data, lambda k: {key: k, value: data[k]},
100 return _hybrid(gen, data, lambda k: {key: k, value: data[k]},
101 lambda k: fmt % (k, data[k]))
101 lambda k: fmt % (k, data[k]))
102
102
103 def hybridlist(data, name, fmt='%s', gen=None):
103 def hybridlist(data, name, fmt='%s', gen=None):
104 """Wrap data to support both list-like and string-like operations"""
104 """Wrap data to support both list-like and string-like operations"""
105 return _hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % x)
105 return _hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % x)
106
106
107 def unwraphybrid(thing):
107 def unwraphybrid(thing):
108 """Return an object which can be stringified possibly by using a legacy
108 """Return an object which can be stringified possibly by using a legacy
109 template"""
109 template"""
110 gen = getattr(thing, 'gen', None)
110 gen = getattr(thing, 'gen', None)
111 if gen is None:
111 if gen is None:
112 return thing
112 return thing
113 if callable(gen):
113 if callable(gen):
114 return gen()
114 return gen()
115 return gen
115 return gen
116
116
117 def unwrapvalue(thing):
117 def unwrapvalue(thing):
118 """Move the inner value object out of the wrapper"""
118 """Move the inner value object out of the wrapper"""
119 if not util.safehasattr(thing, '_value'):
119 if not util.safehasattr(thing, '_value'):
120 return thing
120 return thing
121 return thing._value
121 return thing._value
122
122
123 def wraphybridvalue(container, key, value):
123 def wraphybridvalue(container, key, value):
124 """Wrap an element of hybrid container to be mappable
124 """Wrap an element of hybrid container to be mappable
125
125
126 The key is passed to the makemap function of the given container, which
126 The key is passed to the makemap function of the given container, which
127 should be an item generated by iter(container).
127 should be an item generated by iter(container).
128 """
128 """
129 makemap = getattr(container, '_makemap', None)
129 makemap = getattr(container, '_makemap', None)
130 if makemap is None:
130 if makemap is None:
131 return value
131 return value
132 if util.safehasattr(value, '_makemap'):
132 if util.safehasattr(value, '_makemap'):
133 # a nested hybrid list/dict, which has its own way of map operation
133 # a nested hybrid list/dict, which has its own way of map operation
134 return value
134 return value
135 return _mappable(None, key, value, makemap)
135 return _mappable(None, key, value, makemap)
136
136
137 def showdict(name, data, mapping, plural=None, key='key', value='value',
137 def showdict(name, data, mapping, plural=None, key='key', value='value',
138 fmt='%s=%s', separator=' '):
138 fmt='%s=%s', separator=' '):
139 c = [{key: k, value: v} for k, v in data.iteritems()]
139 c = [{key: k, value: v} for k, v in data.iteritems()]
140 f = _showlist(name, c, mapping, plural, separator)
140 f = _showlist(name, c, mapping, plural, separator)
141 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
141 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
142
142
143 def showlist(name, values, mapping, plural=None, element=None, separator=' '):
143 def showlist(name, values, mapping, plural=None, element=None, separator=' '):
144 if not element:
144 if not element:
145 element = name
145 element = name
146 f = _showlist(name, values, mapping, plural, separator)
146 f = _showlist(name, values, mapping, plural, separator)
147 return hybridlist(values, name=element, gen=f)
147 return hybridlist(values, name=element, gen=f)
148
148
149 def _showlist(name, values, mapping, plural=None, separator=' '):
149 def _showlist(name, values, mapping, plural=None, separator=' '):
150 '''expand set of values.
150 '''expand set of values.
151 name is name of key in template map.
151 name is name of key in template map.
152 values is list of strings or dicts.
152 values is list of strings or dicts.
153 plural is plural of name, if not simply name + 's'.
153 plural is plural of name, if not simply name + 's'.
154 separator is used to join values as a string
154 separator is used to join values as a string
155
155
156 expansion works like this, given name 'foo'.
156 expansion works like this, given name 'foo'.
157
157
158 if values is empty, expand 'no_foos'.
158 if values is empty, expand 'no_foos'.
159
159
160 if 'foo' not in template map, return values as a string,
160 if 'foo' not in template map, return values as a string,
161 joined by 'separator'.
161 joined by 'separator'.
162
162
163 expand 'start_foos'.
163 expand 'start_foos'.
164
164
165 for each value, expand 'foo'. if 'last_foo' in template
165 for each value, expand 'foo'. if 'last_foo' in template
166 map, expand it instead of 'foo' for last key.
166 map, expand it instead of 'foo' for last key.
167
167
168 expand 'end_foos'.
168 expand 'end_foos'.
169 '''
169 '''
170 templ = mapping['templ']
170 templ = mapping['templ']
171 strmapping = pycompat.strkwargs(mapping)
171 strmapping = pycompat.strkwargs(mapping)
172 if not plural:
172 if not plural:
173 plural = name + 's'
173 plural = name + 's'
174 if not values:
174 if not values:
175 noname = 'no_' + plural
175 noname = 'no_' + plural
176 if noname in templ:
176 if noname in templ:
177 yield templ(noname, **strmapping)
177 yield templ(noname, **strmapping)
178 return
178 return
179 if name not in templ:
179 if name not in templ:
180 if isinstance(values[0], bytes):
180 if isinstance(values[0], bytes):
181 yield separator.join(values)
181 yield separator.join(values)
182 else:
182 else:
183 for v in values:
183 for v in values:
184 yield dict(v, **strmapping)
184 yield dict(v, **strmapping)
185 return
185 return
186 startname = 'start_' + plural
186 startname = 'start_' + plural
187 if startname in templ:
187 if startname in templ:
188 yield templ(startname, **strmapping)
188 yield templ(startname, **strmapping)
189 vmapping = mapping.copy()
189 vmapping = mapping.copy()
190 def one(v, tag=name):
190 def one(v, tag=name):
191 try:
191 try:
192 vmapping.update(v)
192 vmapping.update(v)
193 except (AttributeError, ValueError):
193 except (AttributeError, ValueError):
194 try:
194 try:
195 for a, b in v:
195 for a, b in v:
196 vmapping[a] = b
196 vmapping[a] = b
197 except ValueError:
197 except ValueError:
198 vmapping[name] = v
198 vmapping[name] = v
199 return templ(tag, **pycompat.strkwargs(vmapping))
199 return templ(tag, **pycompat.strkwargs(vmapping))
200 lastname = 'last_' + name
200 lastname = 'last_' + name
201 if lastname in templ:
201 if lastname in templ:
202 last = values.pop()
202 last = values.pop()
203 else:
203 else:
204 last = None
204 last = None
205 for v in values:
205 for v in values:
206 yield one(v)
206 yield one(v)
207 if last is not None:
207 if last is not None:
208 yield one(last, tag=lastname)
208 yield one(last, tag=lastname)
209 endname = 'end_' + plural
209 endname = 'end_' + plural
210 if endname in templ:
210 if endname in templ:
211 yield templ(endname, **strmapping)
211 yield templ(endname, **strmapping)
212
212
213 def getfiles(repo, ctx, revcache):
213 def getfiles(repo, ctx, revcache):
214 if 'files' not in revcache:
214 if 'files' not in revcache:
215 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
215 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
216 return revcache['files']
216 return revcache['files']
217
217
218 def getlatesttags(repo, ctx, cache, pattern=None):
218 def getlatesttags(repo, ctx, cache, pattern=None):
219 '''return date, distance and name for the latest tag of rev'''
219 '''return date, distance and name for the latest tag of rev'''
220
220
221 cachename = 'latesttags'
221 cachename = 'latesttags'
222 if pattern is not None:
222 if pattern is not None:
223 cachename += '-' + pattern
223 cachename += '-' + pattern
224 match = util.stringmatcher(pattern)[2]
224 match = util.stringmatcher(pattern)[2]
225 else:
225 else:
226 match = util.always
226 match = util.always
227
227
228 if cachename not in cache:
228 if cachename not in cache:
229 # Cache mapping from rev to a tuple with tag date, tag
229 # Cache mapping from rev to a tuple with tag date, tag
230 # distance and tag name
230 # distance and tag name
231 cache[cachename] = {-1: (0, 0, ['null'])}
231 cache[cachename] = {-1: (0, 0, ['null'])}
232 latesttags = cache[cachename]
232 latesttags = cache[cachename]
233
233
234 rev = ctx.rev()
234 rev = ctx.rev()
235 todo = [rev]
235 todo = [rev]
236 while todo:
236 while todo:
237 rev = todo.pop()
237 rev = todo.pop()
238 if rev in latesttags:
238 if rev in latesttags:
239 continue
239 continue
240 ctx = repo[rev]
240 ctx = repo[rev]
241 tags = [t for t in ctx.tags()
241 tags = [t for t in ctx.tags()
242 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
242 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
243 and match(t))]
243 and match(t))]
244 if tags:
244 if tags:
245 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
245 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
246 continue
246 continue
247 try:
247 try:
248 ptags = [latesttags[p.rev()] for p in ctx.parents()]
248 ptags = [latesttags[p.rev()] for p in ctx.parents()]
249 if len(ptags) > 1:
249 if len(ptags) > 1:
250 if ptags[0][2] == ptags[1][2]:
250 if ptags[0][2] == ptags[1][2]:
251 # The tuples are laid out so the right one can be found by
251 # The tuples are laid out so the right one can be found by
252 # comparison in this case.
252 # comparison in this case.
253 pdate, pdist, ptag = max(ptags)
253 pdate, pdist, ptag = max(ptags)
254 else:
254 else:
255 def key(x):
255 def key(x):
256 changessincetag = len(repo.revs('only(%d, %s)',
256 changessincetag = len(repo.revs('only(%d, %s)',
257 ctx.rev(), x[2][0]))
257 ctx.rev(), x[2][0]))
258 # Smallest number of changes since tag wins. Date is
258 # Smallest number of changes since tag wins. Date is
259 # used as tiebreaker.
259 # used as tiebreaker.
260 return [-changessincetag, x[0]]
260 return [-changessincetag, x[0]]
261 pdate, pdist, ptag = max(ptags, key=key)
261 pdate, pdist, ptag = max(ptags, key=key)
262 else:
262 else:
263 pdate, pdist, ptag = ptags[0]
263 pdate, pdist, ptag = ptags[0]
264 except KeyError:
264 except KeyError:
265 # Cache miss - recurse
265 # Cache miss - recurse
266 todo.append(rev)
266 todo.append(rev)
267 todo.extend(p.rev() for p in ctx.parents())
267 todo.extend(p.rev() for p in ctx.parents())
268 continue
268 continue
269 latesttags[rev] = pdate, pdist + 1, ptag
269 latesttags[rev] = pdate, pdist + 1, ptag
270 return latesttags[rev]
270 return latesttags[rev]
271
271
272 def getrenamedfn(repo, endrev=None):
272 def getrenamedfn(repo, endrev=None):
273 rcache = {}
273 rcache = {}
274 if endrev is None:
274 if endrev is None:
275 endrev = len(repo)
275 endrev = len(repo)
276
276
277 def getrenamed(fn, rev):
277 def getrenamed(fn, rev):
278 '''looks up all renames for a file (up to endrev) the first
278 '''looks up all renames for a file (up to endrev) the first
279 time the file is given. It indexes on the changerev and only
279 time the file is given. It indexes on the changerev and only
280 parses the manifest if linkrev != changerev.
280 parses the manifest if linkrev != changerev.
281 Returns rename info for fn at changerev rev.'''
281 Returns rename info for fn at changerev rev.'''
282 if fn not in rcache:
282 if fn not in rcache:
283 rcache[fn] = {}
283 rcache[fn] = {}
284 fl = repo.file(fn)
284 fl = repo.file(fn)
285 for i in fl:
285 for i in fl:
286 lr = fl.linkrev(i)
286 lr = fl.linkrev(i)
287 renamed = fl.renamed(fl.node(i))
287 renamed = fl.renamed(fl.node(i))
288 rcache[fn][lr] = renamed
288 rcache[fn][lr] = renamed
289 if lr >= endrev:
289 if lr >= endrev:
290 break
290 break
291 if rev in rcache[fn]:
291 if rev in rcache[fn]:
292 return rcache[fn][rev]
292 return rcache[fn][rev]
293
293
294 # If linkrev != rev (i.e. rev not found in rcache) fallback to
294 # If linkrev != rev (i.e. rev not found in rcache) fallback to
295 # filectx logic.
295 # filectx logic.
296 try:
296 try:
297 return repo[rev][fn].renamed()
297 return repo[rev][fn].renamed()
298 except error.LookupError:
298 except error.LookupError:
299 return None
299 return None
300
300
301 return getrenamed
301 return getrenamed
302
302
303 # default templates internally used for rendering of lists
303 # default templates internally used for rendering of lists
304 defaulttempl = {
304 defaulttempl = {
305 'parent': '{rev}:{node|formatnode} ',
305 'parent': '{rev}:{node|formatnode} ',
306 'manifest': '{rev}:{node|formatnode}',
306 'manifest': '{rev}:{node|formatnode}',
307 'file_copy': '{name} ({source})',
307 'file_copy': '{name} ({source})',
308 'envvar': '{key}={value}',
308 'envvar': '{key}={value}',
309 'extra': '{key}={value|stringescape}'
309 'extra': '{key}={value|stringescape}'
310 }
310 }
311 # filecopy is preserved for compatibility reasons
311 # filecopy is preserved for compatibility reasons
312 defaulttempl['filecopy'] = defaulttempl['file_copy']
312 defaulttempl['filecopy'] = defaulttempl['file_copy']
313
313
314 # keywords are callables like:
314 # keywords are callables like:
315 # fn(repo, ctx, templ, cache, revcache, **args)
315 # fn(repo, ctx, templ, cache, revcache, **args)
316 # with:
316 # with:
317 # repo - current repository instance
317 # repo - current repository instance
318 # ctx - the changectx being displayed
318 # ctx - the changectx being displayed
319 # templ - the templater instance
319 # templ - the templater instance
320 # cache - a cache dictionary for the whole templater run
320 # cache - a cache dictionary for the whole templater run
321 # revcache - a cache dictionary for the current revision
321 # revcache - a cache dictionary for the current revision
322 keywords = {}
322 keywords = {}
323
323
324 templatekeyword = registrar.templatekeyword(keywords)
324 templatekeyword = registrar.templatekeyword(keywords)
325
325
326 @templatekeyword('author')
326 @templatekeyword('author')
327 def showauthor(repo, ctx, templ, **args):
327 def showauthor(repo, ctx, templ, **args):
328 """String. The unmodified author of the changeset."""
328 """String. The unmodified author of the changeset."""
329 return ctx.user()
329 return ctx.user()
330
330
331 @templatekeyword('bisect')
331 @templatekeyword('bisect')
332 def showbisect(repo, ctx, templ, **args):
332 def showbisect(repo, ctx, templ, **args):
333 """String. The changeset bisection status."""
333 """String. The changeset bisection status."""
334 return hbisect.label(repo, ctx.node())
334 return hbisect.label(repo, ctx.node())
335
335
336 @templatekeyword('branch')
336 @templatekeyword('branch')
337 def showbranch(**args):
337 def showbranch(**args):
338 """String. The name of the branch on which the changeset was
338 """String. The name of the branch on which the changeset was
339 committed.
339 committed.
340 """
340 """
341 return args[r'ctx'].branch()
341 return args[r'ctx'].branch()
342
342
343 @templatekeyword('branches')
343 @templatekeyword('branches')
344 def showbranches(**args):
344 def showbranches(**args):
345 """List of strings. The name of the branch on which the
345 """List of strings. The name of the branch on which the
346 changeset was committed. Will be empty if the branch name was
346 changeset was committed. Will be empty if the branch name was
347 default. (DEPRECATED)
347 default. (DEPRECATED)
348 """
348 """
349 args = pycompat.byteskwargs(args)
349 args = pycompat.byteskwargs(args)
350 branch = args['ctx'].branch()
350 branch = args['ctx'].branch()
351 if branch != 'default':
351 if branch != 'default':
352 return showlist('branch', [branch], args, plural='branches')
352 return showlist('branch', [branch], args, plural='branches')
353 return showlist('branch', [], args, plural='branches')
353 return showlist('branch', [], args, plural='branches')
354
354
355 @templatekeyword('bookmarks')
355 @templatekeyword('bookmarks')
356 def showbookmarks(**args):
356 def showbookmarks(**args):
357 """List of strings. Any bookmarks associated with the
357 """List of strings. Any bookmarks associated with the
358 changeset. Also sets 'active', the name of the active bookmark.
358 changeset. Also sets 'active', the name of the active bookmark.
359 """
359 """
360 args = pycompat.byteskwargs(args)
360 args = pycompat.byteskwargs(args)
361 repo = args['ctx']._repo
361 repo = args['ctx']._repo
362 bookmarks = args['ctx'].bookmarks()
362 bookmarks = args['ctx'].bookmarks()
363 active = repo._activebookmark
363 active = repo._activebookmark
364 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
364 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
365 f = _showlist('bookmark', bookmarks, args)
365 f = _showlist('bookmark', bookmarks, args)
366 return _hybrid(f, bookmarks, makemap, pycompat.identity)
366 return _hybrid(f, bookmarks, makemap, pycompat.identity)
367
367
368 @templatekeyword('children')
368 @templatekeyword('children')
369 def showchildren(**args):
369 def showchildren(**args):
370 """List of strings. The children of the changeset."""
370 """List of strings. The children of the changeset."""
371 args = pycompat.byteskwargs(args)
371 args = pycompat.byteskwargs(args)
372 ctx = args['ctx']
372 ctx = args['ctx']
373 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
373 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
374 return showlist('children', childrevs, args, element='child')
374 return showlist('children', childrevs, args, element='child')
375
375
376 # Deprecated, but kept alive for help generation a purpose.
376 # Deprecated, but kept alive for help generation a purpose.
377 @templatekeyword('currentbookmark')
377 @templatekeyword('currentbookmark')
378 def showcurrentbookmark(**args):
378 def showcurrentbookmark(**args):
379 """String. The active bookmark, if it is
379 """String. The active bookmark, if it is
380 associated with the changeset (DEPRECATED)"""
380 associated with the changeset (DEPRECATED)"""
381 return showactivebookmark(**args)
381 return showactivebookmark(**args)
382
382
383 @templatekeyword('activebookmark')
383 @templatekeyword('activebookmark')
384 def showactivebookmark(**args):
384 def showactivebookmark(**args):
385 """String. The active bookmark, if it is
385 """String. The active bookmark, if it is
386 associated with the changeset"""
386 associated with the changeset"""
387 active = args[r'repo']._activebookmark
387 active = args[r'repo']._activebookmark
388 if active and active in args[r'ctx'].bookmarks():
388 if active and active in args[r'ctx'].bookmarks():
389 return active
389 return active
390 return ''
390 return ''
391
391
392 @templatekeyword('date')
392 @templatekeyword('date')
393 def showdate(repo, ctx, templ, **args):
393 def showdate(repo, ctx, templ, **args):
394 """Date information. The date when the changeset was committed."""
394 """Date information. The date when the changeset was committed."""
395 return ctx.date()
395 return ctx.date()
396
396
397 @templatekeyword('desc')
397 @templatekeyword('desc')
398 def showdescription(repo, ctx, templ, **args):
398 def showdescription(repo, ctx, templ, **args):
399 """String. The text of the changeset description."""
399 """String. The text of the changeset description."""
400 s = ctx.description()
400 s = ctx.description()
401 if isinstance(s, encoding.localstr):
401 if isinstance(s, encoding.localstr):
402 # try hard to preserve utf-8 bytes
402 # try hard to preserve utf-8 bytes
403 return encoding.tolocal(encoding.fromlocal(s).strip())
403 return encoding.tolocal(encoding.fromlocal(s).strip())
404 else:
404 else:
405 return s.strip()
405 return s.strip()
406
406
407 @templatekeyword('diffstat')
407 @templatekeyword('diffstat')
408 def showdiffstat(repo, ctx, templ, **args):
408 def showdiffstat(repo, ctx, templ, **args):
409 """String. Statistics of changes with the following format:
409 """String. Statistics of changes with the following format:
410 "modified files: +added/-removed lines"
410 "modified files: +added/-removed lines"
411 """
411 """
412 stats = patch.diffstatdata(util.iterlines(ctx.diff(noprefix=False)))
412 stats = patch.diffstatdata(util.iterlines(ctx.diff(noprefix=False)))
413 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
413 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
414 return '%s: +%s/-%s' % (len(stats), adds, removes)
414 return '%s: +%s/-%s' % (len(stats), adds, removes)
415
415
416 @templatekeyword('envvars')
416 @templatekeyword('envvars')
417 def showenvvars(repo, **args):
417 def showenvvars(repo, **args):
418 """A dictionary of environment variables. (EXPERIMENTAL)"""
418 """A dictionary of environment variables. (EXPERIMENTAL)"""
419 args = pycompat.byteskwargs(args)
419 args = pycompat.byteskwargs(args)
420 env = repo.ui.exportableenviron()
420 env = repo.ui.exportableenviron()
421 env = util.sortdict((k, env[k]) for k in sorted(env))
421 env = util.sortdict((k, env[k]) for k in sorted(env))
422 return showdict('envvar', env, args, plural='envvars')
422 return showdict('envvar', env, args, plural='envvars')
423
423
424 @templatekeyword('extras')
424 @templatekeyword('extras')
425 def showextras(**args):
425 def showextras(**args):
426 """List of dicts with key, value entries of the 'extras'
426 """List of dicts with key, value entries of the 'extras'
427 field of this changeset."""
427 field of this changeset."""
428 args = pycompat.byteskwargs(args)
428 args = pycompat.byteskwargs(args)
429 extras = args['ctx'].extra()
429 extras = args['ctx'].extra()
430 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
430 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
431 makemap = lambda k: {'key': k, 'value': extras[k]}
431 makemap = lambda k: {'key': k, 'value': extras[k]}
432 c = [makemap(k) for k in extras]
432 c = [makemap(k) for k in extras]
433 f = _showlist('extra', c, args, plural='extras')
433 f = _showlist('extra', c, args, plural='extras')
434 return _hybrid(f, extras, makemap,
434 return _hybrid(f, extras, makemap,
435 lambda k: '%s=%s' % (k, util.escapestr(extras[k])))
435 lambda k: '%s=%s' % (k, util.escapestr(extras[k])))
436
436
437 @templatekeyword('file_adds')
437 @templatekeyword('file_adds')
438 def showfileadds(**args):
438 def showfileadds(**args):
439 """List of strings. Files added by this changeset."""
439 """List of strings. Files added by this changeset."""
440 args = pycompat.byteskwargs(args)
440 args = pycompat.byteskwargs(args)
441 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
441 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
442 return showlist('file_add', getfiles(repo, ctx, revcache)[1], args,
442 return showlist('file_add', getfiles(repo, ctx, revcache)[1], args,
443 element='file')
443 element='file')
444
444
445 @templatekeyword('file_copies')
445 @templatekeyword('file_copies')
446 def showfilecopies(**args):
446 def showfilecopies(**args):
447 """List of strings. Files copied in this changeset with
447 """List of strings. Files copied in this changeset with
448 their sources.
448 their sources.
449 """
449 """
450 args = pycompat.byteskwargs(args)
450 args = pycompat.byteskwargs(args)
451 cache, ctx = args['cache'], args['ctx']
451 cache, ctx = args['cache'], args['ctx']
452 copies = args['revcache'].get('copies')
452 copies = args['revcache'].get('copies')
453 if copies is None:
453 if copies is None:
454 if 'getrenamed' not in cache:
454 if 'getrenamed' not in cache:
455 cache['getrenamed'] = getrenamedfn(args['repo'])
455 cache['getrenamed'] = getrenamedfn(args['repo'])
456 copies = []
456 copies = []
457 getrenamed = cache['getrenamed']
457 getrenamed = cache['getrenamed']
458 for fn in ctx.files():
458 for fn in ctx.files():
459 rename = getrenamed(fn, ctx.rev())
459 rename = getrenamed(fn, ctx.rev())
460 if rename:
460 if rename:
461 copies.append((fn, rename[0]))
461 copies.append((fn, rename[0]))
462
462
463 copies = util.sortdict(copies)
463 copies = util.sortdict(copies)
464 return showdict('file_copy', copies, args, plural='file_copies',
464 return showdict('file_copy', copies, args, plural='file_copies',
465 key='name', value='source', fmt='%s (%s)')
465 key='name', value='source', fmt='%s (%s)')
466
466
467 # showfilecopiesswitch() displays file copies only if copy records are
467 # showfilecopiesswitch() displays file copies only if copy records are
468 # provided before calling the templater, usually with a --copies
468 # provided before calling the templater, usually with a --copies
469 # command line switch.
469 # command line switch.
470 @templatekeyword('file_copies_switch')
470 @templatekeyword('file_copies_switch')
471 def showfilecopiesswitch(**args):
471 def showfilecopiesswitch(**args):
472 """List of strings. Like "file_copies" but displayed
472 """List of strings. Like "file_copies" but displayed
473 only if the --copied switch is set.
473 only if the --copied switch is set.
474 """
474 """
475 args = pycompat.byteskwargs(args)
475 args = pycompat.byteskwargs(args)
476 copies = args['revcache'].get('copies') or []
476 copies = args['revcache'].get('copies') or []
477 copies = util.sortdict(copies)
477 copies = util.sortdict(copies)
478 return showdict('file_copy', copies, args, plural='file_copies',
478 return showdict('file_copy', copies, args, plural='file_copies',
479 key='name', value='source', fmt='%s (%s)')
479 key='name', value='source', fmt='%s (%s)')
480
480
481 @templatekeyword('file_dels')
481 @templatekeyword('file_dels')
482 def showfiledels(**args):
482 def showfiledels(**args):
483 """List of strings. Files removed by this changeset."""
483 """List of strings. Files removed by this changeset."""
484 args = pycompat.byteskwargs(args)
484 args = pycompat.byteskwargs(args)
485 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
485 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
486 return showlist('file_del', getfiles(repo, ctx, revcache)[2], args,
486 return showlist('file_del', getfiles(repo, ctx, revcache)[2], args,
487 element='file')
487 element='file')
488
488
489 @templatekeyword('file_mods')
489 @templatekeyword('file_mods')
490 def showfilemods(**args):
490 def showfilemods(**args):
491 """List of strings. Files modified by this changeset."""
491 """List of strings. Files modified by this changeset."""
492 args = pycompat.byteskwargs(args)
492 args = pycompat.byteskwargs(args)
493 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
493 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
494 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], args,
494 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], args,
495 element='file')
495 element='file')
496
496
497 @templatekeyword('files')
497 @templatekeyword('files')
498 def showfiles(**args):
498 def showfiles(**args):
499 """List of strings. All files modified, added, or removed by this
499 """List of strings. All files modified, added, or removed by this
500 changeset.
500 changeset.
501 """
501 """
502 args = pycompat.byteskwargs(args)
502 args = pycompat.byteskwargs(args)
503 return showlist('file', args['ctx'].files(), args)
503 return showlist('file', args['ctx'].files(), args)
504
504
505 @templatekeyword('graphnode')
505 @templatekeyword('graphnode')
506 def showgraphnode(repo, ctx, **args):
506 def showgraphnode(repo, ctx, **args):
507 """String. The character representing the changeset node in
507 """String. The character representing the changeset node in
508 an ASCII revision graph"""
508 an ASCII revision graph"""
509 wpnodes = repo.dirstate.parents()
509 wpnodes = repo.dirstate.parents()
510 if wpnodes[1] == nullid:
510 if wpnodes[1] == nullid:
511 wpnodes = wpnodes[:1]
511 wpnodes = wpnodes[:1]
512 if ctx.node() in wpnodes:
512 if ctx.node() in wpnodes:
513 return '@'
513 return '@'
514 elif ctx.obsolete():
514 elif ctx.obsolete():
515 return 'x'
515 return 'x'
516 elif ctx.closesbranch():
516 elif ctx.closesbranch():
517 return '_'
517 return '_'
518 else:
518 else:
519 return 'o'
519 return 'o'
520
520
521 @templatekeyword('graphwidth')
521 @templatekeyword('graphwidth')
522 def showgraphwidth(repo, ctx, templ, **args):
522 def showgraphwidth(repo, ctx, templ, **args):
523 """Integer. The width of the graph drawn by 'log --graph' or zero."""
523 """Integer. The width of the graph drawn by 'log --graph' or zero."""
524 # The value args['graphwidth'] will be this function, so we use an internal
524 # The value args['graphwidth'] will be this function, so we use an internal
525 # name to pass the value through props into this function.
525 # name to pass the value through props into this function.
526 return args.get('_graphwidth', 0)
526 return args.get('_graphwidth', 0)
527
527
528 @templatekeyword('index')
528 @templatekeyword('index')
529 def showindex(**args):
529 def showindex(**args):
530 """Integer. The current iteration of the loop. (0 indexed)"""
530 """Integer. The current iteration of the loop. (0 indexed)"""
531 # just hosts documentation; should be overridden by template mapping
531 # just hosts documentation; should be overridden by template mapping
532 raise error.Abort(_("can't use index in this context"))
532 raise error.Abort(_("can't use index in this context"))
533
533
534 @templatekeyword('latesttag')
534 @templatekeyword('latesttag')
535 def showlatesttag(**args):
535 def showlatesttag(**args):
536 """List of strings. The global tags on the most recent globally
536 """List of strings. The global tags on the most recent globally
537 tagged ancestor of this changeset. If no such tags exist, the list
537 tagged ancestor of this changeset. If no such tags exist, the list
538 consists of the single string "null".
538 consists of the single string "null".
539 """
539 """
540 return showlatesttags(None, **args)
540 return showlatesttags(None, **args)
541
541
542 def showlatesttags(pattern, **args):
542 def showlatesttags(pattern, **args):
543 """helper method for the latesttag keyword and function"""
543 """helper method for the latesttag keyword and function"""
544 args = pycompat.byteskwargs(args)
544 args = pycompat.byteskwargs(args)
545 repo, ctx = args['repo'], args['ctx']
545 repo, ctx = args['repo'], args['ctx']
546 cache = args['cache']
546 cache = args['cache']
547 latesttags = getlatesttags(repo, ctx, cache, pattern)
547 latesttags = getlatesttags(repo, ctx, cache, pattern)
548
548
549 # latesttag[0] is an implementation detail for sorting csets on different
549 # latesttag[0] is an implementation detail for sorting csets on different
550 # branches in a stable manner- it is the date the tagged cset was created,
550 # branches in a stable manner- it is the date the tagged cset was created,
551 # not the date the tag was created. Therefore it isn't made visible here.
551 # not the date the tag was created. Therefore it isn't made visible here.
552 makemap = lambda v: {
552 makemap = lambda v: {
553 'changes': _showchangessincetag,
553 'changes': _showchangessincetag,
554 'distance': latesttags[1],
554 'distance': latesttags[1],
555 'latesttag': v, # BC with {latesttag % '{latesttag}'}
555 'latesttag': v, # BC with {latesttag % '{latesttag}'}
556 'tag': v
556 'tag': v
557 }
557 }
558
558
559 tags = latesttags[2]
559 tags = latesttags[2]
560 f = _showlist('latesttag', tags, args, separator=':')
560 f = _showlist('latesttag', tags, args, separator=':')
561 return _hybrid(f, tags, makemap, pycompat.identity)
561 return _hybrid(f, tags, makemap, pycompat.identity)
562
562
563 @templatekeyword('latesttagdistance')
563 @templatekeyword('latesttagdistance')
564 def showlatesttagdistance(repo, ctx, templ, cache, **args):
564 def showlatesttagdistance(repo, ctx, templ, cache, **args):
565 """Integer. Longest path to the latest tag."""
565 """Integer. Longest path to the latest tag."""
566 return getlatesttags(repo, ctx, cache)[1]
566 return getlatesttags(repo, ctx, cache)[1]
567
567
568 @templatekeyword('changessincelatesttag')
568 @templatekeyword('changessincelatesttag')
569 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
569 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
570 """Integer. All ancestors not in the latest tag."""
570 """Integer. All ancestors not in the latest tag."""
571 latesttag = getlatesttags(repo, ctx, cache)[2][0]
571 latesttag = getlatesttags(repo, ctx, cache)[2][0]
572
572
573 return _showchangessincetag(repo, ctx, tag=latesttag, **args)
573 return _showchangessincetag(repo, ctx, tag=latesttag, **args)
574
574
575 def _showchangessincetag(repo, ctx, **args):
575 def _showchangessincetag(repo, ctx, **args):
576 offset = 0
576 offset = 0
577 revs = [ctx.rev()]
577 revs = [ctx.rev()]
578 tag = args[r'tag']
578 tag = args[r'tag']
579
579
580 # The only() revset doesn't currently support wdir()
580 # The only() revset doesn't currently support wdir()
581 if ctx.rev() is None:
581 if ctx.rev() is None:
582 offset = 1
582 offset = 1
583 revs = [p.rev() for p in ctx.parents()]
583 revs = [p.rev() for p in ctx.parents()]
584
584
585 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
585 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
586
586
587 @templatekeyword('manifest')
587 @templatekeyword('manifest')
588 def showmanifest(**args):
588 def showmanifest(**args):
589 repo, ctx, templ = args[r'repo'], args[r'ctx'], args[r'templ']
589 repo, ctx, templ = args[r'repo'], args[r'ctx'], args[r'templ']
590 mnode = ctx.manifestnode()
590 mnode = ctx.manifestnode()
591 if mnode is None:
591 if mnode is None:
592 # just avoid crash, we might want to use the 'ff...' hash in future
592 # just avoid crash, we might want to use the 'ff...' hash in future
593 return
593 return
594 mrev = repo.manifestlog._revlog.rev(mnode)
594 mrev = repo.manifestlog._revlog.rev(mnode)
595 mhex = hex(mnode)
595 mhex = hex(mnode)
596 args = args.copy()
596 args = args.copy()
597 args.update({r'rev': mrev, r'node': mhex})
597 args.update({r'rev': mrev, r'node': mhex})
598 f = templ('manifest', **args)
598 f = templ('manifest', **args)
599 # TODO: perhaps 'ctx' should be dropped from mapping because manifest
599 # TODO: perhaps 'ctx' should be dropped from mapping because manifest
600 # rev and node are completely different from changeset's.
600 # rev and node are completely different from changeset's.
601 return _mappable(f, None, f, lambda x: {'rev': mrev, 'node': mhex})
601 return _mappable(f, None, f, lambda x: {'rev': mrev, 'node': mhex})
602
602
603 def shownames(namespace, **args):
603 def shownames(namespace, **args):
604 """helper method to generate a template keyword for a namespace"""
604 """helper method to generate a template keyword for a namespace"""
605 args = pycompat.byteskwargs(args)
605 args = pycompat.byteskwargs(args)
606 ctx = args['ctx']
606 ctx = args['ctx']
607 repo = ctx.repo()
607 repo = ctx.repo()
608 ns = repo.names[namespace]
608 ns = repo.names[namespace]
609 names = ns.names(repo, ctx.node())
609 names = ns.names(repo, ctx.node())
610 return showlist(ns.templatename, names, args, plural=namespace)
610 return showlist(ns.templatename, names, args, plural=namespace)
611
611
612 @templatekeyword('namespaces')
612 @templatekeyword('namespaces')
613 def shownamespaces(**args):
613 def shownamespaces(**args):
614 """Dict of lists. Names attached to this changeset per
614 """Dict of lists. Names attached to this changeset per
615 namespace."""
615 namespace."""
616 args = pycompat.byteskwargs(args)
616 args = pycompat.byteskwargs(args)
617 ctx = args['ctx']
617 ctx = args['ctx']
618 repo = ctx.repo()
618 repo = ctx.repo()
619
619
620 namespaces = util.sortdict()
620 namespaces = util.sortdict()
621 colornames = {}
621 colornames = {}
622 builtins = {}
622 builtins = {}
623
623
624 for k, ns in repo.names.iteritems():
624 for k, ns in repo.names.iteritems():
625 namespaces[k] = showlist('name', ns.names(repo, ctx.node()), args)
625 namespaces[k] = showlist('name', ns.names(repo, ctx.node()), args)
626 colornames[k] = ns.colorname
626 colornames[k] = ns.colorname
627 builtins[k] = ns.builtin
627 builtins[k] = ns.builtin
628
628
629 f = _showlist('namespace', list(namespaces), args)
629 f = _showlist('namespace', list(namespaces), args)
630
630
631 def makemap(ns):
631 def makemap(ns):
632 return {
632 return {
633 'namespace': ns,
633 'namespace': ns,
634 'names': namespaces[ns],
634 'names': namespaces[ns],
635 'builtin': builtins[ns],
635 'builtin': builtins[ns],
636 'colorname': colornames[ns],
636 'colorname': colornames[ns],
637 }
637 }
638
638
639 return _hybrid(f, namespaces, makemap, pycompat.identity)
639 return _hybrid(f, namespaces, makemap, pycompat.identity)
640
640
641 @templatekeyword('node')
641 @templatekeyword('node')
642 def shownode(repo, ctx, templ, **args):
642 def shownode(repo, ctx, templ, **args):
643 """String. The changeset identification hash, as a 40 hexadecimal
643 """String. The changeset identification hash, as a 40 hexadecimal
644 digit string.
644 digit string.
645 """
645 """
646 return ctx.hex()
646 return ctx.hex()
647
647
648 @templatekeyword('obsolete')
648 @templatekeyword('obsolete')
649 def showobsolete(repo, ctx, templ, **args):
649 def showobsolete(repo, ctx, templ, **args):
650 """String. Whether the changeset is obsolete.
650 """String. Whether the changeset is obsolete.
651 """
651 """
652 if ctx.obsolete():
652 if ctx.obsolete():
653 return 'obsolete'
653 return 'obsolete'
654 return ''
654 return ''
655
655
656 @templatekeyword('peerpaths')
656 @templatekeyword('peerpaths')
657 def showpeerpaths(repo, **args):
657 def showpeerpaths(repo, **args):
658 """A dictionary of repository locations defined in the [paths] section
658 """A dictionary of repository locations defined in the [paths] section
659 of your configuration file. (EXPERIMENTAL)"""
659 of your configuration file. (EXPERIMENTAL)"""
660 # see commands.paths() for naming of dictionary keys
660 # see commands.paths() for naming of dictionary keys
661 paths = util.sortdict()
661 paths = repo.ui.paths
662 for k, p in sorted(repo.ui.paths.iteritems()):
662 urls = util.sortdict((k, p.rawloc) for k, p in sorted(paths.iteritems()))
663 d = util.sortdict()
663 def makemap(k):
664 d['url'] = p.rawloc
664 p = paths[k]
665 d = {'name': k, 'url': p.rawloc}
665 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
666 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
666 def f(d):
667 return d
667 yield d['url']
668 return _hybrid(None, urls, makemap, lambda k: '%s=%s' % (k, urls[k]))
668 paths[k] = hybriddict(d, gen=f(d))
669
670 # no hybriddict() since d['path'] can't be formatted as a string. perhaps
671 # hybriddict() should call templatefilters.stringify(d[value]).
672 return _hybrid(None, paths, lambda k: {'name': k, 'path': paths[k]},
673 lambda k: '%s=%s' % (k, paths[k]['url']))
674
669
675 @templatekeyword("predecessors")
670 @templatekeyword("predecessors")
676 def showpredecessors(repo, ctx, **args):
671 def showpredecessors(repo, ctx, **args):
677 """Returns the list if the closest visible successors
672 """Returns the list if the closest visible successors
678 """
673 """
679 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
674 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
680 predecessors = map(hex, predecessors)
675 predecessors = map(hex, predecessors)
681
676
682 return _hybrid(None, predecessors,
677 return _hybrid(None, predecessors,
683 lambda x: {'ctx': repo[x], 'revcache': {}},
678 lambda x: {'ctx': repo[x], 'revcache': {}},
684 lambda x: scmutil.formatchangeid(repo[x]))
679 lambda x: scmutil.formatchangeid(repo[x]))
685
680
686 @templatekeyword("successorssets")
681 @templatekeyword("successorssets")
687 def showsuccessorssets(repo, ctx, **args):
682 def showsuccessorssets(repo, ctx, **args):
688 """Returns a string of sets of successors for a changectx
683 """Returns a string of sets of successors for a changectx
689
684
690 Format used is: [ctx1, ctx2], [ctx3] if ctx has been splitted into ctx1 and
685 Format used is: [ctx1, ctx2], [ctx3] if ctx has been splitted into ctx1 and
691 ctx2 while also diverged into ctx3"""
686 ctx2 while also diverged into ctx3"""
692 if not ctx.obsolete():
687 if not ctx.obsolete():
693 return ''
688 return ''
694 args = pycompat.byteskwargs(args)
689 args = pycompat.byteskwargs(args)
695
690
696 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
691 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
697 ssets = [[hex(n) for n in ss] for ss in ssets]
692 ssets = [[hex(n) for n in ss] for ss in ssets]
698
693
699 data = []
694 data = []
700 for ss in ssets:
695 for ss in ssets:
701 h = _hybrid(None, ss, lambda x: {'ctx': repo[x], 'revcache': {}},
696 h = _hybrid(None, ss, lambda x: {'ctx': repo[x], 'revcache': {}},
702 lambda x: scmutil.formatchangeid(repo[x]))
697 lambda x: scmutil.formatchangeid(repo[x]))
703 data.append(h)
698 data.append(h)
704
699
705 # Format the successorssets
700 # Format the successorssets
706 def render(d):
701 def render(d):
707 t = []
702 t = []
708 for i in d.gen():
703 for i in d.gen():
709 t.append(i)
704 t.append(i)
710 return "".join(t)
705 return "".join(t)
711
706
712 def gen(data):
707 def gen(data):
713 yield "; ".join(render(d) for d in data)
708 yield "; ".join(render(d) for d in data)
714
709
715 return _hybrid(gen(data), data, lambda x: {'successorset': x},
710 return _hybrid(gen(data), data, lambda x: {'successorset': x},
716 pycompat.identity)
711 pycompat.identity)
717
712
718 @templatekeyword("succsandmarkers")
713 @templatekeyword("succsandmarkers")
719 def showsuccsandmarkers(repo, ctx, **args):
714 def showsuccsandmarkers(repo, ctx, **args):
720 """Returns a list of dict for each final successor of ctx.
715 """Returns a list of dict for each final successor of ctx.
721
716
722 The dict contains successors node id in "successors" keys and the list of
717 The dict contains successors node id in "successors" keys and the list of
723 obs-markers from ctx to the set of successors in "markers"
718 obs-markers from ctx to the set of successors in "markers"
724
719
725 (EXPERIMENTAL)
720 (EXPERIMENTAL)
726 """
721 """
727
722
728 values = obsutil.successorsandmarkers(repo, ctx)
723 values = obsutil.successorsandmarkers(repo, ctx)
729
724
730 if values is None:
725 if values is None:
731 values = []
726 values = []
732
727
733 # Format successors and markers to avoid exposing binary to templates
728 # Format successors and markers to avoid exposing binary to templates
734 data = []
729 data = []
735 for i in values:
730 for i in values:
736 # Format successors
731 # Format successors
737 successors = i['successors']
732 successors = i['successors']
738
733
739 successors = [hex(n) for n in successors]
734 successors = [hex(n) for n in successors]
740 successors = _hybrid(None, successors,
735 successors = _hybrid(None, successors,
741 lambda x: {'ctx': repo[x], 'revcache': {}},
736 lambda x: {'ctx': repo[x], 'revcache': {}},
742 lambda x: scmutil.formatchangeid(repo[x]))
737 lambda x: scmutil.formatchangeid(repo[x]))
743
738
744 # Format markers
739 # Format markers
745 finalmarkers = []
740 finalmarkers = []
746 for m in i['markers']:
741 for m in i['markers']:
747 hexprec = hex(m[0])
742 hexprec = hex(m[0])
748 hexsucs = tuple(hex(n) for n in m[1])
743 hexsucs = tuple(hex(n) for n in m[1])
749 hexparents = None
744 hexparents = None
750 if m[5] is not None:
745 if m[5] is not None:
751 hexparents = tuple(hex(n) for n in m[5])
746 hexparents = tuple(hex(n) for n in m[5])
752 newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
747 newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
753 finalmarkers.append(newmarker)
748 finalmarkers.append(newmarker)
754
749
755 data.append({'successors': successors, 'markers': finalmarkers})
750 data.append({'successors': successors, 'markers': finalmarkers})
756
751
757 f = _showlist('succsandmarkers', data, args)
752 f = _showlist('succsandmarkers', data, args)
758 return _hybrid(f, data, lambda x: x, pycompat.identity)
753 return _hybrid(f, data, lambda x: x, pycompat.identity)
759
754
760 @templatekeyword('p1rev')
755 @templatekeyword('p1rev')
761 def showp1rev(repo, ctx, templ, **args):
756 def showp1rev(repo, ctx, templ, **args):
762 """Integer. The repository-local revision number of the changeset's
757 """Integer. The repository-local revision number of the changeset's
763 first parent, or -1 if the changeset has no parents."""
758 first parent, or -1 if the changeset has no parents."""
764 return ctx.p1().rev()
759 return ctx.p1().rev()
765
760
766 @templatekeyword('p2rev')
761 @templatekeyword('p2rev')
767 def showp2rev(repo, ctx, templ, **args):
762 def showp2rev(repo, ctx, templ, **args):
768 """Integer. The repository-local revision number of the changeset's
763 """Integer. The repository-local revision number of the changeset's
769 second parent, or -1 if the changeset has no second parent."""
764 second parent, or -1 if the changeset has no second parent."""
770 return ctx.p2().rev()
765 return ctx.p2().rev()
771
766
772 @templatekeyword('p1node')
767 @templatekeyword('p1node')
773 def showp1node(repo, ctx, templ, **args):
768 def showp1node(repo, ctx, templ, **args):
774 """String. The identification hash of the changeset's first parent,
769 """String. The identification hash of the changeset's first parent,
775 as a 40 digit hexadecimal string. If the changeset has no parents, all
770 as a 40 digit hexadecimal string. If the changeset has no parents, all
776 digits are 0."""
771 digits are 0."""
777 return ctx.p1().hex()
772 return ctx.p1().hex()
778
773
779 @templatekeyword('p2node')
774 @templatekeyword('p2node')
780 def showp2node(repo, ctx, templ, **args):
775 def showp2node(repo, ctx, templ, **args):
781 """String. The identification hash of the changeset's second
776 """String. The identification hash of the changeset's second
782 parent, as a 40 digit hexadecimal string. If the changeset has no second
777 parent, as a 40 digit hexadecimal string. If the changeset has no second
783 parent, all digits are 0."""
778 parent, all digits are 0."""
784 return ctx.p2().hex()
779 return ctx.p2().hex()
785
780
786 @templatekeyword('parents')
781 @templatekeyword('parents')
787 def showparents(**args):
782 def showparents(**args):
788 """List of strings. The parents of the changeset in "rev:node"
783 """List of strings. The parents of the changeset in "rev:node"
789 format. If the changeset has only one "natural" parent (the predecessor
784 format. If the changeset has only one "natural" parent (the predecessor
790 revision) nothing is shown."""
785 revision) nothing is shown."""
791 args = pycompat.byteskwargs(args)
786 args = pycompat.byteskwargs(args)
792 repo = args['repo']
787 repo = args['repo']
793 ctx = args['ctx']
788 ctx = args['ctx']
794 pctxs = scmutil.meaningfulparents(repo, ctx)
789 pctxs = scmutil.meaningfulparents(repo, ctx)
795 # ifcontains() needs a list of str
790 # ifcontains() needs a list of str
796 prevs = ["%d" % p.rev() for p in pctxs]
791 prevs = ["%d" % p.rev() for p in pctxs]
797 parents = [[('rev', p.rev()),
792 parents = [[('rev', p.rev()),
798 ('node', p.hex()),
793 ('node', p.hex()),
799 ('phase', p.phasestr())]
794 ('phase', p.phasestr())]
800 for p in pctxs]
795 for p in pctxs]
801 f = _showlist('parent', parents, args)
796 f = _showlist('parent', parents, args)
802 return _hybrid(f, prevs, lambda x: {'ctx': repo[int(x)], 'revcache': {}},
797 return _hybrid(f, prevs, lambda x: {'ctx': repo[int(x)], 'revcache': {}},
803 lambda x: scmutil.formatchangeid(repo[int(x)]))
798 lambda x: scmutil.formatchangeid(repo[int(x)]))
804
799
805 @templatekeyword('phase')
800 @templatekeyword('phase')
806 def showphase(repo, ctx, templ, **args):
801 def showphase(repo, ctx, templ, **args):
807 """String. The changeset phase name."""
802 """String. The changeset phase name."""
808 return ctx.phasestr()
803 return ctx.phasestr()
809
804
810 @templatekeyword('phaseidx')
805 @templatekeyword('phaseidx')
811 def showphaseidx(repo, ctx, templ, **args):
806 def showphaseidx(repo, ctx, templ, **args):
812 """Integer. The changeset phase index."""
807 """Integer. The changeset phase index."""
813 return ctx.phase()
808 return ctx.phase()
814
809
815 @templatekeyword('rev')
810 @templatekeyword('rev')
816 def showrev(repo, ctx, templ, **args):
811 def showrev(repo, ctx, templ, **args):
817 """Integer. The repository-local changeset revision number."""
812 """Integer. The repository-local changeset revision number."""
818 return scmutil.intrev(ctx)
813 return scmutil.intrev(ctx)
819
814
820 def showrevslist(name, revs, **args):
815 def showrevslist(name, revs, **args):
821 """helper to generate a list of revisions in which a mapped template will
816 """helper to generate a list of revisions in which a mapped template will
822 be evaluated"""
817 be evaluated"""
823 args = pycompat.byteskwargs(args)
818 args = pycompat.byteskwargs(args)
824 repo = args['ctx'].repo()
819 repo = args['ctx'].repo()
825 # ifcontains() needs a list of str
820 # ifcontains() needs a list of str
826 revs = ["%d" % r for r in revs]
821 revs = ["%d" % r for r in revs]
827 f = _showlist(name, revs, args)
822 f = _showlist(name, revs, args)
828 return _hybrid(f, revs,
823 return _hybrid(f, revs,
829 lambda x: {name: x, 'ctx': repo[int(x)], 'revcache': {}},
824 lambda x: {name: x, 'ctx': repo[int(x)], 'revcache': {}},
830 pycompat.identity)
825 pycompat.identity)
831
826
832 @templatekeyword('subrepos')
827 @templatekeyword('subrepos')
833 def showsubrepos(**args):
828 def showsubrepos(**args):
834 """List of strings. Updated subrepositories in the changeset."""
829 """List of strings. Updated subrepositories in the changeset."""
835 args = pycompat.byteskwargs(args)
830 args = pycompat.byteskwargs(args)
836 ctx = args['ctx']
831 ctx = args['ctx']
837 substate = ctx.substate
832 substate = ctx.substate
838 if not substate:
833 if not substate:
839 return showlist('subrepo', [], args)
834 return showlist('subrepo', [], args)
840 psubstate = ctx.parents()[0].substate or {}
835 psubstate = ctx.parents()[0].substate or {}
841 subrepos = []
836 subrepos = []
842 for sub in substate:
837 for sub in substate:
843 if sub not in psubstate or substate[sub] != psubstate[sub]:
838 if sub not in psubstate or substate[sub] != psubstate[sub]:
844 subrepos.append(sub) # modified or newly added in ctx
839 subrepos.append(sub) # modified or newly added in ctx
845 for sub in psubstate:
840 for sub in psubstate:
846 if sub not in substate:
841 if sub not in substate:
847 subrepos.append(sub) # removed in ctx
842 subrepos.append(sub) # removed in ctx
848 return showlist('subrepo', sorted(subrepos), args)
843 return showlist('subrepo', sorted(subrepos), args)
849
844
850 # don't remove "showtags" definition, even though namespaces will put
845 # don't remove "showtags" definition, even though namespaces will put
851 # a helper function for "tags" keyword into "keywords" map automatically,
846 # a helper function for "tags" keyword into "keywords" map automatically,
852 # because online help text is built without namespaces initialization
847 # because online help text is built without namespaces initialization
853 @templatekeyword('tags')
848 @templatekeyword('tags')
854 def showtags(**args):
849 def showtags(**args):
855 """List of strings. Any tags associated with the changeset."""
850 """List of strings. Any tags associated with the changeset."""
856 return shownames('tags', **args)
851 return shownames('tags', **args)
857
852
858 def loadkeyword(ui, extname, registrarobj):
853 def loadkeyword(ui, extname, registrarobj):
859 """Load template keyword from specified registrarobj
854 """Load template keyword from specified registrarobj
860 """
855 """
861 for name, func in registrarobj._table.iteritems():
856 for name, func in registrarobj._table.iteritems():
862 keywords[name] = func
857 keywords[name] = func
863
858
864 @templatekeyword('termwidth')
859 @templatekeyword('termwidth')
865 def showtermwidth(repo, ctx, templ, **args):
860 def showtermwidth(repo, ctx, templ, **args):
866 """Integer. The width of the current terminal."""
861 """Integer. The width of the current terminal."""
867 return repo.ui.termwidth()
862 return repo.ui.termwidth()
868
863
869 @templatekeyword('troubles')
864 @templatekeyword('troubles')
870 def showtroubles(repo, **args):
865 def showtroubles(repo, **args):
871 """List of strings. Evolution troubles affecting the changeset.
866 """List of strings. Evolution troubles affecting the changeset.
872
867
873 (DEPRECATED)
868 (DEPRECATED)
874 """
869 """
875 msg = ("'troubles' is deprecated, "
870 msg = ("'troubles' is deprecated, "
876 "use 'instabilities'")
871 "use 'instabilities'")
877 repo.ui.deprecwarn(msg, '4.4')
872 repo.ui.deprecwarn(msg, '4.4')
878
873
879 return showinstabilities(repo=repo, **args)
874 return showinstabilities(repo=repo, **args)
880
875
881 @templatekeyword('instabilities')
876 @templatekeyword('instabilities')
882 def showinstabilities(**args):
877 def showinstabilities(**args):
883 """List of strings. Evolution instabilities affecting the changeset.
878 """List of strings. Evolution instabilities affecting the changeset.
884
879
885 (EXPERIMENTAL)
880 (EXPERIMENTAL)
886 """
881 """
887 args = pycompat.byteskwargs(args)
882 args = pycompat.byteskwargs(args)
888 return showlist('instability', args['ctx'].instabilities(), args,
883 return showlist('instability', args['ctx'].instabilities(), args,
889 plural='instabilities')
884 plural='instabilities')
890
885
891 # tell hggettext to extract docstrings from these functions:
886 # tell hggettext to extract docstrings from these functions:
892 i18nfunctions = keywords.values()
887 i18nfunctions = keywords.values()
@@ -1,222 +1,213 b''
1 $ hg init a
1 $ hg init a
2 $ hg clone a b
2 $ hg clone a b
3 updating to branch default
3 updating to branch default
4 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
5 $ cd a
5 $ cd a
6
6
7 with no paths:
7 with no paths:
8
8
9 $ hg paths
9 $ hg paths
10 $ hg paths unknown
10 $ hg paths unknown
11 not found!
11 not found!
12 [1]
12 [1]
13 $ hg paths -Tjson
13 $ hg paths -Tjson
14 [
14 [
15 ]
15 ]
16
16
17 with paths:
17 with paths:
18
18
19 $ echo '[paths]' >> .hg/hgrc
19 $ echo '[paths]' >> .hg/hgrc
20 $ echo 'dupe = ../b#tip' >> .hg/hgrc
20 $ echo 'dupe = ../b#tip' >> .hg/hgrc
21 $ echo 'expand = $SOMETHING/bar' >> .hg/hgrc
21 $ echo 'expand = $SOMETHING/bar' >> .hg/hgrc
22 $ hg in dupe
22 $ hg in dupe
23 comparing with $TESTTMP/b (glob)
23 comparing with $TESTTMP/b (glob)
24 no changes found
24 no changes found
25 [1]
25 [1]
26 $ cd ..
26 $ cd ..
27 $ hg -R a in dupe
27 $ hg -R a in dupe
28 comparing with $TESTTMP/b (glob)
28 comparing with $TESTTMP/b (glob)
29 no changes found
29 no changes found
30 [1]
30 [1]
31 $ cd a
31 $ cd a
32 $ hg paths
32 $ hg paths
33 dupe = $TESTTMP/b#tip (glob)
33 dupe = $TESTTMP/b#tip (glob)
34 expand = $TESTTMP/a/$SOMETHING/bar (glob)
34 expand = $TESTTMP/a/$SOMETHING/bar (glob)
35 $ SOMETHING=foo hg paths
35 $ SOMETHING=foo hg paths
36 dupe = $TESTTMP/b#tip (glob)
36 dupe = $TESTTMP/b#tip (glob)
37 expand = $TESTTMP/a/foo/bar (glob)
37 expand = $TESTTMP/a/foo/bar (glob)
38 #if msys
38 #if msys
39 $ SOMETHING=//foo hg paths
39 $ SOMETHING=//foo hg paths
40 dupe = $TESTTMP/b#tip (glob)
40 dupe = $TESTTMP/b#tip (glob)
41 expand = /foo/bar
41 expand = /foo/bar
42 #else
42 #else
43 $ SOMETHING=/foo hg paths
43 $ SOMETHING=/foo hg paths
44 dupe = $TESTTMP/b#tip (glob)
44 dupe = $TESTTMP/b#tip (glob)
45 expand = /foo/bar
45 expand = /foo/bar
46 #endif
46 #endif
47 $ hg paths -q
47 $ hg paths -q
48 dupe
48 dupe
49 expand
49 expand
50 $ hg paths dupe
50 $ hg paths dupe
51 $TESTTMP/b#tip (glob)
51 $TESTTMP/b#tip (glob)
52 $ hg paths -q dupe
52 $ hg paths -q dupe
53 $ hg paths unknown
53 $ hg paths unknown
54 not found!
54 not found!
55 [1]
55 [1]
56 $ hg paths -q unknown
56 $ hg paths -q unknown
57 [1]
57 [1]
58
58
59 formatter output with paths:
59 formatter output with paths:
60
60
61 $ echo 'dupe:pushurl = https://example.com/dupe' >> .hg/hgrc
61 $ echo 'dupe:pushurl = https://example.com/dupe' >> .hg/hgrc
62 $ hg paths -Tjson | sed 's|\\\\|\\|g'
62 $ hg paths -Tjson | sed 's|\\\\|\\|g'
63 [
63 [
64 {
64 {
65 "name": "dupe",
65 "name": "dupe",
66 "pushurl": "https://example.com/dupe",
66 "pushurl": "https://example.com/dupe",
67 "url": "$TESTTMP/b#tip" (glob)
67 "url": "$TESTTMP/b#tip" (glob)
68 },
68 },
69 {
69 {
70 "name": "expand",
70 "name": "expand",
71 "url": "$TESTTMP/a/$SOMETHING/bar" (glob)
71 "url": "$TESTTMP/a/$SOMETHING/bar" (glob)
72 }
72 }
73 ]
73 ]
74 $ hg paths -Tjson dupe | sed 's|\\\\|\\|g'
74 $ hg paths -Tjson dupe | sed 's|\\\\|\\|g'
75 [
75 [
76 {
76 {
77 "name": "dupe",
77 "name": "dupe",
78 "pushurl": "https://example.com/dupe",
78 "pushurl": "https://example.com/dupe",
79 "url": "$TESTTMP/b#tip" (glob)
79 "url": "$TESTTMP/b#tip" (glob)
80 }
80 }
81 ]
81 ]
82 $ hg paths -Tjson -q unknown
82 $ hg paths -Tjson -q unknown
83 [
83 [
84 ]
84 ]
85 [1]
85 [1]
86
86
87 log template:
87 log template:
88
88
89 (behaves as a {name: path-string} dict by default)
89 (behaves as a {name: path-string} dict by default)
90
90
91 $ hg log -rnull -T '{peerpaths}\n'
91 $ hg log -rnull -T '{peerpaths}\n'
92 dupe=$TESTTMP/b#tip expand=$TESTTMP/a/$SOMETHING/bar (glob)
92 dupe=$TESTTMP/b#tip expand=$TESTTMP/a/$SOMETHING/bar (glob)
93 $ hg log -rnull -T '{join(peerpaths, "\n")}\n'
93 $ hg log -rnull -T '{join(peerpaths, "\n")}\n'
94 dupe=$TESTTMP/b#tip (glob)
94 dupe=$TESTTMP/b#tip (glob)
95 expand=$TESTTMP/a/$SOMETHING/bar (glob)
95 expand=$TESTTMP/a/$SOMETHING/bar (glob)
96 $ hg log -rnull -T '{peerpaths % "{name}: {path}\n"}'
96 $ hg log -rnull -T '{peerpaths % "{name}: {url}\n"}'
97 dupe: $TESTTMP/b#tip (glob)
97 dupe: $TESTTMP/b#tip (glob)
98 expand: $TESTTMP/a/$SOMETHING/bar (glob)
98 expand: $TESTTMP/a/$SOMETHING/bar (glob)
99 $ hg log -rnull -T '{get(peerpaths, "dupe")}\n'
99 $ hg log -rnull -T '{get(peerpaths, "dupe")}\n'
100 $TESTTMP/b#tip (glob)
100 $TESTTMP/b#tip (glob)
101
101
102 (but a path is actually a dict of url and sub-options)
102 (sub options can be populated by map/dot operation)
103
103
104 $ hg log -rnull -T '{join(get(peerpaths, "dupe"), "\n")}\n'
104 $ hg log -rnull \
105 url=$TESTTMP/b#tip (glob)
105 > -T '{get(peerpaths, "dupe") % "url: {url}\npushurl: {pushurl}\n"}'
106 pushurl=https://example.com/dupe
107 $ hg log -rnull -T '{get(peerpaths, "dupe") % "{key}: {value}\n"}'
108 url: $TESTTMP/b#tip (glob)
106 url: $TESTTMP/b#tip (glob)
109 pushurl: https://example.com/dupe
107 pushurl: https://example.com/dupe
110 $ hg log -rnull -T '{get(get(peerpaths, "dupe"), "pushurl")}\n'
108 $ hg log -rnull -T '{peerpaths.dupe.pushurl}\n'
111 https://example.com/dupe
109 https://example.com/dupe
112
110
113 (so there's weird behavior)
111 (in JSON, it's a dict of urls)
114
115 $ hg log -rnull -T '{get(peerpaths, "dupe")|count}\n'
116 2
117 $ hg log -rnull -T '{get(peerpaths, "dupe")|stringify|count}\n'
118 [0-9]{2,} (re)
119
120 (in JSON, it's a dict of dicts)
121
112
122 $ hg log -rnull -T '{peerpaths|json}\n' | sed 's|\\\\|/|g'
113 $ hg log -rnull -T '{peerpaths|json}\n' | sed 's|\\\\|/|g'
123 {"dupe": {"pushurl": "https://example.com/dupe", "url": "$TESTTMP/b#tip"}, "expand": {"url": "$TESTTMP/a/$SOMETHING/bar"}}
114 {"dupe": "$TESTTMP/b#tip", "expand": "$TESTTMP/a/$SOMETHING/bar"}
124
115
125 password should be masked in plain output, but not in machine-readable/template
116 password should be masked in plain output, but not in machine-readable/template
126 output:
117 output:
127
118
128 $ echo 'insecure = http://foo:insecure@example.com/' >> .hg/hgrc
119 $ echo 'insecure = http://foo:insecure@example.com/' >> .hg/hgrc
129 $ hg paths insecure
120 $ hg paths insecure
130 http://foo:***@example.com/
121 http://foo:***@example.com/
131 $ hg paths -Tjson insecure
122 $ hg paths -Tjson insecure
132 [
123 [
133 {
124 {
134 "name": "insecure",
125 "name": "insecure",
135 "url": "http://foo:insecure@example.com/"
126 "url": "http://foo:insecure@example.com/"
136 }
127 }
137 ]
128 ]
138 $ hg log -rnull -T '{get(peerpaths, "insecure")}\n'
129 $ hg log -rnull -T '{get(peerpaths, "insecure")}\n'
139 http://foo:insecure@example.com/
130 http://foo:insecure@example.com/
140
131
141 zeroconf wraps ui.configitems(), which shouldn't crash at least:
132 zeroconf wraps ui.configitems(), which shouldn't crash at least:
142
133
143 $ hg paths --config extensions.zeroconf=
134 $ hg paths --config extensions.zeroconf=
144 dupe = $TESTTMP/b#tip (glob)
135 dupe = $TESTTMP/b#tip (glob)
145 dupe:pushurl = https://example.com/dupe
136 dupe:pushurl = https://example.com/dupe
146 expand = $TESTTMP/a/$SOMETHING/bar (glob)
137 expand = $TESTTMP/a/$SOMETHING/bar (glob)
147 insecure = http://foo:***@example.com/
138 insecure = http://foo:***@example.com/
148
139
149 $ cd ..
140 $ cd ..
150
141
151 sub-options for an undeclared path are ignored
142 sub-options for an undeclared path are ignored
152
143
153 $ hg init suboptions
144 $ hg init suboptions
154 $ cd suboptions
145 $ cd suboptions
155
146
156 $ cat > .hg/hgrc << EOF
147 $ cat > .hg/hgrc << EOF
157 > [paths]
148 > [paths]
158 > path0 = https://example.com/path0
149 > path0 = https://example.com/path0
159 > path1:pushurl = https://example.com/path1
150 > path1:pushurl = https://example.com/path1
160 > EOF
151 > EOF
161 $ hg paths
152 $ hg paths
162 path0 = https://example.com/path0
153 path0 = https://example.com/path0
163
154
164 unknown sub-options aren't displayed
155 unknown sub-options aren't displayed
165
156
166 $ cat > .hg/hgrc << EOF
157 $ cat > .hg/hgrc << EOF
167 > [paths]
158 > [paths]
168 > path0 = https://example.com/path0
159 > path0 = https://example.com/path0
169 > path0:foo = https://example.com/path1
160 > path0:foo = https://example.com/path1
170 > EOF
161 > EOF
171
162
172 $ hg paths
163 $ hg paths
173 path0 = https://example.com/path0
164 path0 = https://example.com/path0
174
165
175 :pushurl must be a URL
166 :pushurl must be a URL
176
167
177 $ cat > .hg/hgrc << EOF
168 $ cat > .hg/hgrc << EOF
178 > [paths]
169 > [paths]
179 > default = /path/to/nothing
170 > default = /path/to/nothing
180 > default:pushurl = /not/a/url
171 > default:pushurl = /not/a/url
181 > EOF
172 > EOF
182
173
183 $ hg paths
174 $ hg paths
184 (paths.default:pushurl not a URL; ignoring)
175 (paths.default:pushurl not a URL; ignoring)
185 default = /path/to/nothing
176 default = /path/to/nothing
186
177
187 #fragment is not allowed in :pushurl
178 #fragment is not allowed in :pushurl
188
179
189 $ cat > .hg/hgrc << EOF
180 $ cat > .hg/hgrc << EOF
190 > [paths]
181 > [paths]
191 > default = https://example.com/repo
182 > default = https://example.com/repo
192 > invalid = https://example.com/repo
183 > invalid = https://example.com/repo
193 > invalid:pushurl = https://example.com/repo#branch
184 > invalid:pushurl = https://example.com/repo#branch
194 > EOF
185 > EOF
195
186
196 $ hg paths
187 $ hg paths
197 ("#fragment" in paths.invalid:pushurl not supported; ignoring)
188 ("#fragment" in paths.invalid:pushurl not supported; ignoring)
198 default = https://example.com/repo
189 default = https://example.com/repo
199 invalid = https://example.com/repo
190 invalid = https://example.com/repo
200 invalid:pushurl = https://example.com/repo
191 invalid:pushurl = https://example.com/repo
201
192
202 $ cd ..
193 $ cd ..
203
194
204 'file:' disables [paths] entries for clone destination
195 'file:' disables [paths] entries for clone destination
205
196
206 $ cat >> $HGRCPATH <<EOF
197 $ cat >> $HGRCPATH <<EOF
207 > [paths]
198 > [paths]
208 > gpath1 = http://hg.example.com
199 > gpath1 = http://hg.example.com
209 > EOF
200 > EOF
210
201
211 $ hg clone a gpath1
202 $ hg clone a gpath1
212 abort: cannot create new http repository
203 abort: cannot create new http repository
213 [255]
204 [255]
214
205
215 $ hg clone a file:gpath1
206 $ hg clone a file:gpath1
216 updating to branch default
207 updating to branch default
217 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
208 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
218 $ cd gpath1
209 $ cd gpath1
219 $ hg -q id
210 $ hg -q id
220 000000000000
211 000000000000
221
212
222 $ cd ..
213 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now