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