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