##// END OF EJS Templates
smartset: don't ignore hidden revs when intersecting...
Martin von Zweigbergk -
r52004:5ae05937 default
parent child Browse files
Show More
@@ -1,1138 +1,1141 b''
1 # smartset.py - data structure for revision set
1 # smartset.py - data structure for revision set
2 #
2 #
3 # Copyright 2010 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2010 Olivia Mackall <olivia@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
8
9 from . import (
9 from . import (
10 encoding,
10 encoding,
11 error,
11 error,
12 pycompat,
12 pycompat,
13 util,
13 util,
14 )
14 )
15 from .utils import stringutil
15 from .utils import stringutil
16
16
17
17
18 def _typename(o):
18 def _typename(o):
19 return pycompat.sysbytes(type(o).__name__).lstrip(b'_')
19 return pycompat.sysbytes(type(o).__name__).lstrip(b'_')
20
20
21
21
22 class abstractsmartset:
22 class abstractsmartset:
23 def __nonzero__(self):
23 def __nonzero__(self):
24 """True if the smartset is not empty"""
24 """True if the smartset is not empty"""
25 raise NotImplementedError()
25 raise NotImplementedError()
26
26
27 __bool__ = __nonzero__
27 __bool__ = __nonzero__
28
28
29 def __contains__(self, rev):
29 def __contains__(self, rev):
30 """provide fast membership testing"""
30 """provide fast membership testing"""
31 raise NotImplementedError()
31 raise NotImplementedError()
32
32
33 def __iter__(self):
33 def __iter__(self):
34 """iterate the set in the order it is supposed to be iterated"""
34 """iterate the set in the order it is supposed to be iterated"""
35 raise NotImplementedError()
35 raise NotImplementedError()
36
36
37 # Attributes containing a function to perform a fast iteration in a given
37 # Attributes containing a function to perform a fast iteration in a given
38 # direction. A smartset can have none, one, or both defined.
38 # direction. A smartset can have none, one, or both defined.
39 #
39 #
40 # Default value is None instead of a function returning None to avoid
40 # Default value is None instead of a function returning None to avoid
41 # initializing an iterator just for testing if a fast method exists.
41 # initializing an iterator just for testing if a fast method exists.
42 fastasc = None
42 fastasc = None
43 fastdesc = None
43 fastdesc = None
44
44
45 def isascending(self):
45 def isascending(self):
46 """True if the set will iterate in ascending order"""
46 """True if the set will iterate in ascending order"""
47 raise NotImplementedError()
47 raise NotImplementedError()
48
48
49 def isdescending(self):
49 def isdescending(self):
50 """True if the set will iterate in descending order"""
50 """True if the set will iterate in descending order"""
51 raise NotImplementedError()
51 raise NotImplementedError()
52
52
53 def istopo(self):
53 def istopo(self):
54 """True if the set will iterate in topographical order"""
54 """True if the set will iterate in topographical order"""
55 raise NotImplementedError()
55 raise NotImplementedError()
56
56
57 def min(self):
57 def min(self):
58 """return the minimum element in the set"""
58 """return the minimum element in the set"""
59 if self.fastasc is None:
59 if self.fastasc is None:
60 v = min(self)
60 v = min(self)
61 else:
61 else:
62 for v in self.fastasc():
62 for v in self.fastasc():
63 break
63 break
64 else:
64 else:
65 raise ValueError(b'arg is an empty sequence')
65 raise ValueError(b'arg is an empty sequence')
66 self.min = lambda: v
66 self.min = lambda: v
67 return v
67 return v
68
68
69 def max(self):
69 def max(self):
70 """return the maximum element in the set"""
70 """return the maximum element in the set"""
71 if self.fastdesc is None:
71 if self.fastdesc is None:
72 return max(self)
72 return max(self)
73 else:
73 else:
74 for v in self.fastdesc():
74 for v in self.fastdesc():
75 break
75 break
76 else:
76 else:
77 raise ValueError(b'arg is an empty sequence')
77 raise ValueError(b'arg is an empty sequence')
78 self.max = lambda: v
78 self.max = lambda: v
79 return v
79 return v
80
80
81 def first(self):
81 def first(self):
82 """return the first element in the set (user iteration perspective)
82 """return the first element in the set (user iteration perspective)
83
83
84 Return None if the set is empty"""
84 Return None if the set is empty"""
85 raise NotImplementedError()
85 raise NotImplementedError()
86
86
87 def last(self):
87 def last(self):
88 """return the last element in the set (user iteration perspective)
88 """return the last element in the set (user iteration perspective)
89
89
90 Return None if the set is empty"""
90 Return None if the set is empty"""
91 raise NotImplementedError()
91 raise NotImplementedError()
92
92
93 def __len__(self):
93 def __len__(self):
94 """return the length of the smartsets
94 """return the length of the smartsets
95
95
96 This can be expensive on smartset that could be lazy otherwise."""
96 This can be expensive on smartset that could be lazy otherwise."""
97 raise NotImplementedError()
97 raise NotImplementedError()
98
98
99 def reverse(self):
99 def reverse(self):
100 """reverse the expected iteration order"""
100 """reverse the expected iteration order"""
101 raise NotImplementedError()
101 raise NotImplementedError()
102
102
103 def sort(self, reverse=False):
103 def sort(self, reverse=False):
104 """get the set to iterate in an ascending or descending order"""
104 """get the set to iterate in an ascending or descending order"""
105 raise NotImplementedError()
105 raise NotImplementedError()
106
106
107 def __and__(self, other):
107 def __and__(self, other):
108 """Returns a new object with the intersection of the two collections.
108 """Returns a new object with the intersection of the two collections.
109
109
110 This is part of the mandatory API for smartset."""
110 This is part of the mandatory API for smartset."""
111 if isinstance(other, fullreposet):
111 if isinstance(other, fullreposet):
112 return self
112 return self
113 return self.filter(other.__contains__, condrepr=other, cache=False)
113 return self.filter(other.__contains__, condrepr=other, cache=False)
114
114
115 def __add__(self, other):
115 def __add__(self, other):
116 """Returns a new object with the union of the two collections.
116 """Returns a new object with the union of the two collections.
117
117
118 This is part of the mandatory API for smartset."""
118 This is part of the mandatory API for smartset."""
119 return addset(self, other)
119 return addset(self, other)
120
120
121 def __sub__(self, other):
121 def __sub__(self, other):
122 """Returns a new object with the substraction of the two collections.
122 """Returns a new object with the substraction of the two collections.
123
123
124 This is part of the mandatory API for smartset."""
124 This is part of the mandatory API for smartset."""
125 c = other.__contains__
125 c = other.__contains__
126 return self.filter(
126 return self.filter(
127 lambda r: not c(r), condrepr=(b'<not %r>', other), cache=False
127 lambda r: not c(r), condrepr=(b'<not %r>', other), cache=False
128 )
128 )
129
129
130 def filter(self, condition, condrepr=None, cache=True):
130 def filter(self, condition, condrepr=None, cache=True):
131 """Returns this smartset filtered by condition as a new smartset.
131 """Returns this smartset filtered by condition as a new smartset.
132
132
133 `condition` is a callable which takes a revision number and returns a
133 `condition` is a callable which takes a revision number and returns a
134 boolean. Optional `condrepr` provides a printable representation of
134 boolean. Optional `condrepr` provides a printable representation of
135 the given `condition`.
135 the given `condition`.
136
136
137 This is part of the mandatory API for smartset."""
137 This is part of the mandatory API for smartset."""
138 # builtin cannot be cached. but do not needs to
138 # builtin cannot be cached. but do not needs to
139 if cache and hasattr(condition, '__code__'):
139 if cache and hasattr(condition, '__code__'):
140 condition = util.cachefunc(condition)
140 condition = util.cachefunc(condition)
141 return filteredset(self, condition, condrepr)
141 return filteredset(self, condition, condrepr)
142
142
143 def slice(self, start, stop):
143 def slice(self, start, stop):
144 """Return new smartset that contains selected elements from this set"""
144 """Return new smartset that contains selected elements from this set"""
145 if start < 0 or stop < 0:
145 if start < 0 or stop < 0:
146 raise error.ProgrammingError(b'negative index not allowed')
146 raise error.ProgrammingError(b'negative index not allowed')
147 return self._slice(start, stop)
147 return self._slice(start, stop)
148
148
149 def _slice(self, start, stop):
149 def _slice(self, start, stop):
150 # sub classes may override this. start and stop must not be negative,
150 # sub classes may override this. start and stop must not be negative,
151 # but start > stop is allowed, which should be an empty set.
151 # but start > stop is allowed, which should be an empty set.
152 ys = []
152 ys = []
153 it = iter(self)
153 it = iter(self)
154 for x in range(start):
154 for x in range(start):
155 y = next(it, None)
155 y = next(it, None)
156 if y is None:
156 if y is None:
157 break
157 break
158 for x in range(stop - start):
158 for x in range(stop - start):
159 y = next(it, None)
159 y = next(it, None)
160 if y is None:
160 if y is None:
161 break
161 break
162 ys.append(y)
162 ys.append(y)
163 return baseset(ys, datarepr=(b'slice=%d:%d %r', start, stop, self))
163 return baseset(ys, datarepr=(b'slice=%d:%d %r', start, stop, self))
164
164
165
165
166 class baseset(abstractsmartset):
166 class baseset(abstractsmartset):
167 """Basic data structure that represents a revset and contains the basic
167 """Basic data structure that represents a revset and contains the basic
168 operation that it should be able to perform.
168 operation that it should be able to perform.
169
169
170 Every method in this class should be implemented by any smartset class.
170 Every method in this class should be implemented by any smartset class.
171
171
172 This class could be constructed by an (unordered) set, or an (ordered)
172 This class could be constructed by an (unordered) set, or an (ordered)
173 list-like object. If a set is provided, it'll be sorted lazily.
173 list-like object. If a set is provided, it'll be sorted lazily.
174
174
175 >>> x = [4, 0, 7, 6]
175 >>> x = [4, 0, 7, 6]
176 >>> y = [5, 6, 7, 3]
176 >>> y = [5, 6, 7, 3]
177
177
178 Construct by a set:
178 Construct by a set:
179 >>> xs = baseset(set(x))
179 >>> xs = baseset(set(x))
180 >>> ys = baseset(set(y))
180 >>> ys = baseset(set(y))
181 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
181 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
182 [[0, 4, 6, 7, 3, 5], [6, 7], [0, 4]]
182 [[0, 4, 6, 7, 3, 5], [6, 7], [0, 4]]
183 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
183 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
184 ['addset', 'baseset', 'baseset']
184 ['addset', 'baseset', 'baseset']
185
185
186 Construct by a list-like:
186 Construct by a list-like:
187 >>> xs = baseset(x)
187 >>> xs = baseset(x)
188 >>> ys = baseset(i for i in y)
188 >>> ys = baseset(i for i in y)
189 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
189 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
190 [[4, 0, 7, 6, 5, 3], [7, 6], [4, 0]]
190 [[4, 0, 7, 6, 5, 3], [7, 6], [4, 0]]
191 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
191 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
192 ['addset', 'filteredset', 'filteredset']
192 ['addset', 'filteredset', 'filteredset']
193
193
194 Populate "_set" fields in the lists so set optimization may be used:
194 Populate "_set" fields in the lists so set optimization may be used:
195 >>> [1 in xs, 3 in ys]
195 >>> [1 in xs, 3 in ys]
196 [False, True]
196 [False, True]
197
197
198 Without sort(), results won't be changed:
198 Without sort(), results won't be changed:
199 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
199 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
200 [[4, 0, 7, 6, 5, 3], [7, 6], [4, 0]]
200 [[4, 0, 7, 6, 5, 3], [7, 6], [4, 0]]
201 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
201 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
202 ['addset', 'filteredset', 'filteredset']
202 ['addset', 'filteredset', 'filteredset']
203
203
204 With sort(), set optimization could be used:
204 With sort(), set optimization could be used:
205 >>> xs.sort(reverse=True)
205 >>> xs.sort(reverse=True)
206 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
206 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
207 [[7, 6, 4, 0, 5, 3], [7, 6], [4, 0]]
207 [[7, 6, 4, 0, 5, 3], [7, 6], [4, 0]]
208 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
208 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
209 ['addset', 'baseset', 'baseset']
209 ['addset', 'baseset', 'baseset']
210
210
211 >>> ys.sort()
211 >>> ys.sort()
212 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
212 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
213 [[7, 6, 4, 0, 3, 5], [7, 6], [4, 0]]
213 [[7, 6, 4, 0, 3, 5], [7, 6], [4, 0]]
214 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
214 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
215 ['addset', 'baseset', 'baseset']
215 ['addset', 'baseset', 'baseset']
216
216
217 istopo is preserved across set operations
217 istopo is preserved across set operations
218 >>> xs = baseset(set(x), istopo=True)
218 >>> xs = baseset(set(x), istopo=True)
219 >>> rs = xs & ys
219 >>> rs = xs & ys
220 >>> type(rs).__name__
220 >>> type(rs).__name__
221 'baseset'
221 'baseset'
222 >>> rs._istopo
222 >>> rs._istopo
223 True
223 True
224 """
224 """
225
225
226 def __init__(self, data=(), datarepr=None, istopo=False):
226 def __init__(self, data=(), datarepr=None, istopo=False):
227 """
227 """
228 datarepr: a tuple of (format, obj, ...), a function or an object that
228 datarepr: a tuple of (format, obj, ...), a function or an object that
229 provides a printable representation of the given data.
229 provides a printable representation of the given data.
230 """
230 """
231 self._ascending = None
231 self._ascending = None
232 self._istopo = istopo
232 self._istopo = istopo
233 if isinstance(data, set):
233 if isinstance(data, set):
234 # converting set to list has a cost, do it lazily
234 # converting set to list has a cost, do it lazily
235 self._set = data
235 self._set = data
236 # set has no order we pick one for stability purpose
236 # set has no order we pick one for stability purpose
237 self._ascending = True
237 self._ascending = True
238 else:
238 else:
239 if not isinstance(data, list):
239 if not isinstance(data, list):
240 data = list(data)
240 data = list(data)
241 self._list = data
241 self._list = data
242 self._datarepr = datarepr
242 self._datarepr = datarepr
243
243
244 @util.propertycache
244 @util.propertycache
245 def _set(self):
245 def _set(self):
246 return set(self._list)
246 return set(self._list)
247
247
248 @util.propertycache
248 @util.propertycache
249 def _asclist(self):
249 def _asclist(self):
250 asclist = self._list[:]
250 asclist = self._list[:]
251 asclist.sort()
251 asclist.sort()
252 return asclist
252 return asclist
253
253
254 @util.propertycache
254 @util.propertycache
255 def _list(self):
255 def _list(self):
256 # _list is only lazily constructed if we have _set
256 # _list is only lazily constructed if we have _set
257 assert '_set' in self.__dict__
257 assert '_set' in self.__dict__
258 return list(self._set)
258 return list(self._set)
259
259
260 def __iter__(self):
260 def __iter__(self):
261 if self._ascending is None:
261 if self._ascending is None:
262 return iter(self._list)
262 return iter(self._list)
263 elif self._ascending:
263 elif self._ascending:
264 return iter(self._asclist)
264 return iter(self._asclist)
265 else:
265 else:
266 return reversed(self._asclist)
266 return reversed(self._asclist)
267
267
268 def fastasc(self):
268 def fastasc(self):
269 return iter(self._asclist)
269 return iter(self._asclist)
270
270
271 def fastdesc(self):
271 def fastdesc(self):
272 return reversed(self._asclist)
272 return reversed(self._asclist)
273
273
274 @util.propertycache
274 @util.propertycache
275 def __contains__(self):
275 def __contains__(self):
276 return self._set.__contains__
276 return self._set.__contains__
277
277
278 def __nonzero__(self):
278 def __nonzero__(self):
279 return bool(len(self))
279 return bool(len(self))
280
280
281 __bool__ = __nonzero__
281 __bool__ = __nonzero__
282
282
283 def sort(self, reverse=False):
283 def sort(self, reverse=False):
284 self._ascending = not bool(reverse)
284 self._ascending = not bool(reverse)
285 self._istopo = False
285 self._istopo = False
286
286
287 def reverse(self):
287 def reverse(self):
288 if self._ascending is None:
288 if self._ascending is None:
289 self._list.reverse()
289 self._list.reverse()
290 else:
290 else:
291 self._ascending = not self._ascending
291 self._ascending = not self._ascending
292 self._istopo = False
292 self._istopo = False
293
293
294 def __len__(self):
294 def __len__(self):
295 if '_list' in self.__dict__:
295 if '_list' in self.__dict__:
296 return len(self._list)
296 return len(self._list)
297 else:
297 else:
298 return len(self._set)
298 return len(self._set)
299
299
300 def isascending(self):
300 def isascending(self):
301 """Returns True if the collection is ascending order, False if not.
301 """Returns True if the collection is ascending order, False if not.
302
302
303 This is part of the mandatory API for smartset."""
303 This is part of the mandatory API for smartset."""
304 if len(self) <= 1:
304 if len(self) <= 1:
305 return True
305 return True
306 return self._ascending is not None and self._ascending
306 return self._ascending is not None and self._ascending
307
307
308 def isdescending(self):
308 def isdescending(self):
309 """Returns True if the collection is descending order, False if not.
309 """Returns True if the collection is descending order, False if not.
310
310
311 This is part of the mandatory API for smartset."""
311 This is part of the mandatory API for smartset."""
312 if len(self) <= 1:
312 if len(self) <= 1:
313 return True
313 return True
314 return self._ascending is not None and not self._ascending
314 return self._ascending is not None and not self._ascending
315
315
316 def istopo(self):
316 def istopo(self):
317 """Is the collection is in topographical order or not.
317 """Is the collection is in topographical order or not.
318
318
319 This is part of the mandatory API for smartset."""
319 This is part of the mandatory API for smartset."""
320 if len(self) <= 1:
320 if len(self) <= 1:
321 return True
321 return True
322 return self._istopo
322 return self._istopo
323
323
324 def first(self):
324 def first(self):
325 if self:
325 if self:
326 if self._ascending is None:
326 if self._ascending is None:
327 return self._list[0]
327 return self._list[0]
328 elif self._ascending:
328 elif self._ascending:
329 return self._asclist[0]
329 return self._asclist[0]
330 else:
330 else:
331 return self._asclist[-1]
331 return self._asclist[-1]
332 return None
332 return None
333
333
334 def last(self):
334 def last(self):
335 if self:
335 if self:
336 if self._ascending is None:
336 if self._ascending is None:
337 return self._list[-1]
337 return self._list[-1]
338 elif self._ascending:
338 elif self._ascending:
339 return self._asclist[-1]
339 return self._asclist[-1]
340 else:
340 else:
341 return self._asclist[0]
341 return self._asclist[0]
342 return None
342 return None
343
343
344 def _fastsetop(self, other, op):
344 def _fastsetop(self, other, op):
345 # try to use native set operations as fast paths
345 # try to use native set operations as fast paths
346 if (
346 if (
347 type(other) is baseset
347 type(other) is baseset
348 and '_set' in other.__dict__
348 and '_set' in other.__dict__
349 and '_set' in self.__dict__
349 and '_set' in self.__dict__
350 and self._ascending is not None
350 and self._ascending is not None
351 ):
351 ):
352 s = baseset(
352 s = baseset(
353 data=getattr(self._set, op)(other._set), istopo=self._istopo
353 data=getattr(self._set, op)(other._set), istopo=self._istopo
354 )
354 )
355 s._ascending = self._ascending
355 s._ascending = self._ascending
356 else:
356 else:
357 s = getattr(super(baseset, self), op)(other)
357 s = getattr(super(baseset, self), op)(other)
358 return s
358 return s
359
359
360 def __and__(self, other):
360 def __and__(self, other):
361 return self._fastsetop(other, '__and__')
361 return self._fastsetop(other, '__and__')
362
362
363 def __sub__(self, other):
363 def __sub__(self, other):
364 return self._fastsetop(other, '__sub__')
364 return self._fastsetop(other, '__sub__')
365
365
366 def _slice(self, start, stop):
366 def _slice(self, start, stop):
367 # creating new list should be generally cheaper than iterating items
367 # creating new list should be generally cheaper than iterating items
368 if self._ascending is None:
368 if self._ascending is None:
369 return baseset(self._list[start:stop], istopo=self._istopo)
369 return baseset(self._list[start:stop], istopo=self._istopo)
370
370
371 data = self._asclist
371 data = self._asclist
372 if not self._ascending:
372 if not self._ascending:
373 start, stop = max(len(data) - stop, 0), max(len(data) - start, 0)
373 start, stop = max(len(data) - stop, 0), max(len(data) - start, 0)
374 s = baseset(data[start:stop], istopo=self._istopo)
374 s = baseset(data[start:stop], istopo=self._istopo)
375 s._ascending = self._ascending
375 s._ascending = self._ascending
376 return s
376 return s
377
377
378 @encoding.strmethod
378 @encoding.strmethod
379 def __repr__(self):
379 def __repr__(self):
380 d = {None: b'', False: b'-', True: b'+'}[self._ascending]
380 d = {None: b'', False: b'-', True: b'+'}[self._ascending]
381 s = stringutil.buildrepr(self._datarepr)
381 s = stringutil.buildrepr(self._datarepr)
382 if not s:
382 if not s:
383 l = self._list
383 l = self._list
384 # if _list has been built from a set, it might have a different
384 # if _list has been built from a set, it might have a different
385 # order from one python implementation to another.
385 # order from one python implementation to another.
386 # We fallback to the sorted version for a stable output.
386 # We fallback to the sorted version for a stable output.
387 if self._ascending is not None:
387 if self._ascending is not None:
388 l = self._asclist
388 l = self._asclist
389 s = pycompat.byterepr(l)
389 s = pycompat.byterepr(l)
390 return b'<%s%s %s>' % (_typename(self), d, s)
390 return b'<%s%s %s>' % (_typename(self), d, s)
391
391
392
392
393 class filteredset(abstractsmartset):
393 class filteredset(abstractsmartset):
394 """Duck type for baseset class which iterates lazily over the revisions in
394 """Duck type for baseset class which iterates lazily over the revisions in
395 the subset and contains a function which tests for membership in the
395 the subset and contains a function which tests for membership in the
396 revset
396 revset
397 """
397 """
398
398
399 def __init__(self, subset, condition=lambda x: True, condrepr=None):
399 def __init__(self, subset, condition=lambda x: True, condrepr=None):
400 """
400 """
401 condition: a function that decide whether a revision in the subset
401 condition: a function that decide whether a revision in the subset
402 belongs to the revset or not.
402 belongs to the revset or not.
403 condrepr: a tuple of (format, obj, ...), a function or an object that
403 condrepr: a tuple of (format, obj, ...), a function or an object that
404 provides a printable representation of the given condition.
404 provides a printable representation of the given condition.
405 """
405 """
406 self._subset = subset
406 self._subset = subset
407 self._condition = condition
407 self._condition = condition
408 self._condrepr = condrepr
408 self._condrepr = condrepr
409
409
410 def __contains__(self, x):
410 def __contains__(self, x):
411 return x in self._subset and self._condition(x)
411 return x in self._subset and self._condition(x)
412
412
413 def __iter__(self):
413 def __iter__(self):
414 return self._iterfilter(self._subset)
414 return self._iterfilter(self._subset)
415
415
416 def _iterfilter(self, it):
416 def _iterfilter(self, it):
417 cond = self._condition
417 cond = self._condition
418 for x in it:
418 for x in it:
419 if cond(x):
419 if cond(x):
420 yield x
420 yield x
421
421
422 @property
422 @property
423 def fastasc(self):
423 def fastasc(self):
424 it = self._subset.fastasc
424 it = self._subset.fastasc
425 if it is None:
425 if it is None:
426 return None
426 return None
427 return lambda: self._iterfilter(it())
427 return lambda: self._iterfilter(it())
428
428
429 @property
429 @property
430 def fastdesc(self):
430 def fastdesc(self):
431 it = self._subset.fastdesc
431 it = self._subset.fastdesc
432 if it is None:
432 if it is None:
433 return None
433 return None
434 return lambda: self._iterfilter(it())
434 return lambda: self._iterfilter(it())
435
435
436 def __nonzero__(self):
436 def __nonzero__(self):
437 fast = None
437 fast = None
438 candidates = [
438 candidates = [
439 self.fastasc if self.isascending() else None,
439 self.fastasc if self.isascending() else None,
440 self.fastdesc if self.isdescending() else None,
440 self.fastdesc if self.isdescending() else None,
441 self.fastasc,
441 self.fastasc,
442 self.fastdesc,
442 self.fastdesc,
443 ]
443 ]
444 for candidate in candidates:
444 for candidate in candidates:
445 if candidate is not None:
445 if candidate is not None:
446 fast = candidate
446 fast = candidate
447 break
447 break
448
448
449 if fast is not None:
449 if fast is not None:
450 it = fast()
450 it = fast()
451 else:
451 else:
452 it = self
452 it = self
453
453
454 for r in it:
454 for r in it:
455 return True
455 return True
456 return False
456 return False
457
457
458 __bool__ = __nonzero__
458 __bool__ = __nonzero__
459
459
460 def __len__(self):
460 def __len__(self):
461 # Basic implementation to be changed in future patches.
461 # Basic implementation to be changed in future patches.
462 # until this gets improved, we use generator expression
462 # until this gets improved, we use generator expression
463 # here, since list comprehensions are free to call __len__ again
463 # here, since list comprehensions are free to call __len__ again
464 # causing infinite recursion
464 # causing infinite recursion
465 l = baseset(r for r in self)
465 l = baseset(r for r in self)
466 return len(l)
466 return len(l)
467
467
468 def sort(self, reverse=False):
468 def sort(self, reverse=False):
469 self._subset.sort(reverse=reverse)
469 self._subset.sort(reverse=reverse)
470
470
471 def reverse(self):
471 def reverse(self):
472 self._subset.reverse()
472 self._subset.reverse()
473
473
474 def isascending(self):
474 def isascending(self):
475 return self._subset.isascending()
475 return self._subset.isascending()
476
476
477 def isdescending(self):
477 def isdescending(self):
478 return self._subset.isdescending()
478 return self._subset.isdescending()
479
479
480 def istopo(self):
480 def istopo(self):
481 return self._subset.istopo()
481 return self._subset.istopo()
482
482
483 def first(self):
483 def first(self):
484 for x in self:
484 for x in self:
485 return x
485 return x
486 return None
486 return None
487
487
488 def last(self):
488 def last(self):
489 it = None
489 it = None
490 if self.isascending():
490 if self.isascending():
491 it = self.fastdesc
491 it = self.fastdesc
492 elif self.isdescending():
492 elif self.isdescending():
493 it = self.fastasc
493 it = self.fastasc
494 if it is not None:
494 if it is not None:
495 for x in it():
495 for x in it():
496 return x
496 return x
497 return None # empty case
497 return None # empty case
498 else:
498 else:
499 x = None
499 x = None
500 for x in self:
500 for x in self:
501 pass
501 pass
502 return x
502 return x
503
503
504 @encoding.strmethod
504 @encoding.strmethod
505 def __repr__(self):
505 def __repr__(self):
506 xs = [pycompat.byterepr(self._subset)]
506 xs = [pycompat.byterepr(self._subset)]
507 s = stringutil.buildrepr(self._condrepr)
507 s = stringutil.buildrepr(self._condrepr)
508 if s:
508 if s:
509 xs.append(s)
509 xs.append(s)
510 return b'<%s %s>' % (_typename(self), b', '.join(xs))
510 return b'<%s %s>' % (_typename(self), b', '.join(xs))
511
511
512
512
513 def _iterordered(ascending, iter1, iter2):
513 def _iterordered(ascending, iter1, iter2):
514 """produce an ordered iteration from two iterators with the same order
514 """produce an ordered iteration from two iterators with the same order
515
515
516 The ascending is used to indicated the iteration direction.
516 The ascending is used to indicated the iteration direction.
517 """
517 """
518 choice = max
518 choice = max
519 if ascending:
519 if ascending:
520 choice = min
520 choice = min
521
521
522 val1 = None
522 val1 = None
523 val2 = None
523 val2 = None
524 try:
524 try:
525 # Consume both iterators in an ordered way until one is empty
525 # Consume both iterators in an ordered way until one is empty
526 while True:
526 while True:
527 if val1 is None:
527 if val1 is None:
528 val1 = next(iter1)
528 val1 = next(iter1)
529 if val2 is None:
529 if val2 is None:
530 val2 = next(iter2)
530 val2 = next(iter2)
531 n = choice(val1, val2)
531 n = choice(val1, val2)
532 yield n
532 yield n
533 if val1 == n:
533 if val1 == n:
534 val1 = None
534 val1 = None
535 if val2 == n:
535 if val2 == n:
536 val2 = None
536 val2 = None
537 except StopIteration:
537 except StopIteration:
538 # Flush any remaining values and consume the other one
538 # Flush any remaining values and consume the other one
539 it = iter2
539 it = iter2
540 if val1 is not None:
540 if val1 is not None:
541 yield val1
541 yield val1
542 it = iter1
542 it = iter1
543 elif val2 is not None:
543 elif val2 is not None:
544 # might have been equality and both are empty
544 # might have been equality and both are empty
545 yield val2
545 yield val2
546 for val in it:
546 for val in it:
547 yield val
547 yield val
548
548
549
549
550 class addset(abstractsmartset):
550 class addset(abstractsmartset):
551 """Represent the addition of two sets
551 """Represent the addition of two sets
552
552
553 Wrapper structure for lazily adding two structures without losing much
553 Wrapper structure for lazily adding two structures without losing much
554 performance on the __contains__ method
554 performance on the __contains__ method
555
555
556 If the ascending attribute is set, that means the two structures are
556 If the ascending attribute is set, that means the two structures are
557 ordered in either an ascending or descending way. Therefore, we can add
557 ordered in either an ascending or descending way. Therefore, we can add
558 them maintaining the order by iterating over both at the same time
558 them maintaining the order by iterating over both at the same time
559
559
560 >>> xs = baseset([0, 3, 2])
560 >>> xs = baseset([0, 3, 2])
561 >>> ys = baseset([5, 2, 4])
561 >>> ys = baseset([5, 2, 4])
562
562
563 >>> rs = addset(xs, ys)
563 >>> rs = addset(xs, ys)
564 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
564 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
565 (True, True, False, True, 0, 4)
565 (True, True, False, True, 0, 4)
566 >>> rs = addset(xs, baseset([]))
566 >>> rs = addset(xs, baseset([]))
567 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
567 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
568 (True, True, False, 0, 2)
568 (True, True, False, 0, 2)
569 >>> rs = addset(baseset([]), baseset([]))
569 >>> rs = addset(baseset([]), baseset([]))
570 >>> bool(rs), 0 in rs, rs.first(), rs.last()
570 >>> bool(rs), 0 in rs, rs.first(), rs.last()
571 (False, False, None, None)
571 (False, False, None, None)
572
572
573 iterate unsorted:
573 iterate unsorted:
574 >>> rs = addset(xs, ys)
574 >>> rs = addset(xs, ys)
575 >>> # (use generator because pypy could call len())
575 >>> # (use generator because pypy could call len())
576 >>> list(x for x in rs) # without _genlist
576 >>> list(x for x in rs) # without _genlist
577 [0, 3, 2, 5, 4]
577 [0, 3, 2, 5, 4]
578 >>> assert not rs._genlist
578 >>> assert not rs._genlist
579 >>> len(rs)
579 >>> len(rs)
580 5
580 5
581 >>> [x for x in rs] # with _genlist
581 >>> [x for x in rs] # with _genlist
582 [0, 3, 2, 5, 4]
582 [0, 3, 2, 5, 4]
583 >>> assert rs._genlist
583 >>> assert rs._genlist
584
584
585 iterate ascending:
585 iterate ascending:
586 >>> rs = addset(xs, ys, ascending=True)
586 >>> rs = addset(xs, ys, ascending=True)
587 >>> # (use generator because pypy could call len())
587 >>> # (use generator because pypy could call len())
588 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
588 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
589 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
589 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
590 >>> assert not rs._asclist
590 >>> assert not rs._asclist
591 >>> len(rs)
591 >>> len(rs)
592 5
592 5
593 >>> [x for x in rs], [x for x in rs.fastasc()]
593 >>> [x for x in rs], [x for x in rs.fastasc()]
594 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
594 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
595 >>> assert rs._asclist
595 >>> assert rs._asclist
596
596
597 iterate descending:
597 iterate descending:
598 >>> rs = addset(xs, ys, ascending=False)
598 >>> rs = addset(xs, ys, ascending=False)
599 >>> # (use generator because pypy could call len())
599 >>> # (use generator because pypy could call len())
600 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
600 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
601 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
601 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
602 >>> assert not rs._asclist
602 >>> assert not rs._asclist
603 >>> len(rs)
603 >>> len(rs)
604 5
604 5
605 >>> [x for x in rs], [x for x in rs.fastdesc()]
605 >>> [x for x in rs], [x for x in rs.fastdesc()]
606 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
606 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
607 >>> assert rs._asclist
607 >>> assert rs._asclist
608
608
609 iterate ascending without fastasc:
609 iterate ascending without fastasc:
610 >>> rs = addset(xs, generatorset(ys), ascending=True)
610 >>> rs = addset(xs, generatorset(ys), ascending=True)
611 >>> assert rs.fastasc is None
611 >>> assert rs.fastasc is None
612 >>> [x for x in rs]
612 >>> [x for x in rs]
613 [0, 2, 3, 4, 5]
613 [0, 2, 3, 4, 5]
614
614
615 iterate descending without fastdesc:
615 iterate descending without fastdesc:
616 >>> rs = addset(generatorset(xs), ys, ascending=False)
616 >>> rs = addset(generatorset(xs), ys, ascending=False)
617 >>> assert rs.fastdesc is None
617 >>> assert rs.fastdesc is None
618 >>> [x for x in rs]
618 >>> [x for x in rs]
619 [5, 4, 3, 2, 0]
619 [5, 4, 3, 2, 0]
620 """
620 """
621
621
622 def __init__(self, revs1, revs2, ascending=None):
622 def __init__(self, revs1, revs2, ascending=None):
623 self._r1 = revs1
623 self._r1 = revs1
624 self._r2 = revs2
624 self._r2 = revs2
625 self._iter = None
625 self._iter = None
626 self._ascending = ascending
626 self._ascending = ascending
627 self._genlist = None
627 self._genlist = None
628 self._asclist = None
628 self._asclist = None
629
629
630 def __len__(self):
630 def __len__(self):
631 return len(self._list)
631 return len(self._list)
632
632
633 def __nonzero__(self):
633 def __nonzero__(self):
634 return bool(self._r1) or bool(self._r2)
634 return bool(self._r1) or bool(self._r2)
635
635
636 __bool__ = __nonzero__
636 __bool__ = __nonzero__
637
637
638 @util.propertycache
638 @util.propertycache
639 def _list(self):
639 def _list(self):
640 if not self._genlist:
640 if not self._genlist:
641 self._genlist = baseset(iter(self))
641 self._genlist = baseset(iter(self))
642 return self._genlist
642 return self._genlist
643
643
644 def __iter__(self):
644 def __iter__(self):
645 """Iterate over both collections without repeating elements
645 """Iterate over both collections without repeating elements
646
646
647 If the ascending attribute is not set, iterate over the first one and
647 If the ascending attribute is not set, iterate over the first one and
648 then over the second one checking for membership on the first one so we
648 then over the second one checking for membership on the first one so we
649 dont yield any duplicates.
649 dont yield any duplicates.
650
650
651 If the ascending attribute is set, iterate over both collections at the
651 If the ascending attribute is set, iterate over both collections at the
652 same time, yielding only one value at a time in the given order.
652 same time, yielding only one value at a time in the given order.
653 """
653 """
654 if self._ascending is None:
654 if self._ascending is None:
655 if self._genlist:
655 if self._genlist:
656 return iter(self._genlist)
656 return iter(self._genlist)
657
657
658 def arbitraryordergen():
658 def arbitraryordergen():
659 for r in self._r1:
659 for r in self._r1:
660 yield r
660 yield r
661 inr1 = self._r1.__contains__
661 inr1 = self._r1.__contains__
662 for r in self._r2:
662 for r in self._r2:
663 if not inr1(r):
663 if not inr1(r):
664 yield r
664 yield r
665
665
666 return arbitraryordergen()
666 return arbitraryordergen()
667 # try to use our own fast iterator if it exists
667 # try to use our own fast iterator if it exists
668 self._trysetasclist()
668 self._trysetasclist()
669 if self._ascending:
669 if self._ascending:
670 attr = 'fastasc'
670 attr = 'fastasc'
671 else:
671 else:
672 attr = 'fastdesc'
672 attr = 'fastdesc'
673 it = getattr(self, attr)
673 it = getattr(self, attr)
674 if it is not None:
674 if it is not None:
675 return it()
675 return it()
676 # maybe half of the component supports fast
676 # maybe half of the component supports fast
677 # get iterator for _r1
677 # get iterator for _r1
678 iter1 = getattr(self._r1, attr)
678 iter1 = getattr(self._r1, attr)
679 if iter1 is None:
679 if iter1 is None:
680 # let's avoid side effect (not sure it matters)
680 # let's avoid side effect (not sure it matters)
681 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
681 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
682 else:
682 else:
683 iter1 = iter1()
683 iter1 = iter1()
684 # get iterator for _r2
684 # get iterator for _r2
685 iter2 = getattr(self._r2, attr)
685 iter2 = getattr(self._r2, attr)
686 if iter2 is None:
686 if iter2 is None:
687 # let's avoid side effect (not sure it matters)
687 # let's avoid side effect (not sure it matters)
688 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
688 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
689 else:
689 else:
690 iter2 = iter2()
690 iter2 = iter2()
691 return _iterordered(self._ascending, iter1, iter2)
691 return _iterordered(self._ascending, iter1, iter2)
692
692
693 def _trysetasclist(self):
693 def _trysetasclist(self):
694 """populate the _asclist attribute if possible and necessary"""
694 """populate the _asclist attribute if possible and necessary"""
695 if self._genlist is not None and self._asclist is None:
695 if self._genlist is not None and self._asclist is None:
696 self._asclist = sorted(self._genlist)
696 self._asclist = sorted(self._genlist)
697
697
698 @property
698 @property
699 def fastasc(self):
699 def fastasc(self):
700 self._trysetasclist()
700 self._trysetasclist()
701 if self._asclist is not None:
701 if self._asclist is not None:
702 return self._asclist.__iter__
702 return self._asclist.__iter__
703 iter1 = self._r1.fastasc
703 iter1 = self._r1.fastasc
704 iter2 = self._r2.fastasc
704 iter2 = self._r2.fastasc
705 if None in (iter1, iter2):
705 if None in (iter1, iter2):
706 return None
706 return None
707 return lambda: _iterordered(True, iter1(), iter2())
707 return lambda: _iterordered(True, iter1(), iter2())
708
708
709 @property
709 @property
710 def fastdesc(self):
710 def fastdesc(self):
711 self._trysetasclist()
711 self._trysetasclist()
712 if self._asclist is not None:
712 if self._asclist is not None:
713 return self._asclist.__reversed__
713 return self._asclist.__reversed__
714 iter1 = self._r1.fastdesc
714 iter1 = self._r1.fastdesc
715 iter2 = self._r2.fastdesc
715 iter2 = self._r2.fastdesc
716 if None in (iter1, iter2):
716 if None in (iter1, iter2):
717 return None
717 return None
718 return lambda: _iterordered(False, iter1(), iter2())
718 return lambda: _iterordered(False, iter1(), iter2())
719
719
720 def __contains__(self, x):
720 def __contains__(self, x):
721 return x in self._r1 or x in self._r2
721 return x in self._r1 or x in self._r2
722
722
723 def sort(self, reverse=False):
723 def sort(self, reverse=False):
724 """Sort the added set
724 """Sort the added set
725
725
726 For this we use the cached list with all the generated values and if we
726 For this we use the cached list with all the generated values and if we
727 know they are ascending or descending we can sort them in a smart way.
727 know they are ascending or descending we can sort them in a smart way.
728 """
728 """
729 self._ascending = not reverse
729 self._ascending = not reverse
730
730
731 def isascending(self):
731 def isascending(self):
732 return self._ascending is not None and self._ascending
732 return self._ascending is not None and self._ascending
733
733
734 def isdescending(self):
734 def isdescending(self):
735 return self._ascending is not None and not self._ascending
735 return self._ascending is not None and not self._ascending
736
736
737 def istopo(self):
737 def istopo(self):
738 # not worth the trouble asserting if the two sets combined are still
738 # not worth the trouble asserting if the two sets combined are still
739 # in topographical order. Use the sort() predicate to explicitly sort
739 # in topographical order. Use the sort() predicate to explicitly sort
740 # again instead.
740 # again instead.
741 return False
741 return False
742
742
743 def reverse(self):
743 def reverse(self):
744 if self._ascending is None:
744 if self._ascending is None:
745 self._list.reverse()
745 self._list.reverse()
746 else:
746 else:
747 self._ascending = not self._ascending
747 self._ascending = not self._ascending
748
748
749 def first(self):
749 def first(self):
750 for x in self:
750 for x in self:
751 return x
751 return x
752 return None
752 return None
753
753
754 def last(self):
754 def last(self):
755 self.reverse()
755 self.reverse()
756 val = self.first()
756 val = self.first()
757 self.reverse()
757 self.reverse()
758 return val
758 return val
759
759
760 @encoding.strmethod
760 @encoding.strmethod
761 def __repr__(self):
761 def __repr__(self):
762 d = {None: b'', False: b'-', True: b'+'}[self._ascending]
762 d = {None: b'', False: b'-', True: b'+'}[self._ascending]
763 return b'<%s%s %r, %r>' % (_typename(self), d, self._r1, self._r2)
763 return b'<%s%s %r, %r>' % (_typename(self), d, self._r1, self._r2)
764
764
765
765
766 class generatorset(abstractsmartset):
766 class generatorset(abstractsmartset):
767 """Wrap a generator for lazy iteration
767 """Wrap a generator for lazy iteration
768
768
769 Wrapper structure for generators that provides lazy membership and can
769 Wrapper structure for generators that provides lazy membership and can
770 be iterated more than once.
770 be iterated more than once.
771 When asked for membership it generates values until either it finds the
771 When asked for membership it generates values until either it finds the
772 requested one or has gone through all the elements in the generator
772 requested one or has gone through all the elements in the generator
773
773
774 >>> xs = generatorset([0, 1, 4], iterasc=True)
774 >>> xs = generatorset([0, 1, 4], iterasc=True)
775 >>> assert xs.last() == xs.last()
775 >>> assert xs.last() == xs.last()
776 >>> xs.last() # cached
776 >>> xs.last() # cached
777 4
777 4
778 """
778 """
779
779
780 def __new__(cls, gen, iterasc=None):
780 def __new__(cls, gen, iterasc=None):
781 if iterasc is None:
781 if iterasc is None:
782 typ = cls
782 typ = cls
783 elif iterasc:
783 elif iterasc:
784 typ = _generatorsetasc
784 typ = _generatorsetasc
785 else:
785 else:
786 typ = _generatorsetdesc
786 typ = _generatorsetdesc
787
787
788 return super(generatorset, cls).__new__(typ)
788 return super(generatorset, cls).__new__(typ)
789
789
790 def __init__(self, gen, iterasc=None):
790 def __init__(self, gen, iterasc=None):
791 """
791 """
792 gen: a generator producing the values for the generatorset.
792 gen: a generator producing the values for the generatorset.
793 """
793 """
794 self._gen = gen
794 self._gen = gen
795 self._asclist = None
795 self._asclist = None
796 self._cache = {}
796 self._cache = {}
797 self._genlist = []
797 self._genlist = []
798 self._finished = False
798 self._finished = False
799 self._ascending = True
799 self._ascending = True
800
800
801 def __nonzero__(self):
801 def __nonzero__(self):
802 # Do not use 'for r in self' because it will enforce the iteration
802 # Do not use 'for r in self' because it will enforce the iteration
803 # order (default ascending), possibly unrolling a whole descending
803 # order (default ascending), possibly unrolling a whole descending
804 # iterator.
804 # iterator.
805 if self._genlist:
805 if self._genlist:
806 return True
806 return True
807 for r in self._consumegen():
807 for r in self._consumegen():
808 return True
808 return True
809 return False
809 return False
810
810
811 __bool__ = __nonzero__
811 __bool__ = __nonzero__
812
812
813 def __contains__(self, x):
813 def __contains__(self, x):
814 if x in self._cache:
814 if x in self._cache:
815 return self._cache[x]
815 return self._cache[x]
816
816
817 # Use new values only, as existing values would be cached.
817 # Use new values only, as existing values would be cached.
818 for l in self._consumegen():
818 for l in self._consumegen():
819 if l == x:
819 if l == x:
820 return True
820 return True
821
821
822 self._cache[x] = False
822 self._cache[x] = False
823 return False
823 return False
824
824
825 def __iter__(self):
825 def __iter__(self):
826 if self._ascending:
826 if self._ascending:
827 it = self.fastasc
827 it = self.fastasc
828 else:
828 else:
829 it = self.fastdesc
829 it = self.fastdesc
830 if it is not None:
830 if it is not None:
831 return it()
831 return it()
832 # we need to consume the iterator
832 # we need to consume the iterator
833 for x in self._consumegen():
833 for x in self._consumegen():
834 pass
834 pass
835 # recall the same code
835 # recall the same code
836 return iter(self)
836 return iter(self)
837
837
838 def _iterator(self):
838 def _iterator(self):
839 if self._finished:
839 if self._finished:
840 return iter(self._genlist)
840 return iter(self._genlist)
841
841
842 # We have to use this complex iteration strategy to allow multiple
842 # We have to use this complex iteration strategy to allow multiple
843 # iterations at the same time. We need to be able to catch revision
843 # iterations at the same time. We need to be able to catch revision
844 # removed from _consumegen and added to genlist in another instance.
844 # removed from _consumegen and added to genlist in another instance.
845 #
845 #
846 # Getting rid of it would provide an about 15% speed up on this
846 # Getting rid of it would provide an about 15% speed up on this
847 # iteration.
847 # iteration.
848 genlist = self._genlist
848 genlist = self._genlist
849 nextgen = self._consumegen()
849 nextgen = self._consumegen()
850 _len, _next = len, next # cache global lookup
850 _len, _next = len, next # cache global lookup
851
851
852 def gen():
852 def gen():
853 i = 0
853 i = 0
854 while True:
854 while True:
855 if i < _len(genlist):
855 if i < _len(genlist):
856 yield genlist[i]
856 yield genlist[i]
857 else:
857 else:
858 try:
858 try:
859 yield _next(nextgen)
859 yield _next(nextgen)
860 except StopIteration:
860 except StopIteration:
861 return
861 return
862 i += 1
862 i += 1
863
863
864 return gen()
864 return gen()
865
865
866 def _consumegen(self):
866 def _consumegen(self):
867 cache = self._cache
867 cache = self._cache
868 genlist = self._genlist.append
868 genlist = self._genlist.append
869 for item in self._gen:
869 for item in self._gen:
870 cache[item] = True
870 cache[item] = True
871 genlist(item)
871 genlist(item)
872 yield item
872 yield item
873 if not self._finished:
873 if not self._finished:
874 self._finished = True
874 self._finished = True
875 asc = self._genlist[:]
875 asc = self._genlist[:]
876 asc.sort()
876 asc.sort()
877 self._asclist = asc
877 self._asclist = asc
878 self.fastasc = asc.__iter__
878 self.fastasc = asc.__iter__
879 self.fastdesc = asc.__reversed__
879 self.fastdesc = asc.__reversed__
880
880
881 def __len__(self):
881 def __len__(self):
882 for x in self._consumegen():
882 for x in self._consumegen():
883 pass
883 pass
884 return len(self._genlist)
884 return len(self._genlist)
885
885
886 def sort(self, reverse=False):
886 def sort(self, reverse=False):
887 self._ascending = not reverse
887 self._ascending = not reverse
888
888
889 def reverse(self):
889 def reverse(self):
890 self._ascending = not self._ascending
890 self._ascending = not self._ascending
891
891
892 def isascending(self):
892 def isascending(self):
893 return self._ascending
893 return self._ascending
894
894
895 def isdescending(self):
895 def isdescending(self):
896 return not self._ascending
896 return not self._ascending
897
897
898 def istopo(self):
898 def istopo(self):
899 # not worth the trouble asserting if the two sets combined are still
899 # not worth the trouble asserting if the two sets combined are still
900 # in topographical order. Use the sort() predicate to explicitly sort
900 # in topographical order. Use the sort() predicate to explicitly sort
901 # again instead.
901 # again instead.
902 return False
902 return False
903
903
904 def first(self):
904 def first(self):
905 if self._ascending:
905 if self._ascending:
906 it = self.fastasc
906 it = self.fastasc
907 else:
907 else:
908 it = self.fastdesc
908 it = self.fastdesc
909 if it is None:
909 if it is None:
910 # we need to consume all and try again
910 # we need to consume all and try again
911 for x in self._consumegen():
911 for x in self._consumegen():
912 pass
912 pass
913 return self.first()
913 return self.first()
914 return next(it(), None)
914 return next(it(), None)
915
915
916 def last(self):
916 def last(self):
917 if self._ascending:
917 if self._ascending:
918 it = self.fastdesc
918 it = self.fastdesc
919 else:
919 else:
920 it = self.fastasc
920 it = self.fastasc
921 if it is None:
921 if it is None:
922 # we need to consume all and try again
922 # we need to consume all and try again
923 for x in self._consumegen():
923 for x in self._consumegen():
924 pass
924 pass
925 return self.last()
925 return self.last()
926 return next(it(), None)
926 return next(it(), None)
927
927
928 @encoding.strmethod
928 @encoding.strmethod
929 def __repr__(self):
929 def __repr__(self):
930 d = {False: b'-', True: b'+'}[self._ascending]
930 d = {False: b'-', True: b'+'}[self._ascending]
931 return b'<%s%s>' % (_typename(self), d)
931 return b'<%s%s>' % (_typename(self), d)
932
932
933
933
934 class _generatorsetasc(generatorset):
934 class _generatorsetasc(generatorset):
935 """Special case of generatorset optimized for ascending generators."""
935 """Special case of generatorset optimized for ascending generators."""
936
936
937 fastasc = generatorset._iterator
937 fastasc = generatorset._iterator
938
938
939 def __contains__(self, x):
939 def __contains__(self, x):
940 if x in self._cache:
940 if x in self._cache:
941 return self._cache[x]
941 return self._cache[x]
942
942
943 # Use new values only, as existing values would be cached.
943 # Use new values only, as existing values would be cached.
944 for l in self._consumegen():
944 for l in self._consumegen():
945 if l == x:
945 if l == x:
946 return True
946 return True
947 if l > x:
947 if l > x:
948 break
948 break
949
949
950 self._cache[x] = False
950 self._cache[x] = False
951 return False
951 return False
952
952
953
953
954 class _generatorsetdesc(generatorset):
954 class _generatorsetdesc(generatorset):
955 """Special case of generatorset optimized for descending generators."""
955 """Special case of generatorset optimized for descending generators."""
956
956
957 fastdesc = generatorset._iterator
957 fastdesc = generatorset._iterator
958
958
959 def __contains__(self, x):
959 def __contains__(self, x):
960 if x in self._cache:
960 if x in self._cache:
961 return self._cache[x]
961 return self._cache[x]
962
962
963 # Use new values only, as existing values would be cached.
963 # Use new values only, as existing values would be cached.
964 for l in self._consumegen():
964 for l in self._consumegen():
965 if l == x:
965 if l == x:
966 return True
966 return True
967 if l < x:
967 if l < x:
968 break
968 break
969
969
970 self._cache[x] = False
970 self._cache[x] = False
971 return False
971 return False
972
972
973
973
974 def spanset(repo, start=0, end=None):
974 def spanset(repo, start=0, end=None):
975 """Create a spanset that represents a range of repository revisions
975 """Create a spanset that represents a range of repository revisions
976
976
977 start: first revision included the set (default to 0)
977 start: first revision included the set (default to 0)
978 end: first revision excluded (last+1) (default to len(repo))
978 end: first revision excluded (last+1) (default to len(repo))
979
979
980 Spanset will be descending if `end` < `start`.
980 Spanset will be descending if `end` < `start`.
981 """
981 """
982 if end is None:
982 if end is None:
983 end = len(repo)
983 end = len(repo)
984 ascending = start <= end
984 ascending = start <= end
985 if not ascending:
985 if not ascending:
986 start, end = end + 1, start + 1
986 start, end = end + 1, start + 1
987 return _spanset(start, end, ascending, repo.changelog.filteredrevs)
987 return _spanset(start, end, ascending, repo.changelog.filteredrevs)
988
988
989
989
990 class _spanset(abstractsmartset):
990 class _spanset(abstractsmartset):
991 """Duck type for baseset class which represents a range of revisions and
991 """Duck type for baseset class which represents a range of revisions and
992 can work lazily and without having all the range in memory
992 can work lazily and without having all the range in memory
993
993
994 Note that spanset(x, y) behave almost like range(x, y) except for two
994 Note that spanset(x, y) behave almost like range(x, y) except for two
995 notable points:
995 notable points:
996 - when x < y it will be automatically descending,
996 - when x < y it will be automatically descending,
997 - revision filtered with this repoview will be skipped.
997 - revision filtered with this repoview will be skipped.
998
998
999 """
999 """
1000
1000
1001 def __init__(self, start, end, ascending, hiddenrevs):
1001 def __init__(self, start, end, ascending, hiddenrevs):
1002 self._start = start
1002 self._start = start
1003 self._end = end
1003 self._end = end
1004 self._ascending = ascending
1004 self._ascending = ascending
1005 self._hiddenrevs = hiddenrevs
1005 self._hiddenrevs = hiddenrevs
1006
1006
1007 def sort(self, reverse=False):
1007 def sort(self, reverse=False):
1008 self._ascending = not reverse
1008 self._ascending = not reverse
1009
1009
1010 def reverse(self):
1010 def reverse(self):
1011 self._ascending = not self._ascending
1011 self._ascending = not self._ascending
1012
1012
1013 def istopo(self):
1013 def istopo(self):
1014 # not worth the trouble asserting if the two sets combined are still
1014 # not worth the trouble asserting if the two sets combined are still
1015 # in topographical order. Use the sort() predicate to explicitly sort
1015 # in topographical order. Use the sort() predicate to explicitly sort
1016 # again instead.
1016 # again instead.
1017 return False
1017 return False
1018
1018
1019 def _iterfilter(self, iterrange):
1019 def _iterfilter(self, iterrange):
1020 s = self._hiddenrevs
1020 s = self._hiddenrevs
1021 for r in iterrange:
1021 for r in iterrange:
1022 if r not in s:
1022 if r not in s:
1023 yield r
1023 yield r
1024
1024
1025 def __iter__(self):
1025 def __iter__(self):
1026 if self._ascending:
1026 if self._ascending:
1027 return self.fastasc()
1027 return self.fastasc()
1028 else:
1028 else:
1029 return self.fastdesc()
1029 return self.fastdesc()
1030
1030
1031 def fastasc(self):
1031 def fastasc(self):
1032 iterrange = range(self._start, self._end)
1032 iterrange = range(self._start, self._end)
1033 if self._hiddenrevs:
1033 if self._hiddenrevs:
1034 return self._iterfilter(iterrange)
1034 return self._iterfilter(iterrange)
1035 return iter(iterrange)
1035 return iter(iterrange)
1036
1036
1037 def fastdesc(self):
1037 def fastdesc(self):
1038 iterrange = range(self._end - 1, self._start - 1, -1)
1038 iterrange = range(self._end - 1, self._start - 1, -1)
1039 if self._hiddenrevs:
1039 if self._hiddenrevs:
1040 return self._iterfilter(iterrange)
1040 return self._iterfilter(iterrange)
1041 return iter(iterrange)
1041 return iter(iterrange)
1042
1042
1043 def __contains__(self, rev):
1043 def __contains__(self, rev):
1044 hidden = self._hiddenrevs
1044 hidden = self._hiddenrevs
1045 return (self._start <= rev < self._end) and not (
1045 return (self._start <= rev < self._end) and not (
1046 hidden and rev in hidden
1046 hidden and rev in hidden
1047 )
1047 )
1048
1048
1049 def __nonzero__(self):
1049 def __nonzero__(self):
1050 for r in self:
1050 for r in self:
1051 return True
1051 return True
1052 return False
1052 return False
1053
1053
1054 __bool__ = __nonzero__
1054 __bool__ = __nonzero__
1055
1055
1056 def __len__(self):
1056 def __len__(self):
1057 if not self._hiddenrevs:
1057 if not self._hiddenrevs:
1058 return abs(self._end - self._start)
1058 return abs(self._end - self._start)
1059 else:
1059 else:
1060 count = 0
1060 count = 0
1061 start = self._start
1061 start = self._start
1062 end = self._end
1062 end = self._end
1063 for rev in self._hiddenrevs:
1063 for rev in self._hiddenrevs:
1064 if (end < rev <= start) or (start <= rev < end):
1064 if (end < rev <= start) or (start <= rev < end):
1065 count += 1
1065 count += 1
1066 return abs(self._end - self._start) - count
1066 return abs(self._end - self._start) - count
1067
1067
1068 def isascending(self):
1068 def isascending(self):
1069 return self._ascending
1069 return self._ascending
1070
1070
1071 def isdescending(self):
1071 def isdescending(self):
1072 return not self._ascending
1072 return not self._ascending
1073
1073
1074 def first(self):
1074 def first(self):
1075 if self._ascending:
1075 if self._ascending:
1076 it = self.fastasc
1076 it = self.fastasc
1077 else:
1077 else:
1078 it = self.fastdesc
1078 it = self.fastdesc
1079 for x in it():
1079 for x in it():
1080 return x
1080 return x
1081 return None
1081 return None
1082
1082
1083 def last(self):
1083 def last(self):
1084 if self._ascending:
1084 if self._ascending:
1085 it = self.fastdesc
1085 it = self.fastdesc
1086 else:
1086 else:
1087 it = self.fastasc
1087 it = self.fastasc
1088 for x in it():
1088 for x in it():
1089 return x
1089 return x
1090 return None
1090 return None
1091
1091
1092 def _slice(self, start, stop):
1092 def _slice(self, start, stop):
1093 if self._hiddenrevs:
1093 if self._hiddenrevs:
1094 # unoptimized since all hidden revisions in range has to be scanned
1094 # unoptimized since all hidden revisions in range has to be scanned
1095 return super(_spanset, self)._slice(start, stop)
1095 return super(_spanset, self)._slice(start, stop)
1096 if self._ascending:
1096 if self._ascending:
1097 x = min(self._start + start, self._end)
1097 x = min(self._start + start, self._end)
1098 y = min(self._start + stop, self._end)
1098 y = min(self._start + stop, self._end)
1099 else:
1099 else:
1100 x = max(self._end - stop, self._start)
1100 x = max(self._end - stop, self._start)
1101 y = max(self._end - start, self._start)
1101 y = max(self._end - start, self._start)
1102 return _spanset(x, y, self._ascending, self._hiddenrevs)
1102 return _spanset(x, y, self._ascending, self._hiddenrevs)
1103
1103
1104 @encoding.strmethod
1104 @encoding.strmethod
1105 def __repr__(self):
1105 def __repr__(self):
1106 d = {False: b'-', True: b'+'}[self._ascending]
1106 d = {False: b'-', True: b'+'}[self._ascending]
1107 return b'<%s%s %d:%d>' % (_typename(self), d, self._start, self._end)
1107 return b'<%s%s %d:%d>' % (_typename(self), d, self._start, self._end)
1108
1108
1109
1109
1110 class fullreposet(_spanset):
1110 class fullreposet(_spanset):
1111 """a set containing all revisions in the repo
1111 """a set containing all revisions in the repo
1112
1112
1113 This class exists to host special optimization and magic to handle virtual
1113 This class exists to host special optimization and magic to handle virtual
1114 revisions such as "null".
1114 revisions such as "null".
1115 """
1115 """
1116
1116
1117 def __init__(self, repo):
1117 def __init__(self, repo):
1118 super(fullreposet, self).__init__(
1118 super(fullreposet, self).__init__(
1119 0, len(repo), True, repo.changelog.filteredrevs
1119 0, len(repo), True, repo.changelog.filteredrevs
1120 )
1120 )
1121
1121
1122 def __and__(self, other):
1122 def __and__(self, other):
1123 """As self contains the whole repo, all of the other set should also be
1123 """As self contains the whole repo, all of the other set should also be
1124 in self. Therefore `self & other = other`.
1124 in self. Therefore `self & other = other`.
1125
1125
1126 This boldly assumes the other contains valid revs only.
1126 This boldly assumes the other contains valid revs only.
1127 """
1127 """
1128 # other not a smartset, make is so
1128 # other not a smartset, make is so
1129 if not hasattr(other, 'isascending'):
1129 if not hasattr(other, 'isascending'):
1130 # filter out hidden revision
1130 # filter out hidden revision
1131 # (this boldly assumes all smartset are pure)
1131 # (this boldly assumes all smartset are pure)
1132 #
1132 #
1133 # `other` was used with "&", let's assume this is a set like
1133 # `other` was used with "&", let's assume this is a set like
1134 # object.
1134 # object.
1135 other = baseset(other - self._hiddenrevs)
1135 other = baseset(other)
1136
1137 if self._hiddenrevs:
1138 other = other - self._hiddenrevs
1136
1139
1137 other.sort(reverse=self.isdescending())
1140 other.sort(reverse=self.isdescending())
1138 return other
1141 return other
@@ -1,1148 +1,1146 b''
1 $ cat > $TESTTMP/hook.sh << 'EOF'
1 $ cat > $TESTTMP/hook.sh << 'EOF'
2 > echo "test-hook-close-phase: $HG_NODE: $HG_OLDPHASE -> $HG_PHASE"
2 > echo "test-hook-close-phase: $HG_NODE: $HG_OLDPHASE -> $HG_PHASE"
3 > EOF
3 > EOF
4
4
5 $ cat >> $HGRCPATH << EOF
5 $ cat >> $HGRCPATH << EOF
6 > [extensions]
6 > [extensions]
7 > phasereport=$TESTDIR/testlib/ext-phase-report.py
7 > phasereport=$TESTDIR/testlib/ext-phase-report.py
8 > [hooks]
8 > [hooks]
9 > txnclose-phase.test = sh $TESTTMP/hook.sh
9 > txnclose-phase.test = sh $TESTTMP/hook.sh
10 > EOF
10 > EOF
11
11
12 $ hglog() { hg log -G --template "{rev} {phaseidx} {desc}\n" $*; }
12 $ hglog() { hg log -G --template "{rev} {phaseidx} {desc}\n" $*; }
13 $ mkcommit() {
13 $ mkcommit() {
14 > echo "$1" > "$1"
14 > echo "$1" > "$1"
15 > hg add "$1"
15 > hg add "$1"
16 > message="$1"
16 > message="$1"
17 > shift
17 > shift
18 > hg ci -m "$message" $*
18 > hg ci -m "$message" $*
19 > }
19 > }
20
20
21 $ hg init initialrepo
21 $ hg init initialrepo
22 $ cd initialrepo
22 $ cd initialrepo
23
23
24 Cannot change null revision phase
24 Cannot change null revision phase
25
25
26 $ hg phase --force --secret null
26 $ hg phase --force --secret null
27 abort: cannot change null revision phase
27 abort: cannot change null revision phase
28 [255]
28 [255]
29 $ hg phase null
29 $ hg phase null
30 -1: public
30 -1: public
31
31
32 $ mkcommit A
32 $ mkcommit A
33 test-debug-phase: new rev 0: x -> 1
33 test-debug-phase: new rev 0: x -> 1
34 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
34 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
35
35
36 New commit are draft by default
36 New commit are draft by default
37
37
38 $ hglog
38 $ hglog
39 @ 0 1 A
39 @ 0 1 A
40
40
41
41
42 Following commit are draft too
42 Following commit are draft too
43
43
44 $ mkcommit B
44 $ mkcommit B
45 test-debug-phase: new rev 1: x -> 1
45 test-debug-phase: new rev 1: x -> 1
46 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> draft
46 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> draft
47
47
48 $ hglog
48 $ hglog
49 @ 1 1 B
49 @ 1 1 B
50 |
50 |
51 o 0 1 A
51 o 0 1 A
52
52
53
53
54 Working directory phase is secret when its parent is secret.
54 Working directory phase is secret when its parent is secret.
55
55
56 $ hg phase --force --secret .
56 $ hg phase --force --secret .
57 test-debug-phase: move rev 0: 1 -> 2
57 test-debug-phase: move rev 0: 1 -> 2
58 test-debug-phase: move rev 1: 1 -> 2
58 test-debug-phase: move rev 1: 1 -> 2
59 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> secret
59 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> secret
60 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> secret
60 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> secret
61 $ hg log -r 'wdir()' -T '{phase}\n'
61 $ hg log -r 'wdir()' -T '{phase}\n'
62 secret
62 secret
63 $ hg log -r 'wdir() and public()' -T '{phase}\n'
63 $ hg log -r 'wdir() and public()' -T '{phase}\n'
64 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
64 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
65 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
65 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
66 secret
66 secret
67
67
68 Working directory phase is draft when its parent is draft.
68 Working directory phase is draft when its parent is draft.
69
69
70 $ hg phase --draft .
70 $ hg phase --draft .
71 test-debug-phase: move rev 1: 2 -> 1
71 test-debug-phase: move rev 1: 2 -> 1
72 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: secret -> draft
72 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: secret -> draft
73 $ hg log -r 'wdir()' -T '{phase}\n'
73 $ hg log -r 'wdir()' -T '{phase}\n'
74 draft
74 draft
75 $ hg log -r 'wdir() and public()' -T '{phase}\n'
75 $ hg log -r 'wdir() and public()' -T '{phase}\n'
76 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
76 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
77 draft
77 draft
78 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
78 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
79
79
80 Working directory phase is secret when a new commit will be created as secret,
80 Working directory phase is secret when a new commit will be created as secret,
81 even if the parent is draft.
81 even if the parent is draft.
82
82
83 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
83 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
84 > --config phases.new-commit='secret'
84 > --config phases.new-commit='secret'
85 secret
85 secret
86
86
87 Working directory phase is draft when its parent is public.
87 Working directory phase is draft when its parent is public.
88
88
89 $ hg phase --public .
89 $ hg phase --public .
90 test-debug-phase: move rev 0: 1 -> 0
90 test-debug-phase: move rev 0: 1 -> 0
91 test-debug-phase: move rev 1: 1 -> 0
91 test-debug-phase: move rev 1: 1 -> 0
92 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> public
92 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> public
93 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
93 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
94 $ hg log -r 'wdir()' -T '{phase}\n'
94 $ hg log -r 'wdir()' -T '{phase}\n'
95 draft
95 draft
96 $ hg log -r 'wdir() and public()' -T '{phase}\n'
96 $ hg log -r 'wdir() and public()' -T '{phase}\n'
97 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
97 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
98 draft
98 draft
99 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
99 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
100 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
100 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
101 > --config phases.new-commit='secret'
101 > --config phases.new-commit='secret'
102 secret
102 secret
103
103
104 Draft commit are properly created over public one:
104 Draft commit are properly created over public one:
105
105
106 $ hg phase
106 $ hg phase
107 1: public
107 1: public
108 $ hglog
108 $ hglog
109 @ 1 0 B
109 @ 1 0 B
110 |
110 |
111 o 0 0 A
111 o 0 0 A
112
112
113
113
114 $ mkcommit C
114 $ mkcommit C
115 test-debug-phase: new rev 2: x -> 1
115 test-debug-phase: new rev 2: x -> 1
116 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
116 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
117 $ mkcommit D
117 $ mkcommit D
118 test-debug-phase: new rev 3: x -> 1
118 test-debug-phase: new rev 3: x -> 1
119 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
119 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
120
120
121 $ hglog
121 $ hglog
122 @ 3 1 D
122 @ 3 1 D
123 |
123 |
124 o 2 1 C
124 o 2 1 C
125 |
125 |
126 o 1 0 B
126 o 1 0 B
127 |
127 |
128 o 0 0 A
128 o 0 0 A
129
129
130
130
131 Test creating changeset as secret
131 Test creating changeset as secret
132
132
133 $ mkcommit E --config phases.new-commit='secret'
133 $ mkcommit E --config phases.new-commit='secret'
134 test-debug-phase: new rev 4: x -> 2
134 test-debug-phase: new rev 4: x -> 2
135 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> secret
135 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> secret
136 $ hglog
136 $ hglog
137 @ 4 2 E
137 @ 4 2 E
138 |
138 |
139 o 3 1 D
139 o 3 1 D
140 |
140 |
141 o 2 1 C
141 o 2 1 C
142 |
142 |
143 o 1 0 B
143 o 1 0 B
144 |
144 |
145 o 0 0 A
145 o 0 0 A
146
146
147
147
148 Test the secret property is inherited
148 Test the secret property is inherited
149
149
150 $ mkcommit H
150 $ mkcommit H
151 test-debug-phase: new rev 5: x -> 2
151 test-debug-phase: new rev 5: x -> 2
152 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
152 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
153 $ hglog
153 $ hglog
154 @ 5 2 H
154 @ 5 2 H
155 |
155 |
156 o 4 2 E
156 o 4 2 E
157 |
157 |
158 o 3 1 D
158 o 3 1 D
159 |
159 |
160 o 2 1 C
160 o 2 1 C
161 |
161 |
162 o 1 0 B
162 o 1 0 B
163 |
163 |
164 o 0 0 A
164 o 0 0 A
165
165
166
166
167 Even on merge
167 Even on merge
168
168
169 $ hg up -q 1
169 $ hg up -q 1
170 $ mkcommit "B'"
170 $ mkcommit "B'"
171 test-debug-phase: new rev 6: x -> 1
171 test-debug-phase: new rev 6: x -> 1
172 created new head
172 created new head
173 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
173 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
174 $ hglog
174 $ hglog
175 @ 6 1 B'
175 @ 6 1 B'
176 |
176 |
177 | o 5 2 H
177 | o 5 2 H
178 | |
178 | |
179 | o 4 2 E
179 | o 4 2 E
180 | |
180 | |
181 | o 3 1 D
181 | o 3 1 D
182 | |
182 | |
183 | o 2 1 C
183 | o 2 1 C
184 |/
184 |/
185 o 1 0 B
185 o 1 0 B
186 |
186 |
187 o 0 0 A
187 o 0 0 A
188
188
189 $ hg merge 4 # E
189 $ hg merge 4 # E
190 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 (branch merge, don't forget to commit)
191 (branch merge, don't forget to commit)
192 $ hg phase
192 $ hg phase
193 6: draft
193 6: draft
194 4: secret
194 4: secret
195 $ hg ci -m "merge B' and E"
195 $ hg ci -m "merge B' and E"
196 test-debug-phase: new rev 7: x -> 2
196 test-debug-phase: new rev 7: x -> 2
197 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> secret
197 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> secret
198
198
199 $ hglog
199 $ hglog
200 @ 7 2 merge B' and E
200 @ 7 2 merge B' and E
201 |\
201 |\
202 | o 6 1 B'
202 | o 6 1 B'
203 | |
203 | |
204 +---o 5 2 H
204 +---o 5 2 H
205 | |
205 | |
206 o | 4 2 E
206 o | 4 2 E
207 | |
207 | |
208 o | 3 1 D
208 o | 3 1 D
209 | |
209 | |
210 o | 2 1 C
210 o | 2 1 C
211 |/
211 |/
212 o 1 0 B
212 o 1 0 B
213 |
213 |
214 o 0 0 A
214 o 0 0 A
215
215
216
216
217 Test secret changeset are not pushed
217 Test secret changeset are not pushed
218
218
219 $ hg init ../push-dest
219 $ hg init ../push-dest
220 $ cat > ../push-dest/.hg/hgrc << EOF
220 $ cat > ../push-dest/.hg/hgrc << EOF
221 > [phases]
221 > [phases]
222 > publish=False
222 > publish=False
223 > EOF
223 > EOF
224 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
224 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
225 comparing with ../push-dest
225 comparing with ../push-dest
226 searching for changes
226 searching for changes
227 0 public A
227 0 public A
228 1 public B
228 1 public B
229 2 draft C
229 2 draft C
230 3 draft D
230 3 draft D
231 6 draft B'
231 6 draft B'
232 $ hg outgoing -r 'branch(default)' ../push-dest --template='{rev} {phase} {desc|firstline}\n'
232 $ hg outgoing -r 'branch(default)' ../push-dest --template='{rev} {phase} {desc|firstline}\n'
233 comparing with ../push-dest
233 comparing with ../push-dest
234 searching for changes
234 searching for changes
235 0 public A
235 0 public A
236 1 public B
236 1 public B
237 2 draft C
237 2 draft C
238 3 draft D
238 3 draft D
239 6 draft B'
239 6 draft B'
240
240
241 $ hg push ../push-dest -f # force because we push multiple heads
241 $ hg push ../push-dest -f # force because we push multiple heads
242 pushing to ../push-dest
242 pushing to ../push-dest
243 searching for changes
243 searching for changes
244 adding changesets
244 adding changesets
245 adding manifests
245 adding manifests
246 adding file changes
246 adding file changes
247 added 5 changesets with 5 changes to 5 files (+1 heads)
247 added 5 changesets with 5 changes to 5 files (+1 heads)
248 test-debug-phase: new rev 0: x -> 0
248 test-debug-phase: new rev 0: x -> 0
249 test-debug-phase: new rev 1: x -> 0
249 test-debug-phase: new rev 1: x -> 0
250 test-debug-phase: new rev 2: x -> 1
250 test-debug-phase: new rev 2: x -> 1
251 test-debug-phase: new rev 3: x -> 1
251 test-debug-phase: new rev 3: x -> 1
252 test-debug-phase: new rev 4: x -> 1
252 test-debug-phase: new rev 4: x -> 1
253 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
253 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
254 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
254 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
255 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
255 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
256 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
256 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
257 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
257 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
258 $ hglog
258 $ hglog
259 @ 7 2 merge B' and E
259 @ 7 2 merge B' and E
260 |\
260 |\
261 | o 6 1 B'
261 | o 6 1 B'
262 | |
262 | |
263 +---o 5 2 H
263 +---o 5 2 H
264 | |
264 | |
265 o | 4 2 E
265 o | 4 2 E
266 | |
266 | |
267 o | 3 1 D
267 o | 3 1 D
268 | |
268 | |
269 o | 2 1 C
269 o | 2 1 C
270 |/
270 |/
271 o 1 0 B
271 o 1 0 B
272 |
272 |
273 o 0 0 A
273 o 0 0 A
274
274
275 $ cd ../push-dest
275 $ cd ../push-dest
276 $ hglog
276 $ hglog
277 o 4 1 B'
277 o 4 1 B'
278 |
278 |
279 | o 3 1 D
279 | o 3 1 D
280 | |
280 | |
281 | o 2 1 C
281 | o 2 1 C
282 |/
282 |/
283 o 1 0 B
283 o 1 0 B
284 |
284 |
285 o 0 0 A
285 o 0 0 A
286
286
287
287
288 (Issue3303)
288 (Issue3303)
289 Check that remote secret changeset are ignore when checking creation of remote heads
289 Check that remote secret changeset are ignore when checking creation of remote heads
290
290
291 We add a secret head into the push destination. This secret head shadows a
291 We add a secret head into the push destination. This secret head shadows a
292 visible shared between the initial repo and the push destination.
292 visible shared between the initial repo and the push destination.
293
293
294 $ hg up -q 4 # B'
294 $ hg up -q 4 # B'
295 $ mkcommit Z --config phases.new-commit=secret
295 $ mkcommit Z --config phases.new-commit=secret
296 test-debug-phase: new rev 5: x -> 2
296 test-debug-phase: new rev 5: x -> 2
297 test-hook-close-phase: 2713879da13d6eea1ff22b442a5a87cb31a7ce6a: -> secret
297 test-hook-close-phase: 2713879da13d6eea1ff22b442a5a87cb31a7ce6a: -> secret
298 $ hg phase .
298 $ hg phase .
299 5: secret
299 5: secret
300
300
301 We now try to push a new public changeset that descend from the common public
301 We now try to push a new public changeset that descend from the common public
302 head shadowed by the remote secret head.
302 head shadowed by the remote secret head.
303
303
304 $ cd ../initialrepo
304 $ cd ../initialrepo
305 $ hg up -q 6 #B'
305 $ hg up -q 6 #B'
306 $ mkcommit I
306 $ mkcommit I
307 test-debug-phase: new rev 8: x -> 1
307 test-debug-phase: new rev 8: x -> 1
308 created new head
308 created new head
309 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
309 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
310 $ hg push ../push-dest
310 $ hg push ../push-dest
311 pushing to ../push-dest
311 pushing to ../push-dest
312 searching for changes
312 searching for changes
313 adding changesets
313 adding changesets
314 adding manifests
314 adding manifests
315 adding file changes
315 adding file changes
316 added 1 changesets with 1 changes to 1 files (+1 heads)
316 added 1 changesets with 1 changes to 1 files (+1 heads)
317 test-debug-phase: new rev 6: x -> 1
317 test-debug-phase: new rev 6: x -> 1
318 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
318 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
319
319
320 :note: The "(+1 heads)" is wrong as we do not had any visible head
320 :note: The "(+1 heads)" is wrong as we do not had any visible head
321
321
322 check that branch cache with "served" filter are properly computed and stored
322 check that branch cache with "served" filter are properly computed and stored
323
323
324 $ ls ../push-dest/.hg/cache/branch2*
324 $ ls ../push-dest/.hg/cache/branch2*
325 ../push-dest/.hg/cache/branch2-base
325 ../push-dest/.hg/cache/branch2-base
326 ../push-dest/.hg/cache/branch2-served
326 ../push-dest/.hg/cache/branch2-served
327 $ cat ../push-dest/.hg/cache/branch2-served
327 $ cat ../push-dest/.hg/cache/branch2-served
328 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
328 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
329 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
329 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
330 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
330 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
331 $ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n' #update visible cache too
331 $ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n' #update visible cache too
332 6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
332 6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
333 5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
333 5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
334 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
334 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
335 $ ls ../push-dest/.hg/cache/branch2*
335 $ ls ../push-dest/.hg/cache/branch2*
336 ../push-dest/.hg/cache/branch2-base
336 ../push-dest/.hg/cache/branch2-base
337 ../push-dest/.hg/cache/branch2-served
337 ../push-dest/.hg/cache/branch2-served
338 ../push-dest/.hg/cache/branch2-visible
338 ../push-dest/.hg/cache/branch2-visible
339 $ cat ../push-dest/.hg/cache/branch2-served
339 $ cat ../push-dest/.hg/cache/branch2-served
340 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
340 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
341 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
341 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
342 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
342 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
343 $ cat ../push-dest/.hg/cache/branch2-visible
343 $ cat ../push-dest/.hg/cache/branch2-visible
344 6d6770faffce199f1fddd1cf87f6f026138cf061 6
344 6d6770faffce199f1fddd1cf87f6f026138cf061 6
345 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
345 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
346 2713879da13d6eea1ff22b442a5a87cb31a7ce6a o default
346 2713879da13d6eea1ff22b442a5a87cb31a7ce6a o default
347 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
347 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
348
348
349
349
350 Restore condition prior extra insertion.
350 Restore condition prior extra insertion.
351 $ hg -q --config extensions.mq= strip .
351 $ hg -q --config extensions.mq= strip .
352 $ hg up -q 7
352 $ hg up -q 7
353 $ cd ..
353 $ cd ..
354
354
355 Test secret changeset are not pull
355 Test secret changeset are not pull
356
356
357 $ hg init pull-dest
357 $ hg init pull-dest
358 $ cd pull-dest
358 $ cd pull-dest
359 $ hg pull ../initialrepo
359 $ hg pull ../initialrepo
360 pulling from ../initialrepo
360 pulling from ../initialrepo
361 requesting all changes
361 requesting all changes
362 adding changesets
362 adding changesets
363 adding manifests
363 adding manifests
364 adding file changes
364 adding file changes
365 added 5 changesets with 5 changes to 5 files (+1 heads)
365 added 5 changesets with 5 changes to 5 files (+1 heads)
366 new changesets 4a2df7238c3b:cf9fe039dfd6
366 new changesets 4a2df7238c3b:cf9fe039dfd6
367 test-debug-phase: new rev 0: x -> 0
367 test-debug-phase: new rev 0: x -> 0
368 test-debug-phase: new rev 1: x -> 0
368 test-debug-phase: new rev 1: x -> 0
369 test-debug-phase: new rev 2: x -> 0
369 test-debug-phase: new rev 2: x -> 0
370 test-debug-phase: new rev 3: x -> 0
370 test-debug-phase: new rev 3: x -> 0
371 test-debug-phase: new rev 4: x -> 0
371 test-debug-phase: new rev 4: x -> 0
372 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
372 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
373 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
373 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
374 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
374 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
375 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
375 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
376 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
376 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
377 (run 'hg heads' to see heads, 'hg merge' to merge)
377 (run 'hg heads' to see heads, 'hg merge' to merge)
378 $ hglog
378 $ hglog
379 o 4 0 B'
379 o 4 0 B'
380 |
380 |
381 | o 3 0 D
381 | o 3 0 D
382 | |
382 | |
383 | o 2 0 C
383 | o 2 0 C
384 |/
384 |/
385 o 1 0 B
385 o 1 0 B
386 |
386 |
387 o 0 0 A
387 o 0 0 A
388
388
389 $ cd ..
389 $ cd ..
390
390
391 But secret can still be bundled explicitly
391 But secret can still be bundled explicitly
392
392
393 $ cd initialrepo
393 $ cd initialrepo
394 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
394 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
395 4 changesets found
395 4 changesets found
396 $ cd ..
396 $ cd ..
397
397
398 Test secret changeset are not cloned
398 Test secret changeset are not cloned
399 (during local clone)
399 (during local clone)
400
400
401 $ hg clone -qU initialrepo clone-dest
401 $ hg clone -qU initialrepo clone-dest
402 test-debug-phase: new rev 0: x -> 0
402 test-debug-phase: new rev 0: x -> 0
403 test-debug-phase: new rev 1: x -> 0
403 test-debug-phase: new rev 1: x -> 0
404 test-debug-phase: new rev 2: x -> 0
404 test-debug-phase: new rev 2: x -> 0
405 test-debug-phase: new rev 3: x -> 0
405 test-debug-phase: new rev 3: x -> 0
406 test-debug-phase: new rev 4: x -> 0
406 test-debug-phase: new rev 4: x -> 0
407 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
407 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
408 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
408 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
409 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
409 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
410 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
410 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
411 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
411 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
412 $ hglog -R clone-dest
412 $ hglog -R clone-dest
413 o 4 0 B'
413 o 4 0 B'
414 |
414 |
415 | o 3 0 D
415 | o 3 0 D
416 | |
416 | |
417 | o 2 0 C
417 | o 2 0 C
418 |/
418 |/
419 o 1 0 B
419 o 1 0 B
420 |
420 |
421 o 0 0 A
421 o 0 0 A
422
422
423
423
424 Test summary
424 Test summary
425
425
426 $ hg summary -R clone-dest --verbose
426 $ hg summary -R clone-dest --verbose
427 parent: -1:000000000000 (no revision checked out)
427 parent: -1:000000000000 (no revision checked out)
428 branch: default
428 branch: default
429 commit: (clean)
429 commit: (clean)
430 update: 5 new changesets (update)
430 update: 5 new changesets (update)
431 $ hg summary -R initialrepo
431 $ hg summary -R initialrepo
432 parent: 7:17a481b3bccb tip
432 parent: 7:17a481b3bccb tip
433 merge B' and E
433 merge B' and E
434 branch: default
434 branch: default
435 commit: (clean) (secret)
435 commit: (clean) (secret)
436 update: 1 new changesets, 2 branch heads (merge)
436 update: 1 new changesets, 2 branch heads (merge)
437 phases: 3 draft, 3 secret
437 phases: 3 draft, 3 secret
438 $ hg summary -R initialrepo --quiet
438 $ hg summary -R initialrepo --quiet
439 parent: 7:17a481b3bccb tip
439 parent: 7:17a481b3bccb tip
440 update: 1 new changesets, 2 branch heads (merge)
440 update: 1 new changesets, 2 branch heads (merge)
441
441
442 Test revset
442 Test revset
443
443
444 $ cd initialrepo
444 $ cd initialrepo
445 $ hglog -r 'public()'
445 $ hglog -r 'public()'
446 o 1 0 B
446 o 1 0 B
447 |
447 |
448 o 0 0 A
448 o 0 0 A
449
449
450 $ hglog -r 'draft()'
450 $ hglog -r 'draft()'
451 o 6 1 B'
451 o 6 1 B'
452 |
452 |
453 ~
453 ~
454 o 3 1 D
454 o 3 1 D
455 |
455 |
456 o 2 1 C
456 o 2 1 C
457 |
457 |
458 ~
458 ~
459 $ hglog -r 'secret()'
459 $ hglog -r 'secret()'
460 @ 7 2 merge B' and E
460 @ 7 2 merge B' and E
461 |\
461 |\
462 | ~
462 | ~
463 | o 5 2 H
463 | o 5 2 H
464 |/
464 |/
465 o 4 2 E
465 o 4 2 E
466 |
466 |
467 ~
467 ~
468
468
469 test that phase are displayed in log at debug level
469 test that phase are displayed in log at debug level
470
470
471 $ hg log --debug
471 $ hg log --debug
472 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
472 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
473 tag: tip
473 tag: tip
474 phase: secret
474 phase: secret
475 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
475 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
476 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
476 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
477 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
477 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
478 user: test
478 user: test
479 date: Thu Jan 01 00:00:00 1970 +0000
479 date: Thu Jan 01 00:00:00 1970 +0000
480 extra: branch=default
480 extra: branch=default
481 description:
481 description:
482 merge B' and E
482 merge B' and E
483
483
484
484
485 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
485 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
486 phase: draft
486 phase: draft
487 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
487 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
488 parent: -1:0000000000000000000000000000000000000000
488 parent: -1:0000000000000000000000000000000000000000
489 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
489 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
490 user: test
490 user: test
491 date: Thu Jan 01 00:00:00 1970 +0000
491 date: Thu Jan 01 00:00:00 1970 +0000
492 files+: B'
492 files+: B'
493 extra: branch=default
493 extra: branch=default
494 description:
494 description:
495 B'
495 B'
496
496
497
497
498 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
498 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
499 phase: secret
499 phase: secret
500 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
500 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
501 parent: -1:0000000000000000000000000000000000000000
501 parent: -1:0000000000000000000000000000000000000000
502 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
502 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
503 user: test
503 user: test
504 date: Thu Jan 01 00:00:00 1970 +0000
504 date: Thu Jan 01 00:00:00 1970 +0000
505 files+: H
505 files+: H
506 extra: branch=default
506 extra: branch=default
507 description:
507 description:
508 H
508 H
509
509
510
510
511 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
511 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
512 phase: secret
512 phase: secret
513 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
513 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
514 parent: -1:0000000000000000000000000000000000000000
514 parent: -1:0000000000000000000000000000000000000000
515 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
515 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
516 user: test
516 user: test
517 date: Thu Jan 01 00:00:00 1970 +0000
517 date: Thu Jan 01 00:00:00 1970 +0000
518 files+: E
518 files+: E
519 extra: branch=default
519 extra: branch=default
520 description:
520 description:
521 E
521 E
522
522
523
523
524 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
524 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
525 phase: draft
525 phase: draft
526 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
526 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
527 parent: -1:0000000000000000000000000000000000000000
527 parent: -1:0000000000000000000000000000000000000000
528 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
528 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
529 user: test
529 user: test
530 date: Thu Jan 01 00:00:00 1970 +0000
530 date: Thu Jan 01 00:00:00 1970 +0000
531 files+: D
531 files+: D
532 extra: branch=default
532 extra: branch=default
533 description:
533 description:
534 D
534 D
535
535
536
536
537 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
537 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
538 phase: draft
538 phase: draft
539 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
539 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
540 parent: -1:0000000000000000000000000000000000000000
540 parent: -1:0000000000000000000000000000000000000000
541 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
541 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
542 user: test
542 user: test
543 date: Thu Jan 01 00:00:00 1970 +0000
543 date: Thu Jan 01 00:00:00 1970 +0000
544 files+: C
544 files+: C
545 extra: branch=default
545 extra: branch=default
546 description:
546 description:
547 C
547 C
548
548
549
549
550 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
550 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
551 phase: public
551 phase: public
552 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
552 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
553 parent: -1:0000000000000000000000000000000000000000
553 parent: -1:0000000000000000000000000000000000000000
554 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
554 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
555 user: test
555 user: test
556 date: Thu Jan 01 00:00:00 1970 +0000
556 date: Thu Jan 01 00:00:00 1970 +0000
557 files+: B
557 files+: B
558 extra: branch=default
558 extra: branch=default
559 description:
559 description:
560 B
560 B
561
561
562
562
563 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
563 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
564 phase: public
564 phase: public
565 parent: -1:0000000000000000000000000000000000000000
565 parent: -1:0000000000000000000000000000000000000000
566 parent: -1:0000000000000000000000000000000000000000
566 parent: -1:0000000000000000000000000000000000000000
567 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
567 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
568 user: test
568 user: test
569 date: Thu Jan 01 00:00:00 1970 +0000
569 date: Thu Jan 01 00:00:00 1970 +0000
570 files+: A
570 files+: A
571 extra: branch=default
571 extra: branch=default
572 description:
572 description:
573 A
573 A
574
574
575
575
576
576
577
577
578 (Issue3707)
578 (Issue3707)
579 test invalid phase name
579 test invalid phase name
580
580
581 $ mkcommit I --config phases.new-commit='babar'
581 $ mkcommit I --config phases.new-commit='babar'
582 transaction abort!
582 transaction abort!
583 rollback completed
583 rollback completed
584 config error: phases.new-commit: not a valid phase name ('babar')
584 config error: phases.new-commit: not a valid phase name ('babar')
585 [30]
585 [30]
586 Test phase command
586 Test phase command
587 ===================
587 ===================
588
588
589 initial picture
589 initial picture
590
590
591 $ hg log -G --template "{rev} {phase} {desc}\n"
591 $ hg log -G --template "{rev} {phase} {desc}\n"
592 @ 7 secret merge B' and E
592 @ 7 secret merge B' and E
593 |\
593 |\
594 | o 6 draft B'
594 | o 6 draft B'
595 | |
595 | |
596 +---o 5 secret H
596 +---o 5 secret H
597 | |
597 | |
598 o | 4 secret E
598 o | 4 secret E
599 | |
599 | |
600 o | 3 draft D
600 o | 3 draft D
601 | |
601 | |
602 o | 2 draft C
602 o | 2 draft C
603 |/
603 |/
604 o 1 public B
604 o 1 public B
605 |
605 |
606 o 0 public A
606 o 0 public A
607
607
608
608
609 display changesets phase
609 display changesets phase
610
610
611 (mixing -r and plain rev specification)
611 (mixing -r and plain rev specification)
612
612
613 $ hg phase 1::4 -r 7
613 $ hg phase 1::4 -r 7
614 1: public
614 1: public
615 2: draft
615 2: draft
616 3: draft
616 3: draft
617 4: secret
617 4: secret
618 7: secret
618 7: secret
619
619
620
620
621 move changeset forward
621 move changeset forward
622
622
623 (with -r option)
623 (with -r option)
624
624
625 $ hg phase --public -r 2
625 $ hg phase --public -r 2
626 test-debug-phase: move rev 2: 1 -> 0
626 test-debug-phase: move rev 2: 1 -> 0
627 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
627 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
628 $ hg log -G --template "{rev} {phase} {desc}\n"
628 $ hg log -G --template "{rev} {phase} {desc}\n"
629 @ 7 secret merge B' and E
629 @ 7 secret merge B' and E
630 |\
630 |\
631 | o 6 draft B'
631 | o 6 draft B'
632 | |
632 | |
633 +---o 5 secret H
633 +---o 5 secret H
634 | |
634 | |
635 o | 4 secret E
635 o | 4 secret E
636 | |
636 | |
637 o | 3 draft D
637 o | 3 draft D
638 | |
638 | |
639 o | 2 public C
639 o | 2 public C
640 |/
640 |/
641 o 1 public B
641 o 1 public B
642 |
642 |
643 o 0 public A
643 o 0 public A
644
644
645
645
646 move changeset backward
646 move changeset backward
647
647
648 (without -r option)
648 (without -r option)
649
649
650 $ hg phase --draft --force 2
650 $ hg phase --draft --force 2
651 test-debug-phase: move rev 2: 0 -> 1
651 test-debug-phase: move rev 2: 0 -> 1
652 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: public -> draft
652 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: public -> draft
653 $ hg log -G --template "{rev} {phase} {desc}\n"
653 $ hg log -G --template "{rev} {phase} {desc}\n"
654 @ 7 secret merge B' and E
654 @ 7 secret merge B' and E
655 |\
655 |\
656 | o 6 draft B'
656 | o 6 draft B'
657 | |
657 | |
658 +---o 5 secret H
658 +---o 5 secret H
659 | |
659 | |
660 o | 4 secret E
660 o | 4 secret E
661 | |
661 | |
662 o | 3 draft D
662 o | 3 draft D
663 | |
663 | |
664 o | 2 draft C
664 o | 2 draft C
665 |/
665 |/
666 o 1 public B
666 o 1 public B
667 |
667 |
668 o 0 public A
668 o 0 public A
669
669
670
670
671 move changeset forward and backward
671 move changeset forward and backward
672
672
673 $ hg phase --draft --force 1::4
673 $ hg phase --draft --force 1::4
674 test-debug-phase: move rev 1: 0 -> 1
674 test-debug-phase: move rev 1: 0 -> 1
675 test-debug-phase: move rev 4: 2 -> 1
675 test-debug-phase: move rev 4: 2 -> 1
676 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: public -> draft
676 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: public -> draft
677 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
677 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
678 $ hg log -G --template "{rev} {phase} {desc}\n"
678 $ hg log -G --template "{rev} {phase} {desc}\n"
679 @ 7 secret merge B' and E
679 @ 7 secret merge B' and E
680 |\
680 |\
681 | o 6 draft B'
681 | o 6 draft B'
682 | |
682 | |
683 +---o 5 secret H
683 +---o 5 secret H
684 | |
684 | |
685 o | 4 draft E
685 o | 4 draft E
686 | |
686 | |
687 o | 3 draft D
687 o | 3 draft D
688 | |
688 | |
689 o | 2 draft C
689 o | 2 draft C
690 |/
690 |/
691 o 1 draft B
691 o 1 draft B
692 |
692 |
693 o 0 public A
693 o 0 public A
694
694
695 test partial failure
695 test partial failure
696
696
697 $ hg phase --public 7
697 $ hg phase --public 7
698 test-debug-phase: move rev 1: 1 -> 0
698 test-debug-phase: move rev 1: 1 -> 0
699 test-debug-phase: move rev 2: 1 -> 0
699 test-debug-phase: move rev 2: 1 -> 0
700 test-debug-phase: move rev 3: 1 -> 0
700 test-debug-phase: move rev 3: 1 -> 0
701 test-debug-phase: move rev 4: 1 -> 0
701 test-debug-phase: move rev 4: 1 -> 0
702 test-debug-phase: move rev 6: 1 -> 0
702 test-debug-phase: move rev 6: 1 -> 0
703 test-debug-phase: move rev 7: 2 -> 0
703 test-debug-phase: move rev 7: 2 -> 0
704 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
704 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
705 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
705 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
706 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: draft -> public
706 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: draft -> public
707 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: draft -> public
707 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: draft -> public
708 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: draft -> public
708 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: draft -> public
709 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> public
709 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> public
710 $ hg phase --draft '5 or 7'
710 $ hg phase --draft '5 or 7'
711 test-debug-phase: move rev 5: 2 -> 1
711 test-debug-phase: move rev 5: 2 -> 1
712 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: secret -> draft
712 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: secret -> draft
713 cannot move 1 changesets to a higher phase, use --force
713 cannot move 1 changesets to a higher phase, use --force
714 phase changed for 1 changesets
714 phase changed for 1 changesets
715 [1]
715 [1]
716 $ hg log -G --template "{rev} {phase} {desc}\n"
716 $ hg log -G --template "{rev} {phase} {desc}\n"
717 @ 7 public merge B' and E
717 @ 7 public merge B' and E
718 |\
718 |\
719 | o 6 public B'
719 | o 6 public B'
720 | |
720 | |
721 +---o 5 draft H
721 +---o 5 draft H
722 | |
722 | |
723 o | 4 public E
723 o | 4 public E
724 | |
724 | |
725 o | 3 public D
725 o | 3 public D
726 | |
726 | |
727 o | 2 public C
727 o | 2 public C
728 |/
728 |/
729 o 1 public B
729 o 1 public B
730 |
730 |
731 o 0 public A
731 o 0 public A
732
732
733
733
734 test complete failure
734 test complete failure
735
735
736 $ hg phase --draft 7
736 $ hg phase --draft 7
737 cannot move 1 changesets to a higher phase, use --force
737 cannot move 1 changesets to a higher phase, use --force
738 no phases changed
738 no phases changed
739 [1]
739 [1]
740
740
741 $ cd ..
741 $ cd ..
742
742
743 test hidden changeset are not cloned as public (issue3935)
743 test hidden changeset are not cloned as public (issue3935)
744
744
745 $ cd initialrepo
745 $ cd initialrepo
746
746
747 (enabling evolution)
747 (enabling evolution)
748 $ cat >> $HGRCPATH << EOF
748 $ cat >> $HGRCPATH << EOF
749 > [experimental]
749 > [experimental]
750 > evolution.createmarkers=True
750 > evolution.createmarkers=True
751 > EOF
751 > EOF
752
752
753 (making a changeset hidden; H in that case)
753 (making a changeset hidden; H in that case)
754 $ hg debugobsolete `hg id --debug -r 5`
754 $ hg debugobsolete `hg id --debug -r 5`
755 1 new obsolescence markers
755 1 new obsolescence markers
756 obsoleted 1 changesets
756 obsoleted 1 changesets
757
757
758 $ cd ..
758 $ cd ..
759 $ hg clone initialrepo clonewithobs
759 $ hg clone initialrepo clonewithobs
760 requesting all changes
760 requesting all changes
761 adding changesets
761 adding changesets
762 adding manifests
762 adding manifests
763 adding file changes
763 adding file changes
764 added 7 changesets with 6 changes to 6 files
764 added 7 changesets with 6 changes to 6 files
765 new changesets 4a2df7238c3b:17a481b3bccb
765 new changesets 4a2df7238c3b:17a481b3bccb
766 test-debug-phase: new rev 0: x -> 0
766 test-debug-phase: new rev 0: x -> 0
767 test-debug-phase: new rev 1: x -> 0
767 test-debug-phase: new rev 1: x -> 0
768 test-debug-phase: new rev 2: x -> 0
768 test-debug-phase: new rev 2: x -> 0
769 test-debug-phase: new rev 3: x -> 0
769 test-debug-phase: new rev 3: x -> 0
770 test-debug-phase: new rev 4: x -> 0
770 test-debug-phase: new rev 4: x -> 0
771 test-debug-phase: new rev 5: x -> 0
771 test-debug-phase: new rev 5: x -> 0
772 test-debug-phase: new rev 6: x -> 0
772 test-debug-phase: new rev 6: x -> 0
773 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
773 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
774 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
774 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
775 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
775 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
776 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
776 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
777 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> public
777 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> public
778 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
778 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
779 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> public
779 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> public
780 updating to branch default
780 updating to branch default
781 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
781 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
782 $ cd clonewithobs
782 $ cd clonewithobs
783 $ hg log -G --template "{rev} {phase} {desc}\n"
783 $ hg log -G --template "{rev} {phase} {desc}\n"
784 @ 6 public merge B' and E
784 @ 6 public merge B' and E
785 |\
785 |\
786 | o 5 public B'
786 | o 5 public B'
787 | |
787 | |
788 o | 4 public E
788 o | 4 public E
789 | |
789 | |
790 o | 3 public D
790 o | 3 public D
791 | |
791 | |
792 o | 2 public C
792 o | 2 public C
793 |/
793 |/
794 o 1 public B
794 o 1 public B
795 |
795 |
796 o 0 public A
796 o 0 public A
797
797
798
798
799 test verify repo containing hidden changesets, which should not abort just
799 test verify repo containing hidden changesets, which should not abort just
800 because repo.cancopy() is False
800 because repo.cancopy() is False
801
801
802 $ cd ../initialrepo
802 $ cd ../initialrepo
803 $ hg verify -q
803 $ hg verify -q
804
804
805 $ cd ..
805 $ cd ..
806
806
807 check whether HG_PENDING makes pending changes only in related
807 check whether HG_PENDING makes pending changes only in related
808 repositories visible to an external hook.
808 repositories visible to an external hook.
809
809
810 (emulate a transaction running concurrently by copied
810 (emulate a transaction running concurrently by copied
811 .hg/phaseroots.pending in subsequent test)
811 .hg/phaseroots.pending in subsequent test)
812
812
813 $ cat > $TESTTMP/savepending.sh <<EOF
813 $ cat > $TESTTMP/savepending.sh <<EOF
814 > cp .hg/store/phaseroots.pending .hg/store/phaseroots.pending.saved
814 > cp .hg/store/phaseroots.pending .hg/store/phaseroots.pending.saved
815 > exit 1 # to avoid changing phase for subsequent tests
815 > exit 1 # to avoid changing phase for subsequent tests
816 > EOF
816 > EOF
817 $ cd push-dest
817 $ cd push-dest
818 $ hg phase 6
818 $ hg phase 6
819 6: draft
819 6: draft
820 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" phase -f -s 6
820 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" phase -f -s 6
821 abort: pretxnclose hook exited with status 1
821 abort: pretxnclose hook exited with status 1
822 [40]
822 [40]
823 $ cp .hg/store/phaseroots.pending.saved .hg/store/phaseroots.pending
823 $ cp .hg/store/phaseroots.pending.saved .hg/store/phaseroots.pending
824
824
825 (check (in)visibility of phaseroot while transaction running in repo)
825 (check (in)visibility of phaseroot while transaction running in repo)
826
826
827 $ cat > $TESTTMP/checkpending.sh <<EOF
827 $ cat > $TESTTMP/checkpending.sh <<EOF
828 > echo '@initialrepo'
828 > echo '@initialrepo'
829 > hg -R "$TESTTMP/initialrepo" phase 7
829 > hg -R "$TESTTMP/initialrepo" phase 7
830 > echo '@push-dest'
830 > echo '@push-dest'
831 > hg -R "$TESTTMP/push-dest" phase 6
831 > hg -R "$TESTTMP/push-dest" phase 6
832 > exit 1 # to avoid changing phase for subsequent tests
832 > exit 1 # to avoid changing phase for subsequent tests
833 > EOF
833 > EOF
834 $ cd ../initialrepo
834 $ cd ../initialrepo
835 $ hg phase 7
835 $ hg phase 7
836 7: public
836 7: public
837 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" phase -f -s 7
837 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" phase -f -s 7
838 @initialrepo
838 @initialrepo
839 7: secret
839 7: secret
840 @push-dest
840 @push-dest
841 6: draft
841 6: draft
842 abort: pretxnclose hook exited with status 1
842 abort: pretxnclose hook exited with status 1
843 [40]
843 [40]
844
844
845 Check that pretxnclose-phase hook can control phase movement
845 Check that pretxnclose-phase hook can control phase movement
846
846
847 $ hg phase --force b3325c91a4d9 --secret
847 $ hg phase --force b3325c91a4d9 --secret
848 test-debug-phase: move rev 3: 0 -> 2
848 test-debug-phase: move rev 3: 0 -> 2
849 test-debug-phase: move rev 4: 0 -> 2
849 test-debug-phase: move rev 4: 0 -> 2
850 test-debug-phase: move rev 5: 1 -> 2
850 test-debug-phase: move rev 5: 1 -> 2
851 test-debug-phase: move rev 7: 0 -> 2
851 test-debug-phase: move rev 7: 0 -> 2
852 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: public -> secret
852 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: public -> secret
853 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: public -> secret
853 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: public -> secret
854 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: draft -> secret
854 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: draft -> secret
855 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: public -> secret
855 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: public -> secret
856 $ hg log -G -T phases
856 $ hg log -G -T phases
857 @ changeset: 7:17a481b3bccb
857 @ changeset: 7:17a481b3bccb
858 |\ tag: tip
858 |\ tag: tip
859 | | phase: secret
859 | | phase: secret
860 | | parent: 6:cf9fe039dfd6
860 | | parent: 6:cf9fe039dfd6
861 | | parent: 4:a603bfb5a83e
861 | | parent: 4:a603bfb5a83e
862 | | user: test
862 | | user: test
863 | | date: Thu Jan 01 00:00:00 1970 +0000
863 | | date: Thu Jan 01 00:00:00 1970 +0000
864 | | summary: merge B' and E
864 | | summary: merge B' and E
865 | |
865 | |
866 | o changeset: 6:cf9fe039dfd6
866 | o changeset: 6:cf9fe039dfd6
867 | | phase: public
867 | | phase: public
868 | | parent: 1:27547f69f254
868 | | parent: 1:27547f69f254
869 | | user: test
869 | | user: test
870 | | date: Thu Jan 01 00:00:00 1970 +0000
870 | | date: Thu Jan 01 00:00:00 1970 +0000
871 | | summary: B'
871 | | summary: B'
872 | |
872 | |
873 o | changeset: 4:a603bfb5a83e
873 o | changeset: 4:a603bfb5a83e
874 | | phase: secret
874 | | phase: secret
875 | | user: test
875 | | user: test
876 | | date: Thu Jan 01 00:00:00 1970 +0000
876 | | date: Thu Jan 01 00:00:00 1970 +0000
877 | | summary: E
877 | | summary: E
878 | |
878 | |
879 o | changeset: 3:b3325c91a4d9
879 o | changeset: 3:b3325c91a4d9
880 | | phase: secret
880 | | phase: secret
881 | | user: test
881 | | user: test
882 | | date: Thu Jan 01 00:00:00 1970 +0000
882 | | date: Thu Jan 01 00:00:00 1970 +0000
883 | | summary: D
883 | | summary: D
884 | |
884 | |
885 o | changeset: 2:f838bfaca5c7
885 o | changeset: 2:f838bfaca5c7
886 |/ phase: public
886 |/ phase: public
887 | user: test
887 | user: test
888 | date: Thu Jan 01 00:00:00 1970 +0000
888 | date: Thu Jan 01 00:00:00 1970 +0000
889 | summary: C
889 | summary: C
890 |
890 |
891 o changeset: 1:27547f69f254
891 o changeset: 1:27547f69f254
892 | phase: public
892 | phase: public
893 | user: test
893 | user: test
894 | date: Thu Jan 01 00:00:00 1970 +0000
894 | date: Thu Jan 01 00:00:00 1970 +0000
895 | summary: B
895 | summary: B
896 |
896 |
897 o changeset: 0:4a2df7238c3b
897 o changeset: 0:4a2df7238c3b
898 phase: public
898 phase: public
899 user: test
899 user: test
900 date: Thu Jan 01 00:00:00 1970 +0000
900 date: Thu Jan 01 00:00:00 1970 +0000
901 summary: A
901 summary: A
902
902
903
903
904 Install a hook that prevent b3325c91a4d9 to become public
904 Install a hook that prevent b3325c91a4d9 to become public
905
905
906 $ cat >> .hg/hgrc << EOF
906 $ cat >> .hg/hgrc << EOF
907 > [hooks]
907 > [hooks]
908 > pretxnclose-phase.nopublish_D = sh -c "(echo \$HG_NODE| grep -v b3325c91a4d9>/dev/null) || [ 'public' != \$HG_PHASE ]"
908 > pretxnclose-phase.nopublish_D = sh -c "(echo \$HG_NODE| grep -v b3325c91a4d9>/dev/null) || [ 'public' != \$HG_PHASE ]"
909 > EOF
909 > EOF
910
910
911 Try various actions. only the draft move should succeed
911 Try various actions. only the draft move should succeed
912
912
913 $ hg phase --public b3325c91a4d9
913 $ hg phase --public b3325c91a4d9
914 abort: pretxnclose-phase.nopublish_D hook exited with status 1
914 abort: pretxnclose-phase.nopublish_D hook exited with status 1
915 [40]
915 [40]
916 $ hg phase --public a603bfb5a83e
916 $ hg phase --public a603bfb5a83e
917 abort: pretxnclose-phase.nopublish_D hook exited with status 1
917 abort: pretxnclose-phase.nopublish_D hook exited with status 1
918 [40]
918 [40]
919 $ hg phase --draft 17a481b3bccb
919 $ hg phase --draft 17a481b3bccb
920 test-debug-phase: move rev 3: 2 -> 1
920 test-debug-phase: move rev 3: 2 -> 1
921 test-debug-phase: move rev 4: 2 -> 1
921 test-debug-phase: move rev 4: 2 -> 1
922 test-debug-phase: move rev 7: 2 -> 1
922 test-debug-phase: move rev 7: 2 -> 1
923 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: secret -> draft
923 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: secret -> draft
924 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
924 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
925 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> draft
925 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> draft
926 $ hg phase --public 17a481b3bccb
926 $ hg phase --public 17a481b3bccb
927 abort: pretxnclose-phase.nopublish_D hook exited with status 1
927 abort: pretxnclose-phase.nopublish_D hook exited with status 1
928 [40]
928 [40]
929
929
930 $ cd ..
930 $ cd ..
931
931
932 Test for the "internal" phase
932 Test for the "internal" phase
933 =============================
933 =============================
934
934
935 Check we deny its usage on older repository
935 Check we deny its usage on older repository
936
936
937 $ hg init no-internal-phase --config format.use-internal-phase=no
937 $ hg init no-internal-phase --config format.use-internal-phase=no
938 $ cd no-internal-phase
938 $ cd no-internal-phase
939 $ hg debugrequires | grep internal-phase
939 $ hg debugrequires | grep internal-phase
940 [1]
940 [1]
941 $ echo X > X
941 $ echo X > X
942 $ hg add X
942 $ hg add X
943 $ hg status
943 $ hg status
944 A X
944 A X
945 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit" 2>&1 | grep ProgrammingError
945 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit" 2>&1 | grep ProgrammingError
946 ** ProgrammingError: this repository does not support the internal phase
946 ** ProgrammingError: this repository does not support the internal phase
947 raise error.ProgrammingError(msg) (no-pyoxidizer !)
947 raise error.ProgrammingError(msg) (no-pyoxidizer !)
948 *ProgrammingError: this repository does not support the internal phase (glob)
948 *ProgrammingError: this repository does not support the internal phase (glob)
949 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit" 2>&1 | grep ProgrammingError
949 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit" 2>&1 | grep ProgrammingError
950 ** ProgrammingError: this repository does not support the archived phase
950 ** ProgrammingError: this repository does not support the archived phase
951 raise error.ProgrammingError(msg) (no-pyoxidizer !)
951 raise error.ProgrammingError(msg) (no-pyoxidizer !)
952 *ProgrammingError: this repository does not support the archived phase (glob)
952 *ProgrammingError: this repository does not support the archived phase (glob)
953
953
954 $ cd ..
954 $ cd ..
955
955
956 Check it works fine with repository that supports it.
956 Check it works fine with repository that supports it.
957
957
958 $ hg init internal-phase --config format.use-internal-phase=yes
958 $ hg init internal-phase --config format.use-internal-phase=yes
959 $ cd internal-phase
959 $ cd internal-phase
960 $ hg debugrequires | grep internal-phase
960 $ hg debugrequires | grep internal-phase
961 internal-phase-2
961 internal-phase-2
962 $ mkcommit A
962 $ mkcommit A
963 test-debug-phase: new rev 0: x -> 1
963 test-debug-phase: new rev 0: x -> 1
964 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
964 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
965
965
966 Commit an internal changesets
966 Commit an internal changesets
967
967
968 $ echo B > B
968 $ echo B > B
969 $ hg add B
969 $ hg add B
970 $ hg status
970 $ hg status
971 A B
971 A B
972 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit"
972 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit"
973 test-debug-phase: new rev 1: x -> 96
973 test-debug-phase: new rev 1: x -> 96
974 test-hook-close-phase: c01c42dffc7f81223397e99652a0703f83e1c5ea: -> internal
974 test-hook-close-phase: c01c42dffc7f81223397e99652a0703f83e1c5ea: -> internal
975
975
976 The changeset is a working parent descendant.
976 The changeset is a working parent descendant.
977 Per the usual visibility rules, it is made visible.
977 Per the usual visibility rules, it is made visible.
978
978
979 $ hg log -G -l 3
979 $ hg log -G -l 3
980 @ changeset: 1:c01c42dffc7f
980 @ changeset: 1:c01c42dffc7f
981 | tag: tip
981 | tag: tip
982 | user: test
982 | user: test
983 | date: Thu Jan 01 00:00:00 1970 +0000
983 | date: Thu Jan 01 00:00:00 1970 +0000
984 | summary: my test internal commit
984 | summary: my test internal commit
985 |
985 |
986 o changeset: 0:4a2df7238c3b
986 o changeset: 0:4a2df7238c3b
987 user: test
987 user: test
988 date: Thu Jan 01 00:00:00 1970 +0000
988 date: Thu Jan 01 00:00:00 1970 +0000
989 summary: A
989 summary: A
990
990
991
991
992 Commit is hidden as expected
992 Commit is hidden as expected
993
993
994 $ hg up 0
994 $ hg up 0
995 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
995 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
996 $ hg log -G
996 $ hg log -G
997 @ changeset: 0:4a2df7238c3b
997 @ changeset: 0:4a2df7238c3b
998 tag: tip
998 tag: tip
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: A
1001 summary: A
1002
1002
1003 The hidden commit is an orphan but doesn't show up without --hidden
1003 The hidden commit is an orphan but doesn't show up without --hidden
1004
1004
1005 $ hg debugobsolete `hg id --debug -ir 0`
1005 $ hg debugobsolete `hg id --debug -ir 0`
1006 1 new obsolescence markers
1006 1 new obsolescence markers
1007 obsoleted 1 changesets
1007 obsoleted 1 changesets
1008 $ hg --hidden log -G -r 'unstable()'
1008 $ hg --hidden log -G -r 'unstable()'
1009 * changeset: 1:c01c42dffc7f
1009 * changeset: 1:c01c42dffc7f
1010 | tag: tip
1010 | tag: tip
1011 ~ user: test
1011 ~ user: test
1012 date: Thu Jan 01 00:00:00 1970 +0000
1012 date: Thu Jan 01 00:00:00 1970 +0000
1013 instability: orphan
1013 instability: orphan
1014 summary: my test internal commit
1014 summary: my test internal commit
1015
1015
1016 $ hg log -G -r 'unstable()'
1016 $ hg log -G -r 'unstable()'
1017 abort: filtered revision '1' (known-bad-output !)
1018 [10]
1019
1017
1020
1018
1021 Test for archived phase
1019 Test for archived phase
1022 -----------------------
1020 -----------------------
1023
1021
1024 Commit an archived changesets
1022 Commit an archived changesets
1025
1023
1026 $ cd ..
1024 $ cd ..
1027 $ hg clone --quiet --pull internal-phase archived-phase \
1025 $ hg clone --quiet --pull internal-phase archived-phase \
1028 > --config format.exp-archived-phase=yes \
1026 > --config format.exp-archived-phase=yes \
1029 > --config extensions.phasereport='!' \
1027 > --config extensions.phasereport='!' \
1030 > --config hooks.txnclose-phase.test=
1028 > --config hooks.txnclose-phase.test=
1031
1029
1032 $ cd archived-phase
1030 $ cd archived-phase
1033
1031
1034 $ echo B > B
1032 $ echo B > B
1035 $ hg add B
1033 $ hg add B
1036 $ hg status
1034 $ hg status
1037 A B
1035 A B
1038 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit"
1036 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit"
1039 test-debug-phase: new rev 1: x -> 32
1037 test-debug-phase: new rev 1: x -> 32
1040 test-hook-close-phase: 8df5997c3361518f733d1ae67cd3adb9b0eaf125: -> archived
1038 test-hook-close-phase: 8df5997c3361518f733d1ae67cd3adb9b0eaf125: -> archived
1041
1039
1042 The changeset is a working parent descendant.
1040 The changeset is a working parent descendant.
1043 Per the usual visibility rules, it is made visible.
1041 Per the usual visibility rules, it is made visible.
1044
1042
1045 $ hg log -G -l 3
1043 $ hg log -G -l 3
1046 @ changeset: 1:8df5997c3361
1044 @ changeset: 1:8df5997c3361
1047 | tag: tip
1045 | tag: tip
1048 | user: test
1046 | user: test
1049 | date: Thu Jan 01 00:00:00 1970 +0000
1047 | date: Thu Jan 01 00:00:00 1970 +0000
1050 | summary: my test archived commit
1048 | summary: my test archived commit
1051 |
1049 |
1052 o changeset: 0:4a2df7238c3b
1050 o changeset: 0:4a2df7238c3b
1053 user: test
1051 user: test
1054 date: Thu Jan 01 00:00:00 1970 +0000
1052 date: Thu Jan 01 00:00:00 1970 +0000
1055 summary: A
1053 summary: A
1056
1054
1057
1055
1058 Commit is hidden as expected
1056 Commit is hidden as expected
1059
1057
1060 $ hg up 0
1058 $ hg up 0
1061 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1059 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1062 $ hg log -G
1060 $ hg log -G
1063 @ changeset: 0:4a2df7238c3b
1061 @ changeset: 0:4a2df7238c3b
1064 tag: tip
1062 tag: tip
1065 user: test
1063 user: test
1066 date: Thu Jan 01 00:00:00 1970 +0000
1064 date: Thu Jan 01 00:00:00 1970 +0000
1067 summary: A
1065 summary: A
1068
1066
1069 $ cd ..
1067 $ cd ..
1070
1068
1071 Recommitting an exact match of a public commit shouldn't change it to
1069 Recommitting an exact match of a public commit shouldn't change it to
1072 draft:
1070 draft:
1073
1071
1074 $ cd initialrepo
1072 $ cd initialrepo
1075 $ hg phase -r 2
1073 $ hg phase -r 2
1076 2: public
1074 2: public
1077 $ hg up -C 1
1075 $ hg up -C 1
1078 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
1076 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
1079 $ mkcommit C
1077 $ mkcommit C
1080 warning: commit already existed in the repository!
1078 warning: commit already existed in the repository!
1081 $ hg phase -r 2
1079 $ hg phase -r 2
1082 2: public
1080 2: public
1083
1081
1084 Same, but for secret:
1082 Same, but for secret:
1085
1083
1086 $ hg up 7
1084 $ hg up 7
1087 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1085 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1088 $ mkcommit F -s
1086 $ mkcommit F -s
1089 test-debug-phase: new rev 8: x -> 2
1087 test-debug-phase: new rev 8: x -> 2
1090 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1088 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1091 $ hg up 7
1089 $ hg up 7
1092 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1090 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1093 $ hg phase
1091 $ hg phase
1094 7: draft
1092 7: draft
1095 $ mkcommit F
1093 $ mkcommit F
1096 test-debug-phase: new rev 8: x -> 2
1094 test-debug-phase: new rev 8: x -> 2
1097 warning: commit already existed in the repository!
1095 warning: commit already existed in the repository!
1098 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1096 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1099 $ hg phase -r tip
1097 $ hg phase -r tip
1100 8: secret
1098 8: secret
1101
1099
1102 But what about obsoleted changesets?
1100 But what about obsoleted changesets?
1103
1101
1104 $ hg up 4
1102 $ hg up 4
1105 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1103 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1106 $ mkcommit H
1104 $ mkcommit H
1107 test-debug-phase: new rev 5: x -> 2
1105 test-debug-phase: new rev 5: x -> 2
1108 warning: commit already existed in the repository!
1106 warning: commit already existed in the repository!
1109 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
1107 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
1110 $ hg phase -r 5
1108 $ hg phase -r 5
1111 5: secret
1109 5: secret
1112 $ hg par
1110 $ hg par
1113 changeset: 5:a030c6be5127
1111 changeset: 5:a030c6be5127
1114 user: test
1112 user: test
1115 date: Thu Jan 01 00:00:00 1970 +0000
1113 date: Thu Jan 01 00:00:00 1970 +0000
1116 obsolete: pruned
1114 obsolete: pruned
1117 summary: H
1115 summary: H
1118
1116
1119 $ hg up tip
1117 $ hg up tip
1120 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1118 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1121 $ cd ..
1119 $ cd ..
1122
1120
1123 Testing that command line flags override configuration
1121 Testing that command line flags override configuration
1124
1122
1125 $ hg init commit-overrides
1123 $ hg init commit-overrides
1126 $ cd commit-overrides
1124 $ cd commit-overrides
1127
1125
1128 `hg commit --draft` overrides new-commit=secret
1126 `hg commit --draft` overrides new-commit=secret
1129
1127
1130 $ mkcommit A --config phases.new-commit='secret' --draft
1128 $ mkcommit A --config phases.new-commit='secret' --draft
1131 test-debug-phase: new rev 0: x -> 1
1129 test-debug-phase: new rev 0: x -> 1
1132 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
1130 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
1133 $ hglog
1131 $ hglog
1134 @ 0 1 A
1132 @ 0 1 A
1135
1133
1136
1134
1137 `hg commit --secret` overrides new-commit=draft
1135 `hg commit --secret` overrides new-commit=draft
1138
1136
1139 $ mkcommit B --config phases.new-commit='draft' --secret
1137 $ mkcommit B --config phases.new-commit='draft' --secret
1140 test-debug-phase: new rev 1: x -> 2
1138 test-debug-phase: new rev 1: x -> 2
1141 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> secret
1139 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> secret
1142 $ hglog
1140 $ hglog
1143 @ 1 2 B
1141 @ 1 2 B
1144 |
1142 |
1145 o 0 1 A
1143 o 0 1 A
1146
1144
1147
1145
1148 $ cd ..
1146 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now