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