##// END OF EJS Templates
template: compute dates in obsfatedate...
Boris Feld -
r33997:c35c0f54 default
parent child Browse files
Show More
@@ -1,611 +1,616 b''
1 # obsutil.py - utility functions for obsolescence
1 # obsutil.py - utility functions for obsolescence
2 #
2 #
3 # Copyright 2017 Boris Feld <boris.feld@octobus.net>
3 # Copyright 2017 Boris Feld <boris.feld@octobus.net>
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 . import (
10 from . import (
11 phases,
11 phases,
12 util
12 util
13 )
13 )
14
14
15 class marker(object):
15 class marker(object):
16 """Wrap obsolete marker raw data"""
16 """Wrap obsolete marker raw data"""
17
17
18 def __init__(self, repo, data):
18 def __init__(self, repo, data):
19 # the repo argument will be used to create changectx in later version
19 # the repo argument will be used to create changectx in later version
20 self._repo = repo
20 self._repo = repo
21 self._data = data
21 self._data = data
22 self._decodedmeta = None
22 self._decodedmeta = None
23
23
24 def __hash__(self):
24 def __hash__(self):
25 return hash(self._data)
25 return hash(self._data)
26
26
27 def __eq__(self, other):
27 def __eq__(self, other):
28 if type(other) != type(self):
28 if type(other) != type(self):
29 return False
29 return False
30 return self._data == other._data
30 return self._data == other._data
31
31
32 def precnode(self):
32 def precnode(self):
33 msg = ("'marker.precnode' is deprecated, "
33 msg = ("'marker.precnode' is deprecated, "
34 "use 'marker.prednode'")
34 "use 'marker.prednode'")
35 util.nouideprecwarn(msg, '4.4')
35 util.nouideprecwarn(msg, '4.4')
36 return self.prednode()
36 return self.prednode()
37
37
38 def prednode(self):
38 def prednode(self):
39 """Predecessor changeset node identifier"""
39 """Predecessor changeset node identifier"""
40 return self._data[0]
40 return self._data[0]
41
41
42 def succnodes(self):
42 def succnodes(self):
43 """List of successor changesets node identifiers"""
43 """List of successor changesets node identifiers"""
44 return self._data[1]
44 return self._data[1]
45
45
46 def parentnodes(self):
46 def parentnodes(self):
47 """Parents of the predecessors (None if not recorded)"""
47 """Parents of the predecessors (None if not recorded)"""
48 return self._data[5]
48 return self._data[5]
49
49
50 def metadata(self):
50 def metadata(self):
51 """Decoded metadata dictionary"""
51 """Decoded metadata dictionary"""
52 return dict(self._data[3])
52 return dict(self._data[3])
53
53
54 def date(self):
54 def date(self):
55 """Creation date as (unixtime, offset)"""
55 """Creation date as (unixtime, offset)"""
56 return self._data[4]
56 return self._data[4]
57
57
58 def flags(self):
58 def flags(self):
59 """The flags field of the marker"""
59 """The flags field of the marker"""
60 return self._data[2]
60 return self._data[2]
61
61
62 def getmarkers(repo, nodes=None, exclusive=False):
62 def getmarkers(repo, nodes=None, exclusive=False):
63 """returns markers known in a repository
63 """returns markers known in a repository
64
64
65 If <nodes> is specified, only markers "relevant" to those nodes are are
65 If <nodes> is specified, only markers "relevant" to those nodes are are
66 returned"""
66 returned"""
67 if nodes is None:
67 if nodes is None:
68 rawmarkers = repo.obsstore
68 rawmarkers = repo.obsstore
69 elif exclusive:
69 elif exclusive:
70 rawmarkers = exclusivemarkers(repo, nodes)
70 rawmarkers = exclusivemarkers(repo, nodes)
71 else:
71 else:
72 rawmarkers = repo.obsstore.relevantmarkers(nodes)
72 rawmarkers = repo.obsstore.relevantmarkers(nodes)
73
73
74 for markerdata in rawmarkers:
74 for markerdata in rawmarkers:
75 yield marker(repo, markerdata)
75 yield marker(repo, markerdata)
76
76
77 def closestpredecessors(repo, nodeid):
77 def closestpredecessors(repo, nodeid):
78 """yield the list of next predecessors pointing on visible changectx nodes
78 """yield the list of next predecessors pointing on visible changectx nodes
79
79
80 This function respect the repoview filtering, filtered revision will be
80 This function respect the repoview filtering, filtered revision will be
81 considered missing.
81 considered missing.
82 """
82 """
83
83
84 precursors = repo.obsstore.predecessors
84 precursors = repo.obsstore.predecessors
85 stack = [nodeid]
85 stack = [nodeid]
86 seen = set(stack)
86 seen = set(stack)
87
87
88 while stack:
88 while stack:
89 current = stack.pop()
89 current = stack.pop()
90 currentpreccs = precursors.get(current, ())
90 currentpreccs = precursors.get(current, ())
91
91
92 for prec in currentpreccs:
92 for prec in currentpreccs:
93 precnodeid = prec[0]
93 precnodeid = prec[0]
94
94
95 # Basic cycle protection
95 # Basic cycle protection
96 if precnodeid in seen:
96 if precnodeid in seen:
97 continue
97 continue
98 seen.add(precnodeid)
98 seen.add(precnodeid)
99
99
100 if precnodeid in repo:
100 if precnodeid in repo:
101 yield precnodeid
101 yield precnodeid
102 else:
102 else:
103 stack.append(precnodeid)
103 stack.append(precnodeid)
104
104
105 def allprecursors(*args, **kwargs):
105 def allprecursors(*args, **kwargs):
106 """ (DEPRECATED)
106 """ (DEPRECATED)
107 """
107 """
108 msg = ("'obsutil.allprecursors' is deprecated, "
108 msg = ("'obsutil.allprecursors' is deprecated, "
109 "use 'obsutil.allpredecessors'")
109 "use 'obsutil.allpredecessors'")
110 util.nouideprecwarn(msg, '4.4')
110 util.nouideprecwarn(msg, '4.4')
111
111
112 return allpredecessors(*args, **kwargs)
112 return allpredecessors(*args, **kwargs)
113
113
114 def allpredecessors(obsstore, nodes, ignoreflags=0):
114 def allpredecessors(obsstore, nodes, ignoreflags=0):
115 """Yield node for every precursors of <nodes>.
115 """Yield node for every precursors of <nodes>.
116
116
117 Some precursors may be unknown locally.
117 Some precursors may be unknown locally.
118
118
119 This is a linear yield unsuited to detecting folded changesets. It includes
119 This is a linear yield unsuited to detecting folded changesets. It includes
120 initial nodes too."""
120 initial nodes too."""
121
121
122 remaining = set(nodes)
122 remaining = set(nodes)
123 seen = set(remaining)
123 seen = set(remaining)
124 while remaining:
124 while remaining:
125 current = remaining.pop()
125 current = remaining.pop()
126 yield current
126 yield current
127 for mark in obsstore.predecessors.get(current, ()):
127 for mark in obsstore.predecessors.get(current, ()):
128 # ignore marker flagged with specified flag
128 # ignore marker flagged with specified flag
129 if mark[2] & ignoreflags:
129 if mark[2] & ignoreflags:
130 continue
130 continue
131 suc = mark[0]
131 suc = mark[0]
132 if suc not in seen:
132 if suc not in seen:
133 seen.add(suc)
133 seen.add(suc)
134 remaining.add(suc)
134 remaining.add(suc)
135
135
136 def allsuccessors(obsstore, nodes, ignoreflags=0):
136 def allsuccessors(obsstore, nodes, ignoreflags=0):
137 """Yield node for every successor of <nodes>.
137 """Yield node for every successor of <nodes>.
138
138
139 Some successors may be unknown locally.
139 Some successors may be unknown locally.
140
140
141 This is a linear yield unsuited to detecting split changesets. It includes
141 This is a linear yield unsuited to detecting split changesets. It includes
142 initial nodes too."""
142 initial nodes too."""
143 remaining = set(nodes)
143 remaining = set(nodes)
144 seen = set(remaining)
144 seen = set(remaining)
145 while remaining:
145 while remaining:
146 current = remaining.pop()
146 current = remaining.pop()
147 yield current
147 yield current
148 for mark in obsstore.successors.get(current, ()):
148 for mark in obsstore.successors.get(current, ()):
149 # ignore marker flagged with specified flag
149 # ignore marker flagged with specified flag
150 if mark[2] & ignoreflags:
150 if mark[2] & ignoreflags:
151 continue
151 continue
152 for suc in mark[1]:
152 for suc in mark[1]:
153 if suc not in seen:
153 if suc not in seen:
154 seen.add(suc)
154 seen.add(suc)
155 remaining.add(suc)
155 remaining.add(suc)
156
156
157 def _filterprunes(markers):
157 def _filterprunes(markers):
158 """return a set with no prune markers"""
158 """return a set with no prune markers"""
159 return set(m for m in markers if m[1])
159 return set(m for m in markers if m[1])
160
160
161 def exclusivemarkers(repo, nodes):
161 def exclusivemarkers(repo, nodes):
162 """set of markers relevant to "nodes" but no other locally-known nodes
162 """set of markers relevant to "nodes" but no other locally-known nodes
163
163
164 This function compute the set of markers "exclusive" to a locally-known
164 This function compute the set of markers "exclusive" to a locally-known
165 node. This means we walk the markers starting from <nodes> until we reach a
165 node. This means we walk the markers starting from <nodes> until we reach a
166 locally-known precursors outside of <nodes>. Element of <nodes> with
166 locally-known precursors outside of <nodes>. Element of <nodes> with
167 locally-known successors outside of <nodes> are ignored (since their
167 locally-known successors outside of <nodes> are ignored (since their
168 precursors markers are also relevant to these successors).
168 precursors markers are also relevant to these successors).
169
169
170 For example:
170 For example:
171
171
172 # (A0 rewritten as A1)
172 # (A0 rewritten as A1)
173 #
173 #
174 # A0 <-1- A1 # Marker "1" is exclusive to A1
174 # A0 <-1- A1 # Marker "1" is exclusive to A1
175
175
176 or
176 or
177
177
178 # (A0 rewritten as AX; AX rewritten as A1; AX is unkown locally)
178 # (A0 rewritten as AX; AX rewritten as A1; AX is unkown locally)
179 #
179 #
180 # <-1- A0 <-2- AX <-3- A1 # Marker "2,3" are exclusive to A1
180 # <-1- A0 <-2- AX <-3- A1 # Marker "2,3" are exclusive to A1
181
181
182 or
182 or
183
183
184 # (A0 has unknown precursors, A0 rewritten as A1 and A2 (divergence))
184 # (A0 has unknown precursors, A0 rewritten as A1 and A2 (divergence))
185 #
185 #
186 # <-2- A1 # Marker "2" is exclusive to A0,A1
186 # <-2- A1 # Marker "2" is exclusive to A0,A1
187 # /
187 # /
188 # <-1- A0
188 # <-1- A0
189 # \
189 # \
190 # <-3- A2 # Marker "3" is exclusive to A0,A2
190 # <-3- A2 # Marker "3" is exclusive to A0,A2
191 #
191 #
192 # in addition:
192 # in addition:
193 #
193 #
194 # Markers "2,3" are exclusive to A1,A2
194 # Markers "2,3" are exclusive to A1,A2
195 # Markers "1,2,3" are exclusive to A0,A1,A2
195 # Markers "1,2,3" are exclusive to A0,A1,A2
196
196
197 See test/test-obsolete-bundle-strip.t for more examples.
197 See test/test-obsolete-bundle-strip.t for more examples.
198
198
199 An example usage is strip. When stripping a changeset, we also want to
199 An example usage is strip. When stripping a changeset, we also want to
200 strip the markers exclusive to this changeset. Otherwise we would have
200 strip the markers exclusive to this changeset. Otherwise we would have
201 "dangling"" obsolescence markers from its precursors: Obsolescence markers
201 "dangling"" obsolescence markers from its precursors: Obsolescence markers
202 marking a node as obsolete without any successors available locally.
202 marking a node as obsolete without any successors available locally.
203
203
204 As for relevant markers, the prune markers for children will be followed.
204 As for relevant markers, the prune markers for children will be followed.
205 Of course, they will only be followed if the pruned children is
205 Of course, they will only be followed if the pruned children is
206 locally-known. Since the prune markers are relevant to the pruned node.
206 locally-known. Since the prune markers are relevant to the pruned node.
207 However, while prune markers are considered relevant to the parent of the
207 However, while prune markers are considered relevant to the parent of the
208 pruned changesets, prune markers for locally-known changeset (with no
208 pruned changesets, prune markers for locally-known changeset (with no
209 successors) are considered exclusive to the pruned nodes. This allows
209 successors) are considered exclusive to the pruned nodes. This allows
210 to strip the prune markers (with the rest of the exclusive chain) alongside
210 to strip the prune markers (with the rest of the exclusive chain) alongside
211 the pruned changesets.
211 the pruned changesets.
212 """
212 """
213 # running on a filtered repository would be dangerous as markers could be
213 # running on a filtered repository would be dangerous as markers could be
214 # reported as exclusive when they are relevant for other filtered nodes.
214 # reported as exclusive when they are relevant for other filtered nodes.
215 unfi = repo.unfiltered()
215 unfi = repo.unfiltered()
216
216
217 # shortcut to various useful item
217 # shortcut to various useful item
218 nm = unfi.changelog.nodemap
218 nm = unfi.changelog.nodemap
219 precursorsmarkers = unfi.obsstore.predecessors
219 precursorsmarkers = unfi.obsstore.predecessors
220 successormarkers = unfi.obsstore.successors
220 successormarkers = unfi.obsstore.successors
221 childrenmarkers = unfi.obsstore.children
221 childrenmarkers = unfi.obsstore.children
222
222
223 # exclusive markers (return of the function)
223 # exclusive markers (return of the function)
224 exclmarkers = set()
224 exclmarkers = set()
225 # we need fast membership testing
225 # we need fast membership testing
226 nodes = set(nodes)
226 nodes = set(nodes)
227 # looking for head in the obshistory
227 # looking for head in the obshistory
228 #
228 #
229 # XXX we are ignoring all issues in regard with cycle for now.
229 # XXX we are ignoring all issues in regard with cycle for now.
230 stack = [n for n in nodes if not _filterprunes(successormarkers.get(n, ()))]
230 stack = [n for n in nodes if not _filterprunes(successormarkers.get(n, ()))]
231 stack.sort()
231 stack.sort()
232 # nodes already stacked
232 # nodes already stacked
233 seennodes = set(stack)
233 seennodes = set(stack)
234 while stack:
234 while stack:
235 current = stack.pop()
235 current = stack.pop()
236 # fetch precursors markers
236 # fetch precursors markers
237 markers = list(precursorsmarkers.get(current, ()))
237 markers = list(precursorsmarkers.get(current, ()))
238 # extend the list with prune markers
238 # extend the list with prune markers
239 for mark in successormarkers.get(current, ()):
239 for mark in successormarkers.get(current, ()):
240 if not mark[1]:
240 if not mark[1]:
241 markers.append(mark)
241 markers.append(mark)
242 # and markers from children (looking for prune)
242 # and markers from children (looking for prune)
243 for mark in childrenmarkers.get(current, ()):
243 for mark in childrenmarkers.get(current, ()):
244 if not mark[1]:
244 if not mark[1]:
245 markers.append(mark)
245 markers.append(mark)
246 # traverse the markers
246 # traverse the markers
247 for mark in markers:
247 for mark in markers:
248 if mark in exclmarkers:
248 if mark in exclmarkers:
249 # markers already selected
249 # markers already selected
250 continue
250 continue
251
251
252 # If the markers is about the current node, select it
252 # If the markers is about the current node, select it
253 #
253 #
254 # (this delay the addition of markers from children)
254 # (this delay the addition of markers from children)
255 if mark[1] or mark[0] == current:
255 if mark[1] or mark[0] == current:
256 exclmarkers.add(mark)
256 exclmarkers.add(mark)
257
257
258 # should we keep traversing through the precursors?
258 # should we keep traversing through the precursors?
259 prec = mark[0]
259 prec = mark[0]
260
260
261 # nodes in the stack or already processed
261 # nodes in the stack or already processed
262 if prec in seennodes:
262 if prec in seennodes:
263 continue
263 continue
264
264
265 # is this a locally known node ?
265 # is this a locally known node ?
266 known = prec in nm
266 known = prec in nm
267 # if locally-known and not in the <nodes> set the traversal
267 # if locally-known and not in the <nodes> set the traversal
268 # stop here.
268 # stop here.
269 if known and prec not in nodes:
269 if known and prec not in nodes:
270 continue
270 continue
271
271
272 # do not keep going if there are unselected markers pointing to this
272 # do not keep going if there are unselected markers pointing to this
273 # nodes. If we end up traversing these unselected markers later the
273 # nodes. If we end up traversing these unselected markers later the
274 # node will be taken care of at that point.
274 # node will be taken care of at that point.
275 precmarkers = _filterprunes(successormarkers.get(prec))
275 precmarkers = _filterprunes(successormarkers.get(prec))
276 if precmarkers.issubset(exclmarkers):
276 if precmarkers.issubset(exclmarkers):
277 seennodes.add(prec)
277 seennodes.add(prec)
278 stack.append(prec)
278 stack.append(prec)
279
279
280 return exclmarkers
280 return exclmarkers
281
281
282 def foreground(repo, nodes):
282 def foreground(repo, nodes):
283 """return all nodes in the "foreground" of other node
283 """return all nodes in the "foreground" of other node
284
284
285 The foreground of a revision is anything reachable using parent -> children
285 The foreground of a revision is anything reachable using parent -> children
286 or precursor -> successor relation. It is very similar to "descendant" but
286 or precursor -> successor relation. It is very similar to "descendant" but
287 augmented with obsolescence information.
287 augmented with obsolescence information.
288
288
289 Beware that possible obsolescence cycle may result if complex situation.
289 Beware that possible obsolescence cycle may result if complex situation.
290 """
290 """
291 repo = repo.unfiltered()
291 repo = repo.unfiltered()
292 foreground = set(repo.set('%ln::', nodes))
292 foreground = set(repo.set('%ln::', nodes))
293 if repo.obsstore:
293 if repo.obsstore:
294 # We only need this complicated logic if there is obsolescence
294 # We only need this complicated logic if there is obsolescence
295 # XXX will probably deserve an optimised revset.
295 # XXX will probably deserve an optimised revset.
296 nm = repo.changelog.nodemap
296 nm = repo.changelog.nodemap
297 plen = -1
297 plen = -1
298 # compute the whole set of successors or descendants
298 # compute the whole set of successors or descendants
299 while len(foreground) != plen:
299 while len(foreground) != plen:
300 plen = len(foreground)
300 plen = len(foreground)
301 succs = set(c.node() for c in foreground)
301 succs = set(c.node() for c in foreground)
302 mutable = [c.node() for c in foreground if c.mutable()]
302 mutable = [c.node() for c in foreground if c.mutable()]
303 succs.update(allsuccessors(repo.obsstore, mutable))
303 succs.update(allsuccessors(repo.obsstore, mutable))
304 known = (n for n in succs if n in nm)
304 known = (n for n in succs if n in nm)
305 foreground = set(repo.set('%ln::', known))
305 foreground = set(repo.set('%ln::', known))
306 return set(c.node() for c in foreground)
306 return set(c.node() for c in foreground)
307
307
308 def getobsoleted(repo, tr):
308 def getobsoleted(repo, tr):
309 """return the set of pre-existing revisions obsoleted by a transaction"""
309 """return the set of pre-existing revisions obsoleted by a transaction"""
310 torev = repo.unfiltered().changelog.nodemap.get
310 torev = repo.unfiltered().changelog.nodemap.get
311 phase = repo._phasecache.phase
311 phase = repo._phasecache.phase
312 succsmarkers = repo.obsstore.successors.get
312 succsmarkers = repo.obsstore.successors.get
313 public = phases.public
313 public = phases.public
314 addedmarkers = tr.changes.get('obsmarkers')
314 addedmarkers = tr.changes.get('obsmarkers')
315 addedrevs = tr.changes.get('revs')
315 addedrevs = tr.changes.get('revs')
316 seenrevs = set(addedrevs)
316 seenrevs = set(addedrevs)
317 obsoleted = set()
317 obsoleted = set()
318 for mark in addedmarkers:
318 for mark in addedmarkers:
319 node = mark[0]
319 node = mark[0]
320 rev = torev(node)
320 rev = torev(node)
321 if rev is None or rev in seenrevs:
321 if rev is None or rev in seenrevs:
322 continue
322 continue
323 seenrevs.add(rev)
323 seenrevs.add(rev)
324 if phase(repo, rev) == public:
324 if phase(repo, rev) == public:
325 continue
325 continue
326 if set(succsmarkers(node) or []).issubset(addedmarkers):
326 if set(succsmarkers(node) or []).issubset(addedmarkers):
327 obsoleted.add(rev)
327 obsoleted.add(rev)
328 return obsoleted
328 return obsoleted
329
329
330 class _succs(list):
330 class _succs(list):
331 """small class to represent a successors with some metadata about it"""
331 """small class to represent a successors with some metadata about it"""
332
332
333 def __init__(self, *args, **kwargs):
333 def __init__(self, *args, **kwargs):
334 super(_succs, self).__init__(*args, **kwargs)
334 super(_succs, self).__init__(*args, **kwargs)
335 self.markers = set()
335 self.markers = set()
336
336
337 def copy(self):
337 def copy(self):
338 new = _succs(self)
338 new = _succs(self)
339 new.markers = self.markers.copy()
339 new.markers = self.markers.copy()
340 return new
340 return new
341
341
342 @util.propertycache
342 @util.propertycache
343 def _set(self):
343 def _set(self):
344 # immutable
344 # immutable
345 return set(self)
345 return set(self)
346
346
347 def canmerge(self, other):
347 def canmerge(self, other):
348 return self._set.issubset(other._set)
348 return self._set.issubset(other._set)
349
349
350 def successorssets(repo, initialnode, closest=False, cache=None):
350 def successorssets(repo, initialnode, closest=False, cache=None):
351 """Return set of all latest successors of initial nodes
351 """Return set of all latest successors of initial nodes
352
352
353 The successors set of a changeset A are the group of revisions that succeed
353 The successors set of a changeset A are the group of revisions that succeed
354 A. It succeeds A as a consistent whole, each revision being only a partial
354 A. It succeeds A as a consistent whole, each revision being only a partial
355 replacement. By default, the successors set contains non-obsolete
355 replacement. By default, the successors set contains non-obsolete
356 changesets only, walking the obsolescence graph until reaching a leaf. If
356 changesets only, walking the obsolescence graph until reaching a leaf. If
357 'closest' is set to True, closest successors-sets are return (the
357 'closest' is set to True, closest successors-sets are return (the
358 obsolescence walk stops on known changesets).
358 obsolescence walk stops on known changesets).
359
359
360 This function returns the full list of successor sets which is why it
360 This function returns the full list of successor sets which is why it
361 returns a list of tuples and not just a single tuple. Each tuple is a valid
361 returns a list of tuples and not just a single tuple. Each tuple is a valid
362 successors set. Note that (A,) may be a valid successors set for changeset A
362 successors set. Note that (A,) may be a valid successors set for changeset A
363 (see below).
363 (see below).
364
364
365 In most cases, a changeset A will have a single element (e.g. the changeset
365 In most cases, a changeset A will have a single element (e.g. the changeset
366 A is replaced by A') in its successors set. Though, it is also common for a
366 A is replaced by A') in its successors set. Though, it is also common for a
367 changeset A to have no elements in its successor set (e.g. the changeset
367 changeset A to have no elements in its successor set (e.g. the changeset
368 has been pruned). Therefore, the returned list of successors sets will be
368 has been pruned). Therefore, the returned list of successors sets will be
369 [(A',)] or [], respectively.
369 [(A',)] or [], respectively.
370
370
371 When a changeset A is split into A' and B', however, it will result in a
371 When a changeset A is split into A' and B', however, it will result in a
372 successors set containing more than a single element, i.e. [(A',B')].
372 successors set containing more than a single element, i.e. [(A',B')].
373 Divergent changesets will result in multiple successors sets, i.e. [(A',),
373 Divergent changesets will result in multiple successors sets, i.e. [(A',),
374 (A'')].
374 (A'')].
375
375
376 If a changeset A is not obsolete, then it will conceptually have no
376 If a changeset A is not obsolete, then it will conceptually have no
377 successors set. To distinguish this from a pruned changeset, the successor
377 successors set. To distinguish this from a pruned changeset, the successor
378 set will contain itself only, i.e. [(A,)].
378 set will contain itself only, i.e. [(A,)].
379
379
380 Finally, final successors unknown locally are considered to be pruned
380 Finally, final successors unknown locally are considered to be pruned
381 (pruned: obsoleted without any successors). (Final: successors not affected
381 (pruned: obsoleted without any successors). (Final: successors not affected
382 by markers).
382 by markers).
383
383
384 The 'closest' mode respect the repoview filtering. For example, without
384 The 'closest' mode respect the repoview filtering. For example, without
385 filter it will stop at the first locally known changeset, with 'visible'
385 filter it will stop at the first locally known changeset, with 'visible'
386 filter it will stop on visible changesets).
386 filter it will stop on visible changesets).
387
387
388 The optional `cache` parameter is a dictionary that may contains
388 The optional `cache` parameter is a dictionary that may contains
389 precomputed successors sets. It is meant to reuse the computation of a
389 precomputed successors sets. It is meant to reuse the computation of a
390 previous call to `successorssets` when multiple calls are made at the same
390 previous call to `successorssets` when multiple calls are made at the same
391 time. The cache dictionary is updated in place. The caller is responsible
391 time. The cache dictionary is updated in place. The caller is responsible
392 for its life span. Code that makes multiple calls to `successorssets`
392 for its life span. Code that makes multiple calls to `successorssets`
393 *should* use this cache mechanism or risk a performance hit.
393 *should* use this cache mechanism or risk a performance hit.
394
394
395 Since results are different depending of the 'closest' most, the same cache
395 Since results are different depending of the 'closest' most, the same cache
396 cannot be reused for both mode.
396 cannot be reused for both mode.
397 """
397 """
398
398
399 succmarkers = repo.obsstore.successors
399 succmarkers = repo.obsstore.successors
400
400
401 # Stack of nodes we search successors sets for
401 # Stack of nodes we search successors sets for
402 toproceed = [initialnode]
402 toproceed = [initialnode]
403 # set version of above list for fast loop detection
403 # set version of above list for fast loop detection
404 # element added to "toproceed" must be added here
404 # element added to "toproceed" must be added here
405 stackedset = set(toproceed)
405 stackedset = set(toproceed)
406 if cache is None:
406 if cache is None:
407 cache = {}
407 cache = {}
408
408
409 # This while loop is the flattened version of a recursive search for
409 # This while loop is the flattened version of a recursive search for
410 # successors sets
410 # successors sets
411 #
411 #
412 # def successorssets(x):
412 # def successorssets(x):
413 # successors = directsuccessors(x)
413 # successors = directsuccessors(x)
414 # ss = [[]]
414 # ss = [[]]
415 # for succ in directsuccessors(x):
415 # for succ in directsuccessors(x):
416 # # product as in itertools cartesian product
416 # # product as in itertools cartesian product
417 # ss = product(ss, successorssets(succ))
417 # ss = product(ss, successorssets(succ))
418 # return ss
418 # return ss
419 #
419 #
420 # But we can not use plain recursive calls here:
420 # But we can not use plain recursive calls here:
421 # - that would blow the python call stack
421 # - that would blow the python call stack
422 # - obsolescence markers may have cycles, we need to handle them.
422 # - obsolescence markers may have cycles, we need to handle them.
423 #
423 #
424 # The `toproceed` list act as our call stack. Every node we search
424 # The `toproceed` list act as our call stack. Every node we search
425 # successors set for are stacked there.
425 # successors set for are stacked there.
426 #
426 #
427 # The `stackedset` is set version of this stack used to check if a node is
427 # The `stackedset` is set version of this stack used to check if a node is
428 # already stacked. This check is used to detect cycles and prevent infinite
428 # already stacked. This check is used to detect cycles and prevent infinite
429 # loop.
429 # loop.
430 #
430 #
431 # successors set of all nodes are stored in the `cache` dictionary.
431 # successors set of all nodes are stored in the `cache` dictionary.
432 #
432 #
433 # After this while loop ends we use the cache to return the successors sets
433 # After this while loop ends we use the cache to return the successors sets
434 # for the node requested by the caller.
434 # for the node requested by the caller.
435 while toproceed:
435 while toproceed:
436 # Every iteration tries to compute the successors sets of the topmost
436 # Every iteration tries to compute the successors sets of the topmost
437 # node of the stack: CURRENT.
437 # node of the stack: CURRENT.
438 #
438 #
439 # There are four possible outcomes:
439 # There are four possible outcomes:
440 #
440 #
441 # 1) We already know the successors sets of CURRENT:
441 # 1) We already know the successors sets of CURRENT:
442 # -> mission accomplished, pop it from the stack.
442 # -> mission accomplished, pop it from the stack.
443 # 2) Stop the walk:
443 # 2) Stop the walk:
444 # default case: Node is not obsolete
444 # default case: Node is not obsolete
445 # closest case: Node is known at this repo filter level
445 # closest case: Node is known at this repo filter level
446 # -> the node is its own successors sets. Add it to the cache.
446 # -> the node is its own successors sets. Add it to the cache.
447 # 3) We do not know successors set of direct successors of CURRENT:
447 # 3) We do not know successors set of direct successors of CURRENT:
448 # -> We add those successors to the stack.
448 # -> We add those successors to the stack.
449 # 4) We know successors sets of all direct successors of CURRENT:
449 # 4) We know successors sets of all direct successors of CURRENT:
450 # -> We can compute CURRENT successors set and add it to the
450 # -> We can compute CURRENT successors set and add it to the
451 # cache.
451 # cache.
452 #
452 #
453 current = toproceed[-1]
453 current = toproceed[-1]
454
454
455 # case 2 condition is a bit hairy because of closest,
455 # case 2 condition is a bit hairy because of closest,
456 # we compute it on its own
456 # we compute it on its own
457 case2condition = ((current not in succmarkers)
457 case2condition = ((current not in succmarkers)
458 or (closest and current != initialnode
458 or (closest and current != initialnode
459 and current in repo))
459 and current in repo))
460
460
461 if current in cache:
461 if current in cache:
462 # case (1): We already know the successors sets
462 # case (1): We already know the successors sets
463 stackedset.remove(toproceed.pop())
463 stackedset.remove(toproceed.pop())
464 elif case2condition:
464 elif case2condition:
465 # case (2): end of walk.
465 # case (2): end of walk.
466 if current in repo:
466 if current in repo:
467 # We have a valid successors.
467 # We have a valid successors.
468 cache[current] = [_succs((current,))]
468 cache[current] = [_succs((current,))]
469 else:
469 else:
470 # Final obsolete version is unknown locally.
470 # Final obsolete version is unknown locally.
471 # Do not count that as a valid successors
471 # Do not count that as a valid successors
472 cache[current] = []
472 cache[current] = []
473 else:
473 else:
474 # cases (3) and (4)
474 # cases (3) and (4)
475 #
475 #
476 # We proceed in two phases. Phase 1 aims to distinguish case (3)
476 # We proceed in two phases. Phase 1 aims to distinguish case (3)
477 # from case (4):
477 # from case (4):
478 #
478 #
479 # For each direct successors of CURRENT, we check whether its
479 # For each direct successors of CURRENT, we check whether its
480 # successors sets are known. If they are not, we stack the
480 # successors sets are known. If they are not, we stack the
481 # unknown node and proceed to the next iteration of the while
481 # unknown node and proceed to the next iteration of the while
482 # loop. (case 3)
482 # loop. (case 3)
483 #
483 #
484 # During this step, we may detect obsolescence cycles: a node
484 # During this step, we may detect obsolescence cycles: a node
485 # with unknown successors sets but already in the call stack.
485 # with unknown successors sets but already in the call stack.
486 # In such a situation, we arbitrary set the successors sets of
486 # In such a situation, we arbitrary set the successors sets of
487 # the node to nothing (node pruned) to break the cycle.
487 # the node to nothing (node pruned) to break the cycle.
488 #
488 #
489 # If no break was encountered we proceed to phase 2.
489 # If no break was encountered we proceed to phase 2.
490 #
490 #
491 # Phase 2 computes successors sets of CURRENT (case 4); see details
491 # Phase 2 computes successors sets of CURRENT (case 4); see details
492 # in phase 2 itself.
492 # in phase 2 itself.
493 #
493 #
494 # Note the two levels of iteration in each phase.
494 # Note the two levels of iteration in each phase.
495 # - The first one handles obsolescence markers using CURRENT as
495 # - The first one handles obsolescence markers using CURRENT as
496 # precursor (successors markers of CURRENT).
496 # precursor (successors markers of CURRENT).
497 #
497 #
498 # Having multiple entry here means divergence.
498 # Having multiple entry here means divergence.
499 #
499 #
500 # - The second one handles successors defined in each marker.
500 # - The second one handles successors defined in each marker.
501 #
501 #
502 # Having none means pruned node, multiple successors means split,
502 # Having none means pruned node, multiple successors means split,
503 # single successors are standard replacement.
503 # single successors are standard replacement.
504 #
504 #
505 for mark in sorted(succmarkers[current]):
505 for mark in sorted(succmarkers[current]):
506 for suc in mark[1]:
506 for suc in mark[1]:
507 if suc not in cache:
507 if suc not in cache:
508 if suc in stackedset:
508 if suc in stackedset:
509 # cycle breaking
509 # cycle breaking
510 cache[suc] = []
510 cache[suc] = []
511 else:
511 else:
512 # case (3) If we have not computed successors sets
512 # case (3) If we have not computed successors sets
513 # of one of those successors we add it to the
513 # of one of those successors we add it to the
514 # `toproceed` stack and stop all work for this
514 # `toproceed` stack and stop all work for this
515 # iteration.
515 # iteration.
516 toproceed.append(suc)
516 toproceed.append(suc)
517 stackedset.add(suc)
517 stackedset.add(suc)
518 break
518 break
519 else:
519 else:
520 continue
520 continue
521 break
521 break
522 else:
522 else:
523 # case (4): we know all successors sets of all direct
523 # case (4): we know all successors sets of all direct
524 # successors
524 # successors
525 #
525 #
526 # Successors set contributed by each marker depends on the
526 # Successors set contributed by each marker depends on the
527 # successors sets of all its "successors" node.
527 # successors sets of all its "successors" node.
528 #
528 #
529 # Each different marker is a divergence in the obsolescence
529 # Each different marker is a divergence in the obsolescence
530 # history. It contributes successors sets distinct from other
530 # history. It contributes successors sets distinct from other
531 # markers.
531 # markers.
532 #
532 #
533 # Within a marker, a successor may have divergent successors
533 # Within a marker, a successor may have divergent successors
534 # sets. In such a case, the marker will contribute multiple
534 # sets. In such a case, the marker will contribute multiple
535 # divergent successors sets. If multiple successors have
535 # divergent successors sets. If multiple successors have
536 # divergent successors sets, a Cartesian product is used.
536 # divergent successors sets, a Cartesian product is used.
537 #
537 #
538 # At the end we post-process successors sets to remove
538 # At the end we post-process successors sets to remove
539 # duplicated entry and successors set that are strict subset of
539 # duplicated entry and successors set that are strict subset of
540 # another one.
540 # another one.
541 succssets = []
541 succssets = []
542 for mark in sorted(succmarkers[current]):
542 for mark in sorted(succmarkers[current]):
543 # successors sets contributed by this marker
543 # successors sets contributed by this marker
544 base = _succs()
544 base = _succs()
545 base.markers.add(mark)
545 base.markers.add(mark)
546 markss = [base]
546 markss = [base]
547 for suc in mark[1]:
547 for suc in mark[1]:
548 # cardinal product with previous successors
548 # cardinal product with previous successors
549 productresult = []
549 productresult = []
550 for prefix in markss:
550 for prefix in markss:
551 for suffix in cache[suc]:
551 for suffix in cache[suc]:
552 newss = prefix.copy()
552 newss = prefix.copy()
553 newss.markers.update(suffix.markers)
553 newss.markers.update(suffix.markers)
554 for part in suffix:
554 for part in suffix:
555 # do not duplicated entry in successors set
555 # do not duplicated entry in successors set
556 # first entry wins.
556 # first entry wins.
557 if part not in newss:
557 if part not in newss:
558 newss.append(part)
558 newss.append(part)
559 productresult.append(newss)
559 productresult.append(newss)
560 markss = productresult
560 markss = productresult
561 succssets.extend(markss)
561 succssets.extend(markss)
562 # remove duplicated and subset
562 # remove duplicated and subset
563 seen = []
563 seen = []
564 final = []
564 final = []
565 candidates = sorted((s for s in succssets if s),
565 candidates = sorted((s for s in succssets if s),
566 key=len, reverse=True)
566 key=len, reverse=True)
567 for cand in candidates:
567 for cand in candidates:
568 for seensuccs in seen:
568 for seensuccs in seen:
569 if cand.canmerge(seensuccs):
569 if cand.canmerge(seensuccs):
570 seensuccs.markers.update(cand.markers)
570 seensuccs.markers.update(cand.markers)
571 break
571 break
572 else:
572 else:
573 final.append(cand)
573 final.append(cand)
574 seen.append(cand)
574 seen.append(cand)
575 final.reverse() # put small successors set first
575 final.reverse() # put small successors set first
576 cache[current] = final
576 cache[current] = final
577 return cache[initialnode]
577 return cache[initialnode]
578
578
579 def successorsandmarkers(repo, ctx):
579 def successorsandmarkers(repo, ctx):
580 """compute the raw data needed for computing obsfate
580 """compute the raw data needed for computing obsfate
581 Returns a list of dict, one dict per successors set
581 Returns a list of dict, one dict per successors set
582 """
582 """
583 if not ctx.obsolete():
583 if not ctx.obsolete():
584 return None
584 return None
585
585
586 ssets = successorssets(repo, ctx.node(), closest=True)
586 ssets = successorssets(repo, ctx.node(), closest=True)
587
587
588 values = []
588 values = []
589 for sset in ssets:
589 for sset in ssets:
590 values.append({'successors': sset, 'markers': sset.markers})
590 values.append({'successors': sset, 'markers': sset.markers})
591
591
592 return values
592 return values
593
593
594 def successorsetverb(successorset):
594 def successorsetverb(successorset):
595 """ Return the verb summarizing the successorset
595 """ Return the verb summarizing the successorset
596 """
596 """
597 if not successorset:
597 if not successorset:
598 verb = 'pruned'
598 verb = 'pruned'
599 elif len(successorset) == 1:
599 elif len(successorset) == 1:
600 verb = 'rewritten'
600 verb = 'rewritten'
601 else:
601 else:
602 verb = 'split'
602 verb = 'split'
603 return verb
603 return verb
604
604
605 def markersdates(markers):
606 """returns the list of dates for a list of markers
607 """
608 return [m[4] for m in markers]
609
605 def markersusers(markers):
610 def markersusers(markers):
606 """ Returns a sorted list of markers users without duplicates
611 """ Returns a sorted list of markers users without duplicates
607 """
612 """
608 markersmeta = [dict(m[3]) for m in markers]
613 markersmeta = [dict(m[3]) for m in markers]
609 users = set(meta.get('user') for meta in markersmeta if meta.get('user'))
614 users = set(meta.get('user') for meta in markersmeta if meta.get('user'))
610
615
611 return sorted(users)
616 return sorted(users)
@@ -1,1415 +1,1460 b''
1 # templater.py - template expansion for output
1 # templater.py - template expansion for output
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11 import re
11 import re
12 import types
12 import types
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 color,
16 color,
17 config,
17 config,
18 encoding,
18 encoding,
19 error,
19 error,
20 minirst,
20 minirst,
21 obsutil,
21 obsutil,
22 parser,
22 parser,
23 pycompat,
23 pycompat,
24 registrar,
24 registrar,
25 revset as revsetmod,
25 revset as revsetmod,
26 revsetlang,
26 revsetlang,
27 templatefilters,
27 templatefilters,
28 templatekw,
28 templatekw,
29 util,
29 util,
30 )
30 )
31
31
32 # template parsing
32 # template parsing
33
33
34 elements = {
34 elements = {
35 # token-type: binding-strength, primary, prefix, infix, suffix
35 # token-type: binding-strength, primary, prefix, infix, suffix
36 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
36 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
37 "%": (16, None, None, ("%", 16), None),
37 "%": (16, None, None, ("%", 16), None),
38 "|": (15, None, None, ("|", 15), None),
38 "|": (15, None, None, ("|", 15), None),
39 "*": (5, None, None, ("*", 5), None),
39 "*": (5, None, None, ("*", 5), None),
40 "/": (5, None, None, ("/", 5), None),
40 "/": (5, None, None, ("/", 5), None),
41 "+": (4, None, None, ("+", 4), None),
41 "+": (4, None, None, ("+", 4), None),
42 "-": (4, None, ("negate", 19), ("-", 4), None),
42 "-": (4, None, ("negate", 19), ("-", 4), None),
43 "=": (3, None, None, ("keyvalue", 3), None),
43 "=": (3, None, None, ("keyvalue", 3), None),
44 ",": (2, None, None, ("list", 2), None),
44 ",": (2, None, None, ("list", 2), None),
45 ")": (0, None, None, None, None),
45 ")": (0, None, None, None, None),
46 "integer": (0, "integer", None, None, None),
46 "integer": (0, "integer", None, None, None),
47 "symbol": (0, "symbol", None, None, None),
47 "symbol": (0, "symbol", None, None, None),
48 "string": (0, "string", None, None, None),
48 "string": (0, "string", None, None, None),
49 "template": (0, "template", None, None, None),
49 "template": (0, "template", None, None, None),
50 "end": (0, None, None, None, None),
50 "end": (0, None, None, None, None),
51 }
51 }
52
52
53 def tokenize(program, start, end, term=None):
53 def tokenize(program, start, end, term=None):
54 """Parse a template expression into a stream of tokens, which must end
54 """Parse a template expression into a stream of tokens, which must end
55 with term if specified"""
55 with term if specified"""
56 pos = start
56 pos = start
57 program = pycompat.bytestr(program)
57 program = pycompat.bytestr(program)
58 while pos < end:
58 while pos < end:
59 c = program[pos]
59 c = program[pos]
60 if c.isspace(): # skip inter-token whitespace
60 if c.isspace(): # skip inter-token whitespace
61 pass
61 pass
62 elif c in "(=,)%|+-*/": # handle simple operators
62 elif c in "(=,)%|+-*/": # handle simple operators
63 yield (c, None, pos)
63 yield (c, None, pos)
64 elif c in '"\'': # handle quoted templates
64 elif c in '"\'': # handle quoted templates
65 s = pos + 1
65 s = pos + 1
66 data, pos = _parsetemplate(program, s, end, c)
66 data, pos = _parsetemplate(program, s, end, c)
67 yield ('template', data, s)
67 yield ('template', data, s)
68 pos -= 1
68 pos -= 1
69 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
69 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
70 # handle quoted strings
70 # handle quoted strings
71 c = program[pos + 1]
71 c = program[pos + 1]
72 s = pos = pos + 2
72 s = pos = pos + 2
73 while pos < end: # find closing quote
73 while pos < end: # find closing quote
74 d = program[pos]
74 d = program[pos]
75 if d == '\\': # skip over escaped characters
75 if d == '\\': # skip over escaped characters
76 pos += 2
76 pos += 2
77 continue
77 continue
78 if d == c:
78 if d == c:
79 yield ('string', program[s:pos], s)
79 yield ('string', program[s:pos], s)
80 break
80 break
81 pos += 1
81 pos += 1
82 else:
82 else:
83 raise error.ParseError(_("unterminated string"), s)
83 raise error.ParseError(_("unterminated string"), s)
84 elif c.isdigit():
84 elif c.isdigit():
85 s = pos
85 s = pos
86 while pos < end:
86 while pos < end:
87 d = program[pos]
87 d = program[pos]
88 if not d.isdigit():
88 if not d.isdigit():
89 break
89 break
90 pos += 1
90 pos += 1
91 yield ('integer', program[s:pos], s)
91 yield ('integer', program[s:pos], s)
92 pos -= 1
92 pos -= 1
93 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
93 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
94 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
94 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
95 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
95 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
96 # where some of nested templates were preprocessed as strings and
96 # where some of nested templates were preprocessed as strings and
97 # then compiled. therefore, \"...\" was allowed. (issue4733)
97 # then compiled. therefore, \"...\" was allowed. (issue4733)
98 #
98 #
99 # processing flow of _evalifliteral() at 5ab28a2e9962:
99 # processing flow of _evalifliteral() at 5ab28a2e9962:
100 # outer template string -> stringify() -> compiletemplate()
100 # outer template string -> stringify() -> compiletemplate()
101 # ------------------------ ------------ ------------------
101 # ------------------------ ------------ ------------------
102 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
102 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
103 # ~~~~~~~~
103 # ~~~~~~~~
104 # escaped quoted string
104 # escaped quoted string
105 if c == 'r':
105 if c == 'r':
106 pos += 1
106 pos += 1
107 token = 'string'
107 token = 'string'
108 else:
108 else:
109 token = 'template'
109 token = 'template'
110 quote = program[pos:pos + 2]
110 quote = program[pos:pos + 2]
111 s = pos = pos + 2
111 s = pos = pos + 2
112 while pos < end: # find closing escaped quote
112 while pos < end: # find closing escaped quote
113 if program.startswith('\\\\\\', pos, end):
113 if program.startswith('\\\\\\', pos, end):
114 pos += 4 # skip over double escaped characters
114 pos += 4 # skip over double escaped characters
115 continue
115 continue
116 if program.startswith(quote, pos, end):
116 if program.startswith(quote, pos, end):
117 # interpret as if it were a part of an outer string
117 # interpret as if it were a part of an outer string
118 data = parser.unescapestr(program[s:pos])
118 data = parser.unescapestr(program[s:pos])
119 if token == 'template':
119 if token == 'template':
120 data = _parsetemplate(data, 0, len(data))[0]
120 data = _parsetemplate(data, 0, len(data))[0]
121 yield (token, data, s)
121 yield (token, data, s)
122 pos += 1
122 pos += 1
123 break
123 break
124 pos += 1
124 pos += 1
125 else:
125 else:
126 raise error.ParseError(_("unterminated string"), s)
126 raise error.ParseError(_("unterminated string"), s)
127 elif c.isalnum() or c in '_':
127 elif c.isalnum() or c in '_':
128 s = pos
128 s = pos
129 pos += 1
129 pos += 1
130 while pos < end: # find end of symbol
130 while pos < end: # find end of symbol
131 d = program[pos]
131 d = program[pos]
132 if not (d.isalnum() or d == "_"):
132 if not (d.isalnum() or d == "_"):
133 break
133 break
134 pos += 1
134 pos += 1
135 sym = program[s:pos]
135 sym = program[s:pos]
136 yield ('symbol', sym, s)
136 yield ('symbol', sym, s)
137 pos -= 1
137 pos -= 1
138 elif c == term:
138 elif c == term:
139 yield ('end', None, pos + 1)
139 yield ('end', None, pos + 1)
140 return
140 return
141 else:
141 else:
142 raise error.ParseError(_("syntax error"), pos)
142 raise error.ParseError(_("syntax error"), pos)
143 pos += 1
143 pos += 1
144 if term:
144 if term:
145 raise error.ParseError(_("unterminated template expansion"), start)
145 raise error.ParseError(_("unterminated template expansion"), start)
146 yield ('end', None, pos)
146 yield ('end', None, pos)
147
147
148 def _parsetemplate(tmpl, start, stop, quote=''):
148 def _parsetemplate(tmpl, start, stop, quote=''):
149 r"""
149 r"""
150 >>> _parsetemplate('foo{bar}"baz', 0, 12)
150 >>> _parsetemplate('foo{bar}"baz', 0, 12)
151 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
151 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
152 >>> _parsetemplate('foo{bar}"baz', 0, 12, quote='"')
152 >>> _parsetemplate('foo{bar}"baz', 0, 12, quote='"')
153 ([('string', 'foo'), ('symbol', 'bar')], 9)
153 ([('string', 'foo'), ('symbol', 'bar')], 9)
154 >>> _parsetemplate('foo"{bar}', 0, 9, quote='"')
154 >>> _parsetemplate('foo"{bar}', 0, 9, quote='"')
155 ([('string', 'foo')], 4)
155 ([('string', 'foo')], 4)
156 >>> _parsetemplate(r'foo\"bar"baz', 0, 12, quote='"')
156 >>> _parsetemplate(r'foo\"bar"baz', 0, 12, quote='"')
157 ([('string', 'foo"'), ('string', 'bar')], 9)
157 ([('string', 'foo"'), ('string', 'bar')], 9)
158 >>> _parsetemplate(r'foo\\"bar', 0, 10, quote='"')
158 >>> _parsetemplate(r'foo\\"bar', 0, 10, quote='"')
159 ([('string', 'foo\\')], 6)
159 ([('string', 'foo\\')], 6)
160 """
160 """
161 parsed = []
161 parsed = []
162 sepchars = '{' + quote
162 sepchars = '{' + quote
163 pos = start
163 pos = start
164 p = parser.parser(elements)
164 p = parser.parser(elements)
165 while pos < stop:
165 while pos < stop:
166 n = min((tmpl.find(c, pos, stop) for c in sepchars),
166 n = min((tmpl.find(c, pos, stop) for c in sepchars),
167 key=lambda n: (n < 0, n))
167 key=lambda n: (n < 0, n))
168 if n < 0:
168 if n < 0:
169 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
169 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
170 pos = stop
170 pos = stop
171 break
171 break
172 c = tmpl[n]
172 c = tmpl[n]
173 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
173 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
174 if bs % 2 == 1:
174 if bs % 2 == 1:
175 # escaped (e.g. '\{', '\\\{', but not '\\{')
175 # escaped (e.g. '\{', '\\\{', but not '\\{')
176 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
176 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
177 pos = n + 1
177 pos = n + 1
178 continue
178 continue
179 if n > pos:
179 if n > pos:
180 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
180 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
181 if c == quote:
181 if c == quote:
182 return parsed, n + 1
182 return parsed, n + 1
183
183
184 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
184 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
185 parsed.append(parseres)
185 parsed.append(parseres)
186
186
187 if quote:
187 if quote:
188 raise error.ParseError(_("unterminated string"), start)
188 raise error.ParseError(_("unterminated string"), start)
189 return parsed, pos
189 return parsed, pos
190
190
191 def _unnesttemplatelist(tree):
191 def _unnesttemplatelist(tree):
192 """Expand list of templates to node tuple
192 """Expand list of templates to node tuple
193
193
194 >>> def f(tree):
194 >>> def f(tree):
195 ... print prettyformat(_unnesttemplatelist(tree))
195 ... print prettyformat(_unnesttemplatelist(tree))
196 >>> f(('template', []))
196 >>> f(('template', []))
197 ('string', '')
197 ('string', '')
198 >>> f(('template', [('string', 'foo')]))
198 >>> f(('template', [('string', 'foo')]))
199 ('string', 'foo')
199 ('string', 'foo')
200 >>> f(('template', [('string', 'foo'), ('symbol', 'rev')]))
200 >>> f(('template', [('string', 'foo'), ('symbol', 'rev')]))
201 (template
201 (template
202 ('string', 'foo')
202 ('string', 'foo')
203 ('symbol', 'rev'))
203 ('symbol', 'rev'))
204 >>> f(('template', [('symbol', 'rev')])) # template(rev) -> str
204 >>> f(('template', [('symbol', 'rev')])) # template(rev) -> str
205 (template
205 (template
206 ('symbol', 'rev'))
206 ('symbol', 'rev'))
207 >>> f(('template', [('template', [('string', 'foo')])]))
207 >>> f(('template', [('template', [('string', 'foo')])]))
208 ('string', 'foo')
208 ('string', 'foo')
209 """
209 """
210 if not isinstance(tree, tuple):
210 if not isinstance(tree, tuple):
211 return tree
211 return tree
212 op = tree[0]
212 op = tree[0]
213 if op != 'template':
213 if op != 'template':
214 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
214 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
215
215
216 assert len(tree) == 2
216 assert len(tree) == 2
217 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
217 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
218 if not xs:
218 if not xs:
219 return ('string', '') # empty template ""
219 return ('string', '') # empty template ""
220 elif len(xs) == 1 and xs[0][0] == 'string':
220 elif len(xs) == 1 and xs[0][0] == 'string':
221 return xs[0] # fast path for string with no template fragment "x"
221 return xs[0] # fast path for string with no template fragment "x"
222 else:
222 else:
223 return (op,) + xs
223 return (op,) + xs
224
224
225 def parse(tmpl):
225 def parse(tmpl):
226 """Parse template string into tree"""
226 """Parse template string into tree"""
227 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
227 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
228 assert pos == len(tmpl), 'unquoted template should be consumed'
228 assert pos == len(tmpl), 'unquoted template should be consumed'
229 return _unnesttemplatelist(('template', parsed))
229 return _unnesttemplatelist(('template', parsed))
230
230
231 def _parseexpr(expr):
231 def _parseexpr(expr):
232 """Parse a template expression into tree
232 """Parse a template expression into tree
233
233
234 >>> _parseexpr('"foo"')
234 >>> _parseexpr('"foo"')
235 ('string', 'foo')
235 ('string', 'foo')
236 >>> _parseexpr('foo(bar)')
236 >>> _parseexpr('foo(bar)')
237 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
237 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
238 >>> _parseexpr('foo(')
238 >>> _parseexpr('foo(')
239 Traceback (most recent call last):
239 Traceback (most recent call last):
240 ...
240 ...
241 ParseError: ('not a prefix: end', 4)
241 ParseError: ('not a prefix: end', 4)
242 >>> _parseexpr('"foo" "bar"')
242 >>> _parseexpr('"foo" "bar"')
243 Traceback (most recent call last):
243 Traceback (most recent call last):
244 ...
244 ...
245 ParseError: ('invalid token', 7)
245 ParseError: ('invalid token', 7)
246 """
246 """
247 p = parser.parser(elements)
247 p = parser.parser(elements)
248 tree, pos = p.parse(tokenize(expr, 0, len(expr)))
248 tree, pos = p.parse(tokenize(expr, 0, len(expr)))
249 if pos != len(expr):
249 if pos != len(expr):
250 raise error.ParseError(_('invalid token'), pos)
250 raise error.ParseError(_('invalid token'), pos)
251 return _unnesttemplatelist(tree)
251 return _unnesttemplatelist(tree)
252
252
253 def prettyformat(tree):
253 def prettyformat(tree):
254 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
254 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
255
255
256 def compileexp(exp, context, curmethods):
256 def compileexp(exp, context, curmethods):
257 """Compile parsed template tree to (func, data) pair"""
257 """Compile parsed template tree to (func, data) pair"""
258 t = exp[0]
258 t = exp[0]
259 if t in curmethods:
259 if t in curmethods:
260 return curmethods[t](exp, context)
260 return curmethods[t](exp, context)
261 raise error.ParseError(_("unknown method '%s'") % t)
261 raise error.ParseError(_("unknown method '%s'") % t)
262
262
263 # template evaluation
263 # template evaluation
264
264
265 def getsymbol(exp):
265 def getsymbol(exp):
266 if exp[0] == 'symbol':
266 if exp[0] == 'symbol':
267 return exp[1]
267 return exp[1]
268 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
268 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
269
269
270 def getlist(x):
270 def getlist(x):
271 if not x:
271 if not x:
272 return []
272 return []
273 if x[0] == 'list':
273 if x[0] == 'list':
274 return getlist(x[1]) + [x[2]]
274 return getlist(x[1]) + [x[2]]
275 return [x]
275 return [x]
276
276
277 def gettemplate(exp, context):
277 def gettemplate(exp, context):
278 """Compile given template tree or load named template from map file;
278 """Compile given template tree or load named template from map file;
279 returns (func, data) pair"""
279 returns (func, data) pair"""
280 if exp[0] in ('template', 'string'):
280 if exp[0] in ('template', 'string'):
281 return compileexp(exp, context, methods)
281 return compileexp(exp, context, methods)
282 if exp[0] == 'symbol':
282 if exp[0] == 'symbol':
283 # unlike runsymbol(), here 'symbol' is always taken as template name
283 # unlike runsymbol(), here 'symbol' is always taken as template name
284 # even if it exists in mapping. this allows us to override mapping
284 # even if it exists in mapping. this allows us to override mapping
285 # by web templates, e.g. 'changelogtag' is redefined in map file.
285 # by web templates, e.g. 'changelogtag' is redefined in map file.
286 return context._load(exp[1])
286 return context._load(exp[1])
287 raise error.ParseError(_("expected template specifier"))
287 raise error.ParseError(_("expected template specifier"))
288
288
289 def findsymbolicname(arg):
289 def findsymbolicname(arg):
290 """Find symbolic name for the given compiled expression; returns None
290 """Find symbolic name for the given compiled expression; returns None
291 if nothing found reliably"""
291 if nothing found reliably"""
292 while True:
292 while True:
293 func, data = arg
293 func, data = arg
294 if func is runsymbol:
294 if func is runsymbol:
295 return data
295 return data
296 elif func is runfilter:
296 elif func is runfilter:
297 arg = data[0]
297 arg = data[0]
298 else:
298 else:
299 return None
299 return None
300
300
301 def evalfuncarg(context, mapping, arg):
301 def evalfuncarg(context, mapping, arg):
302 func, data = arg
302 func, data = arg
303 # func() may return string, generator of strings or arbitrary object such
303 # func() may return string, generator of strings or arbitrary object such
304 # as date tuple, but filter does not want generator.
304 # as date tuple, but filter does not want generator.
305 thing = func(context, mapping, data)
305 thing = func(context, mapping, data)
306 if isinstance(thing, types.GeneratorType):
306 if isinstance(thing, types.GeneratorType):
307 thing = stringify(thing)
307 thing = stringify(thing)
308 return thing
308 return thing
309
309
310 def evalboolean(context, mapping, arg):
310 def evalboolean(context, mapping, arg):
311 """Evaluate given argument as boolean, but also takes boolean literals"""
311 """Evaluate given argument as boolean, but also takes boolean literals"""
312 func, data = arg
312 func, data = arg
313 if func is runsymbol:
313 if func is runsymbol:
314 thing = func(context, mapping, data, default=None)
314 thing = func(context, mapping, data, default=None)
315 if thing is None:
315 if thing is None:
316 # not a template keyword, takes as a boolean literal
316 # not a template keyword, takes as a boolean literal
317 thing = util.parsebool(data)
317 thing = util.parsebool(data)
318 else:
318 else:
319 thing = func(context, mapping, data)
319 thing = func(context, mapping, data)
320 if isinstance(thing, bool):
320 if isinstance(thing, bool):
321 return thing
321 return thing
322 # other objects are evaluated as strings, which means 0 is True, but
322 # other objects are evaluated as strings, which means 0 is True, but
323 # empty dict/list should be False as they are expected to be ''
323 # empty dict/list should be False as they are expected to be ''
324 return bool(stringify(thing))
324 return bool(stringify(thing))
325
325
326 def evalinteger(context, mapping, arg, err):
326 def evalinteger(context, mapping, arg, err):
327 v = evalfuncarg(context, mapping, arg)
327 v = evalfuncarg(context, mapping, arg)
328 try:
328 try:
329 return int(v)
329 return int(v)
330 except (TypeError, ValueError):
330 except (TypeError, ValueError):
331 raise error.ParseError(err)
331 raise error.ParseError(err)
332
332
333 def evalstring(context, mapping, arg):
333 def evalstring(context, mapping, arg):
334 func, data = arg
334 func, data = arg
335 return stringify(func(context, mapping, data))
335 return stringify(func(context, mapping, data))
336
336
337 def evalstringliteral(context, mapping, arg):
337 def evalstringliteral(context, mapping, arg):
338 """Evaluate given argument as string template, but returns symbol name
338 """Evaluate given argument as string template, but returns symbol name
339 if it is unknown"""
339 if it is unknown"""
340 func, data = arg
340 func, data = arg
341 if func is runsymbol:
341 if func is runsymbol:
342 thing = func(context, mapping, data, default=data)
342 thing = func(context, mapping, data, default=data)
343 else:
343 else:
344 thing = func(context, mapping, data)
344 thing = func(context, mapping, data)
345 return stringify(thing)
345 return stringify(thing)
346
346
347 def runinteger(context, mapping, data):
347 def runinteger(context, mapping, data):
348 return int(data)
348 return int(data)
349
349
350 def runstring(context, mapping, data):
350 def runstring(context, mapping, data):
351 return data
351 return data
352
352
353 def _recursivesymbolblocker(key):
353 def _recursivesymbolblocker(key):
354 def showrecursion(**args):
354 def showrecursion(**args):
355 raise error.Abort(_("recursive reference '%s' in template") % key)
355 raise error.Abort(_("recursive reference '%s' in template") % key)
356 return showrecursion
356 return showrecursion
357
357
358 def _runrecursivesymbol(context, mapping, key):
358 def _runrecursivesymbol(context, mapping, key):
359 raise error.Abort(_("recursive reference '%s' in template") % key)
359 raise error.Abort(_("recursive reference '%s' in template") % key)
360
360
361 def runsymbol(context, mapping, key, default=''):
361 def runsymbol(context, mapping, key, default=''):
362 v = mapping.get(key)
362 v = mapping.get(key)
363 if v is None:
363 if v is None:
364 v = context._defaults.get(key)
364 v = context._defaults.get(key)
365 if v is None:
365 if v is None:
366 # put poison to cut recursion. we can't move this to parsing phase
366 # put poison to cut recursion. we can't move this to parsing phase
367 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
367 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
368 safemapping = mapping.copy()
368 safemapping = mapping.copy()
369 safemapping[key] = _recursivesymbolblocker(key)
369 safemapping[key] = _recursivesymbolblocker(key)
370 try:
370 try:
371 v = context.process(key, safemapping)
371 v = context.process(key, safemapping)
372 except TemplateNotFound:
372 except TemplateNotFound:
373 v = default
373 v = default
374 if callable(v):
374 if callable(v):
375 return v(**pycompat.strkwargs(mapping))
375 return v(**pycompat.strkwargs(mapping))
376 return v
376 return v
377
377
378 def buildtemplate(exp, context):
378 def buildtemplate(exp, context):
379 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
379 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
380 return (runtemplate, ctmpl)
380 return (runtemplate, ctmpl)
381
381
382 def runtemplate(context, mapping, template):
382 def runtemplate(context, mapping, template):
383 for func, data in template:
383 for func, data in template:
384 yield func(context, mapping, data)
384 yield func(context, mapping, data)
385
385
386 def buildfilter(exp, context):
386 def buildfilter(exp, context):
387 n = getsymbol(exp[2])
387 n = getsymbol(exp[2])
388 if n in context._filters:
388 if n in context._filters:
389 filt = context._filters[n]
389 filt = context._filters[n]
390 arg = compileexp(exp[1], context, methods)
390 arg = compileexp(exp[1], context, methods)
391 return (runfilter, (arg, filt))
391 return (runfilter, (arg, filt))
392 if n in funcs:
392 if n in funcs:
393 f = funcs[n]
393 f = funcs[n]
394 args = _buildfuncargs(exp[1], context, methods, n, f._argspec)
394 args = _buildfuncargs(exp[1], context, methods, n, f._argspec)
395 return (f, args)
395 return (f, args)
396 raise error.ParseError(_("unknown function '%s'") % n)
396 raise error.ParseError(_("unknown function '%s'") % n)
397
397
398 def runfilter(context, mapping, data):
398 def runfilter(context, mapping, data):
399 arg, filt = data
399 arg, filt = data
400 thing = evalfuncarg(context, mapping, arg)
400 thing = evalfuncarg(context, mapping, arg)
401 try:
401 try:
402 return filt(thing)
402 return filt(thing)
403 except (ValueError, AttributeError, TypeError):
403 except (ValueError, AttributeError, TypeError):
404 sym = findsymbolicname(arg)
404 sym = findsymbolicname(arg)
405 if sym:
405 if sym:
406 msg = (_("template filter '%s' is not compatible with keyword '%s'")
406 msg = (_("template filter '%s' is not compatible with keyword '%s'")
407 % (filt.func_name, sym))
407 % (filt.func_name, sym))
408 else:
408 else:
409 msg = _("incompatible use of template filter '%s'") % filt.func_name
409 msg = _("incompatible use of template filter '%s'") % filt.func_name
410 raise error.Abort(msg)
410 raise error.Abort(msg)
411
411
412 def buildmap(exp, context):
412 def buildmap(exp, context):
413 func, data = compileexp(exp[1], context, methods)
413 func, data = compileexp(exp[1], context, methods)
414 tfunc, tdata = gettemplate(exp[2], context)
414 tfunc, tdata = gettemplate(exp[2], context)
415 return (runmap, (func, data, tfunc, tdata))
415 return (runmap, (func, data, tfunc, tdata))
416
416
417 def runmap(context, mapping, data):
417 def runmap(context, mapping, data):
418 func, data, tfunc, tdata = data
418 func, data, tfunc, tdata = data
419 d = func(context, mapping, data)
419 d = func(context, mapping, data)
420 if util.safehasattr(d, 'itermaps'):
420 if util.safehasattr(d, 'itermaps'):
421 diter = d.itermaps()
421 diter = d.itermaps()
422 else:
422 else:
423 try:
423 try:
424 diter = iter(d)
424 diter = iter(d)
425 except TypeError:
425 except TypeError:
426 if func is runsymbol:
426 if func is runsymbol:
427 raise error.ParseError(_("keyword '%s' is not iterable") % data)
427 raise error.ParseError(_("keyword '%s' is not iterable") % data)
428 else:
428 else:
429 raise error.ParseError(_("%r is not iterable") % d)
429 raise error.ParseError(_("%r is not iterable") % d)
430
430
431 for i, v in enumerate(diter):
431 for i, v in enumerate(diter):
432 lm = mapping.copy()
432 lm = mapping.copy()
433 lm['index'] = i
433 lm['index'] = i
434 if isinstance(v, dict):
434 if isinstance(v, dict):
435 lm.update(v)
435 lm.update(v)
436 lm['originalnode'] = mapping.get('node')
436 lm['originalnode'] = mapping.get('node')
437 yield tfunc(context, lm, tdata)
437 yield tfunc(context, lm, tdata)
438 else:
438 else:
439 # v is not an iterable of dicts, this happen when 'key'
439 # v is not an iterable of dicts, this happen when 'key'
440 # has been fully expanded already and format is useless.
440 # has been fully expanded already and format is useless.
441 # If so, return the expanded value.
441 # If so, return the expanded value.
442 yield v
442 yield v
443
443
444 def buildnegate(exp, context):
444 def buildnegate(exp, context):
445 arg = compileexp(exp[1], context, exprmethods)
445 arg = compileexp(exp[1], context, exprmethods)
446 return (runnegate, arg)
446 return (runnegate, arg)
447
447
448 def runnegate(context, mapping, data):
448 def runnegate(context, mapping, data):
449 data = evalinteger(context, mapping, data,
449 data = evalinteger(context, mapping, data,
450 _('negation needs an integer argument'))
450 _('negation needs an integer argument'))
451 return -data
451 return -data
452
452
453 def buildarithmetic(exp, context, func):
453 def buildarithmetic(exp, context, func):
454 left = compileexp(exp[1], context, exprmethods)
454 left = compileexp(exp[1], context, exprmethods)
455 right = compileexp(exp[2], context, exprmethods)
455 right = compileexp(exp[2], context, exprmethods)
456 return (runarithmetic, (func, left, right))
456 return (runarithmetic, (func, left, right))
457
457
458 def runarithmetic(context, mapping, data):
458 def runarithmetic(context, mapping, data):
459 func, left, right = data
459 func, left, right = data
460 left = evalinteger(context, mapping, left,
460 left = evalinteger(context, mapping, left,
461 _('arithmetic only defined on integers'))
461 _('arithmetic only defined on integers'))
462 right = evalinteger(context, mapping, right,
462 right = evalinteger(context, mapping, right,
463 _('arithmetic only defined on integers'))
463 _('arithmetic only defined on integers'))
464 try:
464 try:
465 return func(left, right)
465 return func(left, right)
466 except ZeroDivisionError:
466 except ZeroDivisionError:
467 raise error.Abort(_('division by zero is not defined'))
467 raise error.Abort(_('division by zero is not defined'))
468
468
469 def buildfunc(exp, context):
469 def buildfunc(exp, context):
470 n = getsymbol(exp[1])
470 n = getsymbol(exp[1])
471 if n in funcs:
471 if n in funcs:
472 f = funcs[n]
472 f = funcs[n]
473 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec)
473 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec)
474 return (f, args)
474 return (f, args)
475 if n in context._filters:
475 if n in context._filters:
476 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None)
476 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None)
477 if len(args) != 1:
477 if len(args) != 1:
478 raise error.ParseError(_("filter %s expects one argument") % n)
478 raise error.ParseError(_("filter %s expects one argument") % n)
479 f = context._filters[n]
479 f = context._filters[n]
480 return (runfilter, (args[0], f))
480 return (runfilter, (args[0], f))
481 raise error.ParseError(_("unknown function '%s'") % n)
481 raise error.ParseError(_("unknown function '%s'") % n)
482
482
483 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
483 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
484 """Compile parsed tree of function arguments into list or dict of
484 """Compile parsed tree of function arguments into list or dict of
485 (func, data) pairs
485 (func, data) pairs
486
486
487 >>> context = engine(lambda t: (runsymbol, t))
487 >>> context = engine(lambda t: (runsymbol, t))
488 >>> def fargs(expr, argspec):
488 >>> def fargs(expr, argspec):
489 ... x = _parseexpr(expr)
489 ... x = _parseexpr(expr)
490 ... n = getsymbol(x[1])
490 ... n = getsymbol(x[1])
491 ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
491 ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
492 >>> fargs('a(l=1, k=2)', 'k l m').keys()
492 >>> fargs('a(l=1, k=2)', 'k l m').keys()
493 ['l', 'k']
493 ['l', 'k']
494 >>> args = fargs('a(opts=1, k=2)', '**opts')
494 >>> args = fargs('a(opts=1, k=2)', '**opts')
495 >>> args.keys(), args['opts'].keys()
495 >>> args.keys(), args['opts'].keys()
496 (['opts'], ['opts', 'k'])
496 (['opts'], ['opts', 'k'])
497 """
497 """
498 def compiledict(xs):
498 def compiledict(xs):
499 return util.sortdict((k, compileexp(x, context, curmethods))
499 return util.sortdict((k, compileexp(x, context, curmethods))
500 for k, x in xs.iteritems())
500 for k, x in xs.iteritems())
501 def compilelist(xs):
501 def compilelist(xs):
502 return [compileexp(x, context, curmethods) for x in xs]
502 return [compileexp(x, context, curmethods) for x in xs]
503
503
504 if not argspec:
504 if not argspec:
505 # filter or function with no argspec: return list of positional args
505 # filter or function with no argspec: return list of positional args
506 return compilelist(getlist(exp))
506 return compilelist(getlist(exp))
507
507
508 # function with argspec: return dict of named args
508 # function with argspec: return dict of named args
509 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
509 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
510 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
510 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
511 keyvaluenode='keyvalue', keynode='symbol')
511 keyvaluenode='keyvalue', keynode='symbol')
512 compargs = util.sortdict()
512 compargs = util.sortdict()
513 if varkey:
513 if varkey:
514 compargs[varkey] = compilelist(treeargs.pop(varkey))
514 compargs[varkey] = compilelist(treeargs.pop(varkey))
515 if optkey:
515 if optkey:
516 compargs[optkey] = compiledict(treeargs.pop(optkey))
516 compargs[optkey] = compiledict(treeargs.pop(optkey))
517 compargs.update(compiledict(treeargs))
517 compargs.update(compiledict(treeargs))
518 return compargs
518 return compargs
519
519
520 def buildkeyvaluepair(exp, content):
520 def buildkeyvaluepair(exp, content):
521 raise error.ParseError(_("can't use a key-value pair in this context"))
521 raise error.ParseError(_("can't use a key-value pair in this context"))
522
522
523 # dict of template built-in functions
523 # dict of template built-in functions
524 funcs = {}
524 funcs = {}
525
525
526 templatefunc = registrar.templatefunc(funcs)
526 templatefunc = registrar.templatefunc(funcs)
527
527
528 @templatefunc('date(date[, fmt])')
528 @templatefunc('date(date[, fmt])')
529 def date(context, mapping, args):
529 def date(context, mapping, args):
530 """Format a date. See :hg:`help dates` for formatting
530 """Format a date. See :hg:`help dates` for formatting
531 strings. The default is a Unix date format, including the timezone:
531 strings. The default is a Unix date format, including the timezone:
532 "Mon Sep 04 15:13:13 2006 0700"."""
532 "Mon Sep 04 15:13:13 2006 0700"."""
533 if not (1 <= len(args) <= 2):
533 if not (1 <= len(args) <= 2):
534 # i18n: "date" is a keyword
534 # i18n: "date" is a keyword
535 raise error.ParseError(_("date expects one or two arguments"))
535 raise error.ParseError(_("date expects one or two arguments"))
536
536
537 date = evalfuncarg(context, mapping, args[0])
537 date = evalfuncarg(context, mapping, args[0])
538 fmt = None
538 fmt = None
539 if len(args) == 2:
539 if len(args) == 2:
540 fmt = evalstring(context, mapping, args[1])
540 fmt = evalstring(context, mapping, args[1])
541 try:
541 try:
542 if fmt is None:
542 if fmt is None:
543 return util.datestr(date)
543 return util.datestr(date)
544 else:
544 else:
545 return util.datestr(date, fmt)
545 return util.datestr(date, fmt)
546 except (TypeError, ValueError):
546 except (TypeError, ValueError):
547 # i18n: "date" is a keyword
547 # i18n: "date" is a keyword
548 raise error.ParseError(_("date expects a date information"))
548 raise error.ParseError(_("date expects a date information"))
549
549
550 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
550 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
551 def dict_(context, mapping, args):
551 def dict_(context, mapping, args):
552 """Construct a dict from key-value pairs. A key may be omitted if
552 """Construct a dict from key-value pairs. A key may be omitted if
553 a value expression can provide an unambiguous name."""
553 a value expression can provide an unambiguous name."""
554 data = util.sortdict()
554 data = util.sortdict()
555
555
556 for v in args['args']:
556 for v in args['args']:
557 k = findsymbolicname(v)
557 k = findsymbolicname(v)
558 if not k:
558 if not k:
559 raise error.ParseError(_('dict key cannot be inferred'))
559 raise error.ParseError(_('dict key cannot be inferred'))
560 if k in data or k in args['kwargs']:
560 if k in data or k in args['kwargs']:
561 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
561 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
562 data[k] = evalfuncarg(context, mapping, v)
562 data[k] = evalfuncarg(context, mapping, v)
563
563
564 data.update((k, evalfuncarg(context, mapping, v))
564 data.update((k, evalfuncarg(context, mapping, v))
565 for k, v in args['kwargs'].iteritems())
565 for k, v in args['kwargs'].iteritems())
566 return templatekw.hybriddict(data)
566 return templatekw.hybriddict(data)
567
567
568 @templatefunc('diff([includepattern [, excludepattern]])')
568 @templatefunc('diff([includepattern [, excludepattern]])')
569 def diff(context, mapping, args):
569 def diff(context, mapping, args):
570 """Show a diff, optionally
570 """Show a diff, optionally
571 specifying files to include or exclude."""
571 specifying files to include or exclude."""
572 if len(args) > 2:
572 if len(args) > 2:
573 # i18n: "diff" is a keyword
573 # i18n: "diff" is a keyword
574 raise error.ParseError(_("diff expects zero, one, or two arguments"))
574 raise error.ParseError(_("diff expects zero, one, or two arguments"))
575
575
576 def getpatterns(i):
576 def getpatterns(i):
577 if i < len(args):
577 if i < len(args):
578 s = evalstring(context, mapping, args[i]).strip()
578 s = evalstring(context, mapping, args[i]).strip()
579 if s:
579 if s:
580 return [s]
580 return [s]
581 return []
581 return []
582
582
583 ctx = mapping['ctx']
583 ctx = mapping['ctx']
584 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
584 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
585
585
586 return ''.join(chunks)
586 return ''.join(chunks)
587
587
588 @templatefunc('files(pattern)')
588 @templatefunc('files(pattern)')
589 def files(context, mapping, args):
589 def files(context, mapping, args):
590 """All files of the current changeset matching the pattern. See
590 """All files of the current changeset matching the pattern. See
591 :hg:`help patterns`."""
591 :hg:`help patterns`."""
592 if not len(args) == 1:
592 if not len(args) == 1:
593 # i18n: "files" is a keyword
593 # i18n: "files" is a keyword
594 raise error.ParseError(_("files expects one argument"))
594 raise error.ParseError(_("files expects one argument"))
595
595
596 raw = evalstring(context, mapping, args[0])
596 raw = evalstring(context, mapping, args[0])
597 ctx = mapping['ctx']
597 ctx = mapping['ctx']
598 m = ctx.match([raw])
598 m = ctx.match([raw])
599 files = list(ctx.matches(m))
599 files = list(ctx.matches(m))
600 return templatekw.showlist("file", files, mapping)
600 return templatekw.showlist("file", files, mapping)
601
601
602 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
602 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
603 def fill(context, mapping, args):
603 def fill(context, mapping, args):
604 """Fill many
604 """Fill many
605 paragraphs with optional indentation. See the "fill" filter."""
605 paragraphs with optional indentation. See the "fill" filter."""
606 if not (1 <= len(args) <= 4):
606 if not (1 <= len(args) <= 4):
607 # i18n: "fill" is a keyword
607 # i18n: "fill" is a keyword
608 raise error.ParseError(_("fill expects one to four arguments"))
608 raise error.ParseError(_("fill expects one to four arguments"))
609
609
610 text = evalstring(context, mapping, args[0])
610 text = evalstring(context, mapping, args[0])
611 width = 76
611 width = 76
612 initindent = ''
612 initindent = ''
613 hangindent = ''
613 hangindent = ''
614 if 2 <= len(args) <= 4:
614 if 2 <= len(args) <= 4:
615 width = evalinteger(context, mapping, args[1],
615 width = evalinteger(context, mapping, args[1],
616 # i18n: "fill" is a keyword
616 # i18n: "fill" is a keyword
617 _("fill expects an integer width"))
617 _("fill expects an integer width"))
618 try:
618 try:
619 initindent = evalstring(context, mapping, args[2])
619 initindent = evalstring(context, mapping, args[2])
620 hangindent = evalstring(context, mapping, args[3])
620 hangindent = evalstring(context, mapping, args[3])
621 except IndexError:
621 except IndexError:
622 pass
622 pass
623
623
624 return templatefilters.fill(text, width, initindent, hangindent)
624 return templatefilters.fill(text, width, initindent, hangindent)
625
625
626 @templatefunc('formatnode(node)')
626 @templatefunc('formatnode(node)')
627 def formatnode(context, mapping, args):
627 def formatnode(context, mapping, args):
628 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
628 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
629 if len(args) != 1:
629 if len(args) != 1:
630 # i18n: "formatnode" is a keyword
630 # i18n: "formatnode" is a keyword
631 raise error.ParseError(_("formatnode expects one argument"))
631 raise error.ParseError(_("formatnode expects one argument"))
632
632
633 ui = mapping['ui']
633 ui = mapping['ui']
634 node = evalstring(context, mapping, args[0])
634 node = evalstring(context, mapping, args[0])
635 if ui.debugflag:
635 if ui.debugflag:
636 return node
636 return node
637 return templatefilters.short(node)
637 return templatefilters.short(node)
638
638
639 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
639 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
640 argspec='text width fillchar left')
640 argspec='text width fillchar left')
641 def pad(context, mapping, args):
641 def pad(context, mapping, args):
642 """Pad text with a
642 """Pad text with a
643 fill character."""
643 fill character."""
644 if 'text' not in args or 'width' not in args:
644 if 'text' not in args or 'width' not in args:
645 # i18n: "pad" is a keyword
645 # i18n: "pad" is a keyword
646 raise error.ParseError(_("pad() expects two to four arguments"))
646 raise error.ParseError(_("pad() expects two to four arguments"))
647
647
648 width = evalinteger(context, mapping, args['width'],
648 width = evalinteger(context, mapping, args['width'],
649 # i18n: "pad" is a keyword
649 # i18n: "pad" is a keyword
650 _("pad() expects an integer width"))
650 _("pad() expects an integer width"))
651
651
652 text = evalstring(context, mapping, args['text'])
652 text = evalstring(context, mapping, args['text'])
653
653
654 left = False
654 left = False
655 fillchar = ' '
655 fillchar = ' '
656 if 'fillchar' in args:
656 if 'fillchar' in args:
657 fillchar = evalstring(context, mapping, args['fillchar'])
657 fillchar = evalstring(context, mapping, args['fillchar'])
658 if len(color.stripeffects(fillchar)) != 1:
658 if len(color.stripeffects(fillchar)) != 1:
659 # i18n: "pad" is a keyword
659 # i18n: "pad" is a keyword
660 raise error.ParseError(_("pad() expects a single fill character"))
660 raise error.ParseError(_("pad() expects a single fill character"))
661 if 'left' in args:
661 if 'left' in args:
662 left = evalboolean(context, mapping, args['left'])
662 left = evalboolean(context, mapping, args['left'])
663
663
664 fillwidth = width - encoding.colwidth(color.stripeffects(text))
664 fillwidth = width - encoding.colwidth(color.stripeffects(text))
665 if fillwidth <= 0:
665 if fillwidth <= 0:
666 return text
666 return text
667 if left:
667 if left:
668 return fillchar * fillwidth + text
668 return fillchar * fillwidth + text
669 else:
669 else:
670 return text + fillchar * fillwidth
670 return text + fillchar * fillwidth
671
671
672 @templatefunc('indent(text, indentchars[, firstline])')
672 @templatefunc('indent(text, indentchars[, firstline])')
673 def indent(context, mapping, args):
673 def indent(context, mapping, args):
674 """Indents all non-empty lines
674 """Indents all non-empty lines
675 with the characters given in the indentchars string. An optional
675 with the characters given in the indentchars string. An optional
676 third parameter will override the indent for the first line only
676 third parameter will override the indent for the first line only
677 if present."""
677 if present."""
678 if not (2 <= len(args) <= 3):
678 if not (2 <= len(args) <= 3):
679 # i18n: "indent" is a keyword
679 # i18n: "indent" is a keyword
680 raise error.ParseError(_("indent() expects two or three arguments"))
680 raise error.ParseError(_("indent() expects two or three arguments"))
681
681
682 text = evalstring(context, mapping, args[0])
682 text = evalstring(context, mapping, args[0])
683 indent = evalstring(context, mapping, args[1])
683 indent = evalstring(context, mapping, args[1])
684
684
685 if len(args) == 3:
685 if len(args) == 3:
686 firstline = evalstring(context, mapping, args[2])
686 firstline = evalstring(context, mapping, args[2])
687 else:
687 else:
688 firstline = indent
688 firstline = indent
689
689
690 # the indent function doesn't indent the first line, so we do it here
690 # the indent function doesn't indent the first line, so we do it here
691 return templatefilters.indent(firstline + text, indent)
691 return templatefilters.indent(firstline + text, indent)
692
692
693 @templatefunc('get(dict, key)')
693 @templatefunc('get(dict, key)')
694 def get(context, mapping, args):
694 def get(context, mapping, args):
695 """Get an attribute/key from an object. Some keywords
695 """Get an attribute/key from an object. Some keywords
696 are complex types. This function allows you to obtain the value of an
696 are complex types. This function allows you to obtain the value of an
697 attribute on these types."""
697 attribute on these types."""
698 if len(args) != 2:
698 if len(args) != 2:
699 # i18n: "get" is a keyword
699 # i18n: "get" is a keyword
700 raise error.ParseError(_("get() expects two arguments"))
700 raise error.ParseError(_("get() expects two arguments"))
701
701
702 dictarg = evalfuncarg(context, mapping, args[0])
702 dictarg = evalfuncarg(context, mapping, args[0])
703 if not util.safehasattr(dictarg, 'get'):
703 if not util.safehasattr(dictarg, 'get'):
704 # i18n: "get" is a keyword
704 # i18n: "get" is a keyword
705 raise error.ParseError(_("get() expects a dict as first argument"))
705 raise error.ParseError(_("get() expects a dict as first argument"))
706
706
707 key = evalfuncarg(context, mapping, args[1])
707 key = evalfuncarg(context, mapping, args[1])
708 return dictarg.get(key)
708 return dictarg.get(key)
709
709
710 @templatefunc('if(expr, then[, else])')
710 @templatefunc('if(expr, then[, else])')
711 def if_(context, mapping, args):
711 def if_(context, mapping, args):
712 """Conditionally execute based on the result of
712 """Conditionally execute based on the result of
713 an expression."""
713 an expression."""
714 if not (2 <= len(args) <= 3):
714 if not (2 <= len(args) <= 3):
715 # i18n: "if" is a keyword
715 # i18n: "if" is a keyword
716 raise error.ParseError(_("if expects two or three arguments"))
716 raise error.ParseError(_("if expects two or three arguments"))
717
717
718 test = evalboolean(context, mapping, args[0])
718 test = evalboolean(context, mapping, args[0])
719 if test:
719 if test:
720 yield args[1][0](context, mapping, args[1][1])
720 yield args[1][0](context, mapping, args[1][1])
721 elif len(args) == 3:
721 elif len(args) == 3:
722 yield args[2][0](context, mapping, args[2][1])
722 yield args[2][0](context, mapping, args[2][1])
723
723
724 @templatefunc('ifcontains(needle, haystack, then[, else])')
724 @templatefunc('ifcontains(needle, haystack, then[, else])')
725 def ifcontains(context, mapping, args):
725 def ifcontains(context, mapping, args):
726 """Conditionally execute based
726 """Conditionally execute based
727 on whether the item "needle" is in "haystack"."""
727 on whether the item "needle" is in "haystack"."""
728 if not (3 <= len(args) <= 4):
728 if not (3 <= len(args) <= 4):
729 # i18n: "ifcontains" is a keyword
729 # i18n: "ifcontains" is a keyword
730 raise error.ParseError(_("ifcontains expects three or four arguments"))
730 raise error.ParseError(_("ifcontains expects three or four arguments"))
731
731
732 needle = evalstring(context, mapping, args[0])
732 needle = evalstring(context, mapping, args[0])
733 haystack = evalfuncarg(context, mapping, args[1])
733 haystack = evalfuncarg(context, mapping, args[1])
734
734
735 if needle in haystack:
735 if needle in haystack:
736 yield args[2][0](context, mapping, args[2][1])
736 yield args[2][0](context, mapping, args[2][1])
737 elif len(args) == 4:
737 elif len(args) == 4:
738 yield args[3][0](context, mapping, args[3][1])
738 yield args[3][0](context, mapping, args[3][1])
739
739
740 @templatefunc('ifeq(expr1, expr2, then[, else])')
740 @templatefunc('ifeq(expr1, expr2, then[, else])')
741 def ifeq(context, mapping, args):
741 def ifeq(context, mapping, args):
742 """Conditionally execute based on
742 """Conditionally execute based on
743 whether 2 items are equivalent."""
743 whether 2 items are equivalent."""
744 if not (3 <= len(args) <= 4):
744 if not (3 <= len(args) <= 4):
745 # i18n: "ifeq" is a keyword
745 # i18n: "ifeq" is a keyword
746 raise error.ParseError(_("ifeq expects three or four arguments"))
746 raise error.ParseError(_("ifeq expects three or four arguments"))
747
747
748 test = evalstring(context, mapping, args[0])
748 test = evalstring(context, mapping, args[0])
749 match = evalstring(context, mapping, args[1])
749 match = evalstring(context, mapping, args[1])
750 if test == match:
750 if test == match:
751 yield args[2][0](context, mapping, args[2][1])
751 yield args[2][0](context, mapping, args[2][1])
752 elif len(args) == 4:
752 elif len(args) == 4:
753 yield args[3][0](context, mapping, args[3][1])
753 yield args[3][0](context, mapping, args[3][1])
754
754
755 @templatefunc('join(list, sep)')
755 @templatefunc('join(list, sep)')
756 def join(context, mapping, args):
756 def join(context, mapping, args):
757 """Join items in a list with a delimiter."""
757 """Join items in a list with a delimiter."""
758 if not (1 <= len(args) <= 2):
758 if not (1 <= len(args) <= 2):
759 # i18n: "join" is a keyword
759 # i18n: "join" is a keyword
760 raise error.ParseError(_("join expects one or two arguments"))
760 raise error.ParseError(_("join expects one or two arguments"))
761
761
762 joinset = args[0][0](context, mapping, args[0][1])
762 joinset = args[0][0](context, mapping, args[0][1])
763 if util.safehasattr(joinset, 'itermaps'):
763 if util.safehasattr(joinset, 'itermaps'):
764 jf = joinset.joinfmt
764 jf = joinset.joinfmt
765 joinset = [jf(x) for x in joinset.itermaps()]
765 joinset = [jf(x) for x in joinset.itermaps()]
766
766
767 joiner = " "
767 joiner = " "
768 if len(args) > 1:
768 if len(args) > 1:
769 joiner = evalstring(context, mapping, args[1])
769 joiner = evalstring(context, mapping, args[1])
770
770
771 first = True
771 first = True
772 for x in joinset:
772 for x in joinset:
773 if first:
773 if first:
774 first = False
774 first = False
775 else:
775 else:
776 yield joiner
776 yield joiner
777 yield x
777 yield x
778
778
779 @templatefunc('label(label, expr)')
779 @templatefunc('label(label, expr)')
780 def label(context, mapping, args):
780 def label(context, mapping, args):
781 """Apply a label to generated content. Content with
781 """Apply a label to generated content. Content with
782 a label applied can result in additional post-processing, such as
782 a label applied can result in additional post-processing, such as
783 automatic colorization."""
783 automatic colorization."""
784 if len(args) != 2:
784 if len(args) != 2:
785 # i18n: "label" is a keyword
785 # i18n: "label" is a keyword
786 raise error.ParseError(_("label expects two arguments"))
786 raise error.ParseError(_("label expects two arguments"))
787
787
788 ui = mapping['ui']
788 ui = mapping['ui']
789 thing = evalstring(context, mapping, args[1])
789 thing = evalstring(context, mapping, args[1])
790 # preserve unknown symbol as literal so effects like 'red', 'bold',
790 # preserve unknown symbol as literal so effects like 'red', 'bold',
791 # etc. don't need to be quoted
791 # etc. don't need to be quoted
792 label = evalstringliteral(context, mapping, args[0])
792 label = evalstringliteral(context, mapping, args[0])
793
793
794 return ui.label(thing, label)
794 return ui.label(thing, label)
795
795
796 @templatefunc('latesttag([pattern])')
796 @templatefunc('latesttag([pattern])')
797 def latesttag(context, mapping, args):
797 def latesttag(context, mapping, args):
798 """The global tags matching the given pattern on the
798 """The global tags matching the given pattern on the
799 most recent globally tagged ancestor of this changeset.
799 most recent globally tagged ancestor of this changeset.
800 If no such tags exist, the "{tag}" template resolves to
800 If no such tags exist, the "{tag}" template resolves to
801 the string "null"."""
801 the string "null"."""
802 if len(args) > 1:
802 if len(args) > 1:
803 # i18n: "latesttag" is a keyword
803 # i18n: "latesttag" is a keyword
804 raise error.ParseError(_("latesttag expects at most one argument"))
804 raise error.ParseError(_("latesttag expects at most one argument"))
805
805
806 pattern = None
806 pattern = None
807 if len(args) == 1:
807 if len(args) == 1:
808 pattern = evalstring(context, mapping, args[0])
808 pattern = evalstring(context, mapping, args[0])
809
809
810 return templatekw.showlatesttags(pattern, **mapping)
810 return templatekw.showlatesttags(pattern, **mapping)
811
811
812 @templatefunc('localdate(date[, tz])')
812 @templatefunc('localdate(date[, tz])')
813 def localdate(context, mapping, args):
813 def localdate(context, mapping, args):
814 """Converts a date to the specified timezone.
814 """Converts a date to the specified timezone.
815 The default is local date."""
815 The default is local date."""
816 if not (1 <= len(args) <= 2):
816 if not (1 <= len(args) <= 2):
817 # i18n: "localdate" is a keyword
817 # i18n: "localdate" is a keyword
818 raise error.ParseError(_("localdate expects one or two arguments"))
818 raise error.ParseError(_("localdate expects one or two arguments"))
819
819
820 date = evalfuncarg(context, mapping, args[0])
820 date = evalfuncarg(context, mapping, args[0])
821 try:
821 try:
822 date = util.parsedate(date)
822 date = util.parsedate(date)
823 except AttributeError: # not str nor date tuple
823 except AttributeError: # not str nor date tuple
824 # i18n: "localdate" is a keyword
824 # i18n: "localdate" is a keyword
825 raise error.ParseError(_("localdate expects a date information"))
825 raise error.ParseError(_("localdate expects a date information"))
826 if len(args) >= 2:
826 if len(args) >= 2:
827 tzoffset = None
827 tzoffset = None
828 tz = evalfuncarg(context, mapping, args[1])
828 tz = evalfuncarg(context, mapping, args[1])
829 if isinstance(tz, str):
829 if isinstance(tz, str):
830 tzoffset, remainder = util.parsetimezone(tz)
830 tzoffset, remainder = util.parsetimezone(tz)
831 if remainder:
831 if remainder:
832 tzoffset = None
832 tzoffset = None
833 if tzoffset is None:
833 if tzoffset is None:
834 try:
834 try:
835 tzoffset = int(tz)
835 tzoffset = int(tz)
836 except (TypeError, ValueError):
836 except (TypeError, ValueError):
837 # i18n: "localdate" is a keyword
837 # i18n: "localdate" is a keyword
838 raise error.ParseError(_("localdate expects a timezone"))
838 raise error.ParseError(_("localdate expects a timezone"))
839 else:
839 else:
840 tzoffset = util.makedate()[1]
840 tzoffset = util.makedate()[1]
841 return (date[0], tzoffset)
841 return (date[0], tzoffset)
842
842
843 @templatefunc('max(iterable)')
844 def max_(context, mapping, args, **kwargs):
845 """Return the max of an iterable"""
846 if len(args) != 1:
847 # i18n: "max" is a keyword
848 raise error.ParseError(_("max expects one arguments"))
849
850 iterable = evalfuncarg(context, mapping, args[0])
851 try:
852 return max(iterable)
853 except (TypeError, ValueError):
854 # i18n: "max" is a keyword
855 raise error.ParseError(_("max first argument should be an iterable"))
856
857 @templatefunc('min(iterable)')
858 def min_(context, mapping, args, **kwargs):
859 """Return the min of an iterable"""
860 if len(args) != 1:
861 # i18n: "min" is a keyword
862 raise error.ParseError(_("min expects one arguments"))
863
864 iterable = evalfuncarg(context, mapping, args[0])
865 try:
866 return min(iterable)
867 except (TypeError, ValueError):
868 # i18n: "min" is a keyword
869 raise error.ParseError(_("min first argument should be an iterable"))
870
843 @templatefunc('mod(a, b)')
871 @templatefunc('mod(a, b)')
844 def mod(context, mapping, args):
872 def mod(context, mapping, args):
845 """Calculate a mod b such that a / b + a mod b == a"""
873 """Calculate a mod b such that a / b + a mod b == a"""
846 if not len(args) == 2:
874 if not len(args) == 2:
847 # i18n: "mod" is a keyword
875 # i18n: "mod" is a keyword
848 raise error.ParseError(_("mod expects two arguments"))
876 raise error.ParseError(_("mod expects two arguments"))
849
877
850 func = lambda a, b: a % b
878 func = lambda a, b: a % b
851 return runarithmetic(context, mapping, (func, args[0], args[1]))
879 return runarithmetic(context, mapping, (func, args[0], args[1]))
852
880
881 @templatefunc('obsfatedate(markers)')
882 def obsfatedate(context, mapping, args):
883 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
884 if len(args) != 1:
885 # i18n: "obsfatedate" is a keyword
886 raise error.ParseError(_("obsfatedate expects one arguments"))
887
888 markers = evalfuncarg(context, mapping, args[0])
889
890 try:
891 data = obsutil.markersdates(markers)
892 return templatekw.hybridlist(data, name='date', fmt='%d %d')
893 except (TypeError, KeyError):
894 # i18n: "obsfatedate" is a keyword
895 errmsg = _("obsfatedate first argument should be an iterable")
896 raise error.ParseError(errmsg)
897
853 @templatefunc('obsfateusers(markers)')
898 @templatefunc('obsfateusers(markers)')
854 def obsfateusers(context, mapping, args):
899 def obsfateusers(context, mapping, args):
855 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
900 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
856 if len(args) != 1:
901 if len(args) != 1:
857 # i18n: "obsfateusers" is a keyword
902 # i18n: "obsfateusers" is a keyword
858 raise error.ParseError(_("obsfateusers expects one arguments"))
903 raise error.ParseError(_("obsfateusers expects one arguments"))
859
904
860 markers = evalfuncarg(context, mapping, args[0])
905 markers = evalfuncarg(context, mapping, args[0])
861
906
862 try:
907 try:
863 data = obsutil.markersusers(markers)
908 data = obsutil.markersusers(markers)
864 return templatekw.hybridlist(data, name='user')
909 return templatekw.hybridlist(data, name='user')
865 except (TypeError, KeyError, ValueError):
910 except (TypeError, KeyError, ValueError):
866 # i18n: "obsfateusers" is a keyword
911 # i18n: "obsfateusers" is a keyword
867 msg = _("obsfateusers first argument should be an iterable of "
912 msg = _("obsfateusers first argument should be an iterable of "
868 "obsmakers")
913 "obsmakers")
869 raise error.ParseError(msg)
914 raise error.ParseError(msg)
870
915
871 @templatefunc('obsfateverb(successors)')
916 @templatefunc('obsfateverb(successors)')
872 def obsfateverb(context, mapping, args):
917 def obsfateverb(context, mapping, args):
873 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
918 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
874 if len(args) != 1:
919 if len(args) != 1:
875 # i18n: "obsfateverb" is a keyword
920 # i18n: "obsfateverb" is a keyword
876 raise error.ParseError(_("obsfateverb expects one arguments"))
921 raise error.ParseError(_("obsfateverb expects one arguments"))
877
922
878 successors = evalfuncarg(context, mapping, args[0])
923 successors = evalfuncarg(context, mapping, args[0])
879
924
880 try:
925 try:
881 return obsutil.successorsetverb(successors)
926 return obsutil.successorsetverb(successors)
882 except TypeError:
927 except TypeError:
883 # i18n: "obsfateverb" is a keyword
928 # i18n: "obsfateverb" is a keyword
884 errmsg = _("obsfateverb first argument should be countable")
929 errmsg = _("obsfateverb first argument should be countable")
885 raise error.ParseError(errmsg)
930 raise error.ParseError(errmsg)
886
931
887 @templatefunc('relpath(path)')
932 @templatefunc('relpath(path)')
888 def relpath(context, mapping, args):
933 def relpath(context, mapping, args):
889 """Convert a repository-absolute path into a filesystem path relative to
934 """Convert a repository-absolute path into a filesystem path relative to
890 the current working directory."""
935 the current working directory."""
891 if len(args) != 1:
936 if len(args) != 1:
892 # i18n: "relpath" is a keyword
937 # i18n: "relpath" is a keyword
893 raise error.ParseError(_("relpath expects one argument"))
938 raise error.ParseError(_("relpath expects one argument"))
894
939
895 repo = mapping['ctx'].repo()
940 repo = mapping['ctx'].repo()
896 path = evalstring(context, mapping, args[0])
941 path = evalstring(context, mapping, args[0])
897 return repo.pathto(path)
942 return repo.pathto(path)
898
943
899 @templatefunc('revset(query[, formatargs...])')
944 @templatefunc('revset(query[, formatargs...])')
900 def revset(context, mapping, args):
945 def revset(context, mapping, args):
901 """Execute a revision set query. See
946 """Execute a revision set query. See
902 :hg:`help revset`."""
947 :hg:`help revset`."""
903 if not len(args) > 0:
948 if not len(args) > 0:
904 # i18n: "revset" is a keyword
949 # i18n: "revset" is a keyword
905 raise error.ParseError(_("revset expects one or more arguments"))
950 raise error.ParseError(_("revset expects one or more arguments"))
906
951
907 raw = evalstring(context, mapping, args[0])
952 raw = evalstring(context, mapping, args[0])
908 ctx = mapping['ctx']
953 ctx = mapping['ctx']
909 repo = ctx.repo()
954 repo = ctx.repo()
910
955
911 def query(expr):
956 def query(expr):
912 m = revsetmod.match(repo.ui, expr, repo=repo)
957 m = revsetmod.match(repo.ui, expr, repo=repo)
913 return m(repo)
958 return m(repo)
914
959
915 if len(args) > 1:
960 if len(args) > 1:
916 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
961 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
917 revs = query(revsetlang.formatspec(raw, *formatargs))
962 revs = query(revsetlang.formatspec(raw, *formatargs))
918 revs = list(revs)
963 revs = list(revs)
919 else:
964 else:
920 revsetcache = mapping['cache'].setdefault("revsetcache", {})
965 revsetcache = mapping['cache'].setdefault("revsetcache", {})
921 if raw in revsetcache:
966 if raw in revsetcache:
922 revs = revsetcache[raw]
967 revs = revsetcache[raw]
923 else:
968 else:
924 revs = query(raw)
969 revs = query(raw)
925 revs = list(revs)
970 revs = list(revs)
926 revsetcache[raw] = revs
971 revsetcache[raw] = revs
927
972
928 return templatekw.showrevslist("revision", revs, **mapping)
973 return templatekw.showrevslist("revision", revs, **mapping)
929
974
930 @templatefunc('rstdoc(text, style)')
975 @templatefunc('rstdoc(text, style)')
931 def rstdoc(context, mapping, args):
976 def rstdoc(context, mapping, args):
932 """Format reStructuredText."""
977 """Format reStructuredText."""
933 if len(args) != 2:
978 if len(args) != 2:
934 # i18n: "rstdoc" is a keyword
979 # i18n: "rstdoc" is a keyword
935 raise error.ParseError(_("rstdoc expects two arguments"))
980 raise error.ParseError(_("rstdoc expects two arguments"))
936
981
937 text = evalstring(context, mapping, args[0])
982 text = evalstring(context, mapping, args[0])
938 style = evalstring(context, mapping, args[1])
983 style = evalstring(context, mapping, args[1])
939
984
940 return minirst.format(text, style=style, keep=['verbose'])
985 return minirst.format(text, style=style, keep=['verbose'])
941
986
942 @templatefunc('separate(sep, args)', argspec='sep *args')
987 @templatefunc('separate(sep, args)', argspec='sep *args')
943 def separate(context, mapping, args):
988 def separate(context, mapping, args):
944 """Add a separator between non-empty arguments."""
989 """Add a separator between non-empty arguments."""
945 if 'sep' not in args:
990 if 'sep' not in args:
946 # i18n: "separate" is a keyword
991 # i18n: "separate" is a keyword
947 raise error.ParseError(_("separate expects at least one argument"))
992 raise error.ParseError(_("separate expects at least one argument"))
948
993
949 sep = evalstring(context, mapping, args['sep'])
994 sep = evalstring(context, mapping, args['sep'])
950 first = True
995 first = True
951 for arg in args['args']:
996 for arg in args['args']:
952 argstr = evalstring(context, mapping, arg)
997 argstr = evalstring(context, mapping, arg)
953 if not argstr:
998 if not argstr:
954 continue
999 continue
955 if first:
1000 if first:
956 first = False
1001 first = False
957 else:
1002 else:
958 yield sep
1003 yield sep
959 yield argstr
1004 yield argstr
960
1005
961 @templatefunc('shortest(node, minlength=4)')
1006 @templatefunc('shortest(node, minlength=4)')
962 def shortest(context, mapping, args):
1007 def shortest(context, mapping, args):
963 """Obtain the shortest representation of
1008 """Obtain the shortest representation of
964 a node."""
1009 a node."""
965 if not (1 <= len(args) <= 2):
1010 if not (1 <= len(args) <= 2):
966 # i18n: "shortest" is a keyword
1011 # i18n: "shortest" is a keyword
967 raise error.ParseError(_("shortest() expects one or two arguments"))
1012 raise error.ParseError(_("shortest() expects one or two arguments"))
968
1013
969 node = evalstring(context, mapping, args[0])
1014 node = evalstring(context, mapping, args[0])
970
1015
971 minlength = 4
1016 minlength = 4
972 if len(args) > 1:
1017 if len(args) > 1:
973 minlength = evalinteger(context, mapping, args[1],
1018 minlength = evalinteger(context, mapping, args[1],
974 # i18n: "shortest" is a keyword
1019 # i18n: "shortest" is a keyword
975 _("shortest() expects an integer minlength"))
1020 _("shortest() expects an integer minlength"))
976
1021
977 # _partialmatch() of filtered changelog could take O(len(repo)) time,
1022 # _partialmatch() of filtered changelog could take O(len(repo)) time,
978 # which would be unacceptably slow. so we look for hash collision in
1023 # which would be unacceptably slow. so we look for hash collision in
979 # unfiltered space, which means some hashes may be slightly longer.
1024 # unfiltered space, which means some hashes may be slightly longer.
980 cl = mapping['ctx']._repo.unfiltered().changelog
1025 cl = mapping['ctx']._repo.unfiltered().changelog
981 def isvalid(test):
1026 def isvalid(test):
982 try:
1027 try:
983 if cl._partialmatch(test) is None:
1028 if cl._partialmatch(test) is None:
984 return False
1029 return False
985
1030
986 try:
1031 try:
987 i = int(test)
1032 i = int(test)
988 # if we are a pure int, then starting with zero will not be
1033 # if we are a pure int, then starting with zero will not be
989 # confused as a rev; or, obviously, if the int is larger than
1034 # confused as a rev; or, obviously, if the int is larger than
990 # the value of the tip rev
1035 # the value of the tip rev
991 if test[0] == '0' or i > len(cl):
1036 if test[0] == '0' or i > len(cl):
992 return True
1037 return True
993 return False
1038 return False
994 except ValueError:
1039 except ValueError:
995 return True
1040 return True
996 except error.RevlogError:
1041 except error.RevlogError:
997 return False
1042 return False
998 except error.WdirUnsupported:
1043 except error.WdirUnsupported:
999 # single 'ff...' match
1044 # single 'ff...' match
1000 return True
1045 return True
1001
1046
1002 shortest = node
1047 shortest = node
1003 startlength = max(6, minlength)
1048 startlength = max(6, minlength)
1004 length = startlength
1049 length = startlength
1005 while True:
1050 while True:
1006 test = node[:length]
1051 test = node[:length]
1007 if isvalid(test):
1052 if isvalid(test):
1008 shortest = test
1053 shortest = test
1009 if length == minlength or length > startlength:
1054 if length == minlength or length > startlength:
1010 return shortest
1055 return shortest
1011 length -= 1
1056 length -= 1
1012 else:
1057 else:
1013 length += 1
1058 length += 1
1014 if len(shortest) <= length:
1059 if len(shortest) <= length:
1015 return shortest
1060 return shortest
1016
1061
1017 @templatefunc('strip(text[, chars])')
1062 @templatefunc('strip(text[, chars])')
1018 def strip(context, mapping, args):
1063 def strip(context, mapping, args):
1019 """Strip characters from a string. By default,
1064 """Strip characters from a string. By default,
1020 strips all leading and trailing whitespace."""
1065 strips all leading and trailing whitespace."""
1021 if not (1 <= len(args) <= 2):
1066 if not (1 <= len(args) <= 2):
1022 # i18n: "strip" is a keyword
1067 # i18n: "strip" is a keyword
1023 raise error.ParseError(_("strip expects one or two arguments"))
1068 raise error.ParseError(_("strip expects one or two arguments"))
1024
1069
1025 text = evalstring(context, mapping, args[0])
1070 text = evalstring(context, mapping, args[0])
1026 if len(args) == 2:
1071 if len(args) == 2:
1027 chars = evalstring(context, mapping, args[1])
1072 chars = evalstring(context, mapping, args[1])
1028 return text.strip(chars)
1073 return text.strip(chars)
1029 return text.strip()
1074 return text.strip()
1030
1075
1031 @templatefunc('sub(pattern, replacement, expression)')
1076 @templatefunc('sub(pattern, replacement, expression)')
1032 def sub(context, mapping, args):
1077 def sub(context, mapping, args):
1033 """Perform text substitution
1078 """Perform text substitution
1034 using regular expressions."""
1079 using regular expressions."""
1035 if len(args) != 3:
1080 if len(args) != 3:
1036 # i18n: "sub" is a keyword
1081 # i18n: "sub" is a keyword
1037 raise error.ParseError(_("sub expects three arguments"))
1082 raise error.ParseError(_("sub expects three arguments"))
1038
1083
1039 pat = evalstring(context, mapping, args[0])
1084 pat = evalstring(context, mapping, args[0])
1040 rpl = evalstring(context, mapping, args[1])
1085 rpl = evalstring(context, mapping, args[1])
1041 src = evalstring(context, mapping, args[2])
1086 src = evalstring(context, mapping, args[2])
1042 try:
1087 try:
1043 patre = re.compile(pat)
1088 patre = re.compile(pat)
1044 except re.error:
1089 except re.error:
1045 # i18n: "sub" is a keyword
1090 # i18n: "sub" is a keyword
1046 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
1091 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
1047 try:
1092 try:
1048 yield patre.sub(rpl, src)
1093 yield patre.sub(rpl, src)
1049 except re.error:
1094 except re.error:
1050 # i18n: "sub" is a keyword
1095 # i18n: "sub" is a keyword
1051 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
1096 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
1052
1097
1053 @templatefunc('startswith(pattern, text)')
1098 @templatefunc('startswith(pattern, text)')
1054 def startswith(context, mapping, args):
1099 def startswith(context, mapping, args):
1055 """Returns the value from the "text" argument
1100 """Returns the value from the "text" argument
1056 if it begins with the content from the "pattern" argument."""
1101 if it begins with the content from the "pattern" argument."""
1057 if len(args) != 2:
1102 if len(args) != 2:
1058 # i18n: "startswith" is a keyword
1103 # i18n: "startswith" is a keyword
1059 raise error.ParseError(_("startswith expects two arguments"))
1104 raise error.ParseError(_("startswith expects two arguments"))
1060
1105
1061 patn = evalstring(context, mapping, args[0])
1106 patn = evalstring(context, mapping, args[0])
1062 text = evalstring(context, mapping, args[1])
1107 text = evalstring(context, mapping, args[1])
1063 if text.startswith(patn):
1108 if text.startswith(patn):
1064 return text
1109 return text
1065 return ''
1110 return ''
1066
1111
1067 @templatefunc('word(number, text[, separator])')
1112 @templatefunc('word(number, text[, separator])')
1068 def word(context, mapping, args):
1113 def word(context, mapping, args):
1069 """Return the nth word from a string."""
1114 """Return the nth word from a string."""
1070 if not (2 <= len(args) <= 3):
1115 if not (2 <= len(args) <= 3):
1071 # i18n: "word" is a keyword
1116 # i18n: "word" is a keyword
1072 raise error.ParseError(_("word expects two or three arguments, got %d")
1117 raise error.ParseError(_("word expects two or three arguments, got %d")
1073 % len(args))
1118 % len(args))
1074
1119
1075 num = evalinteger(context, mapping, args[0],
1120 num = evalinteger(context, mapping, args[0],
1076 # i18n: "word" is a keyword
1121 # i18n: "word" is a keyword
1077 _("word expects an integer index"))
1122 _("word expects an integer index"))
1078 text = evalstring(context, mapping, args[1])
1123 text = evalstring(context, mapping, args[1])
1079 if len(args) == 3:
1124 if len(args) == 3:
1080 splitter = evalstring(context, mapping, args[2])
1125 splitter = evalstring(context, mapping, args[2])
1081 else:
1126 else:
1082 splitter = None
1127 splitter = None
1083
1128
1084 tokens = text.split(splitter)
1129 tokens = text.split(splitter)
1085 if num >= len(tokens) or num < -len(tokens):
1130 if num >= len(tokens) or num < -len(tokens):
1086 return ''
1131 return ''
1087 else:
1132 else:
1088 return tokens[num]
1133 return tokens[num]
1089
1134
1090 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
1135 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
1091 exprmethods = {
1136 exprmethods = {
1092 "integer": lambda e, c: (runinteger, e[1]),
1137 "integer": lambda e, c: (runinteger, e[1]),
1093 "string": lambda e, c: (runstring, e[1]),
1138 "string": lambda e, c: (runstring, e[1]),
1094 "symbol": lambda e, c: (runsymbol, e[1]),
1139 "symbol": lambda e, c: (runsymbol, e[1]),
1095 "template": buildtemplate,
1140 "template": buildtemplate,
1096 "group": lambda e, c: compileexp(e[1], c, exprmethods),
1141 "group": lambda e, c: compileexp(e[1], c, exprmethods),
1097 # ".": buildmember,
1142 # ".": buildmember,
1098 "|": buildfilter,
1143 "|": buildfilter,
1099 "%": buildmap,
1144 "%": buildmap,
1100 "func": buildfunc,
1145 "func": buildfunc,
1101 "keyvalue": buildkeyvaluepair,
1146 "keyvalue": buildkeyvaluepair,
1102 "+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
1147 "+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
1103 "-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
1148 "-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
1104 "negate": buildnegate,
1149 "negate": buildnegate,
1105 "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
1150 "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
1106 "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
1151 "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
1107 }
1152 }
1108
1153
1109 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
1154 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
1110 methods = exprmethods.copy()
1155 methods = exprmethods.copy()
1111 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
1156 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
1112
1157
1113 class _aliasrules(parser.basealiasrules):
1158 class _aliasrules(parser.basealiasrules):
1114 """Parsing and expansion rule set of template aliases"""
1159 """Parsing and expansion rule set of template aliases"""
1115 _section = _('template alias')
1160 _section = _('template alias')
1116 _parse = staticmethod(_parseexpr)
1161 _parse = staticmethod(_parseexpr)
1117
1162
1118 @staticmethod
1163 @staticmethod
1119 def _trygetfunc(tree):
1164 def _trygetfunc(tree):
1120 """Return (name, args) if tree is func(...) or ...|filter; otherwise
1165 """Return (name, args) if tree is func(...) or ...|filter; otherwise
1121 None"""
1166 None"""
1122 if tree[0] == 'func' and tree[1][0] == 'symbol':
1167 if tree[0] == 'func' and tree[1][0] == 'symbol':
1123 return tree[1][1], getlist(tree[2])
1168 return tree[1][1], getlist(tree[2])
1124 if tree[0] == '|' and tree[2][0] == 'symbol':
1169 if tree[0] == '|' and tree[2][0] == 'symbol':
1125 return tree[2][1], [tree[1]]
1170 return tree[2][1], [tree[1]]
1126
1171
1127 def expandaliases(tree, aliases):
1172 def expandaliases(tree, aliases):
1128 """Return new tree of aliases are expanded"""
1173 """Return new tree of aliases are expanded"""
1129 aliasmap = _aliasrules.buildmap(aliases)
1174 aliasmap = _aliasrules.buildmap(aliases)
1130 return _aliasrules.expand(aliasmap, tree)
1175 return _aliasrules.expand(aliasmap, tree)
1131
1176
1132 # template engine
1177 # template engine
1133
1178
1134 stringify = templatefilters.stringify
1179 stringify = templatefilters.stringify
1135
1180
1136 def _flatten(thing):
1181 def _flatten(thing):
1137 '''yield a single stream from a possibly nested set of iterators'''
1182 '''yield a single stream from a possibly nested set of iterators'''
1138 thing = templatekw.unwraphybrid(thing)
1183 thing = templatekw.unwraphybrid(thing)
1139 if isinstance(thing, bytes):
1184 if isinstance(thing, bytes):
1140 yield thing
1185 yield thing
1141 elif thing is None:
1186 elif thing is None:
1142 pass
1187 pass
1143 elif not util.safehasattr(thing, '__iter__'):
1188 elif not util.safehasattr(thing, '__iter__'):
1144 yield pycompat.bytestr(thing)
1189 yield pycompat.bytestr(thing)
1145 else:
1190 else:
1146 for i in thing:
1191 for i in thing:
1147 i = templatekw.unwraphybrid(i)
1192 i = templatekw.unwraphybrid(i)
1148 if isinstance(i, bytes):
1193 if isinstance(i, bytes):
1149 yield i
1194 yield i
1150 elif i is None:
1195 elif i is None:
1151 pass
1196 pass
1152 elif not util.safehasattr(i, '__iter__'):
1197 elif not util.safehasattr(i, '__iter__'):
1153 yield pycompat.bytestr(i)
1198 yield pycompat.bytestr(i)
1154 else:
1199 else:
1155 for j in _flatten(i):
1200 for j in _flatten(i):
1156 yield j
1201 yield j
1157
1202
1158 def unquotestring(s):
1203 def unquotestring(s):
1159 '''unwrap quotes if any; otherwise returns unmodified string'''
1204 '''unwrap quotes if any; otherwise returns unmodified string'''
1160 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
1205 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
1161 return s
1206 return s
1162 return s[1:-1]
1207 return s[1:-1]
1163
1208
1164 class engine(object):
1209 class engine(object):
1165 '''template expansion engine.
1210 '''template expansion engine.
1166
1211
1167 template expansion works like this. a map file contains key=value
1212 template expansion works like this. a map file contains key=value
1168 pairs. if value is quoted, it is treated as string. otherwise, it
1213 pairs. if value is quoted, it is treated as string. otherwise, it
1169 is treated as name of template file.
1214 is treated as name of template file.
1170
1215
1171 templater is asked to expand a key in map. it looks up key, and
1216 templater is asked to expand a key in map. it looks up key, and
1172 looks for strings like this: {foo}. it expands {foo} by looking up
1217 looks for strings like this: {foo}. it expands {foo} by looking up
1173 foo in map, and substituting it. expansion is recursive: it stops
1218 foo in map, and substituting it. expansion is recursive: it stops
1174 when there is no more {foo} to replace.
1219 when there is no more {foo} to replace.
1175
1220
1176 expansion also allows formatting and filtering.
1221 expansion also allows formatting and filtering.
1177
1222
1178 format uses key to expand each item in list. syntax is
1223 format uses key to expand each item in list. syntax is
1179 {key%format}.
1224 {key%format}.
1180
1225
1181 filter uses function to transform value. syntax is
1226 filter uses function to transform value. syntax is
1182 {key|filter1|filter2|...}.'''
1227 {key|filter1|filter2|...}.'''
1183
1228
1184 def __init__(self, loader, filters=None, defaults=None, aliases=()):
1229 def __init__(self, loader, filters=None, defaults=None, aliases=()):
1185 self._loader = loader
1230 self._loader = loader
1186 if filters is None:
1231 if filters is None:
1187 filters = {}
1232 filters = {}
1188 self._filters = filters
1233 self._filters = filters
1189 if defaults is None:
1234 if defaults is None:
1190 defaults = {}
1235 defaults = {}
1191 self._defaults = defaults
1236 self._defaults = defaults
1192 self._aliasmap = _aliasrules.buildmap(aliases)
1237 self._aliasmap = _aliasrules.buildmap(aliases)
1193 self._cache = {} # key: (func, data)
1238 self._cache = {} # key: (func, data)
1194
1239
1195 def _load(self, t):
1240 def _load(self, t):
1196 '''load, parse, and cache a template'''
1241 '''load, parse, and cache a template'''
1197 if t not in self._cache:
1242 if t not in self._cache:
1198 # put poison to cut recursion while compiling 't'
1243 # put poison to cut recursion while compiling 't'
1199 self._cache[t] = (_runrecursivesymbol, t)
1244 self._cache[t] = (_runrecursivesymbol, t)
1200 try:
1245 try:
1201 x = parse(self._loader(t))
1246 x = parse(self._loader(t))
1202 if self._aliasmap:
1247 if self._aliasmap:
1203 x = _aliasrules.expand(self._aliasmap, x)
1248 x = _aliasrules.expand(self._aliasmap, x)
1204 self._cache[t] = compileexp(x, self, methods)
1249 self._cache[t] = compileexp(x, self, methods)
1205 except: # re-raises
1250 except: # re-raises
1206 del self._cache[t]
1251 del self._cache[t]
1207 raise
1252 raise
1208 return self._cache[t]
1253 return self._cache[t]
1209
1254
1210 def process(self, t, mapping):
1255 def process(self, t, mapping):
1211 '''Perform expansion. t is name of map element to expand.
1256 '''Perform expansion. t is name of map element to expand.
1212 mapping contains added elements for use during expansion. Is a
1257 mapping contains added elements for use during expansion. Is a
1213 generator.'''
1258 generator.'''
1214 func, data = self._load(t)
1259 func, data = self._load(t)
1215 return _flatten(func(self, mapping, data))
1260 return _flatten(func(self, mapping, data))
1216
1261
1217 engines = {'default': engine}
1262 engines = {'default': engine}
1218
1263
1219 def stylelist():
1264 def stylelist():
1220 paths = templatepaths()
1265 paths = templatepaths()
1221 if not paths:
1266 if not paths:
1222 return _('no templates found, try `hg debuginstall` for more info')
1267 return _('no templates found, try `hg debuginstall` for more info')
1223 dirlist = os.listdir(paths[0])
1268 dirlist = os.listdir(paths[0])
1224 stylelist = []
1269 stylelist = []
1225 for file in dirlist:
1270 for file in dirlist:
1226 split = file.split(".")
1271 split = file.split(".")
1227 if split[-1] in ('orig', 'rej'):
1272 if split[-1] in ('orig', 'rej'):
1228 continue
1273 continue
1229 if split[0] == "map-cmdline":
1274 if split[0] == "map-cmdline":
1230 stylelist.append(split[1])
1275 stylelist.append(split[1])
1231 return ", ".join(sorted(stylelist))
1276 return ", ".join(sorted(stylelist))
1232
1277
1233 def _readmapfile(mapfile):
1278 def _readmapfile(mapfile):
1234 """Load template elements from the given map file"""
1279 """Load template elements from the given map file"""
1235 if not os.path.exists(mapfile):
1280 if not os.path.exists(mapfile):
1236 raise error.Abort(_("style '%s' not found") % mapfile,
1281 raise error.Abort(_("style '%s' not found") % mapfile,
1237 hint=_("available styles: %s") % stylelist())
1282 hint=_("available styles: %s") % stylelist())
1238
1283
1239 base = os.path.dirname(mapfile)
1284 base = os.path.dirname(mapfile)
1240 conf = config.config(includepaths=templatepaths())
1285 conf = config.config(includepaths=templatepaths())
1241 conf.read(mapfile)
1286 conf.read(mapfile)
1242
1287
1243 cache = {}
1288 cache = {}
1244 tmap = {}
1289 tmap = {}
1245 for key, val in conf[''].items():
1290 for key, val in conf[''].items():
1246 if not val:
1291 if not val:
1247 raise error.ParseError(_('missing value'), conf.source('', key))
1292 raise error.ParseError(_('missing value'), conf.source('', key))
1248 if val[0] in "'\"":
1293 if val[0] in "'\"":
1249 if val[0] != val[-1]:
1294 if val[0] != val[-1]:
1250 raise error.ParseError(_('unmatched quotes'),
1295 raise error.ParseError(_('unmatched quotes'),
1251 conf.source('', key))
1296 conf.source('', key))
1252 cache[key] = unquotestring(val)
1297 cache[key] = unquotestring(val)
1253 elif key == "__base__":
1298 elif key == "__base__":
1254 # treat as a pointer to a base class for this style
1299 # treat as a pointer to a base class for this style
1255 path = util.normpath(os.path.join(base, val))
1300 path = util.normpath(os.path.join(base, val))
1256
1301
1257 # fallback check in template paths
1302 # fallback check in template paths
1258 if not os.path.exists(path):
1303 if not os.path.exists(path):
1259 for p in templatepaths():
1304 for p in templatepaths():
1260 p2 = util.normpath(os.path.join(p, val))
1305 p2 = util.normpath(os.path.join(p, val))
1261 if os.path.isfile(p2):
1306 if os.path.isfile(p2):
1262 path = p2
1307 path = p2
1263 break
1308 break
1264 p3 = util.normpath(os.path.join(p2, "map"))
1309 p3 = util.normpath(os.path.join(p2, "map"))
1265 if os.path.isfile(p3):
1310 if os.path.isfile(p3):
1266 path = p3
1311 path = p3
1267 break
1312 break
1268
1313
1269 bcache, btmap = _readmapfile(path)
1314 bcache, btmap = _readmapfile(path)
1270 for k in bcache:
1315 for k in bcache:
1271 if k not in cache:
1316 if k not in cache:
1272 cache[k] = bcache[k]
1317 cache[k] = bcache[k]
1273 for k in btmap:
1318 for k in btmap:
1274 if k not in tmap:
1319 if k not in tmap:
1275 tmap[k] = btmap[k]
1320 tmap[k] = btmap[k]
1276 else:
1321 else:
1277 val = 'default', val
1322 val = 'default', val
1278 if ':' in val[1]:
1323 if ':' in val[1]:
1279 val = val[1].split(':', 1)
1324 val = val[1].split(':', 1)
1280 tmap[key] = val[0], os.path.join(base, val[1])
1325 tmap[key] = val[0], os.path.join(base, val[1])
1281 return cache, tmap
1326 return cache, tmap
1282
1327
1283 class TemplateNotFound(error.Abort):
1328 class TemplateNotFound(error.Abort):
1284 pass
1329 pass
1285
1330
1286 class templater(object):
1331 class templater(object):
1287
1332
1288 def __init__(self, filters=None, defaults=None, cache=None, aliases=(),
1333 def __init__(self, filters=None, defaults=None, cache=None, aliases=(),
1289 minchunk=1024, maxchunk=65536):
1334 minchunk=1024, maxchunk=65536):
1290 '''set up template engine.
1335 '''set up template engine.
1291 filters is dict of functions. each transforms a value into another.
1336 filters is dict of functions. each transforms a value into another.
1292 defaults is dict of default map definitions.
1337 defaults is dict of default map definitions.
1293 aliases is list of alias (name, replacement) pairs.
1338 aliases is list of alias (name, replacement) pairs.
1294 '''
1339 '''
1295 if filters is None:
1340 if filters is None:
1296 filters = {}
1341 filters = {}
1297 if defaults is None:
1342 if defaults is None:
1298 defaults = {}
1343 defaults = {}
1299 if cache is None:
1344 if cache is None:
1300 cache = {}
1345 cache = {}
1301 self.cache = cache.copy()
1346 self.cache = cache.copy()
1302 self.map = {}
1347 self.map = {}
1303 self.filters = templatefilters.filters.copy()
1348 self.filters = templatefilters.filters.copy()
1304 self.filters.update(filters)
1349 self.filters.update(filters)
1305 self.defaults = defaults
1350 self.defaults = defaults
1306 self._aliases = aliases
1351 self._aliases = aliases
1307 self.minchunk, self.maxchunk = minchunk, maxchunk
1352 self.minchunk, self.maxchunk = minchunk, maxchunk
1308 self.ecache = {}
1353 self.ecache = {}
1309
1354
1310 @classmethod
1355 @classmethod
1311 def frommapfile(cls, mapfile, filters=None, defaults=None, cache=None,
1356 def frommapfile(cls, mapfile, filters=None, defaults=None, cache=None,
1312 minchunk=1024, maxchunk=65536):
1357 minchunk=1024, maxchunk=65536):
1313 """Create templater from the specified map file"""
1358 """Create templater from the specified map file"""
1314 t = cls(filters, defaults, cache, [], minchunk, maxchunk)
1359 t = cls(filters, defaults, cache, [], minchunk, maxchunk)
1315 cache, tmap = _readmapfile(mapfile)
1360 cache, tmap = _readmapfile(mapfile)
1316 t.cache.update(cache)
1361 t.cache.update(cache)
1317 t.map = tmap
1362 t.map = tmap
1318 return t
1363 return t
1319
1364
1320 def __contains__(self, key):
1365 def __contains__(self, key):
1321 return key in self.cache or key in self.map
1366 return key in self.cache or key in self.map
1322
1367
1323 def load(self, t):
1368 def load(self, t):
1324 '''Get the template for the given template name. Use a local cache.'''
1369 '''Get the template for the given template name. Use a local cache.'''
1325 if t not in self.cache:
1370 if t not in self.cache:
1326 try:
1371 try:
1327 self.cache[t] = util.readfile(self.map[t][1])
1372 self.cache[t] = util.readfile(self.map[t][1])
1328 except KeyError as inst:
1373 except KeyError as inst:
1329 raise TemplateNotFound(_('"%s" not in template map') %
1374 raise TemplateNotFound(_('"%s" not in template map') %
1330 inst.args[0])
1375 inst.args[0])
1331 except IOError as inst:
1376 except IOError as inst:
1332 raise IOError(inst.args[0], _('template file %s: %s') %
1377 raise IOError(inst.args[0], _('template file %s: %s') %
1333 (self.map[t][1], inst.args[1]))
1378 (self.map[t][1], inst.args[1]))
1334 return self.cache[t]
1379 return self.cache[t]
1335
1380
1336 def render(self, mapping):
1381 def render(self, mapping):
1337 """Render the default unnamed template and return result as string"""
1382 """Render the default unnamed template and return result as string"""
1338 return stringify(self('', **mapping))
1383 return stringify(self('', **mapping))
1339
1384
1340 def __call__(self, t, **mapping):
1385 def __call__(self, t, **mapping):
1341 mapping = pycompat.byteskwargs(mapping)
1386 mapping = pycompat.byteskwargs(mapping)
1342 ttype = t in self.map and self.map[t][0] or 'default'
1387 ttype = t in self.map and self.map[t][0] or 'default'
1343 if ttype not in self.ecache:
1388 if ttype not in self.ecache:
1344 try:
1389 try:
1345 ecls = engines[ttype]
1390 ecls = engines[ttype]
1346 except KeyError:
1391 except KeyError:
1347 raise error.Abort(_('invalid template engine: %s') % ttype)
1392 raise error.Abort(_('invalid template engine: %s') % ttype)
1348 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
1393 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
1349 self._aliases)
1394 self._aliases)
1350 proc = self.ecache[ttype]
1395 proc = self.ecache[ttype]
1351
1396
1352 stream = proc.process(t, mapping)
1397 stream = proc.process(t, mapping)
1353 if self.minchunk:
1398 if self.minchunk:
1354 stream = util.increasingchunks(stream, min=self.minchunk,
1399 stream = util.increasingchunks(stream, min=self.minchunk,
1355 max=self.maxchunk)
1400 max=self.maxchunk)
1356 return stream
1401 return stream
1357
1402
1358 def templatepaths():
1403 def templatepaths():
1359 '''return locations used for template files.'''
1404 '''return locations used for template files.'''
1360 pathsrel = ['templates']
1405 pathsrel = ['templates']
1361 paths = [os.path.normpath(os.path.join(util.datapath, f))
1406 paths = [os.path.normpath(os.path.join(util.datapath, f))
1362 for f in pathsrel]
1407 for f in pathsrel]
1363 return [p for p in paths if os.path.isdir(p)]
1408 return [p for p in paths if os.path.isdir(p)]
1364
1409
1365 def templatepath(name):
1410 def templatepath(name):
1366 '''return location of template file. returns None if not found.'''
1411 '''return location of template file. returns None if not found.'''
1367 for p in templatepaths():
1412 for p in templatepaths():
1368 f = os.path.join(p, name)
1413 f = os.path.join(p, name)
1369 if os.path.exists(f):
1414 if os.path.exists(f):
1370 return f
1415 return f
1371 return None
1416 return None
1372
1417
1373 def stylemap(styles, paths=None):
1418 def stylemap(styles, paths=None):
1374 """Return path to mapfile for a given style.
1419 """Return path to mapfile for a given style.
1375
1420
1376 Searches mapfile in the following locations:
1421 Searches mapfile in the following locations:
1377 1. templatepath/style/map
1422 1. templatepath/style/map
1378 2. templatepath/map-style
1423 2. templatepath/map-style
1379 3. templatepath/map
1424 3. templatepath/map
1380 """
1425 """
1381
1426
1382 if paths is None:
1427 if paths is None:
1383 paths = templatepaths()
1428 paths = templatepaths()
1384 elif isinstance(paths, str):
1429 elif isinstance(paths, str):
1385 paths = [paths]
1430 paths = [paths]
1386
1431
1387 if isinstance(styles, str):
1432 if isinstance(styles, str):
1388 styles = [styles]
1433 styles = [styles]
1389
1434
1390 for style in styles:
1435 for style in styles:
1391 # only plain name is allowed to honor template paths
1436 # only plain name is allowed to honor template paths
1392 if (not style
1437 if (not style
1393 or style in (os.curdir, os.pardir)
1438 or style in (os.curdir, os.pardir)
1394 or pycompat.ossep in style
1439 or pycompat.ossep in style
1395 or pycompat.osaltsep and pycompat.osaltsep in style):
1440 or pycompat.osaltsep and pycompat.osaltsep in style):
1396 continue
1441 continue
1397 locations = [os.path.join(style, 'map'), 'map-' + style]
1442 locations = [os.path.join(style, 'map'), 'map-' + style]
1398 locations.append('map')
1443 locations.append('map')
1399
1444
1400 for path in paths:
1445 for path in paths:
1401 for location in locations:
1446 for location in locations:
1402 mapfile = os.path.join(path, location)
1447 mapfile = os.path.join(path, location)
1403 if os.path.isfile(mapfile):
1448 if os.path.isfile(mapfile):
1404 return style, mapfile
1449 return style, mapfile
1405
1450
1406 raise RuntimeError("No hgweb templates found in %r" % paths)
1451 raise RuntimeError("No hgweb templates found in %r" % paths)
1407
1452
1408 def loadfunction(ui, extname, registrarobj):
1453 def loadfunction(ui, extname, registrarobj):
1409 """Load template function from specified registrarobj
1454 """Load template function from specified registrarobj
1410 """
1455 """
1411 for name, func in registrarobj._table.iteritems():
1456 for name, func in registrarobj._table.iteritems():
1412 funcs[name] = func
1457 funcs[name] = func
1413
1458
1414 # tell hggettext to extract docstrings from these functions:
1459 # tell hggettext to extract docstrings from these functions:
1415 i18nfunctions = funcs.values()
1460 i18nfunctions = funcs.values()
@@ -1,1644 +1,1645 b''
1 This test file test the various templates related to obsmarkers.
1 This test file test the various templates related to obsmarkers.
2
2
3 Global setup
3 Global setup
4 ============
4 ============
5
5
6 $ . $TESTDIR/testlib/obsmarker-common.sh
6 $ . $TESTDIR/testlib/obsmarker-common.sh
7 $ cat >> $HGRCPATH <<EOF
7 $ cat >> $HGRCPATH <<EOF
8 > [ui]
8 > [ui]
9 > interactive = true
9 > interactive = true
10 > [phases]
10 > [phases]
11 > publish=False
11 > publish=False
12 > [experimental]
12 > [experimental]
13 > stabilization=all
13 > stabilization=all
14 > [templates]
14 > [templates]
15 > obsfatesuccessors = " as {join(successors, ", ")}"
15 > obsfatesuccessors = " as {join(successors, ", ")}"
16 > obsfateverb = "{obsfateverb(successors)}"
16 > obsfateverb = "{obsfateverb(successors)}"
17 > obsfateusers = "{if(obsfateusers(markers), " by {join(obsfateusers(markers), ", ")}")}"
17 > obsfateusers = "{if(obsfateusers(markers), " by {join(obsfateusers(markers), ", ")}")}"
18 > obsfate = "{obsfateverb}{obsfatesuccessors}{obsfateusers}; "
18 > obsfatedate = "{if(obsfatedate(markers), "{ifeq(min(obsfatedate(markers)), max(obsfatedate(markers)), " (at {min(obsfatedate(markers))|isodate})", " (between {min(obsfatedate(markers))|isodate} and {max(obsfatedate(markers))|isodate})")}")}"
19 > obsfate = "{obsfateverb}{obsfatesuccessors}{obsfateusers}{obsfatedate}; "
19 > [alias]
20 > [alias]
20 > tlog = log -G -T '{node|short}\
21 > tlog = log -G -T '{node|short}\
21 > {if(predecessors, "\n Predecessors: {predecessors}")}\
22 > {if(predecessors, "\n Predecessors: {predecessors}")}\
22 > {if(predecessors, "\n semi-colon: {join(predecessors, "; ")}")}\
23 > {if(predecessors, "\n semi-colon: {join(predecessors, "; ")}")}\
23 > {if(predecessors, "\n json: {predecessors|json}")}\
24 > {if(predecessors, "\n json: {predecessors|json}")}\
24 > {if(predecessors, "\n map: {join(predecessors % "{rev}:{node}", " ")}")}\
25 > {if(predecessors, "\n map: {join(predecessors % "{rev}:{node}", " ")}")}\
25 > {if(successorssets, "\n Successors: {successorssets}")}\
26 > {if(successorssets, "\n Successors: {successorssets}")}\
26 > {if(successorssets, "\n multi-line: {join(successorssets, "\n multi-line: ")}")}\
27 > {if(successorssets, "\n multi-line: {join(successorssets, "\n multi-line: ")}")}\
27 > {if(successorssets, "\n json: {successorssets|json}")}\n'
28 > {if(successorssets, "\n json: {successorssets|json}")}\n'
28 > fatelog = log -G -T '{node|short}\n{if(succsandmarkers, " Obsfate: {succsandmarkers % "{obsfate}"} \n" )}'
29 > fatelog = log -G -T '{node|short}\n{if(succsandmarkers, " Obsfate: {succsandmarkers % "{obsfate}"} \n" )}'
29 > fatelogjson = log -G -T '{node|short}\n{if(succsandmarkers, " Obsfate: {succsandmarkers|json}\n")}'
30 > fatelogjson = log -G -T '{node|short}\n{if(succsandmarkers, " Obsfate: {succsandmarkers|json}\n")}'
30 > EOF
31 > EOF
31
32
32 Test templates on amended commit
33 Test templates on amended commit
33 ================================
34 ================================
34
35
35 Test setup
36 Test setup
36 ----------
37 ----------
37
38
38 $ hg init $TESTTMP/templates-local-amend
39 $ hg init $TESTTMP/templates-local-amend
39 $ cd $TESTTMP/templates-local-amend
40 $ cd $TESTTMP/templates-local-amend
40 $ mkcommit ROOT
41 $ mkcommit ROOT
41 $ mkcommit A0
42 $ mkcommit A0
42 $ echo 42 >> A0
43 $ echo 42 >> A0
43 $ HGUSER=test1 hg commit --amend -m "A1" --config devel.default-date="1234567890 0"
44 $ HGUSER=test1 hg commit --amend -m "A1" --config devel.default-date="1234567890 0"
44 $ HGUSER=test2 hg commit --amend -m "A2" --config devel.default-date="987654321 0"
45 $ HGUSER=test2 hg commit --amend -m "A2" --config devel.default-date="987654321 0"
45
46
46 $ hg log --hidden -G
47 $ hg log --hidden -G
47 @ changeset: 4:d004c8f274b9
48 @ changeset: 4:d004c8f274b9
48 | tag: tip
49 | tag: tip
49 | parent: 0:ea207398892e
50 | parent: 0:ea207398892e
50 | user: test
51 | user: test
51 | date: Thu Jan 01 00:00:00 1970 +0000
52 | date: Thu Jan 01 00:00:00 1970 +0000
52 | summary: A2
53 | summary: A2
53 |
54 |
54 | x changeset: 3:a468dc9b3633
55 | x changeset: 3:a468dc9b3633
55 |/ parent: 0:ea207398892e
56 |/ parent: 0:ea207398892e
56 | user: test
57 | user: test
57 | date: Thu Jan 01 00:00:00 1970 +0000
58 | date: Thu Jan 01 00:00:00 1970 +0000
58 | summary: A1
59 | summary: A1
59 |
60 |
60 | x changeset: 2:f137d23bb3e1
61 | x changeset: 2:f137d23bb3e1
61 | | user: test
62 | | user: test
62 | | date: Thu Jan 01 00:00:00 1970 +0000
63 | | date: Thu Jan 01 00:00:00 1970 +0000
63 | | summary: temporary amend commit for 471f378eab4c
64 | | summary: temporary amend commit for 471f378eab4c
64 | |
65 | |
65 | x changeset: 1:471f378eab4c
66 | x changeset: 1:471f378eab4c
66 |/ user: test
67 |/ user: test
67 | date: Thu Jan 01 00:00:00 1970 +0000
68 | date: Thu Jan 01 00:00:00 1970 +0000
68 | summary: A0
69 | summary: A0
69 |
70 |
70 o changeset: 0:ea207398892e
71 o changeset: 0:ea207398892e
71 user: test
72 user: test
72 date: Thu Jan 01 00:00:00 1970 +0000
73 date: Thu Jan 01 00:00:00 1970 +0000
73 summary: ROOT
74 summary: ROOT
74
75
75 Check templates
76 Check templates
76 ---------------
77 ---------------
77 $ hg up 'desc(A0)' --hidden
78 $ hg up 'desc(A0)' --hidden
78 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
79
80
80 Predecessors template should show current revision as it is the working copy
81 Predecessors template should show current revision as it is the working copy
81 $ hg tlog
82 $ hg tlog
82 o d004c8f274b9
83 o d004c8f274b9
83 | Predecessors: 1:471f378eab4c
84 | Predecessors: 1:471f378eab4c
84 | semi-colon: 1:471f378eab4c
85 | semi-colon: 1:471f378eab4c
85 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
86 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
86 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
87 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
87 | @ 471f378eab4c
88 | @ 471f378eab4c
88 |/ Successors: 4:d004c8f274b9
89 |/ Successors: 4:d004c8f274b9
89 | multi-line: 4:d004c8f274b9
90 | multi-line: 4:d004c8f274b9
90 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
91 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
91 o ea207398892e
92 o ea207398892e
92
93
93 $ hg fatelog -q --traceback
94 $ hg fatelog -q --traceback
94 o d004c8f274b9
95 o d004c8f274b9
95 |
96 |
96 | @ 471f378eab4c
97 | @ 471f378eab4c
97 |/ Obsfate: rewritten as 4:d004c8f274b9 by test1, test2;
98 |/ Obsfate: rewritten as 4:d004c8f274b9 by test1, test2 (between 2001-04-19 04:25 +0000 and 2009-02-13 23:31 +0000);
98 o ea207398892e
99 o ea207398892e
99
100
100 $ hg fatelog
101 $ hg fatelog
101 o d004c8f274b9
102 o d004c8f274b9
102 |
103 |
103 | @ 471f378eab4c
104 | @ 471f378eab4c
104 |/ Obsfate: rewritten as 4:d004c8f274b9 by test1, test2;
105 |/ Obsfate: rewritten as 4:d004c8f274b9 by test1, test2 (between 2001-04-19 04:25 +0000 and 2009-02-13 23:31 +0000);
105 o ea207398892e
106 o ea207398892e
106
107
107 $ hg fatelog -v
108 $ hg fatelog -v
108 o d004c8f274b9
109 o d004c8f274b9
109 |
110 |
110 | @ 471f378eab4c
111 | @ 471f378eab4c
111 |/ Obsfate: rewritten as 4:d004c8f274b9 by test1, test2;
112 |/ Obsfate: rewritten as 4:d004c8f274b9 by test1, test2 (between 2001-04-19 04:25 +0000 and 2009-02-13 23:31 +0000);
112 o ea207398892e
113 o ea207398892e
113
114
114 $ hg up 'desc(A1)' --hidden
115 $ hg up 'desc(A1)' --hidden
115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
116
117
117 Predecessors template should show current revision as it is the working copy
118 Predecessors template should show current revision as it is the working copy
118 $ hg tlog
119 $ hg tlog
119 o d004c8f274b9
120 o d004c8f274b9
120 | Predecessors: 3:a468dc9b3633
121 | Predecessors: 3:a468dc9b3633
121 | semi-colon: 3:a468dc9b3633
122 | semi-colon: 3:a468dc9b3633
122 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
123 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
123 | map: 3:a468dc9b36338b14fdb7825f55ce3df4e71517ad
124 | map: 3:a468dc9b36338b14fdb7825f55ce3df4e71517ad
124 | @ a468dc9b3633
125 | @ a468dc9b3633
125 |/ Successors: 4:d004c8f274b9
126 |/ Successors: 4:d004c8f274b9
126 | multi-line: 4:d004c8f274b9
127 | multi-line: 4:d004c8f274b9
127 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
128 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
128 o ea207398892e
129 o ea207398892e
129
130
130 $ hg fatelog -v
131 $ hg fatelog -v
131 o d004c8f274b9
132 o d004c8f274b9
132 |
133 |
133 | @ a468dc9b3633
134 | @ a468dc9b3633
134 |/ Obsfate: rewritten as 4:d004c8f274b9 by test2;
135 |/ Obsfate: rewritten as 4:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
135 o ea207398892e
136 o ea207398892e
136
137
137 Predecessors template should show all the predecessors as we force their display
138 Predecessors template should show all the predecessors as we force their display
138 with --hidden
139 with --hidden
139 $ hg tlog --hidden
140 $ hg tlog --hidden
140 o d004c8f274b9
141 o d004c8f274b9
141 | Predecessors: 3:a468dc9b3633
142 | Predecessors: 3:a468dc9b3633
142 | semi-colon: 3:a468dc9b3633
143 | semi-colon: 3:a468dc9b3633
143 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
144 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
144 | map: 3:a468dc9b36338b14fdb7825f55ce3df4e71517ad
145 | map: 3:a468dc9b36338b14fdb7825f55ce3df4e71517ad
145 | @ a468dc9b3633
146 | @ a468dc9b3633
146 |/ Predecessors: 1:471f378eab4c
147 |/ Predecessors: 1:471f378eab4c
147 | semi-colon: 1:471f378eab4c
148 | semi-colon: 1:471f378eab4c
148 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
149 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
149 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
150 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
150 | Successors: 4:d004c8f274b9
151 | Successors: 4:d004c8f274b9
151 | multi-line: 4:d004c8f274b9
152 | multi-line: 4:d004c8f274b9
152 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
153 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
153 | x f137d23bb3e1
154 | x f137d23bb3e1
154 | |
155 | |
155 | x 471f378eab4c
156 | x 471f378eab4c
156 |/ Successors: 3:a468dc9b3633
157 |/ Successors: 3:a468dc9b3633
157 | multi-line: 3:a468dc9b3633
158 | multi-line: 3:a468dc9b3633
158 | json: [["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]]
159 | json: [["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]]
159 o ea207398892e
160 o ea207398892e
160
161
161 $ hg fatelog --hidden -q
162 $ hg fatelog --hidden -q
162 o d004c8f274b9
163 o d004c8f274b9
163 |
164 |
164 | @ a468dc9b3633
165 | @ a468dc9b3633
165 |/ Obsfate: rewritten as 4:d004c8f274b9 by test2;
166 |/ Obsfate: rewritten as 4:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
166 | x f137d23bb3e1
167 | x f137d23bb3e1
167 | |
168 | |
168 | x 471f378eab4c
169 | x 471f378eab4c
169 |/ Obsfate: rewritten as 3:a468dc9b3633 by test1;
170 |/ Obsfate: rewritten as 3:a468dc9b3633 by test1 (at 2009-02-13 23:31 +0000);
170 o ea207398892e
171 o ea207398892e
171
172
172
173
173 Predecessors template shouldn't show anything as all obsolete commit are not
174 Predecessors template shouldn't show anything as all obsolete commit are not
174 visible.
175 visible.
175 $ hg up 'desc(A2)'
176 $ hg up 'desc(A2)'
176 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 $ hg tlog
178 $ hg tlog
178 @ d004c8f274b9
179 @ d004c8f274b9
179 |
180 |
180 o ea207398892e
181 o ea207398892e
181
182
182 $ hg tlog --hidden
183 $ hg tlog --hidden
183 @ d004c8f274b9
184 @ d004c8f274b9
184 | Predecessors: 3:a468dc9b3633
185 | Predecessors: 3:a468dc9b3633
185 | semi-colon: 3:a468dc9b3633
186 | semi-colon: 3:a468dc9b3633
186 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
187 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
187 | map: 3:a468dc9b36338b14fdb7825f55ce3df4e71517ad
188 | map: 3:a468dc9b36338b14fdb7825f55ce3df4e71517ad
188 | x a468dc9b3633
189 | x a468dc9b3633
189 |/ Predecessors: 1:471f378eab4c
190 |/ Predecessors: 1:471f378eab4c
190 | semi-colon: 1:471f378eab4c
191 | semi-colon: 1:471f378eab4c
191 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
192 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
192 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
193 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
193 | Successors: 4:d004c8f274b9
194 | Successors: 4:d004c8f274b9
194 | multi-line: 4:d004c8f274b9
195 | multi-line: 4:d004c8f274b9
195 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
196 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
196 | x f137d23bb3e1
197 | x f137d23bb3e1
197 | |
198 | |
198 | x 471f378eab4c
199 | x 471f378eab4c
199 |/ Successors: 3:a468dc9b3633
200 |/ Successors: 3:a468dc9b3633
200 | multi-line: 3:a468dc9b3633
201 | multi-line: 3:a468dc9b3633
201 | json: [["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]]
202 | json: [["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]]
202 o ea207398892e
203 o ea207398892e
203
204
204 $ hg fatelog -v
205 $ hg fatelog -v
205 @ d004c8f274b9
206 @ d004c8f274b9
206 |
207 |
207 o ea207398892e
208 o ea207398892e
208
209
209
210
210 $ hg fatelog -v --hidden
211 $ hg fatelog -v --hidden
211 @ d004c8f274b9
212 @ d004c8f274b9
212 |
213 |
213 | x a468dc9b3633
214 | x a468dc9b3633
214 |/ Obsfate: rewritten as 4:d004c8f274b9 by test2;
215 |/ Obsfate: rewritten as 4:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
215 | x f137d23bb3e1
216 | x f137d23bb3e1
216 | |
217 | |
217 | x 471f378eab4c
218 | x 471f378eab4c
218 |/ Obsfate: rewritten as 3:a468dc9b3633 by test1;
219 |/ Obsfate: rewritten as 3:a468dc9b3633 by test1 (at 2009-02-13 23:31 +0000);
219 o ea207398892e
220 o ea207398892e
220
221
221 $ hg fatelogjson --hidden
222 $ hg fatelogjson --hidden
222 @ d004c8f274b9
223 @ d004c8f274b9
223 |
224 |
224 | x a468dc9b3633
225 | x a468dc9b3633
225 |/ Obsfate: [{"markers": [["a468dc9b36338b14fdb7825f55ce3df4e71517ad", ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], 0, [["user", "test2"]], [987654321.0, 0], null]], "successors": ["d004c8f274b9ec480a47a93c10dac5eee63adb78"]}]
226 |/ Obsfate: [{"markers": [["a468dc9b36338b14fdb7825f55ce3df4e71517ad", ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], 0, [["user", "test2"]], [987654321.0, 0], null]], "successors": ["d004c8f274b9ec480a47a93c10dac5eee63adb78"]}]
226 | x f137d23bb3e1
227 | x f137d23bb3e1
227 | |
228 | |
228 | x 471f378eab4c
229 | x 471f378eab4c
229 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], 0, [["user", "test1"]], [1234567890.0, 0], null]], "successors": ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]}]
230 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], 0, [["user", "test1"]], [1234567890.0, 0], null]], "successors": ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]}]
230 o ea207398892e
231 o ea207398892e
231
232
232 Test templates with splitted commit
233 Test templates with splitted commit
233 ===================================
234 ===================================
234
235
235 $ hg init $TESTTMP/templates-local-split
236 $ hg init $TESTTMP/templates-local-split
236 $ cd $TESTTMP/templates-local-split
237 $ cd $TESTTMP/templates-local-split
237 $ mkcommit ROOT
238 $ mkcommit ROOT
238 $ echo 42 >> a
239 $ echo 42 >> a
239 $ echo 43 >> b
240 $ echo 43 >> b
240 $ hg commit -A -m "A0"
241 $ hg commit -A -m "A0"
241 adding a
242 adding a
242 adding b
243 adding b
243 $ hg log --hidden -G
244 $ hg log --hidden -G
244 @ changeset: 1:471597cad322
245 @ changeset: 1:471597cad322
245 | tag: tip
246 | tag: tip
246 | user: test
247 | user: test
247 | date: Thu Jan 01 00:00:00 1970 +0000
248 | date: Thu Jan 01 00:00:00 1970 +0000
248 | summary: A0
249 | summary: A0
249 |
250 |
250 o changeset: 0:ea207398892e
251 o changeset: 0:ea207398892e
251 user: test
252 user: test
252 date: Thu Jan 01 00:00:00 1970 +0000
253 date: Thu Jan 01 00:00:00 1970 +0000
253 summary: ROOT
254 summary: ROOT
254
255
255 # Simulate split
256 # Simulate split
256 $ hg up -r "desc(ROOT)"
257 $ hg up -r "desc(ROOT)"
257 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
258 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
258 $ echo 42 >> a
259 $ echo 42 >> a
259 $ hg commit -A -m "A0"
260 $ hg commit -A -m "A0"
260 adding a
261 adding a
261 created new head
262 created new head
262 $ echo 43 >> b
263 $ echo 43 >> b
263 $ hg commit -A -m "A0"
264 $ hg commit -A -m "A0"
264 adding b
265 adding b
265 $ hg debugobsolete `getid "1"` `getid "2"` `getid "3"`
266 $ hg debugobsolete `getid "1"` `getid "2"` `getid "3"`
266 obsoleted 1 changesets
267 obsoleted 1 changesets
267
268
268 $ hg log --hidden -G
269 $ hg log --hidden -G
269 @ changeset: 3:f257fde29c7a
270 @ changeset: 3:f257fde29c7a
270 | tag: tip
271 | tag: tip
271 | user: test
272 | user: test
272 | date: Thu Jan 01 00:00:00 1970 +0000
273 | date: Thu Jan 01 00:00:00 1970 +0000
273 | summary: A0
274 | summary: A0
274 |
275 |
275 o changeset: 2:337fec4d2edc
276 o changeset: 2:337fec4d2edc
276 | parent: 0:ea207398892e
277 | parent: 0:ea207398892e
277 | user: test
278 | user: test
278 | date: Thu Jan 01 00:00:00 1970 +0000
279 | date: Thu Jan 01 00:00:00 1970 +0000
279 | summary: A0
280 | summary: A0
280 |
281 |
281 | x changeset: 1:471597cad322
282 | x changeset: 1:471597cad322
282 |/ user: test
283 |/ user: test
283 | date: Thu Jan 01 00:00:00 1970 +0000
284 | date: Thu Jan 01 00:00:00 1970 +0000
284 | summary: A0
285 | summary: A0
285 |
286 |
286 o changeset: 0:ea207398892e
287 o changeset: 0:ea207398892e
287 user: test
288 user: test
288 date: Thu Jan 01 00:00:00 1970 +0000
289 date: Thu Jan 01 00:00:00 1970 +0000
289 summary: ROOT
290 summary: ROOT
290
291
291 Check templates
292 Check templates
292 ---------------
293 ---------------
293
294
294 $ hg up 'obsolete()' --hidden
295 $ hg up 'obsolete()' --hidden
295 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
296
297
297 Predecessors template should show current revision as it is the working copy
298 Predecessors template should show current revision as it is the working copy
298 $ hg tlog
299 $ hg tlog
299 o f257fde29c7a
300 o f257fde29c7a
300 | Predecessors: 1:471597cad322
301 | Predecessors: 1:471597cad322
301 | semi-colon: 1:471597cad322
302 | semi-colon: 1:471597cad322
302 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
303 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
303 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
304 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
304 o 337fec4d2edc
305 o 337fec4d2edc
305 | Predecessors: 1:471597cad322
306 | Predecessors: 1:471597cad322
306 | semi-colon: 1:471597cad322
307 | semi-colon: 1:471597cad322
307 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
308 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
308 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
309 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
309 | @ 471597cad322
310 | @ 471597cad322
310 |/ Successors: 2:337fec4d2edc 3:f257fde29c7a
311 |/ Successors: 2:337fec4d2edc 3:f257fde29c7a
311 | multi-line: 2:337fec4d2edc 3:f257fde29c7a
312 | multi-line: 2:337fec4d2edc 3:f257fde29c7a
312 | json: [["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]]
313 | json: [["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]]
313 o ea207398892e
314 o ea207398892e
314
315
315
316
316 $ hg fatelog
317 $ hg fatelog
317 o f257fde29c7a
318 o f257fde29c7a
318 |
319 |
319 o 337fec4d2edc
320 o 337fec4d2edc
320 |
321 |
321 | @ 471597cad322
322 | @ 471597cad322
322 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test;
323 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test (at 1970-01-01 00:00 +0000);
323 o ea207398892e
324 o ea207398892e
324
325
325 $ hg up f257fde29c7a
326 $ hg up f257fde29c7a
326 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
327 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
327
328
328 Predecessors template should not show a predecessor as it's not displayed in
329 Predecessors template should not show a predecessor as it's not displayed in
329 the log
330 the log
330 $ hg tlog
331 $ hg tlog
331 @ f257fde29c7a
332 @ f257fde29c7a
332 |
333 |
333 o 337fec4d2edc
334 o 337fec4d2edc
334 |
335 |
335 o ea207398892e
336 o ea207398892e
336
337
337 Predecessors template should show both predecessors as we force their display
338 Predecessors template should show both predecessors as we force their display
338 with --hidden
339 with --hidden
339 $ hg tlog --hidden
340 $ hg tlog --hidden
340 @ f257fde29c7a
341 @ f257fde29c7a
341 | Predecessors: 1:471597cad322
342 | Predecessors: 1:471597cad322
342 | semi-colon: 1:471597cad322
343 | semi-colon: 1:471597cad322
343 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
344 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
344 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
345 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
345 o 337fec4d2edc
346 o 337fec4d2edc
346 | Predecessors: 1:471597cad322
347 | Predecessors: 1:471597cad322
347 | semi-colon: 1:471597cad322
348 | semi-colon: 1:471597cad322
348 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
349 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
349 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
350 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
350 | x 471597cad322
351 | x 471597cad322
351 |/ Successors: 2:337fec4d2edc 3:f257fde29c7a
352 |/ Successors: 2:337fec4d2edc 3:f257fde29c7a
352 | multi-line: 2:337fec4d2edc 3:f257fde29c7a
353 | multi-line: 2:337fec4d2edc 3:f257fde29c7a
353 | json: [["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]]
354 | json: [["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]]
354 o ea207398892e
355 o ea207398892e
355
356
356
357
357 $ hg fatelog --hidden
358 $ hg fatelog --hidden
358 @ f257fde29c7a
359 @ f257fde29c7a
359 |
360 |
360 o 337fec4d2edc
361 o 337fec4d2edc
361 |
362 |
362 | x 471597cad322
363 | x 471597cad322
363 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test;
364 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test (at 1970-01-01 00:00 +0000);
364 o ea207398892e
365 o ea207398892e
365
366
366 $ hg fatelogjson --hidden
367 $ hg fatelogjson --hidden
367 @ f257fde29c7a
368 @ f257fde29c7a
368 |
369 |
369 o 337fec4d2edc
370 o 337fec4d2edc
370 |
371 |
371 | x 471597cad322
372 | x 471597cad322
372 |/ Obsfate: [{"markers": [["471597cad322d1f659bb169751be9133dad92ef3", ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]}]
373 |/ Obsfate: [{"markers": [["471597cad322d1f659bb169751be9133dad92ef3", ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]}]
373 o ea207398892e
374 o ea207398892e
374
375
375 Test templates with folded commit
376 Test templates with folded commit
376 =================================
377 =================================
377
378
378 Test setup
379 Test setup
379 ----------
380 ----------
380
381
381 $ hg init $TESTTMP/templates-local-fold
382 $ hg init $TESTTMP/templates-local-fold
382 $ cd $TESTTMP/templates-local-fold
383 $ cd $TESTTMP/templates-local-fold
383 $ mkcommit ROOT
384 $ mkcommit ROOT
384 $ mkcommit A0
385 $ mkcommit A0
385 $ mkcommit B0
386 $ mkcommit B0
386 $ hg log --hidden -G
387 $ hg log --hidden -G
387 @ changeset: 2:0dec01379d3b
388 @ changeset: 2:0dec01379d3b
388 | tag: tip
389 | tag: tip
389 | user: test
390 | user: test
390 | date: Thu Jan 01 00:00:00 1970 +0000
391 | date: Thu Jan 01 00:00:00 1970 +0000
391 | summary: B0
392 | summary: B0
392 |
393 |
393 o changeset: 1:471f378eab4c
394 o changeset: 1:471f378eab4c
394 | user: test
395 | user: test
395 | date: Thu Jan 01 00:00:00 1970 +0000
396 | date: Thu Jan 01 00:00:00 1970 +0000
396 | summary: A0
397 | summary: A0
397 |
398 |
398 o changeset: 0:ea207398892e
399 o changeset: 0:ea207398892e
399 user: test
400 user: test
400 date: Thu Jan 01 00:00:00 1970 +0000
401 date: Thu Jan 01 00:00:00 1970 +0000
401 summary: ROOT
402 summary: ROOT
402
403
403 Simulate a fold
404 Simulate a fold
404 $ hg up -r "desc(ROOT)"
405 $ hg up -r "desc(ROOT)"
405 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
406 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
406 $ echo "A0" > A0
407 $ echo "A0" > A0
407 $ echo "B0" > B0
408 $ echo "B0" > B0
408 $ hg commit -A -m "C0"
409 $ hg commit -A -m "C0"
409 adding A0
410 adding A0
410 adding B0
411 adding B0
411 created new head
412 created new head
412 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(C0)"`
413 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(C0)"`
413 obsoleted 1 changesets
414 obsoleted 1 changesets
414 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(C0)"`
415 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(C0)"`
415 obsoleted 1 changesets
416 obsoleted 1 changesets
416
417
417 $ hg log --hidden -G
418 $ hg log --hidden -G
418 @ changeset: 3:eb5a0daa2192
419 @ changeset: 3:eb5a0daa2192
419 | tag: tip
420 | tag: tip
420 | parent: 0:ea207398892e
421 | parent: 0:ea207398892e
421 | user: test
422 | user: test
422 | date: Thu Jan 01 00:00:00 1970 +0000
423 | date: Thu Jan 01 00:00:00 1970 +0000
423 | summary: C0
424 | summary: C0
424 |
425 |
425 | x changeset: 2:0dec01379d3b
426 | x changeset: 2:0dec01379d3b
426 | | user: test
427 | | user: test
427 | | date: Thu Jan 01 00:00:00 1970 +0000
428 | | date: Thu Jan 01 00:00:00 1970 +0000
428 | | summary: B0
429 | | summary: B0
429 | |
430 | |
430 | x changeset: 1:471f378eab4c
431 | x changeset: 1:471f378eab4c
431 |/ user: test
432 |/ user: test
432 | date: Thu Jan 01 00:00:00 1970 +0000
433 | date: Thu Jan 01 00:00:00 1970 +0000
433 | summary: A0
434 | summary: A0
434 |
435 |
435 o changeset: 0:ea207398892e
436 o changeset: 0:ea207398892e
436 user: test
437 user: test
437 date: Thu Jan 01 00:00:00 1970 +0000
438 date: Thu Jan 01 00:00:00 1970 +0000
438 summary: ROOT
439 summary: ROOT
439
440
440 Check templates
441 Check templates
441 ---------------
442 ---------------
442
443
443 $ hg up 'desc(A0)' --hidden
444 $ hg up 'desc(A0)' --hidden
444 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
445 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
445
446
446 Predecessors template should show current revision as it is the working copy
447 Predecessors template should show current revision as it is the working copy
447 $ hg tlog
448 $ hg tlog
448 o eb5a0daa2192
449 o eb5a0daa2192
449 | Predecessors: 1:471f378eab4c
450 | Predecessors: 1:471f378eab4c
450 | semi-colon: 1:471f378eab4c
451 | semi-colon: 1:471f378eab4c
451 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
452 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
452 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
453 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
453 | @ 471f378eab4c
454 | @ 471f378eab4c
454 |/ Successors: 3:eb5a0daa2192
455 |/ Successors: 3:eb5a0daa2192
455 | multi-line: 3:eb5a0daa2192
456 | multi-line: 3:eb5a0daa2192
456 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
457 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
457 o ea207398892e
458 o ea207398892e
458
459
459
460
460 $ hg fatelog
461 $ hg fatelog
461 o eb5a0daa2192
462 o eb5a0daa2192
462 |
463 |
463 | @ 471f378eab4c
464 | @ 471f378eab4c
464 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test;
465 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
465 o ea207398892e
466 o ea207398892e
466
467
467 $ hg up 'desc(B0)' --hidden
468 $ hg up 'desc(B0)' --hidden
468 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
469 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
469
470
470 Predecessors template should show both predecessors as they should be both
471 Predecessors template should show both predecessors as they should be both
471 displayed
472 displayed
472 $ hg tlog
473 $ hg tlog
473 o eb5a0daa2192
474 o eb5a0daa2192
474 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
475 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
475 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
476 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
476 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
477 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
477 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
478 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
478 | @ 0dec01379d3b
479 | @ 0dec01379d3b
479 | | Successors: 3:eb5a0daa2192
480 | | Successors: 3:eb5a0daa2192
480 | | multi-line: 3:eb5a0daa2192
481 | | multi-line: 3:eb5a0daa2192
481 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
482 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
482 | x 471f378eab4c
483 | x 471f378eab4c
483 |/ Successors: 3:eb5a0daa2192
484 |/ Successors: 3:eb5a0daa2192
484 | multi-line: 3:eb5a0daa2192
485 | multi-line: 3:eb5a0daa2192
485 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
486 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
486 o ea207398892e
487 o ea207398892e
487
488
488
489
489 $ hg fatelog
490 $ hg fatelog
490 o eb5a0daa2192
491 o eb5a0daa2192
491 |
492 |
492 | @ 0dec01379d3b
493 | @ 0dec01379d3b
493 | | Obsfate: rewritten as 3:eb5a0daa2192 by test;
494 | | Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
494 | x 471f378eab4c
495 | x 471f378eab4c
495 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test;
496 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
496 o ea207398892e
497 o ea207398892e
497
498
498 $ hg up 'desc(C0)'
499 $ hg up 'desc(C0)'
499 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
500 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
500
501
501 Predecessors template should not show predecessors as they are not displayed in
502 Predecessors template should not show predecessors as they are not displayed in
502 the log
503 the log
503 $ hg tlog
504 $ hg tlog
504 @ eb5a0daa2192
505 @ eb5a0daa2192
505 |
506 |
506 o ea207398892e
507 o ea207398892e
507
508
508 Predecessors template should show both predecessors as we force their display
509 Predecessors template should show both predecessors as we force their display
509 with --hidden
510 with --hidden
510 $ hg tlog --hidden
511 $ hg tlog --hidden
511 @ eb5a0daa2192
512 @ eb5a0daa2192
512 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
513 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
513 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
514 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
514 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
515 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
515 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
516 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
516 | x 0dec01379d3b
517 | x 0dec01379d3b
517 | | Successors: 3:eb5a0daa2192
518 | | Successors: 3:eb5a0daa2192
518 | | multi-line: 3:eb5a0daa2192
519 | | multi-line: 3:eb5a0daa2192
519 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
520 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
520 | x 471f378eab4c
521 | x 471f378eab4c
521 |/ Successors: 3:eb5a0daa2192
522 |/ Successors: 3:eb5a0daa2192
522 | multi-line: 3:eb5a0daa2192
523 | multi-line: 3:eb5a0daa2192
523 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
524 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
524 o ea207398892e
525 o ea207398892e
525
526
526
527
527 $ hg fatelog --hidden
528 $ hg fatelog --hidden
528 @ eb5a0daa2192
529 @ eb5a0daa2192
529 |
530 |
530 | x 0dec01379d3b
531 | x 0dec01379d3b
531 | | Obsfate: rewritten as 3:eb5a0daa2192 by test;
532 | | Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
532 | x 471f378eab4c
533 | x 471f378eab4c
533 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test;
534 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
534 o ea207398892e
535 o ea207398892e
535
536
536
537
537 $ hg fatelogjson --hidden
538 $ hg fatelogjson --hidden
538 @ eb5a0daa2192
539 @ eb5a0daa2192
539 |
540 |
540 | x 0dec01379d3b
541 | x 0dec01379d3b
541 | | Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
542 | | Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
542 | x 471f378eab4c
543 | x 471f378eab4c
543 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
544 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
544 o ea207398892e
545 o ea207398892e
545
546
546
547
547 Test templates with divergence
548 Test templates with divergence
548 ==============================
549 ==============================
549
550
550 Test setup
551 Test setup
551 ----------
552 ----------
552
553
553 $ hg init $TESTTMP/templates-local-divergence
554 $ hg init $TESTTMP/templates-local-divergence
554 $ cd $TESTTMP/templates-local-divergence
555 $ cd $TESTTMP/templates-local-divergence
555 $ mkcommit ROOT
556 $ mkcommit ROOT
556 $ mkcommit A0
557 $ mkcommit A0
557 $ hg commit --amend -m "A1"
558 $ hg commit --amend -m "A1"
558 $ hg log --hidden -G
559 $ hg log --hidden -G
559 @ changeset: 2:fdf9bde5129a
560 @ changeset: 2:fdf9bde5129a
560 | tag: tip
561 | tag: tip
561 | parent: 0:ea207398892e
562 | parent: 0:ea207398892e
562 | user: test
563 | user: test
563 | date: Thu Jan 01 00:00:00 1970 +0000
564 | date: Thu Jan 01 00:00:00 1970 +0000
564 | summary: A1
565 | summary: A1
565 |
566 |
566 | x changeset: 1:471f378eab4c
567 | x changeset: 1:471f378eab4c
567 |/ user: test
568 |/ user: test
568 | date: Thu Jan 01 00:00:00 1970 +0000
569 | date: Thu Jan 01 00:00:00 1970 +0000
569 | summary: A0
570 | summary: A0
570 |
571 |
571 o changeset: 0:ea207398892e
572 o changeset: 0:ea207398892e
572 user: test
573 user: test
573 date: Thu Jan 01 00:00:00 1970 +0000
574 date: Thu Jan 01 00:00:00 1970 +0000
574 summary: ROOT
575 summary: ROOT
575
576
576 $ hg update --hidden 'desc(A0)'
577 $ hg update --hidden 'desc(A0)'
577 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
578 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
578 $ hg commit --amend -m "A2"
579 $ hg commit --amend -m "A2"
579 $ hg log --hidden -G
580 $ hg log --hidden -G
580 @ changeset: 3:65b757b745b9
581 @ changeset: 3:65b757b745b9
581 | tag: tip
582 | tag: tip
582 | parent: 0:ea207398892e
583 | parent: 0:ea207398892e
583 | user: test
584 | user: test
584 | date: Thu Jan 01 00:00:00 1970 +0000
585 | date: Thu Jan 01 00:00:00 1970 +0000
585 | instability: content-divergent
586 | instability: content-divergent
586 | summary: A2
587 | summary: A2
587 |
588 |
588 | o changeset: 2:fdf9bde5129a
589 | o changeset: 2:fdf9bde5129a
589 |/ parent: 0:ea207398892e
590 |/ parent: 0:ea207398892e
590 | user: test
591 | user: test
591 | date: Thu Jan 01 00:00:00 1970 +0000
592 | date: Thu Jan 01 00:00:00 1970 +0000
592 | instability: content-divergent
593 | instability: content-divergent
593 | summary: A1
594 | summary: A1
594 |
595 |
595 | x changeset: 1:471f378eab4c
596 | x changeset: 1:471f378eab4c
596 |/ user: test
597 |/ user: test
597 | date: Thu Jan 01 00:00:00 1970 +0000
598 | date: Thu Jan 01 00:00:00 1970 +0000
598 | summary: A0
599 | summary: A0
599 |
600 |
600 o changeset: 0:ea207398892e
601 o changeset: 0:ea207398892e
601 user: test
602 user: test
602 date: Thu Jan 01 00:00:00 1970 +0000
603 date: Thu Jan 01 00:00:00 1970 +0000
603 summary: ROOT
604 summary: ROOT
604
605
605 $ hg commit --amend -m 'A3'
606 $ hg commit --amend -m 'A3'
606 $ hg log --hidden -G
607 $ hg log --hidden -G
607 @ changeset: 4:019fadeab383
608 @ changeset: 4:019fadeab383
608 | tag: tip
609 | tag: tip
609 | parent: 0:ea207398892e
610 | parent: 0:ea207398892e
610 | user: test
611 | user: test
611 | date: Thu Jan 01 00:00:00 1970 +0000
612 | date: Thu Jan 01 00:00:00 1970 +0000
612 | instability: content-divergent
613 | instability: content-divergent
613 | summary: A3
614 | summary: A3
614 |
615 |
615 | x changeset: 3:65b757b745b9
616 | x changeset: 3:65b757b745b9
616 |/ parent: 0:ea207398892e
617 |/ parent: 0:ea207398892e
617 | user: test
618 | user: test
618 | date: Thu Jan 01 00:00:00 1970 +0000
619 | date: Thu Jan 01 00:00:00 1970 +0000
619 | summary: A2
620 | summary: A2
620 |
621 |
621 | o changeset: 2:fdf9bde5129a
622 | o changeset: 2:fdf9bde5129a
622 |/ parent: 0:ea207398892e
623 |/ parent: 0:ea207398892e
623 | user: test
624 | user: test
624 | date: Thu Jan 01 00:00:00 1970 +0000
625 | date: Thu Jan 01 00:00:00 1970 +0000
625 | instability: content-divergent
626 | instability: content-divergent
626 | summary: A1
627 | summary: A1
627 |
628 |
628 | x changeset: 1:471f378eab4c
629 | x changeset: 1:471f378eab4c
629 |/ user: test
630 |/ user: test
630 | date: Thu Jan 01 00:00:00 1970 +0000
631 | date: Thu Jan 01 00:00:00 1970 +0000
631 | summary: A0
632 | summary: A0
632 |
633 |
633 o changeset: 0:ea207398892e
634 o changeset: 0:ea207398892e
634 user: test
635 user: test
635 date: Thu Jan 01 00:00:00 1970 +0000
636 date: Thu Jan 01 00:00:00 1970 +0000
636 summary: ROOT
637 summary: ROOT
637
638
638
639
639 Check templates
640 Check templates
640 ---------------
641 ---------------
641
642
642 $ hg up 'desc(A0)' --hidden
643 $ hg up 'desc(A0)' --hidden
643 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
644 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
644
645
645 Predecessors template should show current revision as it is the working copy
646 Predecessors template should show current revision as it is the working copy
646 $ hg tlog
647 $ hg tlog
647 o 019fadeab383
648 o 019fadeab383
648 | Predecessors: 1:471f378eab4c
649 | Predecessors: 1:471f378eab4c
649 | semi-colon: 1:471f378eab4c
650 | semi-colon: 1:471f378eab4c
650 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
651 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
651 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
652 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
652 | o fdf9bde5129a
653 | o fdf9bde5129a
653 |/ Predecessors: 1:471f378eab4c
654 |/ Predecessors: 1:471f378eab4c
654 | semi-colon: 1:471f378eab4c
655 | semi-colon: 1:471f378eab4c
655 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
656 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
656 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
657 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
657 | @ 471f378eab4c
658 | @ 471f378eab4c
658 |/ Successors: 2:fdf9bde5129a; 4:019fadeab383
659 |/ Successors: 2:fdf9bde5129a; 4:019fadeab383
659 | multi-line: 2:fdf9bde5129a
660 | multi-line: 2:fdf9bde5129a
660 | multi-line: 4:019fadeab383
661 | multi-line: 4:019fadeab383
661 | json: [["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]]
662 | json: [["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]]
662 o ea207398892e
663 o ea207398892e
663
664
664 $ hg fatelog
665 $ hg fatelog
665 o 019fadeab383
666 o 019fadeab383
666 |
667 |
667 | o fdf9bde5129a
668 | o fdf9bde5129a
668 |/
669 |/
669 | @ 471f378eab4c
670 | @ 471f378eab4c
670 |/ Obsfate: rewritten as 2:fdf9bde5129a by test; rewritten as 4:019fadeab383 by test;
671 |/ Obsfate: rewritten as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000); rewritten as 4:019fadeab383 by test (at 1970-01-01 00:00 +0000);
671 o ea207398892e
672 o ea207398892e
672
673
673 $ hg up 'desc(A1)'
674 $ hg up 'desc(A1)'
674 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
675 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
675
676
676 Predecessors template should not show predecessors as they are not displayed in
677 Predecessors template should not show predecessors as they are not displayed in
677 the log
678 the log
678 $ hg tlog
679 $ hg tlog
679 o 019fadeab383
680 o 019fadeab383
680 |
681 |
681 | @ fdf9bde5129a
682 | @ fdf9bde5129a
682 |/
683 |/
683 o ea207398892e
684 o ea207398892e
684
685
685
686
686 $ hg fatelog
687 $ hg fatelog
687 o 019fadeab383
688 o 019fadeab383
688 |
689 |
689 | @ fdf9bde5129a
690 | @ fdf9bde5129a
690 |/
691 |/
691 o ea207398892e
692 o ea207398892e
692
693
693 Predecessors template should the predecessors as we force their display with
694 Predecessors template should the predecessors as we force their display with
694 --hidden
695 --hidden
695 $ hg tlog --hidden
696 $ hg tlog --hidden
696 o 019fadeab383
697 o 019fadeab383
697 | Predecessors: 3:65b757b745b9
698 | Predecessors: 3:65b757b745b9
698 | semi-colon: 3:65b757b745b9
699 | semi-colon: 3:65b757b745b9
699 | json: ["65b757b745b935093c87a2bccd877521cccffcbd"]
700 | json: ["65b757b745b935093c87a2bccd877521cccffcbd"]
700 | map: 3:65b757b745b935093c87a2bccd877521cccffcbd
701 | map: 3:65b757b745b935093c87a2bccd877521cccffcbd
701 | x 65b757b745b9
702 | x 65b757b745b9
702 |/ Predecessors: 1:471f378eab4c
703 |/ Predecessors: 1:471f378eab4c
703 | semi-colon: 1:471f378eab4c
704 | semi-colon: 1:471f378eab4c
704 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
705 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
705 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
706 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
706 | Successors: 4:019fadeab383
707 | Successors: 4:019fadeab383
707 | multi-line: 4:019fadeab383
708 | multi-line: 4:019fadeab383
708 | json: [["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]]
709 | json: [["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]]
709 | @ fdf9bde5129a
710 | @ fdf9bde5129a
710 |/ Predecessors: 1:471f378eab4c
711 |/ Predecessors: 1:471f378eab4c
711 | semi-colon: 1:471f378eab4c
712 | semi-colon: 1:471f378eab4c
712 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
713 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
713 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
714 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
714 | x 471f378eab4c
715 | x 471f378eab4c
715 |/ Successors: 2:fdf9bde5129a; 3:65b757b745b9
716 |/ Successors: 2:fdf9bde5129a; 3:65b757b745b9
716 | multi-line: 2:fdf9bde5129a
717 | multi-line: 2:fdf9bde5129a
717 | multi-line: 3:65b757b745b9
718 | multi-line: 3:65b757b745b9
718 | json: [["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], ["65b757b745b935093c87a2bccd877521cccffcbd"]]
719 | json: [["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], ["65b757b745b935093c87a2bccd877521cccffcbd"]]
719 o ea207398892e
720 o ea207398892e
720
721
721
722
722 $ hg fatelog --hidden
723 $ hg fatelog --hidden
723 o 019fadeab383
724 o 019fadeab383
724 |
725 |
725 | x 65b757b745b9
726 | x 65b757b745b9
726 |/ Obsfate: rewritten as 4:019fadeab383 by test;
727 |/ Obsfate: rewritten as 4:019fadeab383 by test (at 1970-01-01 00:00 +0000);
727 | @ fdf9bde5129a
728 | @ fdf9bde5129a
728 |/
729 |/
729 | x 471f378eab4c
730 | x 471f378eab4c
730 |/ Obsfate: rewritten as 2:fdf9bde5129a by test; rewritten as 3:65b757b745b9 by test;
731 |/ Obsfate: rewritten as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000); rewritten as 3:65b757b745b9 by test (at 1970-01-01 00:00 +0000);
731 o ea207398892e
732 o ea207398892e
732
733
733
734
734 $ hg fatelogjson --hidden
735 $ hg fatelogjson --hidden
735 o 019fadeab383
736 o 019fadeab383
736 |
737 |
737 | x 65b757b745b9
738 | x 65b757b745b9
738 |/ Obsfate: [{"markers": [["65b757b745b935093c87a2bccd877521cccffcbd", ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]}]
739 |/ Obsfate: [{"markers": [["65b757b745b935093c87a2bccd877521cccffcbd", ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]}]
739 | @ fdf9bde5129a
740 | @ fdf9bde5129a
740 |/
741 |/
741 | x 471f378eab4c
742 | x 471f378eab4c
742 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"]}, {"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["65b757b745b935093c87a2bccd877521cccffcbd"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["65b757b745b935093c87a2bccd877521cccffcbd"]}]
743 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"]}, {"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["65b757b745b935093c87a2bccd877521cccffcbd"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["65b757b745b935093c87a2bccd877521cccffcbd"]}]
743 o ea207398892e
744 o ea207398892e
744
745
745
746
746 Test templates with amended + folded commit
747 Test templates with amended + folded commit
747 ===========================================
748 ===========================================
748
749
749 Test setup
750 Test setup
750 ----------
751 ----------
751
752
752 $ hg init $TESTTMP/templates-local-amend-fold
753 $ hg init $TESTTMP/templates-local-amend-fold
753 $ cd $TESTTMP/templates-local-amend-fold
754 $ cd $TESTTMP/templates-local-amend-fold
754 $ mkcommit ROOT
755 $ mkcommit ROOT
755 $ mkcommit A0
756 $ mkcommit A0
756 $ mkcommit B0
757 $ mkcommit B0
757 $ hg commit --amend -m "B1"
758 $ hg commit --amend -m "B1"
758 $ hg log --hidden -G
759 $ hg log --hidden -G
759 @ changeset: 3:b7ea6d14e664
760 @ changeset: 3:b7ea6d14e664
760 | tag: tip
761 | tag: tip
761 | parent: 1:471f378eab4c
762 | parent: 1:471f378eab4c
762 | user: test
763 | user: test
763 | date: Thu Jan 01 00:00:00 1970 +0000
764 | date: Thu Jan 01 00:00:00 1970 +0000
764 | summary: B1
765 | summary: B1
765 |
766 |
766 | x changeset: 2:0dec01379d3b
767 | x changeset: 2:0dec01379d3b
767 |/ user: test
768 |/ user: test
768 | date: Thu Jan 01 00:00:00 1970 +0000
769 | date: Thu Jan 01 00:00:00 1970 +0000
769 | summary: B0
770 | summary: B0
770 |
771 |
771 o changeset: 1:471f378eab4c
772 o changeset: 1:471f378eab4c
772 | user: test
773 | user: test
773 | date: Thu Jan 01 00:00:00 1970 +0000
774 | date: Thu Jan 01 00:00:00 1970 +0000
774 | summary: A0
775 | summary: A0
775 |
776 |
776 o changeset: 0:ea207398892e
777 o changeset: 0:ea207398892e
777 user: test
778 user: test
778 date: Thu Jan 01 00:00:00 1970 +0000
779 date: Thu Jan 01 00:00:00 1970 +0000
779 summary: ROOT
780 summary: ROOT
780
781
781 # Simulate a fold
782 # Simulate a fold
782 $ hg up -r "desc(ROOT)"
783 $ hg up -r "desc(ROOT)"
783 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
784 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
784 $ echo "A0" > A0
785 $ echo "A0" > A0
785 $ echo "B0" > B0
786 $ echo "B0" > B0
786 $ hg commit -A -m "C0"
787 $ hg commit -A -m "C0"
787 adding A0
788 adding A0
788 adding B0
789 adding B0
789 created new head
790 created new head
790 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(C0)"`
791 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(C0)"`
791 obsoleted 1 changesets
792 obsoleted 1 changesets
792 $ hg debugobsolete `getid "desc(B1)"` `getid "desc(C0)"`
793 $ hg debugobsolete `getid "desc(B1)"` `getid "desc(C0)"`
793 obsoleted 1 changesets
794 obsoleted 1 changesets
794
795
795 $ hg log --hidden -G
796 $ hg log --hidden -G
796 @ changeset: 4:eb5a0daa2192
797 @ changeset: 4:eb5a0daa2192
797 | tag: tip
798 | tag: tip
798 | parent: 0:ea207398892e
799 | parent: 0:ea207398892e
799 | user: test
800 | user: test
800 | date: Thu Jan 01 00:00:00 1970 +0000
801 | date: Thu Jan 01 00:00:00 1970 +0000
801 | summary: C0
802 | summary: C0
802 |
803 |
803 | x changeset: 3:b7ea6d14e664
804 | x changeset: 3:b7ea6d14e664
804 | | parent: 1:471f378eab4c
805 | | parent: 1:471f378eab4c
805 | | user: test
806 | | user: test
806 | | date: Thu Jan 01 00:00:00 1970 +0000
807 | | date: Thu Jan 01 00:00:00 1970 +0000
807 | | summary: B1
808 | | summary: B1
808 | |
809 | |
809 | | x changeset: 2:0dec01379d3b
810 | | x changeset: 2:0dec01379d3b
810 | |/ user: test
811 | |/ user: test
811 | | date: Thu Jan 01 00:00:00 1970 +0000
812 | | date: Thu Jan 01 00:00:00 1970 +0000
812 | | summary: B0
813 | | summary: B0
813 | |
814 | |
814 | x changeset: 1:471f378eab4c
815 | x changeset: 1:471f378eab4c
815 |/ user: test
816 |/ user: test
816 | date: Thu Jan 01 00:00:00 1970 +0000
817 | date: Thu Jan 01 00:00:00 1970 +0000
817 | summary: A0
818 | summary: A0
818 |
819 |
819 o changeset: 0:ea207398892e
820 o changeset: 0:ea207398892e
820 user: test
821 user: test
821 date: Thu Jan 01 00:00:00 1970 +0000
822 date: Thu Jan 01 00:00:00 1970 +0000
822 summary: ROOT
823 summary: ROOT
823
824
824 Check templates
825 Check templates
825 ---------------
826 ---------------
826
827
827 $ hg up 'desc(A0)' --hidden
828 $ hg up 'desc(A0)' --hidden
828 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
829 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
829
830
830 Predecessors template should show current revision as it is the working copy
831 Predecessors template should show current revision as it is the working copy
831 $ hg tlog
832 $ hg tlog
832 o eb5a0daa2192
833 o eb5a0daa2192
833 | Predecessors: 1:471f378eab4c
834 | Predecessors: 1:471f378eab4c
834 | semi-colon: 1:471f378eab4c
835 | semi-colon: 1:471f378eab4c
835 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
836 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
836 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
837 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
837 | @ 471f378eab4c
838 | @ 471f378eab4c
838 |/ Successors: 4:eb5a0daa2192
839 |/ Successors: 4:eb5a0daa2192
839 | multi-line: 4:eb5a0daa2192
840 | multi-line: 4:eb5a0daa2192
840 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
841 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
841 o ea207398892e
842 o ea207398892e
842
843
843
844
844 $ hg fatelog
845 $ hg fatelog
845 o eb5a0daa2192
846 o eb5a0daa2192
846 |
847 |
847 | @ 471f378eab4c
848 | @ 471f378eab4c
848 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test;
849 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
849 o ea207398892e
850 o ea207398892e
850
851
851 $ hg up 'desc(B0)' --hidden
852 $ hg up 'desc(B0)' --hidden
852 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
853 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
853
854
854 Predecessors template should both predecessors as they are visible
855 Predecessors template should both predecessors as they are visible
855 $ hg tlog
856 $ hg tlog
856 o eb5a0daa2192
857 o eb5a0daa2192
857 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
858 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
858 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
859 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
859 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
860 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
860 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
861 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
861 | @ 0dec01379d3b
862 | @ 0dec01379d3b
862 | | Successors: 4:eb5a0daa2192
863 | | Successors: 4:eb5a0daa2192
863 | | multi-line: 4:eb5a0daa2192
864 | | multi-line: 4:eb5a0daa2192
864 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
865 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
865 | x 471f378eab4c
866 | x 471f378eab4c
866 |/ Successors: 4:eb5a0daa2192
867 |/ Successors: 4:eb5a0daa2192
867 | multi-line: 4:eb5a0daa2192
868 | multi-line: 4:eb5a0daa2192
868 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
869 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
869 o ea207398892e
870 o ea207398892e
870
871
871
872
872 $ hg fatelog
873 $ hg fatelog
873 o eb5a0daa2192
874 o eb5a0daa2192
874 |
875 |
875 | @ 0dec01379d3b
876 | @ 0dec01379d3b
876 | | Obsfate: rewritten as 4:eb5a0daa2192 by test;
877 | | Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
877 | x 471f378eab4c
878 | x 471f378eab4c
878 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test;
879 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
879 o ea207398892e
880 o ea207398892e
880
881
881 $ hg up 'desc(B1)' --hidden
882 $ hg up 'desc(B1)' --hidden
882 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
883 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
883
884
884 Predecessors template should both predecessors as they are visible
885 Predecessors template should both predecessors as they are visible
885 $ hg tlog
886 $ hg tlog
886 o eb5a0daa2192
887 o eb5a0daa2192
887 | Predecessors: 1:471f378eab4c 3:b7ea6d14e664
888 | Predecessors: 1:471f378eab4c 3:b7ea6d14e664
888 | semi-colon: 1:471f378eab4c; 3:b7ea6d14e664
889 | semi-colon: 1:471f378eab4c; 3:b7ea6d14e664
889 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874", "b7ea6d14e664bdc8922221f7992631b50da3fb07"]
890 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874", "b7ea6d14e664bdc8922221f7992631b50da3fb07"]
890 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874 3:b7ea6d14e664bdc8922221f7992631b50da3fb07
891 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874 3:b7ea6d14e664bdc8922221f7992631b50da3fb07
891 | @ b7ea6d14e664
892 | @ b7ea6d14e664
892 | | Successors: 4:eb5a0daa2192
893 | | Successors: 4:eb5a0daa2192
893 | | multi-line: 4:eb5a0daa2192
894 | | multi-line: 4:eb5a0daa2192
894 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
895 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
895 | x 471f378eab4c
896 | x 471f378eab4c
896 |/ Successors: 4:eb5a0daa2192
897 |/ Successors: 4:eb5a0daa2192
897 | multi-line: 4:eb5a0daa2192
898 | multi-line: 4:eb5a0daa2192
898 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
899 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
899 o ea207398892e
900 o ea207398892e
900
901
901
902
902 $ hg fatelog
903 $ hg fatelog
903 o eb5a0daa2192
904 o eb5a0daa2192
904 |
905 |
905 | @ b7ea6d14e664
906 | @ b7ea6d14e664
906 | | Obsfate: rewritten as 4:eb5a0daa2192 by test;
907 | | Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
907 | x 471f378eab4c
908 | x 471f378eab4c
908 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test;
909 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
909 o ea207398892e
910 o ea207398892e
910
911
911 $ hg up 'desc(C0)'
912 $ hg up 'desc(C0)'
912 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
913 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
913
914
914 Predecessors template should show no predecessors as they are both non visible
915 Predecessors template should show no predecessors as they are both non visible
915 $ hg tlog
916 $ hg tlog
916 @ eb5a0daa2192
917 @ eb5a0daa2192
917 |
918 |
918 o ea207398892e
919 o ea207398892e
919
920
920
921
921 $ hg fatelog
922 $ hg fatelog
922 @ eb5a0daa2192
923 @ eb5a0daa2192
923 |
924 |
924 o ea207398892e
925 o ea207398892e
925
926
926 Predecessors template should show all predecessors as we force their display
927 Predecessors template should show all predecessors as we force their display
927 with --hidden
928 with --hidden
928 $ hg tlog --hidden
929 $ hg tlog --hidden
929 @ eb5a0daa2192
930 @ eb5a0daa2192
930 | Predecessors: 1:471f378eab4c 3:b7ea6d14e664
931 | Predecessors: 1:471f378eab4c 3:b7ea6d14e664
931 | semi-colon: 1:471f378eab4c; 3:b7ea6d14e664
932 | semi-colon: 1:471f378eab4c; 3:b7ea6d14e664
932 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874", "b7ea6d14e664bdc8922221f7992631b50da3fb07"]
933 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874", "b7ea6d14e664bdc8922221f7992631b50da3fb07"]
933 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874 3:b7ea6d14e664bdc8922221f7992631b50da3fb07
934 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874 3:b7ea6d14e664bdc8922221f7992631b50da3fb07
934 | x b7ea6d14e664
935 | x b7ea6d14e664
935 | | Predecessors: 2:0dec01379d3b
936 | | Predecessors: 2:0dec01379d3b
936 | | semi-colon: 2:0dec01379d3b
937 | | semi-colon: 2:0dec01379d3b
937 | | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
938 | | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
938 | | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
939 | | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
939 | | Successors: 4:eb5a0daa2192
940 | | Successors: 4:eb5a0daa2192
940 | | multi-line: 4:eb5a0daa2192
941 | | multi-line: 4:eb5a0daa2192
941 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
942 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
942 | | x 0dec01379d3b
943 | | x 0dec01379d3b
943 | |/ Successors: 3:b7ea6d14e664
944 | |/ Successors: 3:b7ea6d14e664
944 | | multi-line: 3:b7ea6d14e664
945 | | multi-line: 3:b7ea6d14e664
945 | | json: [["b7ea6d14e664bdc8922221f7992631b50da3fb07"]]
946 | | json: [["b7ea6d14e664bdc8922221f7992631b50da3fb07"]]
946 | x 471f378eab4c
947 | x 471f378eab4c
947 |/ Successors: 4:eb5a0daa2192
948 |/ Successors: 4:eb5a0daa2192
948 | multi-line: 4:eb5a0daa2192
949 | multi-line: 4:eb5a0daa2192
949 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
950 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
950 o ea207398892e
951 o ea207398892e
951
952
952
953
953 $ hg fatelog --hidden
954 $ hg fatelog --hidden
954 @ eb5a0daa2192
955 @ eb5a0daa2192
955 |
956 |
956 | x b7ea6d14e664
957 | x b7ea6d14e664
957 | | Obsfate: rewritten as 4:eb5a0daa2192 by test;
958 | | Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
958 | | x 0dec01379d3b
959 | | x 0dec01379d3b
959 | |/ Obsfate: rewritten as 3:b7ea6d14e664 by test;
960 | |/ Obsfate: rewritten as 3:b7ea6d14e664 by test (at 1970-01-01 00:00 +0000);
960 | x 471f378eab4c
961 | x 471f378eab4c
961 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test;
962 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
962 o ea207398892e
963 o ea207398892e
963
964
964
965
965 $ hg fatelogjson --hidden
966 $ hg fatelogjson --hidden
966 @ eb5a0daa2192
967 @ eb5a0daa2192
967 |
968 |
968 | x b7ea6d14e664
969 | x b7ea6d14e664
969 | | Obsfate: [{"markers": [["b7ea6d14e664bdc8922221f7992631b50da3fb07", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
970 | | Obsfate: [{"markers": [["b7ea6d14e664bdc8922221f7992631b50da3fb07", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
970 | | x 0dec01379d3b
971 | | x 0dec01379d3b
971 | |/ Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["b7ea6d14e664bdc8922221f7992631b50da3fb07"]}]
972 | |/ Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["b7ea6d14e664bdc8922221f7992631b50da3fb07"]}]
972 | x 471f378eab4c
973 | x 471f378eab4c
973 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
974 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
974 o ea207398892e
975 o ea207398892e
975
976
976
977
977 Test template with pushed and pulled obs markers
978 Test template with pushed and pulled obs markers
978 ================================================
979 ================================================
979
980
980 Test setup
981 Test setup
981 ----------
982 ----------
982
983
983 $ hg init $TESTTMP/templates-local-remote-markers-1
984 $ hg init $TESTTMP/templates-local-remote-markers-1
984 $ cd $TESTTMP/templates-local-remote-markers-1
985 $ cd $TESTTMP/templates-local-remote-markers-1
985 $ mkcommit ROOT
986 $ mkcommit ROOT
986 $ mkcommit A0
987 $ mkcommit A0
987 $ hg clone $TESTTMP/templates-local-remote-markers-1 $TESTTMP/templates-local-remote-markers-2
988 $ hg clone $TESTTMP/templates-local-remote-markers-1 $TESTTMP/templates-local-remote-markers-2
988 updating to branch default
989 updating to branch default
989 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
990 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
990 $ cd $TESTTMP/templates-local-remote-markers-2
991 $ cd $TESTTMP/templates-local-remote-markers-2
991 $ hg log --hidden -G
992 $ hg log --hidden -G
992 @ changeset: 1:471f378eab4c
993 @ changeset: 1:471f378eab4c
993 | tag: tip
994 | tag: tip
994 | user: test
995 | user: test
995 | date: Thu Jan 01 00:00:00 1970 +0000
996 | date: Thu Jan 01 00:00:00 1970 +0000
996 | summary: A0
997 | summary: A0
997 |
998 |
998 o changeset: 0:ea207398892e
999 o changeset: 0:ea207398892e
999 user: test
1000 user: test
1000 date: Thu Jan 01 00:00:00 1970 +0000
1001 date: Thu Jan 01 00:00:00 1970 +0000
1001 summary: ROOT
1002 summary: ROOT
1002
1003
1003 $ cd $TESTTMP/templates-local-remote-markers-1
1004 $ cd $TESTTMP/templates-local-remote-markers-1
1004 $ hg commit --amend -m "A1"
1005 $ hg commit --amend -m "A1"
1005 $ hg commit --amend -m "A2"
1006 $ hg commit --amend -m "A2"
1006 $ hg log --hidden -G
1007 $ hg log --hidden -G
1007 @ changeset: 3:7a230b46bf61
1008 @ changeset: 3:7a230b46bf61
1008 | tag: tip
1009 | tag: tip
1009 | parent: 0:ea207398892e
1010 | parent: 0:ea207398892e
1010 | user: test
1011 | user: test
1011 | date: Thu Jan 01 00:00:00 1970 +0000
1012 | date: Thu Jan 01 00:00:00 1970 +0000
1012 | summary: A2
1013 | summary: A2
1013 |
1014 |
1014 | x changeset: 2:fdf9bde5129a
1015 | x changeset: 2:fdf9bde5129a
1015 |/ parent: 0:ea207398892e
1016 |/ parent: 0:ea207398892e
1016 | user: test
1017 | user: test
1017 | date: Thu Jan 01 00:00:00 1970 +0000
1018 | date: Thu Jan 01 00:00:00 1970 +0000
1018 | summary: A1
1019 | summary: A1
1019 |
1020 |
1020 | x changeset: 1:471f378eab4c
1021 | x changeset: 1:471f378eab4c
1021 |/ user: test
1022 |/ user: test
1022 | date: Thu Jan 01 00:00:00 1970 +0000
1023 | date: Thu Jan 01 00:00:00 1970 +0000
1023 | summary: A0
1024 | summary: A0
1024 |
1025 |
1025 o changeset: 0:ea207398892e
1026 o changeset: 0:ea207398892e
1026 user: test
1027 user: test
1027 date: Thu Jan 01 00:00:00 1970 +0000
1028 date: Thu Jan 01 00:00:00 1970 +0000
1028 summary: ROOT
1029 summary: ROOT
1029
1030
1030 $ cd $TESTTMP/templates-local-remote-markers-2
1031 $ cd $TESTTMP/templates-local-remote-markers-2
1031 $ hg pull
1032 $ hg pull
1032 pulling from $TESTTMP/templates-local-remote-markers-1 (glob)
1033 pulling from $TESTTMP/templates-local-remote-markers-1 (glob)
1033 searching for changes
1034 searching for changes
1034 adding changesets
1035 adding changesets
1035 adding manifests
1036 adding manifests
1036 adding file changes
1037 adding file changes
1037 added 1 changesets with 0 changes to 1 files (+1 heads)
1038 added 1 changesets with 0 changes to 1 files (+1 heads)
1038 2 new obsolescence markers
1039 2 new obsolescence markers
1039 obsoleted 1 changesets
1040 obsoleted 1 changesets
1040 (run 'hg heads' to see heads, 'hg merge' to merge)
1041 (run 'hg heads' to see heads, 'hg merge' to merge)
1041 $ hg log --hidden -G
1042 $ hg log --hidden -G
1042 o changeset: 2:7a230b46bf61
1043 o changeset: 2:7a230b46bf61
1043 | tag: tip
1044 | tag: tip
1044 | parent: 0:ea207398892e
1045 | parent: 0:ea207398892e
1045 | user: test
1046 | user: test
1046 | date: Thu Jan 01 00:00:00 1970 +0000
1047 | date: Thu Jan 01 00:00:00 1970 +0000
1047 | summary: A2
1048 | summary: A2
1048 |
1049 |
1049 | @ changeset: 1:471f378eab4c
1050 | @ changeset: 1:471f378eab4c
1050 |/ user: test
1051 |/ user: test
1051 | date: Thu Jan 01 00:00:00 1970 +0000
1052 | date: Thu Jan 01 00:00:00 1970 +0000
1052 | summary: A0
1053 | summary: A0
1053 |
1054 |
1054 o changeset: 0:ea207398892e
1055 o changeset: 0:ea207398892e
1055 user: test
1056 user: test
1056 date: Thu Jan 01 00:00:00 1970 +0000
1057 date: Thu Jan 01 00:00:00 1970 +0000
1057 summary: ROOT
1058 summary: ROOT
1058
1059
1059
1060
1060 $ hg debugobsolete
1061 $ hg debugobsolete
1061 471f378eab4c5e25f6c77f785b27c936efb22874 fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1062 471f378eab4c5e25f6c77f785b27c936efb22874 fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1062 fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e 7a230b46bf61e50b30308c6cfd7bd1269ef54702 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1063 fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e 7a230b46bf61e50b30308c6cfd7bd1269ef54702 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1063
1064
1064 Check templates
1065 Check templates
1065 ---------------
1066 ---------------
1066
1067
1067 Predecessors template should show current revision as it is the working copy
1068 Predecessors template should show current revision as it is the working copy
1068 $ hg tlog
1069 $ hg tlog
1069 o 7a230b46bf61
1070 o 7a230b46bf61
1070 | Predecessors: 1:471f378eab4c
1071 | Predecessors: 1:471f378eab4c
1071 | semi-colon: 1:471f378eab4c
1072 | semi-colon: 1:471f378eab4c
1072 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1073 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1073 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1074 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1074 | @ 471f378eab4c
1075 | @ 471f378eab4c
1075 |/ Successors: 2:7a230b46bf61
1076 |/ Successors: 2:7a230b46bf61
1076 | multi-line: 2:7a230b46bf61
1077 | multi-line: 2:7a230b46bf61
1077 | json: [["7a230b46bf61e50b30308c6cfd7bd1269ef54702"]]
1078 | json: [["7a230b46bf61e50b30308c6cfd7bd1269ef54702"]]
1078 o ea207398892e
1079 o ea207398892e
1079
1080
1080
1081
1081 $ hg fatelog
1082 $ hg fatelog
1082 o 7a230b46bf61
1083 o 7a230b46bf61
1083 |
1084 |
1084 | @ 471f378eab4c
1085 | @ 471f378eab4c
1085 |/ Obsfate: rewritten as 2:7a230b46bf61 by test;
1086 |/ Obsfate: rewritten as 2:7a230b46bf61 by test (at 1970-01-01 00:00 +0000);
1086 o ea207398892e
1087 o ea207398892e
1087
1088
1088 $ hg up 'desc(A2)'
1089 $ hg up 'desc(A2)'
1089 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1090 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1090
1091
1091 Predecessors template should show no predecessors as they are non visible
1092 Predecessors template should show no predecessors as they are non visible
1092 $ hg tlog
1093 $ hg tlog
1093 @ 7a230b46bf61
1094 @ 7a230b46bf61
1094 |
1095 |
1095 o ea207398892e
1096 o ea207398892e
1096
1097
1097
1098
1098 $ hg fatelog
1099 $ hg fatelog
1099 @ 7a230b46bf61
1100 @ 7a230b46bf61
1100 |
1101 |
1101 o ea207398892e
1102 o ea207398892e
1102
1103
1103 Predecessors template should show all predecessors as we force their display
1104 Predecessors template should show all predecessors as we force their display
1104 with --hidden
1105 with --hidden
1105 $ hg tlog --hidden
1106 $ hg tlog --hidden
1106 @ 7a230b46bf61
1107 @ 7a230b46bf61
1107 | Predecessors: 1:471f378eab4c
1108 | Predecessors: 1:471f378eab4c
1108 | semi-colon: 1:471f378eab4c
1109 | semi-colon: 1:471f378eab4c
1109 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1110 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1110 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1111 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1111 | x 471f378eab4c
1112 | x 471f378eab4c
1112 |/ Successors: 2:7a230b46bf61
1113 |/ Successors: 2:7a230b46bf61
1113 | multi-line: 2:7a230b46bf61
1114 | multi-line: 2:7a230b46bf61
1114 | json: [["7a230b46bf61e50b30308c6cfd7bd1269ef54702"]]
1115 | json: [["7a230b46bf61e50b30308c6cfd7bd1269ef54702"]]
1115 o ea207398892e
1116 o ea207398892e
1116
1117
1117
1118
1118 $ hg fatelog --hidden
1119 $ hg fatelog --hidden
1119 @ 7a230b46bf61
1120 @ 7a230b46bf61
1120 |
1121 |
1121 | x 471f378eab4c
1122 | x 471f378eab4c
1122 |/ Obsfate: rewritten as 2:7a230b46bf61 by test;
1123 |/ Obsfate: rewritten as 2:7a230b46bf61 by test (at 1970-01-01 00:00 +0000);
1123 o ea207398892e
1124 o ea207398892e
1124
1125
1125
1126
1126 Test template with obsmarkers cycle
1127 Test template with obsmarkers cycle
1127 ===================================
1128 ===================================
1128
1129
1129 Test setup
1130 Test setup
1130 ----------
1131 ----------
1131
1132
1132 $ hg init $TESTTMP/templates-local-cycle
1133 $ hg init $TESTTMP/templates-local-cycle
1133 $ cd $TESTTMP/templates-local-cycle
1134 $ cd $TESTTMP/templates-local-cycle
1134 $ mkcommit ROOT
1135 $ mkcommit ROOT
1135 $ mkcommit A0
1136 $ mkcommit A0
1136 $ mkcommit B0
1137 $ mkcommit B0
1137 $ hg up -r 0
1138 $ hg up -r 0
1138 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1139 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1139 $ mkcommit C0
1140 $ mkcommit C0
1140 created new head
1141 created new head
1141
1142
1142 Create the cycle
1143 Create the cycle
1143
1144
1144 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(B0)"`
1145 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(B0)"`
1145 obsoleted 1 changesets
1146 obsoleted 1 changesets
1146 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(C0)"`
1147 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(C0)"`
1147 obsoleted 1 changesets
1148 obsoleted 1 changesets
1148 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(A0)"`
1149 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(A0)"`
1149
1150
1150 Check templates
1151 Check templates
1151 ---------------
1152 ---------------
1152
1153
1153 $ hg tlog
1154 $ hg tlog
1154 @ f897c6137566
1155 @ f897c6137566
1155 |
1156 |
1156 o ea207398892e
1157 o ea207398892e
1157
1158
1158
1159
1159 $ hg fatelog
1160 $ hg fatelog
1160 @ f897c6137566
1161 @ f897c6137566
1161 |
1162 |
1162 o ea207398892e
1163 o ea207398892e
1163
1164
1164
1165
1165 $ hg up -r "desc(B0)" --hidden
1166 $ hg up -r "desc(B0)" --hidden
1166 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1167 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1167 $ hg tlog
1168 $ hg tlog
1168 o f897c6137566
1169 o f897c6137566
1169 | Predecessors: 2:0dec01379d3b
1170 | Predecessors: 2:0dec01379d3b
1170 | semi-colon: 2:0dec01379d3b
1171 | semi-colon: 2:0dec01379d3b
1171 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1172 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1172 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1173 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1173 | @ 0dec01379d3b
1174 | @ 0dec01379d3b
1174 | | Predecessors: 1:471f378eab4c
1175 | | Predecessors: 1:471f378eab4c
1175 | | semi-colon: 1:471f378eab4c
1176 | | semi-colon: 1:471f378eab4c
1176 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1177 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1177 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1178 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1178 | | Successors: 3:f897c6137566; 1:471f378eab4c
1179 | | Successors: 3:f897c6137566; 1:471f378eab4c
1179 | | multi-line: 3:f897c6137566
1180 | | multi-line: 3:f897c6137566
1180 | | multi-line: 1:471f378eab4c
1181 | | multi-line: 1:471f378eab4c
1181 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
1182 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
1182 | x 471f378eab4c
1183 | x 471f378eab4c
1183 |/ Predecessors: 2:0dec01379d3b
1184 |/ Predecessors: 2:0dec01379d3b
1184 | semi-colon: 2:0dec01379d3b
1185 | semi-colon: 2:0dec01379d3b
1185 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1186 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1186 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1187 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1187 | Successors: 2:0dec01379d3b
1188 | Successors: 2:0dec01379d3b
1188 | multi-line: 2:0dec01379d3b
1189 | multi-line: 2:0dec01379d3b
1189 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
1190 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
1190 o ea207398892e
1191 o ea207398892e
1191
1192
1192
1193
1193 $ hg fatelog
1194 $ hg fatelog
1194 o f897c6137566
1195 o f897c6137566
1195 |
1196 |
1196 | @ 0dec01379d3b
1197 | @ 0dec01379d3b
1197 | | Obsfate: rewritten as 3:f897c6137566 by test; rewritten as 1:471f378eab4c by test;
1198 | | Obsfate: rewritten as 3:f897c6137566 by test (at 1970-01-01 00:00 +0000); rewritten as 1:471f378eab4c by test (at 1970-01-01 00:00 +0000);
1198 | x 471f378eab4c
1199 | x 471f378eab4c
1199 |/ Obsfate: rewritten as 2:0dec01379d3b by test;
1200 |/ Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000);
1200 o ea207398892e
1201 o ea207398892e
1201
1202
1202
1203
1203 $ hg up -r "desc(A0)" --hidden
1204 $ hg up -r "desc(A0)" --hidden
1204 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1205 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1205 $ hg tlog
1206 $ hg tlog
1206 o f897c6137566
1207 o f897c6137566
1207 | Predecessors: 1:471f378eab4c
1208 | Predecessors: 1:471f378eab4c
1208 | semi-colon: 1:471f378eab4c
1209 | semi-colon: 1:471f378eab4c
1209 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1210 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1210 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1211 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1211 | @ 471f378eab4c
1212 | @ 471f378eab4c
1212 |/
1213 |/
1213 o ea207398892e
1214 o ea207398892e
1214
1215
1215
1216
1216 $ hg fatelog
1217 $ hg fatelog
1217 o f897c6137566
1218 o f897c6137566
1218 |
1219 |
1219 | @ 471f378eab4c
1220 | @ 471f378eab4c
1220 |/
1221 |/
1221 o ea207398892e
1222 o ea207398892e
1222
1223
1223
1224
1224 $ hg up -r "desc(ROOT)" --hidden
1225 $ hg up -r "desc(ROOT)" --hidden
1225 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1226 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1226 $ hg tlog
1227 $ hg tlog
1227 o f897c6137566
1228 o f897c6137566
1228 |
1229 |
1229 @ ea207398892e
1230 @ ea207398892e
1230
1231
1231
1232
1232 $ hg fatelog
1233 $ hg fatelog
1233 o f897c6137566
1234 o f897c6137566
1234 |
1235 |
1235 @ ea207398892e
1236 @ ea207398892e
1236
1237
1237
1238
1238 $ hg tlog --hidden
1239 $ hg tlog --hidden
1239 o f897c6137566
1240 o f897c6137566
1240 | Predecessors: 2:0dec01379d3b
1241 | Predecessors: 2:0dec01379d3b
1241 | semi-colon: 2:0dec01379d3b
1242 | semi-colon: 2:0dec01379d3b
1242 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1243 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1243 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1244 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1244 | x 0dec01379d3b
1245 | x 0dec01379d3b
1245 | | Predecessors: 1:471f378eab4c
1246 | | Predecessors: 1:471f378eab4c
1246 | | semi-colon: 1:471f378eab4c
1247 | | semi-colon: 1:471f378eab4c
1247 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1248 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1248 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1249 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1249 | | Successors: 3:f897c6137566; 1:471f378eab4c
1250 | | Successors: 3:f897c6137566; 1:471f378eab4c
1250 | | multi-line: 3:f897c6137566
1251 | | multi-line: 3:f897c6137566
1251 | | multi-line: 1:471f378eab4c
1252 | | multi-line: 1:471f378eab4c
1252 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
1253 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
1253 | x 471f378eab4c
1254 | x 471f378eab4c
1254 |/ Predecessors: 2:0dec01379d3b
1255 |/ Predecessors: 2:0dec01379d3b
1255 | semi-colon: 2:0dec01379d3b
1256 | semi-colon: 2:0dec01379d3b
1256 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1257 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1257 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1258 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1258 | Successors: 2:0dec01379d3b
1259 | Successors: 2:0dec01379d3b
1259 | multi-line: 2:0dec01379d3b
1260 | multi-line: 2:0dec01379d3b
1260 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
1261 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
1261 @ ea207398892e
1262 @ ea207398892e
1262
1263
1263 Test template with split + divergence with cycles
1264 Test template with split + divergence with cycles
1264 =================================================
1265 =================================================
1265
1266
1266 $ hg log -G
1267 $ hg log -G
1267 o changeset: 3:f897c6137566
1268 o changeset: 3:f897c6137566
1268 | tag: tip
1269 | tag: tip
1269 | parent: 0:ea207398892e
1270 | parent: 0:ea207398892e
1270 | user: test
1271 | user: test
1271 | date: Thu Jan 01 00:00:00 1970 +0000
1272 | date: Thu Jan 01 00:00:00 1970 +0000
1272 | summary: C0
1273 | summary: C0
1273 |
1274 |
1274 @ changeset: 0:ea207398892e
1275 @ changeset: 0:ea207398892e
1275 user: test
1276 user: test
1276 date: Thu Jan 01 00:00:00 1970 +0000
1277 date: Thu Jan 01 00:00:00 1970 +0000
1277 summary: ROOT
1278 summary: ROOT
1278
1279
1279 $ hg up
1280 $ hg up
1280 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1281 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1281
1282
1282 Create a commit with three files
1283 Create a commit with three files
1283 $ touch A B C
1284 $ touch A B C
1284 $ hg commit -A -m "Add A,B,C" A B C
1285 $ hg commit -A -m "Add A,B,C" A B C
1285
1286
1286 Split it
1287 Split it
1287 $ hg up 3
1288 $ hg up 3
1288 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
1289 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
1289 $ touch A
1290 $ touch A
1290 $ hg commit -A -m "Add A,B,C" A
1291 $ hg commit -A -m "Add A,B,C" A
1291 created new head
1292 created new head
1292
1293
1293 $ touch B
1294 $ touch B
1294 $ hg commit -A -m "Add A,B,C" B
1295 $ hg commit -A -m "Add A,B,C" B
1295
1296
1296 $ touch C
1297 $ touch C
1297 $ hg commit -A -m "Add A,B,C" C
1298 $ hg commit -A -m "Add A,B,C" C
1298
1299
1299 $ hg log -G
1300 $ hg log -G
1300 @ changeset: 7:ba2ed02b0c9a
1301 @ changeset: 7:ba2ed02b0c9a
1301 | tag: tip
1302 | tag: tip
1302 | user: test
1303 | user: test
1303 | date: Thu Jan 01 00:00:00 1970 +0000
1304 | date: Thu Jan 01 00:00:00 1970 +0000
1304 | summary: Add A,B,C
1305 | summary: Add A,B,C
1305 |
1306 |
1306 o changeset: 6:4a004186e638
1307 o changeset: 6:4a004186e638
1307 | user: test
1308 | user: test
1308 | date: Thu Jan 01 00:00:00 1970 +0000
1309 | date: Thu Jan 01 00:00:00 1970 +0000
1309 | summary: Add A,B,C
1310 | summary: Add A,B,C
1310 |
1311 |
1311 o changeset: 5:dd800401bd8c
1312 o changeset: 5:dd800401bd8c
1312 | parent: 3:f897c6137566
1313 | parent: 3:f897c6137566
1313 | user: test
1314 | user: test
1314 | date: Thu Jan 01 00:00:00 1970 +0000
1315 | date: Thu Jan 01 00:00:00 1970 +0000
1315 | summary: Add A,B,C
1316 | summary: Add A,B,C
1316 |
1317 |
1317 | o changeset: 4:9bd10a0775e4
1318 | o changeset: 4:9bd10a0775e4
1318 |/ user: test
1319 |/ user: test
1319 | date: Thu Jan 01 00:00:00 1970 +0000
1320 | date: Thu Jan 01 00:00:00 1970 +0000
1320 | summary: Add A,B,C
1321 | summary: Add A,B,C
1321 |
1322 |
1322 o changeset: 3:f897c6137566
1323 o changeset: 3:f897c6137566
1323 | parent: 0:ea207398892e
1324 | parent: 0:ea207398892e
1324 | user: test
1325 | user: test
1325 | date: Thu Jan 01 00:00:00 1970 +0000
1326 | date: Thu Jan 01 00:00:00 1970 +0000
1326 | summary: C0
1327 | summary: C0
1327 |
1328 |
1328 o changeset: 0:ea207398892e
1329 o changeset: 0:ea207398892e
1329 user: test
1330 user: test
1330 date: Thu Jan 01 00:00:00 1970 +0000
1331 date: Thu Jan 01 00:00:00 1970 +0000
1331 summary: ROOT
1332 summary: ROOT
1332
1333
1333 $ hg debugobsolete `getid "4"` `getid "5"` `getid "6"` `getid "7"`
1334 $ hg debugobsolete `getid "4"` `getid "5"` `getid "6"` `getid "7"`
1334 obsoleted 1 changesets
1335 obsoleted 1 changesets
1335 $ hg log -G
1336 $ hg log -G
1336 @ changeset: 7:ba2ed02b0c9a
1337 @ changeset: 7:ba2ed02b0c9a
1337 | tag: tip
1338 | tag: tip
1338 | user: test
1339 | user: test
1339 | date: Thu Jan 01 00:00:00 1970 +0000
1340 | date: Thu Jan 01 00:00:00 1970 +0000
1340 | summary: Add A,B,C
1341 | summary: Add A,B,C
1341 |
1342 |
1342 o changeset: 6:4a004186e638
1343 o changeset: 6:4a004186e638
1343 | user: test
1344 | user: test
1344 | date: Thu Jan 01 00:00:00 1970 +0000
1345 | date: Thu Jan 01 00:00:00 1970 +0000
1345 | summary: Add A,B,C
1346 | summary: Add A,B,C
1346 |
1347 |
1347 o changeset: 5:dd800401bd8c
1348 o changeset: 5:dd800401bd8c
1348 | parent: 3:f897c6137566
1349 | parent: 3:f897c6137566
1349 | user: test
1350 | user: test
1350 | date: Thu Jan 01 00:00:00 1970 +0000
1351 | date: Thu Jan 01 00:00:00 1970 +0000
1351 | summary: Add A,B,C
1352 | summary: Add A,B,C
1352 |
1353 |
1353 o changeset: 3:f897c6137566
1354 o changeset: 3:f897c6137566
1354 | parent: 0:ea207398892e
1355 | parent: 0:ea207398892e
1355 | user: test
1356 | user: test
1356 | date: Thu Jan 01 00:00:00 1970 +0000
1357 | date: Thu Jan 01 00:00:00 1970 +0000
1357 | summary: C0
1358 | summary: C0
1358 |
1359 |
1359 o changeset: 0:ea207398892e
1360 o changeset: 0:ea207398892e
1360 user: test
1361 user: test
1361 date: Thu Jan 01 00:00:00 1970 +0000
1362 date: Thu Jan 01 00:00:00 1970 +0000
1362 summary: ROOT
1363 summary: ROOT
1363
1364
1364 Diverge one of the splitted commit
1365 Diverge one of the splitted commit
1365
1366
1366 $ hg up 6
1367 $ hg up 6
1367 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1368 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1368 $ hg commit --amend -m "Add only B"
1369 $ hg commit --amend -m "Add only B"
1369
1370
1370 $ hg up 6 --hidden
1371 $ hg up 6 --hidden
1371 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1372 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1372 $ hg commit --amend -m "Add B only"
1373 $ hg commit --amend -m "Add B only"
1373
1374
1374 $ hg log -G
1375 $ hg log -G
1375 @ changeset: 9:0b997eb7ceee
1376 @ changeset: 9:0b997eb7ceee
1376 | tag: tip
1377 | tag: tip
1377 | parent: 5:dd800401bd8c
1378 | parent: 5:dd800401bd8c
1378 | user: test
1379 | user: test
1379 | date: Thu Jan 01 00:00:00 1970 +0000
1380 | date: Thu Jan 01 00:00:00 1970 +0000
1380 | instability: content-divergent
1381 | instability: content-divergent
1381 | summary: Add B only
1382 | summary: Add B only
1382 |
1383 |
1383 | o changeset: 8:b18bc8331526
1384 | o changeset: 8:b18bc8331526
1384 |/ parent: 5:dd800401bd8c
1385 |/ parent: 5:dd800401bd8c
1385 | user: test
1386 | user: test
1386 | date: Thu Jan 01 00:00:00 1970 +0000
1387 | date: Thu Jan 01 00:00:00 1970 +0000
1387 | instability: content-divergent
1388 | instability: content-divergent
1388 | summary: Add only B
1389 | summary: Add only B
1389 |
1390 |
1390 | o changeset: 7:ba2ed02b0c9a
1391 | o changeset: 7:ba2ed02b0c9a
1391 | | user: test
1392 | | user: test
1392 | | date: Thu Jan 01 00:00:00 1970 +0000
1393 | | date: Thu Jan 01 00:00:00 1970 +0000
1393 | | instability: orphan, content-divergent
1394 | | instability: orphan, content-divergent
1394 | | summary: Add A,B,C
1395 | | summary: Add A,B,C
1395 | |
1396 | |
1396 | x changeset: 6:4a004186e638
1397 | x changeset: 6:4a004186e638
1397 |/ user: test
1398 |/ user: test
1398 | date: Thu Jan 01 00:00:00 1970 +0000
1399 | date: Thu Jan 01 00:00:00 1970 +0000
1399 | summary: Add A,B,C
1400 | summary: Add A,B,C
1400 |
1401 |
1401 o changeset: 5:dd800401bd8c
1402 o changeset: 5:dd800401bd8c
1402 | parent: 3:f897c6137566
1403 | parent: 3:f897c6137566
1403 | user: test
1404 | user: test
1404 | date: Thu Jan 01 00:00:00 1970 +0000
1405 | date: Thu Jan 01 00:00:00 1970 +0000
1405 | instability: content-divergent
1406 | instability: content-divergent
1406 | summary: Add A,B,C
1407 | summary: Add A,B,C
1407 |
1408 |
1408 o changeset: 3:f897c6137566
1409 o changeset: 3:f897c6137566
1409 | parent: 0:ea207398892e
1410 | parent: 0:ea207398892e
1410 | user: test
1411 | user: test
1411 | date: Thu Jan 01 00:00:00 1970 +0000
1412 | date: Thu Jan 01 00:00:00 1970 +0000
1412 | summary: C0
1413 | summary: C0
1413 |
1414 |
1414 o changeset: 0:ea207398892e
1415 o changeset: 0:ea207398892e
1415 user: test
1416 user: test
1416 date: Thu Jan 01 00:00:00 1970 +0000
1417 date: Thu Jan 01 00:00:00 1970 +0000
1417 summary: ROOT
1418 summary: ROOT
1418
1419
1419
1420
1420 Check templates
1421 Check templates
1421 ---------------
1422 ---------------
1422
1423
1423 $ hg tlog
1424 $ hg tlog
1424 @ 0b997eb7ceee
1425 @ 0b997eb7ceee
1425 | Predecessors: 6:4a004186e638
1426 | Predecessors: 6:4a004186e638
1426 | semi-colon: 6:4a004186e638
1427 | semi-colon: 6:4a004186e638
1427 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1428 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1428 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1429 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1429 | o b18bc8331526
1430 | o b18bc8331526
1430 |/ Predecessors: 6:4a004186e638
1431 |/ Predecessors: 6:4a004186e638
1431 | semi-colon: 6:4a004186e638
1432 | semi-colon: 6:4a004186e638
1432 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1433 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1433 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1434 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1434 | o ba2ed02b0c9a
1435 | o ba2ed02b0c9a
1435 | |
1436 | |
1436 | x 4a004186e638
1437 | x 4a004186e638
1437 |/ Successors: 8:b18bc8331526; 9:0b997eb7ceee
1438 |/ Successors: 8:b18bc8331526; 9:0b997eb7ceee
1438 | multi-line: 8:b18bc8331526
1439 | multi-line: 8:b18bc8331526
1439 | multi-line: 9:0b997eb7ceee
1440 | multi-line: 9:0b997eb7ceee
1440 | json: [["b18bc8331526a22cbb1801022bd1555bf291c48b"], ["0b997eb7ceeee06200a02f8aab185979092d514e"]]
1441 | json: [["b18bc8331526a22cbb1801022bd1555bf291c48b"], ["0b997eb7ceeee06200a02f8aab185979092d514e"]]
1441 o dd800401bd8c
1442 o dd800401bd8c
1442 |
1443 |
1443 o f897c6137566
1444 o f897c6137566
1444 |
1445 |
1445 o ea207398892e
1446 o ea207398892e
1446
1447
1447 $ hg fatelog
1448 $ hg fatelog
1448 @ 0b997eb7ceee
1449 @ 0b997eb7ceee
1449 |
1450 |
1450 | o b18bc8331526
1451 | o b18bc8331526
1451 |/
1452 |/
1452 | o ba2ed02b0c9a
1453 | o ba2ed02b0c9a
1453 | |
1454 | |
1454 | x 4a004186e638
1455 | x 4a004186e638
1455 |/ Obsfate: rewritten as 8:b18bc8331526 by test; rewritten as 9:0b997eb7ceee by test;
1456 |/ Obsfate: rewritten as 8:b18bc8331526 by test (at 1970-01-01 00:00 +0000); rewritten as 9:0b997eb7ceee by test (at 1970-01-01 00:00 +0000);
1456 o dd800401bd8c
1457 o dd800401bd8c
1457 |
1458 |
1458 o f897c6137566
1459 o f897c6137566
1459 |
1460 |
1460 o ea207398892e
1461 o ea207398892e
1461
1462
1462 $ hg tlog --hidden
1463 $ hg tlog --hidden
1463 @ 0b997eb7ceee
1464 @ 0b997eb7ceee
1464 | Predecessors: 6:4a004186e638
1465 | Predecessors: 6:4a004186e638
1465 | semi-colon: 6:4a004186e638
1466 | semi-colon: 6:4a004186e638
1466 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1467 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1467 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1468 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1468 | o b18bc8331526
1469 | o b18bc8331526
1469 |/ Predecessors: 6:4a004186e638
1470 |/ Predecessors: 6:4a004186e638
1470 | semi-colon: 6:4a004186e638
1471 | semi-colon: 6:4a004186e638
1471 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1472 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1472 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1473 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1473 | o ba2ed02b0c9a
1474 | o ba2ed02b0c9a
1474 | | Predecessors: 4:9bd10a0775e4
1475 | | Predecessors: 4:9bd10a0775e4
1475 | | semi-colon: 4:9bd10a0775e4
1476 | | semi-colon: 4:9bd10a0775e4
1476 | | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1477 | | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1477 | | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1478 | | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1478 | x 4a004186e638
1479 | x 4a004186e638
1479 |/ Predecessors: 4:9bd10a0775e4
1480 |/ Predecessors: 4:9bd10a0775e4
1480 | semi-colon: 4:9bd10a0775e4
1481 | semi-colon: 4:9bd10a0775e4
1481 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1482 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1482 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1483 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1483 | Successors: 8:b18bc8331526; 9:0b997eb7ceee
1484 | Successors: 8:b18bc8331526; 9:0b997eb7ceee
1484 | multi-line: 8:b18bc8331526
1485 | multi-line: 8:b18bc8331526
1485 | multi-line: 9:0b997eb7ceee
1486 | multi-line: 9:0b997eb7ceee
1486 | json: [["b18bc8331526a22cbb1801022bd1555bf291c48b"], ["0b997eb7ceeee06200a02f8aab185979092d514e"]]
1487 | json: [["b18bc8331526a22cbb1801022bd1555bf291c48b"], ["0b997eb7ceeee06200a02f8aab185979092d514e"]]
1487 o dd800401bd8c
1488 o dd800401bd8c
1488 | Predecessors: 4:9bd10a0775e4
1489 | Predecessors: 4:9bd10a0775e4
1489 | semi-colon: 4:9bd10a0775e4
1490 | semi-colon: 4:9bd10a0775e4
1490 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1491 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1491 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1492 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1492 | x 9bd10a0775e4
1493 | x 9bd10a0775e4
1493 |/ Successors: 5:dd800401bd8c 6:4a004186e638 7:ba2ed02b0c9a
1494 |/ Successors: 5:dd800401bd8c 6:4a004186e638 7:ba2ed02b0c9a
1494 | multi-line: 5:dd800401bd8c 6:4a004186e638 7:ba2ed02b0c9a
1495 | multi-line: 5:dd800401bd8c 6:4a004186e638 7:ba2ed02b0c9a
1495 | json: [["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"]]
1496 | json: [["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"]]
1496 o f897c6137566
1497 o f897c6137566
1497 | Predecessors: 2:0dec01379d3b
1498 | Predecessors: 2:0dec01379d3b
1498 | semi-colon: 2:0dec01379d3b
1499 | semi-colon: 2:0dec01379d3b
1499 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1500 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1500 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1501 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1501 | x 0dec01379d3b
1502 | x 0dec01379d3b
1502 | | Predecessors: 1:471f378eab4c
1503 | | Predecessors: 1:471f378eab4c
1503 | | semi-colon: 1:471f378eab4c
1504 | | semi-colon: 1:471f378eab4c
1504 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1505 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1505 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1506 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1506 | | Successors: 3:f897c6137566; 1:471f378eab4c
1507 | | Successors: 3:f897c6137566; 1:471f378eab4c
1507 | | multi-line: 3:f897c6137566
1508 | | multi-line: 3:f897c6137566
1508 | | multi-line: 1:471f378eab4c
1509 | | multi-line: 1:471f378eab4c
1509 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
1510 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
1510 | x 471f378eab4c
1511 | x 471f378eab4c
1511 |/ Predecessors: 2:0dec01379d3b
1512 |/ Predecessors: 2:0dec01379d3b
1512 | semi-colon: 2:0dec01379d3b
1513 | semi-colon: 2:0dec01379d3b
1513 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1514 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1514 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1515 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1515 | Successors: 2:0dec01379d3b
1516 | Successors: 2:0dec01379d3b
1516 | multi-line: 2:0dec01379d3b
1517 | multi-line: 2:0dec01379d3b
1517 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
1518 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
1518 o ea207398892e
1519 o ea207398892e
1519
1520
1520 $ hg fatelog --hidden
1521 $ hg fatelog --hidden
1521 @ 0b997eb7ceee
1522 @ 0b997eb7ceee
1522 |
1523 |
1523 | o b18bc8331526
1524 | o b18bc8331526
1524 |/
1525 |/
1525 | o ba2ed02b0c9a
1526 | o ba2ed02b0c9a
1526 | |
1527 | |
1527 | x 4a004186e638
1528 | x 4a004186e638
1528 |/ Obsfate: rewritten as 8:b18bc8331526 by test; rewritten as 9:0b997eb7ceee by test;
1529 |/ Obsfate: rewritten as 8:b18bc8331526 by test (at 1970-01-01 00:00 +0000); rewritten as 9:0b997eb7ceee by test (at 1970-01-01 00:00 +0000);
1529 o dd800401bd8c
1530 o dd800401bd8c
1530 |
1531 |
1531 | x 9bd10a0775e4
1532 | x 9bd10a0775e4
1532 |/ Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a by test;
1533 |/ Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a by test (at 1970-01-01 00:00 +0000);
1533 o f897c6137566
1534 o f897c6137566
1534 |
1535 |
1535 | x 0dec01379d3b
1536 | x 0dec01379d3b
1536 | | Obsfate: rewritten as 3:f897c6137566 by test; rewritten as 1:471f378eab4c by test;
1537 | | Obsfate: rewritten as 3:f897c6137566 by test (at 1970-01-01 00:00 +0000); rewritten as 1:471f378eab4c by test (at 1970-01-01 00:00 +0000);
1537 | x 471f378eab4c
1538 | x 471f378eab4c
1538 |/ Obsfate: rewritten as 2:0dec01379d3b by test;
1539 |/ Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000);
1539 o ea207398892e
1540 o ea207398892e
1540
1541
1541 $ hg fatelogjson --hidden
1542 $ hg fatelogjson --hidden
1542 @ 0b997eb7ceee
1543 @ 0b997eb7ceee
1543 |
1544 |
1544 | o b18bc8331526
1545 | o b18bc8331526
1545 |/
1546 |/
1546 | o ba2ed02b0c9a
1547 | o ba2ed02b0c9a
1547 | |
1548 | |
1548 | x 4a004186e638
1549 | x 4a004186e638
1549 |/ Obsfate: [{"markers": [["4a004186e63889f20cb16434fcbd72220bd1eace", ["b18bc8331526a22cbb1801022bd1555bf291c48b"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["b18bc8331526a22cbb1801022bd1555bf291c48b"]}, {"markers": [["4a004186e63889f20cb16434fcbd72220bd1eace", ["0b997eb7ceeee06200a02f8aab185979092d514e"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["0b997eb7ceeee06200a02f8aab185979092d514e"]}]
1550 |/ Obsfate: [{"markers": [["4a004186e63889f20cb16434fcbd72220bd1eace", ["b18bc8331526a22cbb1801022bd1555bf291c48b"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["b18bc8331526a22cbb1801022bd1555bf291c48b"]}, {"markers": [["4a004186e63889f20cb16434fcbd72220bd1eace", ["0b997eb7ceeee06200a02f8aab185979092d514e"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["0b997eb7ceeee06200a02f8aab185979092d514e"]}]
1550 o dd800401bd8c
1551 o dd800401bd8c
1551 |
1552 |
1552 | x 9bd10a0775e4
1553 | x 9bd10a0775e4
1553 |/ Obsfate: [{"markers": [["9bd10a0775e478708cada5f176ec6de654359ce7", ["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"]}]
1554 |/ Obsfate: [{"markers": [["9bd10a0775e478708cada5f176ec6de654359ce7", ["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"]}]
1554 o f897c6137566
1555 o f897c6137566
1555 |
1556 |
1556 | x 0dec01379d3b
1557 | x 0dec01379d3b
1557 | | Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["f897c6137566320b081514b4c7227ecc3d384b39"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["f897c6137566320b081514b4c7227ecc3d384b39"]}, {"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["471f378eab4c5e25f6c77f785b27c936efb22874"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["471f378eab4c5e25f6c77f785b27c936efb22874"]}]
1558 | | Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["f897c6137566320b081514b4c7227ecc3d384b39"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["f897c6137566320b081514b4c7227ecc3d384b39"]}, {"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["471f378eab4c5e25f6c77f785b27c936efb22874"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["471f378eab4c5e25f6c77f785b27c936efb22874"]}]
1558 | x 471f378eab4c
1559 | x 471f378eab4c
1559 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]}]
1560 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]}]
1560 o ea207398892e
1561 o ea207398892e
1561
1562
1562 $ hg up --hidden 4
1563 $ hg up --hidden 4
1563 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1564 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1564 $ hg rebase -r 7 -d 8 --config extensions.rebase=
1565 $ hg rebase -r 7 -d 8 --config extensions.rebase=
1565 rebasing 7:ba2ed02b0c9a "Add A,B,C"
1566 rebasing 7:ba2ed02b0c9a "Add A,B,C"
1566 $ hg tlog
1567 $ hg tlog
1567 o eceed8f98ffc
1568 o eceed8f98ffc
1568 | Predecessors: 4:9bd10a0775e4
1569 | Predecessors: 4:9bd10a0775e4
1569 | semi-colon: 4:9bd10a0775e4
1570 | semi-colon: 4:9bd10a0775e4
1570 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1571 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1571 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1572 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1572 | o 0b997eb7ceee
1573 | o 0b997eb7ceee
1573 | | Predecessors: 4:9bd10a0775e4
1574 | | Predecessors: 4:9bd10a0775e4
1574 | | semi-colon: 4:9bd10a0775e4
1575 | | semi-colon: 4:9bd10a0775e4
1575 | | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1576 | | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1576 | | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1577 | | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1577 o | b18bc8331526
1578 o | b18bc8331526
1578 |/ Predecessors: 4:9bd10a0775e4
1579 |/ Predecessors: 4:9bd10a0775e4
1579 | semi-colon: 4:9bd10a0775e4
1580 | semi-colon: 4:9bd10a0775e4
1580 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1581 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1581 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1582 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1582 o dd800401bd8c
1583 o dd800401bd8c
1583 | Predecessors: 4:9bd10a0775e4
1584 | Predecessors: 4:9bd10a0775e4
1584 | semi-colon: 4:9bd10a0775e4
1585 | semi-colon: 4:9bd10a0775e4
1585 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1586 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1586 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1587 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1587 | @ 9bd10a0775e4
1588 | @ 9bd10a0775e4
1588 |/ Successors: 5:dd800401bd8c 9:0b997eb7ceee 10:eceed8f98ffc; 5:dd800401bd8c 8:b18bc8331526 10:eceed8f98ffc
1589 |/ Successors: 5:dd800401bd8c 9:0b997eb7ceee 10:eceed8f98ffc; 5:dd800401bd8c 8:b18bc8331526 10:eceed8f98ffc
1589 | multi-line: 5:dd800401bd8c 9:0b997eb7ceee 10:eceed8f98ffc
1590 | multi-line: 5:dd800401bd8c 9:0b997eb7ceee 10:eceed8f98ffc
1590 | multi-line: 5:dd800401bd8c 8:b18bc8331526 10:eceed8f98ffc
1591 | multi-line: 5:dd800401bd8c 8:b18bc8331526 10:eceed8f98ffc
1591 | json: [["dd800401bd8c79d815329277739e433e883f784e", "0b997eb7ceeee06200a02f8aab185979092d514e", "eceed8f98ffc4186032e29a6542ab98888ebf68d"], ["dd800401bd8c79d815329277739e433e883f784e", "b18bc8331526a22cbb1801022bd1555bf291c48b", "eceed8f98ffc4186032e29a6542ab98888ebf68d"]]
1592 | json: [["dd800401bd8c79d815329277739e433e883f784e", "0b997eb7ceeee06200a02f8aab185979092d514e", "eceed8f98ffc4186032e29a6542ab98888ebf68d"], ["dd800401bd8c79d815329277739e433e883f784e", "b18bc8331526a22cbb1801022bd1555bf291c48b", "eceed8f98ffc4186032e29a6542ab98888ebf68d"]]
1592 o f897c6137566
1593 o f897c6137566
1593 |
1594 |
1594 o ea207398892e
1595 o ea207398892e
1595
1596
1596
1597
1597 $ hg fatelog
1598 $ hg fatelog
1598 o eceed8f98ffc
1599 o eceed8f98ffc
1599 |
1600 |
1600 | o 0b997eb7ceee
1601 | o 0b997eb7ceee
1601 | |
1602 | |
1602 o | b18bc8331526
1603 o | b18bc8331526
1603 |/
1604 |/
1604 o dd800401bd8c
1605 o dd800401bd8c
1605 |
1606 |
1606 | @ 9bd10a0775e4
1607 | @ 9bd10a0775e4
1607 |/ Obsfate: split as 5:dd800401bd8c, 9:0b997eb7ceee, 10:eceed8f98ffc by test; split as 5:dd800401bd8c, 8:b18bc8331526, 10:eceed8f98ffc by test;
1608 |/ Obsfate: split as 5:dd800401bd8c, 9:0b997eb7ceee, 10:eceed8f98ffc by test (at 1970-01-01 00:00 +0000); split as 5:dd800401bd8c, 8:b18bc8331526, 10:eceed8f98ffc by test (at 1970-01-01 00:00 +0000);
1608 o f897c6137566
1609 o f897c6137566
1609 |
1610 |
1610 o ea207398892e
1611 o ea207398892e
1611
1612
1612 Test templates with pruned commits
1613 Test templates with pruned commits
1613 ==================================
1614 ==================================
1614
1615
1615 Test setup
1616 Test setup
1616 ----------
1617 ----------
1617
1618
1618 $ hg init $TESTTMP/templates-local-prune
1619 $ hg init $TESTTMP/templates-local-prune
1619 $ cd $TESTTMP/templates-local-prune
1620 $ cd $TESTTMP/templates-local-prune
1620 $ mkcommit ROOT
1621 $ mkcommit ROOT
1621 $ mkcommit A0
1622 $ mkcommit A0
1622 $ hg debugobsolete --record-parent `getid "."`
1623 $ hg debugobsolete --record-parent `getid "."`
1623 obsoleted 1 changesets
1624 obsoleted 1 changesets
1624
1625
1625 Check output
1626 Check output
1626 ------------
1627 ------------
1627
1628
1628 $ hg up "desc(A0)" --hidden
1629 $ hg up "desc(A0)" --hidden
1629 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1630 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1630 $ hg tlog
1631 $ hg tlog
1631 @ 471f378eab4c
1632 @ 471f378eab4c
1632 |
1633 |
1633 o ea207398892e
1634 o ea207398892e
1634
1635
1635 $ hg fatelog
1636 $ hg fatelog
1636 @ 471f378eab4c
1637 @ 471f378eab4c
1637 |
1638 |
1638 o ea207398892e
1639 o ea207398892e
1639
1640
1640 $ hg fatelog -v
1641 $ hg fatelog -v
1641 @ 471f378eab4c
1642 @ 471f378eab4c
1642 |
1643 |
1643 o ea207398892e
1644 o ea207398892e
1644
1645
General Comments 0
You need to be logged in to leave comments. Login now