##// END OF EJS Templates
phases: enforce internal phase support...
Boris Feld -
r39335:7775c1fb default
parent child Browse files
Show More
@@ -1,734 +1,741 b''
1 """ Mercurial phases support code
1 """ Mercurial phases support code
2
2
3 ---
3 ---
4
4
5 Copyright 2011 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
5 Copyright 2011 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
6 Logilab SA <contact@logilab.fr>
6 Logilab SA <contact@logilab.fr>
7 Augie Fackler <durin42@gmail.com>
7 Augie Fackler <durin42@gmail.com>
8
8
9 This software may be used and distributed according to the terms
9 This software may be used and distributed according to the terms
10 of the GNU General Public License version 2 or any later version.
10 of the GNU General Public License version 2 or any later version.
11
11
12 ---
12 ---
13
13
14 This module implements most phase logic in mercurial.
14 This module implements most phase logic in mercurial.
15
15
16
16
17 Basic Concept
17 Basic Concept
18 =============
18 =============
19
19
20 A 'changeset phase' is an indicator that tells us how a changeset is
20 A 'changeset phase' is an indicator that tells us how a changeset is
21 manipulated and communicated. The details of each phase is described
21 manipulated and communicated. The details of each phase is described
22 below, here we describe the properties they have in common.
22 below, here we describe the properties they have in common.
23
23
24 Like bookmarks, phases are not stored in history and thus are not
24 Like bookmarks, phases are not stored in history and thus are not
25 permanent and leave no audit trail.
25 permanent and leave no audit trail.
26
26
27 First, no changeset can be in two phases at once. Phases are ordered,
27 First, no changeset can be in two phases at once. Phases are ordered,
28 so they can be considered from lowest to highest. The default, lowest
28 so they can be considered from lowest to highest. The default, lowest
29 phase is 'public' - this is the normal phase of existing changesets. A
29 phase is 'public' - this is the normal phase of existing changesets. A
30 child changeset can not be in a lower phase than its parents.
30 child changeset can not be in a lower phase than its parents.
31
31
32 These phases share a hierarchy of traits:
32 These phases share a hierarchy of traits:
33
33
34 immutable shared
34 immutable shared
35 public: X X
35 public: X X
36 draft: X
36 draft: X
37 secret:
37 secret:
38
38
39 Local commits are draft by default.
39 Local commits are draft by default.
40
40
41 Phase Movement and Exchange
41 Phase Movement and Exchange
42 ===========================
42 ===========================
43
43
44 Phase data is exchanged by pushkey on pull and push. Some servers have
44 Phase data is exchanged by pushkey on pull and push. Some servers have
45 a publish option set, we call such a server a "publishing server".
45 a publish option set, we call such a server a "publishing server".
46 Pushing a draft changeset to a publishing server changes the phase to
46 Pushing a draft changeset to a publishing server changes the phase to
47 public.
47 public.
48
48
49 A small list of fact/rules define the exchange of phase:
49 A small list of fact/rules define the exchange of phase:
50
50
51 * old client never changes server states
51 * old client never changes server states
52 * pull never changes server states
52 * pull never changes server states
53 * publish and old server changesets are seen as public by client
53 * publish and old server changesets are seen as public by client
54 * any secret changeset seen in another repository is lowered to at
54 * any secret changeset seen in another repository is lowered to at
55 least draft
55 least draft
56
56
57 Here is the final table summing up the 49 possible use cases of phase
57 Here is the final table summing up the 49 possible use cases of phase
58 exchange:
58 exchange:
59
59
60 server
60 server
61 old publish non-publish
61 old publish non-publish
62 N X N D P N D P
62 N X N D P N D P
63 old client
63 old client
64 pull
64 pull
65 N - X/X - X/D X/P - X/D X/P
65 N - X/X - X/D X/P - X/D X/P
66 X - X/X - X/D X/P - X/D X/P
66 X - X/X - X/D X/P - X/D X/P
67 push
67 push
68 X X/X X/X X/P X/P X/P X/D X/D X/P
68 X X/X X/X X/P X/P X/P X/D X/D X/P
69 new client
69 new client
70 pull
70 pull
71 N - P/X - P/D P/P - D/D P/P
71 N - P/X - P/D P/P - D/D P/P
72 D - P/X - P/D P/P - D/D P/P
72 D - P/X - P/D P/P - D/D P/P
73 P - P/X - P/D P/P - P/D P/P
73 P - P/X - P/D P/P - P/D P/P
74 push
74 push
75 D P/X P/X P/P P/P P/P D/D D/D P/P
75 D P/X P/X P/P P/P P/P D/D D/D P/P
76 P P/X P/X P/P P/P P/P P/P P/P P/P
76 P P/X P/X P/P P/P P/P P/P P/P P/P
77
77
78 Legend:
78 Legend:
79
79
80 A/B = final state on client / state on server
80 A/B = final state on client / state on server
81
81
82 * N = new/not present,
82 * N = new/not present,
83 * P = public,
83 * P = public,
84 * D = draft,
84 * D = draft,
85 * X = not tracked (i.e., the old client or server has no internal
85 * X = not tracked (i.e., the old client or server has no internal
86 way of recording the phase.)
86 way of recording the phase.)
87
87
88 passive = only pushes
88 passive = only pushes
89
89
90
90
91 A cell here can be read like this:
91 A cell here can be read like this:
92
92
93 "When a new client pushes a draft changeset (D) to a publishing
93 "When a new client pushes a draft changeset (D) to a publishing
94 server where it's not present (N), it's marked public on both
94 server where it's not present (N), it's marked public on both
95 sides (P/P)."
95 sides (P/P)."
96
96
97 Note: old client behave as a publishing server with draft only content
97 Note: old client behave as a publishing server with draft only content
98 - other people see it as public
98 - other people see it as public
99 - content is pushed as draft
99 - content is pushed as draft
100
100
101 """
101 """
102
102
103 from __future__ import absolute_import
103 from __future__ import absolute_import
104
104
105 import errno
105 import errno
106 import struct
106 import struct
107
107
108 from .i18n import _
108 from .i18n import _
109 from .node import (
109 from .node import (
110 bin,
110 bin,
111 hex,
111 hex,
112 nullid,
112 nullid,
113 nullrev,
113 nullrev,
114 short,
114 short,
115 )
115 )
116 from . import (
116 from . import (
117 error,
117 error,
118 pycompat,
118 pycompat,
119 smartset,
119 smartset,
120 txnutil,
120 txnutil,
121 util,
121 util,
122 )
122 )
123
123
124 _fphasesentry = struct.Struct('>i20s')
124 _fphasesentry = struct.Struct('>i20s')
125
125
126 INTERNAL_FLAG = 64 # Phases for mercurial internal usage only
126 INTERNAL_FLAG = 64 # Phases for mercurial internal usage only
127 HIDEABLE_FLAG = 32 # Phases that are hideable
127 HIDEABLE_FLAG = 32 # Phases that are hideable
128
128
129 # record phase index
129 # record phase index
130 public, draft, secret = range(3)
130 public, draft, secret = range(3)
131 internal = INTERNAL_FLAG | HIDEABLE_FLAG
131 internal = INTERNAL_FLAG | HIDEABLE_FLAG
132 allphases = range(internal + 1)
132 allphases = range(internal + 1)
133 trackedphases = allphases[1:]
133 trackedphases = allphases[1:]
134 # record phase names
134 # record phase names
135 phasenames = [None] * len(allphases)
135 phasenames = [None] * len(allphases)
136 phasenames[:3] = ['public', 'draft', 'secret']
136 phasenames[:3] = ['public', 'draft', 'secret']
137 phasenames[internal] = 'internal'
137 phasenames[internal] = 'internal'
138 # record phase property
138 # record phase property
139 mutablephases = tuple(allphases[1:])
139 mutablephases = tuple(allphases[1:])
140 remotehiddenphases = tuple(allphases[2:])
140 remotehiddenphases = tuple(allphases[2:])
141 localhiddenphases = tuple(p for p in allphases if p & HIDEABLE_FLAG)
141 localhiddenphases = tuple(p for p in allphases if p & HIDEABLE_FLAG)
142
142
143 def supportinternal(repo):
144 """True if the internal phase can be used on a repository"""
145 return 'internal-phase' in repo.requirements
146
143 def _readroots(repo, phasedefaults=None):
147 def _readroots(repo, phasedefaults=None):
144 """Read phase roots from disk
148 """Read phase roots from disk
145
149
146 phasedefaults is a list of fn(repo, roots) callable, which are
150 phasedefaults is a list of fn(repo, roots) callable, which are
147 executed if the phase roots file does not exist. When phases are
151 executed if the phase roots file does not exist. When phases are
148 being initialized on an existing repository, this could be used to
152 being initialized on an existing repository, this could be used to
149 set selected changesets phase to something else than public.
153 set selected changesets phase to something else than public.
150
154
151 Return (roots, dirty) where dirty is true if roots differ from
155 Return (roots, dirty) where dirty is true if roots differ from
152 what is being stored.
156 what is being stored.
153 """
157 """
154 repo = repo.unfiltered()
158 repo = repo.unfiltered()
155 dirty = False
159 dirty = False
156 roots = [set() for i in allphases]
160 roots = [set() for i in allphases]
157 try:
161 try:
158 f, pending = txnutil.trypending(repo.root, repo.svfs, 'phaseroots')
162 f, pending = txnutil.trypending(repo.root, repo.svfs, 'phaseroots')
159 try:
163 try:
160 for line in f:
164 for line in f:
161 phase, nh = line.split()
165 phase, nh = line.split()
162 roots[int(phase)].add(bin(nh))
166 roots[int(phase)].add(bin(nh))
163 finally:
167 finally:
164 f.close()
168 f.close()
165 except IOError as inst:
169 except IOError as inst:
166 if inst.errno != errno.ENOENT:
170 if inst.errno != errno.ENOENT:
167 raise
171 raise
168 if phasedefaults:
172 if phasedefaults:
169 for f in phasedefaults:
173 for f in phasedefaults:
170 roots = f(repo, roots)
174 roots = f(repo, roots)
171 dirty = True
175 dirty = True
172 return roots, dirty
176 return roots, dirty
173
177
174 def binaryencode(phasemapping):
178 def binaryencode(phasemapping):
175 """encode a 'phase -> nodes' mapping into a binary stream
179 """encode a 'phase -> nodes' mapping into a binary stream
176
180
177 Since phases are integer the mapping is actually a python list:
181 Since phases are integer the mapping is actually a python list:
178 [[PUBLIC_HEADS], [DRAFTS_HEADS], [SECRET_HEADS]]
182 [[PUBLIC_HEADS], [DRAFTS_HEADS], [SECRET_HEADS]]
179 """
183 """
180 binarydata = []
184 binarydata = []
181 for phase, nodes in enumerate(phasemapping):
185 for phase, nodes in enumerate(phasemapping):
182 for head in nodes:
186 for head in nodes:
183 binarydata.append(_fphasesentry.pack(phase, head))
187 binarydata.append(_fphasesentry.pack(phase, head))
184 return ''.join(binarydata)
188 return ''.join(binarydata)
185
189
186 def binarydecode(stream):
190 def binarydecode(stream):
187 """decode a binary stream into a 'phase -> nodes' mapping
191 """decode a binary stream into a 'phase -> nodes' mapping
188
192
189 Since phases are integer the mapping is actually a python list."""
193 Since phases are integer the mapping is actually a python list."""
190 headsbyphase = [[] for i in allphases]
194 headsbyphase = [[] for i in allphases]
191 entrysize = _fphasesentry.size
195 entrysize = _fphasesentry.size
192 while True:
196 while True:
193 entry = stream.read(entrysize)
197 entry = stream.read(entrysize)
194 if len(entry) < entrysize:
198 if len(entry) < entrysize:
195 if entry:
199 if entry:
196 raise error.Abort(_('bad phase-heads stream'))
200 raise error.Abort(_('bad phase-heads stream'))
197 break
201 break
198 phase, node = _fphasesentry.unpack(entry)
202 phase, node = _fphasesentry.unpack(entry)
199 headsbyphase[phase].append(node)
203 headsbyphase[phase].append(node)
200 return headsbyphase
204 return headsbyphase
201
205
202 def _trackphasechange(data, rev, old, new):
206 def _trackphasechange(data, rev, old, new):
203 """add a phase move the <data> dictionnary
207 """add a phase move the <data> dictionnary
204
208
205 If data is None, nothing happens.
209 If data is None, nothing happens.
206 """
210 """
207 if data is None:
211 if data is None:
208 return
212 return
209 existing = data.get(rev)
213 existing = data.get(rev)
210 if existing is not None:
214 if existing is not None:
211 old = existing[0]
215 old = existing[0]
212 data[rev] = (old, new)
216 data[rev] = (old, new)
213
217
214 class phasecache(object):
218 class phasecache(object):
215 def __init__(self, repo, phasedefaults, _load=True):
219 def __init__(self, repo, phasedefaults, _load=True):
216 if _load:
220 if _load:
217 # Cheap trick to allow shallow-copy without copy module
221 # Cheap trick to allow shallow-copy without copy module
218 self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
222 self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
219 self._loadedrevslen = 0
223 self._loadedrevslen = 0
220 self._phasesets = None
224 self._phasesets = None
221 self.filterunknown(repo)
225 self.filterunknown(repo)
222 self.opener = repo.svfs
226 self.opener = repo.svfs
223
227
224 def getrevset(self, repo, phases, subset=None):
228 def getrevset(self, repo, phases, subset=None):
225 """return a smartset for the given phases"""
229 """return a smartset for the given phases"""
226 self.loadphaserevs(repo) # ensure phase's sets are loaded
230 self.loadphaserevs(repo) # ensure phase's sets are loaded
227 phases = set(phases)
231 phases = set(phases)
228 if public not in phases:
232 if public not in phases:
229 # fast path: _phasesets contains the interesting sets,
233 # fast path: _phasesets contains the interesting sets,
230 # might only need a union and post-filtering.
234 # might only need a union and post-filtering.
231 if len(phases) == 1:
235 if len(phases) == 1:
232 [p] = phases
236 [p] = phases
233 revs = self._phasesets[p]
237 revs = self._phasesets[p]
234 else:
238 else:
235 revs = set.union(*[self._phasesets[p] for p in phases])
239 revs = set.union(*[self._phasesets[p] for p in phases])
236 if repo.changelog.filteredrevs:
240 if repo.changelog.filteredrevs:
237 revs = revs - repo.changelog.filteredrevs
241 revs = revs - repo.changelog.filteredrevs
238 if subset is None:
242 if subset is None:
239 return smartset.baseset(revs)
243 return smartset.baseset(revs)
240 else:
244 else:
241 return subset & smartset.baseset(revs)
245 return subset & smartset.baseset(revs)
242 else:
246 else:
243 phases = set(allphases).difference(phases)
247 phases = set(allphases).difference(phases)
244 if not phases:
248 if not phases:
245 return smartset.fullreposet(repo)
249 return smartset.fullreposet(repo)
246 if len(phases) == 1:
250 if len(phases) == 1:
247 [p] = phases
251 [p] = phases
248 revs = self._phasesets[p]
252 revs = self._phasesets[p]
249 else:
253 else:
250 revs = set.union(*[self._phasesets[p] for p in phases])
254 revs = set.union(*[self._phasesets[p] for p in phases])
251 if subset is None:
255 if subset is None:
252 subset = smartset.fullreposet(repo)
256 subset = smartset.fullreposet(repo)
253 if not revs:
257 if not revs:
254 return subset
258 return subset
255 return subset.filter(lambda r: r not in revs)
259 return subset.filter(lambda r: r not in revs)
256
260
257 def copy(self):
261 def copy(self):
258 # Shallow copy meant to ensure isolation in
262 # Shallow copy meant to ensure isolation in
259 # advance/retractboundary(), nothing more.
263 # advance/retractboundary(), nothing more.
260 ph = self.__class__(None, None, _load=False)
264 ph = self.__class__(None, None, _load=False)
261 ph.phaseroots = self.phaseroots[:]
265 ph.phaseroots = self.phaseroots[:]
262 ph.dirty = self.dirty
266 ph.dirty = self.dirty
263 ph.opener = self.opener
267 ph.opener = self.opener
264 ph._loadedrevslen = self._loadedrevslen
268 ph._loadedrevslen = self._loadedrevslen
265 ph._phasesets = self._phasesets
269 ph._phasesets = self._phasesets
266 return ph
270 return ph
267
271
268 def replace(self, phcache):
272 def replace(self, phcache):
269 """replace all values in 'self' with content of phcache"""
273 """replace all values in 'self' with content of phcache"""
270 for a in ('phaseroots', 'dirty', 'opener', '_loadedrevslen',
274 for a in ('phaseroots', 'dirty', 'opener', '_loadedrevslen',
271 '_phasesets'):
275 '_phasesets'):
272 setattr(self, a, getattr(phcache, a))
276 setattr(self, a, getattr(phcache, a))
273
277
274 def _getphaserevsnative(self, repo):
278 def _getphaserevsnative(self, repo):
275 repo = repo.unfiltered()
279 repo = repo.unfiltered()
276 nativeroots = []
280 nativeroots = []
277 for phase in trackedphases:
281 for phase in trackedphases:
278 nativeroots.append(pycompat.maplist(repo.changelog.rev,
282 nativeroots.append(pycompat.maplist(repo.changelog.rev,
279 self.phaseroots[phase]))
283 self.phaseroots[phase]))
280 return repo.changelog.computephases(nativeroots)
284 return repo.changelog.computephases(nativeroots)
281
285
282 def _computephaserevspure(self, repo):
286 def _computephaserevspure(self, repo):
283 repo = repo.unfiltered()
287 repo = repo.unfiltered()
284 cl = repo.changelog
288 cl = repo.changelog
285 self._phasesets = [set() for phase in allphases]
289 self._phasesets = [set() for phase in allphases]
286 lowerroots = set()
290 lowerroots = set()
287 for phase in reversed(trackedphases):
291 for phase in reversed(trackedphases):
288 roots = pycompat.maplist(cl.rev, self.phaseroots[phase])
292 roots = pycompat.maplist(cl.rev, self.phaseroots[phase])
289 if roots:
293 if roots:
290 ps = set(cl.descendants(roots))
294 ps = set(cl.descendants(roots))
291 for root in roots:
295 for root in roots:
292 ps.add(root)
296 ps.add(root)
293 ps.difference_update(lowerroots)
297 ps.difference_update(lowerroots)
294 lowerroots.update(ps)
298 lowerroots.update(ps)
295 self._phasesets[phase] = ps
299 self._phasesets[phase] = ps
296 self._loadedrevslen = len(cl)
300 self._loadedrevslen = len(cl)
297
301
298 def loadphaserevs(self, repo):
302 def loadphaserevs(self, repo):
299 """ensure phase information is loaded in the object"""
303 """ensure phase information is loaded in the object"""
300 if self._phasesets is None:
304 if self._phasesets is None:
301 try:
305 try:
302 res = self._getphaserevsnative(repo)
306 res = self._getphaserevsnative(repo)
303 self._loadedrevslen, self._phasesets = res
307 self._loadedrevslen, self._phasesets = res
304 except AttributeError:
308 except AttributeError:
305 self._computephaserevspure(repo)
309 self._computephaserevspure(repo)
306
310
307 def invalidate(self):
311 def invalidate(self):
308 self._loadedrevslen = 0
312 self._loadedrevslen = 0
309 self._phasesets = None
313 self._phasesets = None
310
314
311 def phase(self, repo, rev):
315 def phase(self, repo, rev):
312 # We need a repo argument here to be able to build _phasesets
316 # We need a repo argument here to be able to build _phasesets
313 # if necessary. The repository instance is not stored in
317 # if necessary. The repository instance is not stored in
314 # phasecache to avoid reference cycles. The changelog instance
318 # phasecache to avoid reference cycles. The changelog instance
315 # is not stored because it is a filecache() property and can
319 # is not stored because it is a filecache() property and can
316 # be replaced without us being notified.
320 # be replaced without us being notified.
317 if rev == nullrev:
321 if rev == nullrev:
318 return public
322 return public
319 if rev < nullrev:
323 if rev < nullrev:
320 raise ValueError(_('cannot lookup negative revision'))
324 raise ValueError(_('cannot lookup negative revision'))
321 if rev >= self._loadedrevslen:
325 if rev >= self._loadedrevslen:
322 self.invalidate()
326 self.invalidate()
323 self.loadphaserevs(repo)
327 self.loadphaserevs(repo)
324 for phase in trackedphases:
328 for phase in trackedphases:
325 if rev in self._phasesets[phase]:
329 if rev in self._phasesets[phase]:
326 return phase
330 return phase
327 return public
331 return public
328
332
329 def write(self):
333 def write(self):
330 if not self.dirty:
334 if not self.dirty:
331 return
335 return
332 f = self.opener('phaseroots', 'w', atomictemp=True, checkambig=True)
336 f = self.opener('phaseroots', 'w', atomictemp=True, checkambig=True)
333 try:
337 try:
334 self._write(f)
338 self._write(f)
335 finally:
339 finally:
336 f.close()
340 f.close()
337
341
338 def _write(self, fp):
342 def _write(self, fp):
339 for phase, roots in enumerate(self.phaseroots):
343 for phase, roots in enumerate(self.phaseroots):
340 for h in sorted(roots):
344 for h in sorted(roots):
341 fp.write('%i %s\n' % (phase, hex(h)))
345 fp.write('%i %s\n' % (phase, hex(h)))
342 self.dirty = False
346 self.dirty = False
343
347
344 def _updateroots(self, phase, newroots, tr):
348 def _updateroots(self, phase, newroots, tr):
345 self.phaseroots[phase] = newroots
349 self.phaseroots[phase] = newroots
346 self.invalidate()
350 self.invalidate()
347 self.dirty = True
351 self.dirty = True
348
352
349 tr.addfilegenerator('phase', ('phaseroots',), self._write)
353 tr.addfilegenerator('phase', ('phaseroots',), self._write)
350 tr.hookargs['phases_moved'] = '1'
354 tr.hookargs['phases_moved'] = '1'
351
355
352 def registernew(self, repo, tr, targetphase, nodes):
356 def registernew(self, repo, tr, targetphase, nodes):
353 repo = repo.unfiltered()
357 repo = repo.unfiltered()
354 self._retractboundary(repo, tr, targetphase, nodes)
358 self._retractboundary(repo, tr, targetphase, nodes)
355 if tr is not None and 'phases' in tr.changes:
359 if tr is not None and 'phases' in tr.changes:
356 phasetracking = tr.changes['phases']
360 phasetracking = tr.changes['phases']
357 torev = repo.changelog.rev
361 torev = repo.changelog.rev
358 phase = self.phase
362 phase = self.phase
359 for n in nodes:
363 for n in nodes:
360 rev = torev(n)
364 rev = torev(n)
361 revphase = phase(repo, rev)
365 revphase = phase(repo, rev)
362 _trackphasechange(phasetracking, rev, None, revphase)
366 _trackphasechange(phasetracking, rev, None, revphase)
363 repo.invalidatevolatilesets()
367 repo.invalidatevolatilesets()
364
368
365 def advanceboundary(self, repo, tr, targetphase, nodes, dryrun=None):
369 def advanceboundary(self, repo, tr, targetphase, nodes, dryrun=None):
366 """Set all 'nodes' to phase 'targetphase'
370 """Set all 'nodes' to phase 'targetphase'
367
371
368 Nodes with a phase lower than 'targetphase' are not affected.
372 Nodes with a phase lower than 'targetphase' are not affected.
369
373
370 If dryrun is True, no actions will be performed
374 If dryrun is True, no actions will be performed
371
375
372 Returns a set of revs whose phase is changed or should be changed
376 Returns a set of revs whose phase is changed or should be changed
373 """
377 """
374 # Be careful to preserve shallow-copied values: do not update
378 # Be careful to preserve shallow-copied values: do not update
375 # phaseroots values, replace them.
379 # phaseroots values, replace them.
376 if tr is None:
380 if tr is None:
377 phasetracking = None
381 phasetracking = None
378 else:
382 else:
379 phasetracking = tr.changes.get('phases')
383 phasetracking = tr.changes.get('phases')
380
384
381 repo = repo.unfiltered()
385 repo = repo.unfiltered()
382
386
383 changes = set() # set of revisions to be changed
387 changes = set() # set of revisions to be changed
384 delroots = [] # set of root deleted by this path
388 delroots = [] # set of root deleted by this path
385 for phase in pycompat.xrange(targetphase + 1, len(allphases)):
389 for phase in pycompat.xrange(targetphase + 1, len(allphases)):
386 # filter nodes that are not in a compatible phase already
390 # filter nodes that are not in a compatible phase already
387 nodes = [n for n in nodes
391 nodes = [n for n in nodes
388 if self.phase(repo, repo[n].rev()) >= phase]
392 if self.phase(repo, repo[n].rev()) >= phase]
389 if not nodes:
393 if not nodes:
390 break # no roots to move anymore
394 break # no roots to move anymore
391
395
392 olds = self.phaseroots[phase]
396 olds = self.phaseroots[phase]
393
397
394 affected = repo.revs('%ln::%ln', olds, nodes)
398 affected = repo.revs('%ln::%ln', olds, nodes)
395 changes.update(affected)
399 changes.update(affected)
396 if dryrun:
400 if dryrun:
397 continue
401 continue
398 for r in affected:
402 for r in affected:
399 _trackphasechange(phasetracking, r, self.phase(repo, r),
403 _trackphasechange(phasetracking, r, self.phase(repo, r),
400 targetphase)
404 targetphase)
401
405
402 roots = set(ctx.node() for ctx in repo.set(
406 roots = set(ctx.node() for ctx in repo.set(
403 'roots((%ln::) - %ld)', olds, affected))
407 'roots((%ln::) - %ld)', olds, affected))
404 if olds != roots:
408 if olds != roots:
405 self._updateroots(phase, roots, tr)
409 self._updateroots(phase, roots, tr)
406 # some roots may need to be declared for lower phases
410 # some roots may need to be declared for lower phases
407 delroots.extend(olds - roots)
411 delroots.extend(olds - roots)
408 if not dryrun:
412 if not dryrun:
409 # declare deleted root in the target phase
413 # declare deleted root in the target phase
410 if targetphase != 0:
414 if targetphase != 0:
411 self._retractboundary(repo, tr, targetphase, delroots)
415 self._retractboundary(repo, tr, targetphase, delroots)
412 repo.invalidatevolatilesets()
416 repo.invalidatevolatilesets()
413 return changes
417 return changes
414
418
415 def retractboundary(self, repo, tr, targetphase, nodes):
419 def retractboundary(self, repo, tr, targetphase, nodes):
416 oldroots = self.phaseroots[:targetphase + 1]
420 oldroots = self.phaseroots[:targetphase + 1]
417 if tr is None:
421 if tr is None:
418 phasetracking = None
422 phasetracking = None
419 else:
423 else:
420 phasetracking = tr.changes.get('phases')
424 phasetracking = tr.changes.get('phases')
421 repo = repo.unfiltered()
425 repo = repo.unfiltered()
422 if (self._retractboundary(repo, tr, targetphase, nodes)
426 if (self._retractboundary(repo, tr, targetphase, nodes)
423 and phasetracking is not None):
427 and phasetracking is not None):
424
428
425 # find the affected revisions
429 # find the affected revisions
426 new = self.phaseroots[targetphase]
430 new = self.phaseroots[targetphase]
427 old = oldroots[targetphase]
431 old = oldroots[targetphase]
428 affected = set(repo.revs('(%ln::) - (%ln::)', new, old))
432 affected = set(repo.revs('(%ln::) - (%ln::)', new, old))
429
433
430 # find the phase of the affected revision
434 # find the phase of the affected revision
431 for phase in pycompat.xrange(targetphase, -1, -1):
435 for phase in pycompat.xrange(targetphase, -1, -1):
432 if phase:
436 if phase:
433 roots = oldroots[phase]
437 roots = oldroots[phase]
434 revs = set(repo.revs('%ln::%ld', roots, affected))
438 revs = set(repo.revs('%ln::%ld', roots, affected))
435 affected -= revs
439 affected -= revs
436 else: # public phase
440 else: # public phase
437 revs = affected
441 revs = affected
438 for r in revs:
442 for r in revs:
439 _trackphasechange(phasetracking, r, phase, targetphase)
443 _trackphasechange(phasetracking, r, phase, targetphase)
440 repo.invalidatevolatilesets()
444 repo.invalidatevolatilesets()
441
445
442 def _retractboundary(self, repo, tr, targetphase, nodes):
446 def _retractboundary(self, repo, tr, targetphase, nodes):
443 # Be careful to preserve shallow-copied values: do not update
447 # Be careful to preserve shallow-copied values: do not update
444 # phaseroots values, replace them.
448 # phaseroots values, replace them.
449 if targetphase == internal and not supportinternal(repo):
450 msg = 'this repository does not support the internal phase'
451 raise error.ProgrammingError(msg)
445
452
446 repo = repo.unfiltered()
453 repo = repo.unfiltered()
447 currentroots = self.phaseroots[targetphase]
454 currentroots = self.phaseroots[targetphase]
448 finalroots = oldroots = set(currentroots)
455 finalroots = oldroots = set(currentroots)
449 newroots = [n for n in nodes
456 newroots = [n for n in nodes
450 if self.phase(repo, repo[n].rev()) < targetphase]
457 if self.phase(repo, repo[n].rev()) < targetphase]
451 if newroots:
458 if newroots:
452
459
453 if nullid in newroots:
460 if nullid in newroots:
454 raise error.Abort(_('cannot change null revision phase'))
461 raise error.Abort(_('cannot change null revision phase'))
455 currentroots = currentroots.copy()
462 currentroots = currentroots.copy()
456 currentroots.update(newroots)
463 currentroots.update(newroots)
457
464
458 # Only compute new roots for revs above the roots that are being
465 # Only compute new roots for revs above the roots that are being
459 # retracted.
466 # retracted.
460 minnewroot = min(repo[n].rev() for n in newroots)
467 minnewroot = min(repo[n].rev() for n in newroots)
461 aboveroots = [n for n in currentroots
468 aboveroots = [n for n in currentroots
462 if repo[n].rev() >= minnewroot]
469 if repo[n].rev() >= minnewroot]
463 updatedroots = repo.set('roots(%ln::)', aboveroots)
470 updatedroots = repo.set('roots(%ln::)', aboveroots)
464
471
465 finalroots = set(n for n in currentroots if repo[n].rev() <
472 finalroots = set(n for n in currentroots if repo[n].rev() <
466 minnewroot)
473 minnewroot)
467 finalroots.update(ctx.node() for ctx in updatedroots)
474 finalroots.update(ctx.node() for ctx in updatedroots)
468 if finalroots != oldroots:
475 if finalroots != oldroots:
469 self._updateroots(targetphase, finalroots, tr)
476 self._updateroots(targetphase, finalroots, tr)
470 return True
477 return True
471 return False
478 return False
472
479
473 def filterunknown(self, repo):
480 def filterunknown(self, repo):
474 """remove unknown nodes from the phase boundary
481 """remove unknown nodes from the phase boundary
475
482
476 Nothing is lost as unknown nodes only hold data for their descendants.
483 Nothing is lost as unknown nodes only hold data for their descendants.
477 """
484 """
478 filtered = False
485 filtered = False
479 nodemap = repo.changelog.nodemap # to filter unknown nodes
486 nodemap = repo.changelog.nodemap # to filter unknown nodes
480 for phase, nodes in enumerate(self.phaseroots):
487 for phase, nodes in enumerate(self.phaseroots):
481 missing = sorted(node for node in nodes if node not in nodemap)
488 missing = sorted(node for node in nodes if node not in nodemap)
482 if missing:
489 if missing:
483 for mnode in missing:
490 for mnode in missing:
484 repo.ui.debug(
491 repo.ui.debug(
485 'removing unknown node %s from %i-phase boundary\n'
492 'removing unknown node %s from %i-phase boundary\n'
486 % (short(mnode), phase))
493 % (short(mnode), phase))
487 nodes.symmetric_difference_update(missing)
494 nodes.symmetric_difference_update(missing)
488 filtered = True
495 filtered = True
489 if filtered:
496 if filtered:
490 self.dirty = True
497 self.dirty = True
491 # filterunknown is called by repo.destroyed, we may have no changes in
498 # filterunknown is called by repo.destroyed, we may have no changes in
492 # root but _phasesets contents is certainly invalid (or at least we
499 # root but _phasesets contents is certainly invalid (or at least we
493 # have not proper way to check that). related to issue 3858.
500 # have not proper way to check that). related to issue 3858.
494 #
501 #
495 # The other caller is __init__ that have no _phasesets initialized
502 # The other caller is __init__ that have no _phasesets initialized
496 # anyway. If this change we should consider adding a dedicated
503 # anyway. If this change we should consider adding a dedicated
497 # "destroyed" function to phasecache or a proper cache key mechanism
504 # "destroyed" function to phasecache or a proper cache key mechanism
498 # (see branchmap one)
505 # (see branchmap one)
499 self.invalidate()
506 self.invalidate()
500
507
501 def advanceboundary(repo, tr, targetphase, nodes, dryrun=None):
508 def advanceboundary(repo, tr, targetphase, nodes, dryrun=None):
502 """Add nodes to a phase changing other nodes phases if necessary.
509 """Add nodes to a phase changing other nodes phases if necessary.
503
510
504 This function move boundary *forward* this means that all nodes
511 This function move boundary *forward* this means that all nodes
505 are set in the target phase or kept in a *lower* phase.
512 are set in the target phase or kept in a *lower* phase.
506
513
507 Simplify boundary to contains phase roots only.
514 Simplify boundary to contains phase roots only.
508
515
509 If dryrun is True, no actions will be performed
516 If dryrun is True, no actions will be performed
510
517
511 Returns a set of revs whose phase is changed or should be changed
518 Returns a set of revs whose phase is changed or should be changed
512 """
519 """
513 phcache = repo._phasecache.copy()
520 phcache = repo._phasecache.copy()
514 changes = phcache.advanceboundary(repo, tr, targetphase, nodes,
521 changes = phcache.advanceboundary(repo, tr, targetphase, nodes,
515 dryrun=dryrun)
522 dryrun=dryrun)
516 if not dryrun:
523 if not dryrun:
517 repo._phasecache.replace(phcache)
524 repo._phasecache.replace(phcache)
518 return changes
525 return changes
519
526
520 def retractboundary(repo, tr, targetphase, nodes):
527 def retractboundary(repo, tr, targetphase, nodes):
521 """Set nodes back to a phase changing other nodes phases if
528 """Set nodes back to a phase changing other nodes phases if
522 necessary.
529 necessary.
523
530
524 This function move boundary *backward* this means that all nodes
531 This function move boundary *backward* this means that all nodes
525 are set in the target phase or kept in a *higher* phase.
532 are set in the target phase or kept in a *higher* phase.
526
533
527 Simplify boundary to contains phase roots only."""
534 Simplify boundary to contains phase roots only."""
528 phcache = repo._phasecache.copy()
535 phcache = repo._phasecache.copy()
529 phcache.retractboundary(repo, tr, targetphase, nodes)
536 phcache.retractboundary(repo, tr, targetphase, nodes)
530 repo._phasecache.replace(phcache)
537 repo._phasecache.replace(phcache)
531
538
532 def registernew(repo, tr, targetphase, nodes):
539 def registernew(repo, tr, targetphase, nodes):
533 """register a new revision and its phase
540 """register a new revision and its phase
534
541
535 Code adding revisions to the repository should use this function to
542 Code adding revisions to the repository should use this function to
536 set new changeset in their target phase (or higher).
543 set new changeset in their target phase (or higher).
537 """
544 """
538 phcache = repo._phasecache.copy()
545 phcache = repo._phasecache.copy()
539 phcache.registernew(repo, tr, targetphase, nodes)
546 phcache.registernew(repo, tr, targetphase, nodes)
540 repo._phasecache.replace(phcache)
547 repo._phasecache.replace(phcache)
541
548
542 def listphases(repo):
549 def listphases(repo):
543 """List phases root for serialization over pushkey"""
550 """List phases root for serialization over pushkey"""
544 # Use ordered dictionary so behavior is deterministic.
551 # Use ordered dictionary so behavior is deterministic.
545 keys = util.sortdict()
552 keys = util.sortdict()
546 value = '%i' % draft
553 value = '%i' % draft
547 cl = repo.unfiltered().changelog
554 cl = repo.unfiltered().changelog
548 for root in repo._phasecache.phaseroots[draft]:
555 for root in repo._phasecache.phaseroots[draft]:
549 if repo._phasecache.phase(repo, cl.rev(root)) <= draft:
556 if repo._phasecache.phase(repo, cl.rev(root)) <= draft:
550 keys[hex(root)] = value
557 keys[hex(root)] = value
551
558
552 if repo.publishing():
559 if repo.publishing():
553 # Add an extra data to let remote know we are a publishing
560 # Add an extra data to let remote know we are a publishing
554 # repo. Publishing repo can't just pretend they are old repo.
561 # repo. Publishing repo can't just pretend they are old repo.
555 # When pushing to a publishing repo, the client still need to
562 # When pushing to a publishing repo, the client still need to
556 # push phase boundary
563 # push phase boundary
557 #
564 #
558 # Push do not only push changeset. It also push phase data.
565 # Push do not only push changeset. It also push phase data.
559 # New phase data may apply to common changeset which won't be
566 # New phase data may apply to common changeset which won't be
560 # push (as they are common). Here is a very simple example:
567 # push (as they are common). Here is a very simple example:
561 #
568 #
562 # 1) repo A push changeset X as draft to repo B
569 # 1) repo A push changeset X as draft to repo B
563 # 2) repo B make changeset X public
570 # 2) repo B make changeset X public
564 # 3) repo B push to repo A. X is not pushed but the data that
571 # 3) repo B push to repo A. X is not pushed but the data that
565 # X as now public should
572 # X as now public should
566 #
573 #
567 # The server can't handle it on it's own as it has no idea of
574 # The server can't handle it on it's own as it has no idea of
568 # client phase data.
575 # client phase data.
569 keys['publishing'] = 'True'
576 keys['publishing'] = 'True'
570 return keys
577 return keys
571
578
572 def pushphase(repo, nhex, oldphasestr, newphasestr):
579 def pushphase(repo, nhex, oldphasestr, newphasestr):
573 """List phases root for serialization over pushkey"""
580 """List phases root for serialization over pushkey"""
574 repo = repo.unfiltered()
581 repo = repo.unfiltered()
575 with repo.lock():
582 with repo.lock():
576 currentphase = repo[nhex].phase()
583 currentphase = repo[nhex].phase()
577 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
584 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
578 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
585 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
579 if currentphase == oldphase and newphase < oldphase:
586 if currentphase == oldphase and newphase < oldphase:
580 with repo.transaction('pushkey-phase') as tr:
587 with repo.transaction('pushkey-phase') as tr:
581 advanceboundary(repo, tr, newphase, [bin(nhex)])
588 advanceboundary(repo, tr, newphase, [bin(nhex)])
582 return True
589 return True
583 elif currentphase == newphase:
590 elif currentphase == newphase:
584 # raced, but got correct result
591 # raced, but got correct result
585 return True
592 return True
586 else:
593 else:
587 return False
594 return False
588
595
589 def subsetphaseheads(repo, subset):
596 def subsetphaseheads(repo, subset):
590 """Finds the phase heads for a subset of a history
597 """Finds the phase heads for a subset of a history
591
598
592 Returns a list indexed by phase number where each item is a list of phase
599 Returns a list indexed by phase number where each item is a list of phase
593 head nodes.
600 head nodes.
594 """
601 """
595 cl = repo.changelog
602 cl = repo.changelog
596
603
597 headsbyphase = [[] for i in allphases]
604 headsbyphase = [[] for i in allphases]
598 # No need to keep track of secret phase; any heads in the subset that
605 # No need to keep track of secret phase; any heads in the subset that
599 # are not mentioned are implicitly secret.
606 # are not mentioned are implicitly secret.
600 for phase in allphases[:secret]:
607 for phase in allphases[:secret]:
601 revset = "heads(%%ln & %s())" % phasenames[phase]
608 revset = "heads(%%ln & %s())" % phasenames[phase]
602 headsbyphase[phase] = [cl.node(r) for r in repo.revs(revset, subset)]
609 headsbyphase[phase] = [cl.node(r) for r in repo.revs(revset, subset)]
603 return headsbyphase
610 return headsbyphase
604
611
605 def updatephases(repo, trgetter, headsbyphase):
612 def updatephases(repo, trgetter, headsbyphase):
606 """Updates the repo with the given phase heads"""
613 """Updates the repo with the given phase heads"""
607 # Now advance phase boundaries of all but secret phase
614 # Now advance phase boundaries of all but secret phase
608 #
615 #
609 # run the update (and fetch transaction) only if there are actually things
616 # run the update (and fetch transaction) only if there are actually things
610 # to update. This avoid creating empty transaction during no-op operation.
617 # to update. This avoid creating empty transaction during no-op operation.
611
618
612 for phase in allphases[:-1]:
619 for phase in allphases[:-1]:
613 revset = '%ln - _phase(%s)'
620 revset = '%ln - _phase(%s)'
614 heads = [c.node() for c in repo.set(revset, headsbyphase[phase], phase)]
621 heads = [c.node() for c in repo.set(revset, headsbyphase[phase], phase)]
615 if heads:
622 if heads:
616 advanceboundary(repo, trgetter(), phase, heads)
623 advanceboundary(repo, trgetter(), phase, heads)
617
624
618 def analyzeremotephases(repo, subset, roots):
625 def analyzeremotephases(repo, subset, roots):
619 """Compute phases heads and root in a subset of node from root dict
626 """Compute phases heads and root in a subset of node from root dict
620
627
621 * subset is heads of the subset
628 * subset is heads of the subset
622 * roots is {<nodeid> => phase} mapping. key and value are string.
629 * roots is {<nodeid> => phase} mapping. key and value are string.
623
630
624 Accept unknown element input
631 Accept unknown element input
625 """
632 """
626 repo = repo.unfiltered()
633 repo = repo.unfiltered()
627 # build list from dictionary
634 # build list from dictionary
628 draftroots = []
635 draftroots = []
629 nodemap = repo.changelog.nodemap # to filter unknown nodes
636 nodemap = repo.changelog.nodemap # to filter unknown nodes
630 for nhex, phase in roots.iteritems():
637 for nhex, phase in roots.iteritems():
631 if nhex == 'publishing': # ignore data related to publish option
638 if nhex == 'publishing': # ignore data related to publish option
632 continue
639 continue
633 node = bin(nhex)
640 node = bin(nhex)
634 phase = int(phase)
641 phase = int(phase)
635 if phase == public:
642 if phase == public:
636 if node != nullid:
643 if node != nullid:
637 repo.ui.warn(_('ignoring inconsistent public root'
644 repo.ui.warn(_('ignoring inconsistent public root'
638 ' from remote: %s\n') % nhex)
645 ' from remote: %s\n') % nhex)
639 elif phase == draft:
646 elif phase == draft:
640 if node in nodemap:
647 if node in nodemap:
641 draftroots.append(node)
648 draftroots.append(node)
642 else:
649 else:
643 repo.ui.warn(_('ignoring unexpected root from remote: %i %s\n')
650 repo.ui.warn(_('ignoring unexpected root from remote: %i %s\n')
644 % (phase, nhex))
651 % (phase, nhex))
645 # compute heads
652 # compute heads
646 publicheads = newheads(repo, subset, draftroots)
653 publicheads = newheads(repo, subset, draftroots)
647 return publicheads, draftroots
654 return publicheads, draftroots
648
655
649 class remotephasessummary(object):
656 class remotephasessummary(object):
650 """summarize phase information on the remote side
657 """summarize phase information on the remote side
651
658
652 :publishing: True is the remote is publishing
659 :publishing: True is the remote is publishing
653 :publicheads: list of remote public phase heads (nodes)
660 :publicheads: list of remote public phase heads (nodes)
654 :draftheads: list of remote draft phase heads (nodes)
661 :draftheads: list of remote draft phase heads (nodes)
655 :draftroots: list of remote draft phase root (nodes)
662 :draftroots: list of remote draft phase root (nodes)
656 """
663 """
657
664
658 def __init__(self, repo, remotesubset, remoteroots):
665 def __init__(self, repo, remotesubset, remoteroots):
659 unfi = repo.unfiltered()
666 unfi = repo.unfiltered()
660 self._allremoteroots = remoteroots
667 self._allremoteroots = remoteroots
661
668
662 self.publishing = remoteroots.get('publishing', False)
669 self.publishing = remoteroots.get('publishing', False)
663
670
664 ana = analyzeremotephases(repo, remotesubset, remoteroots)
671 ana = analyzeremotephases(repo, remotesubset, remoteroots)
665 self.publicheads, self.draftroots = ana
672 self.publicheads, self.draftroots = ana
666 # Get the list of all "heads" revs draft on remote
673 # Get the list of all "heads" revs draft on remote
667 dheads = unfi.set('heads(%ln::%ln)', self.draftroots, remotesubset)
674 dheads = unfi.set('heads(%ln::%ln)', self.draftroots, remotesubset)
668 self.draftheads = [c.node() for c in dheads]
675 self.draftheads = [c.node() for c in dheads]
669
676
670 def newheads(repo, heads, roots):
677 def newheads(repo, heads, roots):
671 """compute new head of a subset minus another
678 """compute new head of a subset minus another
672
679
673 * `heads`: define the first subset
680 * `heads`: define the first subset
674 * `roots`: define the second we subtract from the first"""
681 * `roots`: define the second we subtract from the first"""
675 # prevent an import cycle
682 # prevent an import cycle
676 # phases > dagop > patch > copies > scmutil > obsolete > obsutil > phases
683 # phases > dagop > patch > copies > scmutil > obsolete > obsutil > phases
677 from . import dagop
684 from . import dagop
678
685
679 repo = repo.unfiltered()
686 repo = repo.unfiltered()
680 cl = repo.changelog
687 cl = repo.changelog
681 rev = cl.nodemap.get
688 rev = cl.nodemap.get
682 if not roots:
689 if not roots:
683 return heads
690 return heads
684 if not heads or heads == [nullid]:
691 if not heads or heads == [nullid]:
685 return []
692 return []
686 # The logic operated on revisions, convert arguments early for convenience
693 # The logic operated on revisions, convert arguments early for convenience
687 new_heads = set(rev(n) for n in heads if n != nullid)
694 new_heads = set(rev(n) for n in heads if n != nullid)
688 roots = [rev(n) for n in roots]
695 roots = [rev(n) for n in roots]
689 # compute the area we need to remove
696 # compute the area we need to remove
690 affected_zone = repo.revs("(%ld::%ld)", roots, new_heads)
697 affected_zone = repo.revs("(%ld::%ld)", roots, new_heads)
691 # heads in the area are no longer heads
698 # heads in the area are no longer heads
692 new_heads.difference_update(affected_zone)
699 new_heads.difference_update(affected_zone)
693 # revisions in the area have children outside of it,
700 # revisions in the area have children outside of it,
694 # They might be new heads
701 # They might be new heads
695 candidates = repo.revs("parents(%ld + (%ld and merge())) and not null",
702 candidates = repo.revs("parents(%ld + (%ld and merge())) and not null",
696 roots, affected_zone)
703 roots, affected_zone)
697 candidates -= affected_zone
704 candidates -= affected_zone
698 if new_heads or candidates:
705 if new_heads or candidates:
699 # remove candidate that are ancestors of other heads
706 # remove candidate that are ancestors of other heads
700 new_heads.update(candidates)
707 new_heads.update(candidates)
701 prunestart = repo.revs("parents(%ld) and not null", new_heads)
708 prunestart = repo.revs("parents(%ld) and not null", new_heads)
702 pruned = dagop.reachableroots(repo, candidates, prunestart)
709 pruned = dagop.reachableroots(repo, candidates, prunestart)
703 new_heads.difference_update(pruned)
710 new_heads.difference_update(pruned)
704
711
705 return pycompat.maplist(cl.node, sorted(new_heads))
712 return pycompat.maplist(cl.node, sorted(new_heads))
706
713
707 def newcommitphase(ui):
714 def newcommitphase(ui):
708 """helper to get the target phase of new commit
715 """helper to get the target phase of new commit
709
716
710 Handle all possible values for the phases.new-commit options.
717 Handle all possible values for the phases.new-commit options.
711
718
712 """
719 """
713 v = ui.config('phases', 'new-commit')
720 v = ui.config('phases', 'new-commit')
714 try:
721 try:
715 return phasenames.index(v)
722 return phasenames.index(v)
716 except ValueError:
723 except ValueError:
717 try:
724 try:
718 return int(v)
725 return int(v)
719 except ValueError:
726 except ValueError:
720 msg = _("phases.new-commit: not a valid phase name ('%s')")
727 msg = _("phases.new-commit: not a valid phase name ('%s')")
721 raise error.ConfigError(msg % v)
728 raise error.ConfigError(msg % v)
722
729
723 def hassecret(repo):
730 def hassecret(repo):
724 """utility function that check if a repo have any secret changeset."""
731 """utility function that check if a repo have any secret changeset."""
725 return bool(repo._phasecache.phaseroots[2])
732 return bool(repo._phasecache.phaseroots[2])
726
733
727 def preparehookargs(node, old, new):
734 def preparehookargs(node, old, new):
728 if old is None:
735 if old is None:
729 old = ''
736 old = ''
730 else:
737 else:
731 old = phasenames[old]
738 old = phasenames[old]
732 return {'node': node,
739 return {'node': node,
733 'oldphase': old,
740 'oldphase': old,
734 'phase': phasenames[new]}
741 'phase': phasenames[new]}
@@ -1,876 +1,906 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 --template "{rev} {phaseidx} {desc}\n" $*; }
12 $ hglog() { hg log --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 Following commit are draft too
41 Following commit are draft too
42
42
43 $ mkcommit B
43 $ mkcommit B
44 test-debug-phase: new rev 1: x -> 1
44 test-debug-phase: new rev 1: x -> 1
45 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> draft
45 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> draft
46
46
47 $ hglog
47 $ hglog
48 1 1 B
48 1 1 B
49 0 1 A
49 0 1 A
50
50
51 Draft commit are properly created over public one:
51 Draft commit are properly created over public one:
52
52
53 $ hg phase --public .
53 $ hg phase --public .
54 test-debug-phase: move rev 0: 1 -> 0
54 test-debug-phase: move rev 0: 1 -> 0
55 test-debug-phase: move rev 1: 1 -> 0
55 test-debug-phase: move rev 1: 1 -> 0
56 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> public
56 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> public
57 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
57 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
58 $ hg phase
58 $ hg phase
59 1: public
59 1: public
60 $ hglog
60 $ hglog
61 1 0 B
61 1 0 B
62 0 0 A
62 0 0 A
63
63
64 $ mkcommit C
64 $ mkcommit C
65 test-debug-phase: new rev 2: x -> 1
65 test-debug-phase: new rev 2: x -> 1
66 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
66 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
67 $ mkcommit D
67 $ mkcommit D
68 test-debug-phase: new rev 3: x -> 1
68 test-debug-phase: new rev 3: x -> 1
69 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
69 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
70
70
71 $ hglog
71 $ hglog
72 3 1 D
72 3 1 D
73 2 1 C
73 2 1 C
74 1 0 B
74 1 0 B
75 0 0 A
75 0 0 A
76
76
77 Test creating changeset as secret
77 Test creating changeset as secret
78
78
79 $ mkcommit E --config phases.new-commit='secret'
79 $ mkcommit E --config phases.new-commit='secret'
80 test-debug-phase: new rev 4: x -> 2
80 test-debug-phase: new rev 4: x -> 2
81 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> secret
81 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> secret
82 $ hglog
82 $ hglog
83 4 2 E
83 4 2 E
84 3 1 D
84 3 1 D
85 2 1 C
85 2 1 C
86 1 0 B
86 1 0 B
87 0 0 A
87 0 0 A
88
88
89 Test the secret property is inherited
89 Test the secret property is inherited
90
90
91 $ mkcommit H
91 $ mkcommit H
92 test-debug-phase: new rev 5: x -> 2
92 test-debug-phase: new rev 5: x -> 2
93 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
93 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
94 $ hglog
94 $ hglog
95 5 2 H
95 5 2 H
96 4 2 E
96 4 2 E
97 3 1 D
97 3 1 D
98 2 1 C
98 2 1 C
99 1 0 B
99 1 0 B
100 0 0 A
100 0 0 A
101
101
102 Even on merge
102 Even on merge
103
103
104 $ hg up -q 1
104 $ hg up -q 1
105 $ mkcommit "B'"
105 $ mkcommit "B'"
106 test-debug-phase: new rev 6: x -> 1
106 test-debug-phase: new rev 6: x -> 1
107 created new head
107 created new head
108 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
108 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
109 $ hglog
109 $ hglog
110 6 1 B'
110 6 1 B'
111 5 2 H
111 5 2 H
112 4 2 E
112 4 2 E
113 3 1 D
113 3 1 D
114 2 1 C
114 2 1 C
115 1 0 B
115 1 0 B
116 0 0 A
116 0 0 A
117 $ hg merge 4 # E
117 $ hg merge 4 # E
118 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
119 (branch merge, don't forget to commit)
119 (branch merge, don't forget to commit)
120 $ hg phase
120 $ hg phase
121 6: draft
121 6: draft
122 4: secret
122 4: secret
123 $ hg ci -m "merge B' and E"
123 $ hg ci -m "merge B' and E"
124 test-debug-phase: new rev 7: x -> 2
124 test-debug-phase: new rev 7: x -> 2
125 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> secret
125 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> secret
126
126
127 $ hglog
127 $ hglog
128 7 2 merge B' and E
128 7 2 merge B' and E
129 6 1 B'
129 6 1 B'
130 5 2 H
130 5 2 H
131 4 2 E
131 4 2 E
132 3 1 D
132 3 1 D
133 2 1 C
133 2 1 C
134 1 0 B
134 1 0 B
135 0 0 A
135 0 0 A
136
136
137 Test secret changeset are not pushed
137 Test secret changeset are not pushed
138
138
139 $ hg init ../push-dest
139 $ hg init ../push-dest
140 $ cat > ../push-dest/.hg/hgrc << EOF
140 $ cat > ../push-dest/.hg/hgrc << EOF
141 > [phases]
141 > [phases]
142 > publish=False
142 > publish=False
143 > EOF
143 > EOF
144 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
144 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
145 comparing with ../push-dest
145 comparing with ../push-dest
146 searching for changes
146 searching for changes
147 0 public A
147 0 public A
148 1 public B
148 1 public B
149 2 draft C
149 2 draft C
150 3 draft D
150 3 draft D
151 6 draft B'
151 6 draft B'
152 $ hg outgoing -r 'branch(default)' ../push-dest --template='{rev} {phase} {desc|firstline}\n'
152 $ hg outgoing -r 'branch(default)' ../push-dest --template='{rev} {phase} {desc|firstline}\n'
153 comparing with ../push-dest
153 comparing with ../push-dest
154 searching for changes
154 searching for changes
155 0 public A
155 0 public A
156 1 public B
156 1 public B
157 2 draft C
157 2 draft C
158 3 draft D
158 3 draft D
159 6 draft B'
159 6 draft B'
160
160
161 $ hg push ../push-dest -f # force because we push multiple heads
161 $ hg push ../push-dest -f # force because we push multiple heads
162 pushing to ../push-dest
162 pushing to ../push-dest
163 searching for changes
163 searching for changes
164 adding changesets
164 adding changesets
165 adding manifests
165 adding manifests
166 adding file changes
166 adding file changes
167 added 5 changesets with 5 changes to 5 files (+1 heads)
167 added 5 changesets with 5 changes to 5 files (+1 heads)
168 test-debug-phase: new rev 0: x -> 0
168 test-debug-phase: new rev 0: x -> 0
169 test-debug-phase: new rev 1: x -> 0
169 test-debug-phase: new rev 1: x -> 0
170 test-debug-phase: new rev 2: x -> 1
170 test-debug-phase: new rev 2: x -> 1
171 test-debug-phase: new rev 3: x -> 1
171 test-debug-phase: new rev 3: x -> 1
172 test-debug-phase: new rev 4: x -> 1
172 test-debug-phase: new rev 4: x -> 1
173 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
173 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
174 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
174 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
175 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
175 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
176 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
176 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
177 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
177 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
178 $ hglog
178 $ hglog
179 7 2 merge B' and E
179 7 2 merge B' and E
180 6 1 B'
180 6 1 B'
181 5 2 H
181 5 2 H
182 4 2 E
182 4 2 E
183 3 1 D
183 3 1 D
184 2 1 C
184 2 1 C
185 1 0 B
185 1 0 B
186 0 0 A
186 0 0 A
187 $ cd ../push-dest
187 $ cd ../push-dest
188 $ hglog
188 $ hglog
189 4 1 B'
189 4 1 B'
190 3 1 D
190 3 1 D
191 2 1 C
191 2 1 C
192 1 0 B
192 1 0 B
193 0 0 A
193 0 0 A
194
194
195 (Issue3303)
195 (Issue3303)
196 Check that remote secret changeset are ignore when checking creation of remote heads
196 Check that remote secret changeset are ignore when checking creation of remote heads
197
197
198 We add a secret head into the push destination. This secret head shadows a
198 We add a secret head into the push destination. This secret head shadows a
199 visible shared between the initial repo and the push destination.
199 visible shared between the initial repo and the push destination.
200
200
201 $ hg up -q 4 # B'
201 $ hg up -q 4 # B'
202 $ mkcommit Z --config phases.new-commit=secret
202 $ mkcommit Z --config phases.new-commit=secret
203 test-debug-phase: new rev 5: x -> 2
203 test-debug-phase: new rev 5: x -> 2
204 test-hook-close-phase: 2713879da13d6eea1ff22b442a5a87cb31a7ce6a: -> secret
204 test-hook-close-phase: 2713879da13d6eea1ff22b442a5a87cb31a7ce6a: -> secret
205 $ hg phase .
205 $ hg phase .
206 5: secret
206 5: secret
207
207
208 We now try to push a new public changeset that descend from the common public
208 We now try to push a new public changeset that descend from the common public
209 head shadowed by the remote secret head.
209 head shadowed by the remote secret head.
210
210
211 $ cd ../initialrepo
211 $ cd ../initialrepo
212 $ hg up -q 6 #B'
212 $ hg up -q 6 #B'
213 $ mkcommit I
213 $ mkcommit I
214 test-debug-phase: new rev 8: x -> 1
214 test-debug-phase: new rev 8: x -> 1
215 created new head
215 created new head
216 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
216 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
217 $ hg push ../push-dest
217 $ hg push ../push-dest
218 pushing to ../push-dest
218 pushing to ../push-dest
219 searching for changes
219 searching for changes
220 adding changesets
220 adding changesets
221 adding manifests
221 adding manifests
222 adding file changes
222 adding file changes
223 added 1 changesets with 1 changes to 1 files (+1 heads)
223 added 1 changesets with 1 changes to 1 files (+1 heads)
224 test-debug-phase: new rev 6: x -> 1
224 test-debug-phase: new rev 6: x -> 1
225 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
225 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
226
226
227 :note: The "(+1 heads)" is wrong as we do not had any visible head
227 :note: The "(+1 heads)" is wrong as we do not had any visible head
228
228
229 check that branch cache with "served" filter are properly computed and stored
229 check that branch cache with "served" filter are properly computed and stored
230
230
231 $ ls ../push-dest/.hg/cache/branch2*
231 $ ls ../push-dest/.hg/cache/branch2*
232 ../push-dest/.hg/cache/branch2-base
232 ../push-dest/.hg/cache/branch2-base
233 ../push-dest/.hg/cache/branch2-served
233 ../push-dest/.hg/cache/branch2-served
234 $ cat ../push-dest/.hg/cache/branch2-served
234 $ cat ../push-dest/.hg/cache/branch2-served
235 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
235 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
236 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
236 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
237 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
237 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
238 $ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n' #update visible cache too
238 $ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n' #update visible cache too
239 6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
239 6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
240 5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
240 5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
241 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
241 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
242 $ ls ../push-dest/.hg/cache/branch2*
242 $ ls ../push-dest/.hg/cache/branch2*
243 ../push-dest/.hg/cache/branch2-base
243 ../push-dest/.hg/cache/branch2-base
244 ../push-dest/.hg/cache/branch2-served
244 ../push-dest/.hg/cache/branch2-served
245 ../push-dest/.hg/cache/branch2-visible
245 ../push-dest/.hg/cache/branch2-visible
246 $ cat ../push-dest/.hg/cache/branch2-served
246 $ cat ../push-dest/.hg/cache/branch2-served
247 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
247 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
248 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
248 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
249 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
249 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
250 $ cat ../push-dest/.hg/cache/branch2-visible
250 $ cat ../push-dest/.hg/cache/branch2-visible
251 6d6770faffce199f1fddd1cf87f6f026138cf061 6
251 6d6770faffce199f1fddd1cf87f6f026138cf061 6
252 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
252 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
253 2713879da13d6eea1ff22b442a5a87cb31a7ce6a o default
253 2713879da13d6eea1ff22b442a5a87cb31a7ce6a o default
254 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
254 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
255
255
256
256
257 Restore condition prior extra insertion.
257 Restore condition prior extra insertion.
258 $ hg -q --config extensions.mq= strip .
258 $ hg -q --config extensions.mq= strip .
259 $ hg up -q 7
259 $ hg up -q 7
260 $ cd ..
260 $ cd ..
261
261
262 Test secret changeset are not pull
262 Test secret changeset are not pull
263
263
264 $ hg init pull-dest
264 $ hg init pull-dest
265 $ cd pull-dest
265 $ cd pull-dest
266 $ hg pull ../initialrepo
266 $ hg pull ../initialrepo
267 pulling from ../initialrepo
267 pulling from ../initialrepo
268 requesting all changes
268 requesting all changes
269 adding changesets
269 adding changesets
270 adding manifests
270 adding manifests
271 adding file changes
271 adding file changes
272 added 5 changesets with 5 changes to 5 files (+1 heads)
272 added 5 changesets with 5 changes to 5 files (+1 heads)
273 new changesets 4a2df7238c3b:cf9fe039dfd6
273 new changesets 4a2df7238c3b:cf9fe039dfd6
274 test-debug-phase: new rev 0: x -> 0
274 test-debug-phase: new rev 0: x -> 0
275 test-debug-phase: new rev 1: x -> 0
275 test-debug-phase: new rev 1: x -> 0
276 test-debug-phase: new rev 2: x -> 0
276 test-debug-phase: new rev 2: x -> 0
277 test-debug-phase: new rev 3: x -> 0
277 test-debug-phase: new rev 3: x -> 0
278 test-debug-phase: new rev 4: x -> 0
278 test-debug-phase: new rev 4: x -> 0
279 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
279 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
280 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
280 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
281 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
281 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
282 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
282 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
283 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
283 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
284 (run 'hg heads' to see heads, 'hg merge' to merge)
284 (run 'hg heads' to see heads, 'hg merge' to merge)
285 $ hglog
285 $ hglog
286 4 0 B'
286 4 0 B'
287 3 0 D
287 3 0 D
288 2 0 C
288 2 0 C
289 1 0 B
289 1 0 B
290 0 0 A
290 0 0 A
291 $ cd ..
291 $ cd ..
292
292
293 But secret can still be bundled explicitly
293 But secret can still be bundled explicitly
294
294
295 $ cd initialrepo
295 $ cd initialrepo
296 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
296 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
297 4 changesets found
297 4 changesets found
298 $ cd ..
298 $ cd ..
299
299
300 Test secret changeset are not cloned
300 Test secret changeset are not cloned
301 (during local clone)
301 (during local clone)
302
302
303 $ hg clone -qU initialrepo clone-dest
303 $ hg clone -qU initialrepo clone-dest
304 test-debug-phase: new rev 0: x -> 0
304 test-debug-phase: new rev 0: x -> 0
305 test-debug-phase: new rev 1: x -> 0
305 test-debug-phase: new rev 1: x -> 0
306 test-debug-phase: new rev 2: x -> 0
306 test-debug-phase: new rev 2: x -> 0
307 test-debug-phase: new rev 3: x -> 0
307 test-debug-phase: new rev 3: x -> 0
308 test-debug-phase: new rev 4: x -> 0
308 test-debug-phase: new rev 4: x -> 0
309 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
309 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
310 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
310 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
311 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
311 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
312 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
312 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
313 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
313 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
314 $ hglog -R clone-dest
314 $ hglog -R clone-dest
315 4 0 B'
315 4 0 B'
316 3 0 D
316 3 0 D
317 2 0 C
317 2 0 C
318 1 0 B
318 1 0 B
319 0 0 A
319 0 0 A
320
320
321 Test summary
321 Test summary
322
322
323 $ hg summary -R clone-dest --verbose
323 $ hg summary -R clone-dest --verbose
324 parent: -1:000000000000 (no revision checked out)
324 parent: -1:000000000000 (no revision checked out)
325 branch: default
325 branch: default
326 commit: (clean)
326 commit: (clean)
327 update: 5 new changesets (update)
327 update: 5 new changesets (update)
328 $ hg summary -R initialrepo
328 $ hg summary -R initialrepo
329 parent: 7:17a481b3bccb tip
329 parent: 7:17a481b3bccb tip
330 merge B' and E
330 merge B' and E
331 branch: default
331 branch: default
332 commit: (clean) (secret)
332 commit: (clean) (secret)
333 update: 1 new changesets, 2 branch heads (merge)
333 update: 1 new changesets, 2 branch heads (merge)
334 phases: 3 draft, 3 secret
334 phases: 3 draft, 3 secret
335 $ hg summary -R initialrepo --quiet
335 $ hg summary -R initialrepo --quiet
336 parent: 7:17a481b3bccb tip
336 parent: 7:17a481b3bccb tip
337 update: 1 new changesets, 2 branch heads (merge)
337 update: 1 new changesets, 2 branch heads (merge)
338
338
339 Test revset
339 Test revset
340
340
341 $ cd initialrepo
341 $ cd initialrepo
342 $ hglog -r 'public()'
342 $ hglog -r 'public()'
343 0 0 A
343 0 0 A
344 1 0 B
344 1 0 B
345 $ hglog -r 'draft()'
345 $ hglog -r 'draft()'
346 2 1 C
346 2 1 C
347 3 1 D
347 3 1 D
348 6 1 B'
348 6 1 B'
349 $ hglog -r 'secret()'
349 $ hglog -r 'secret()'
350 4 2 E
350 4 2 E
351 5 2 H
351 5 2 H
352 7 2 merge B' and E
352 7 2 merge B' and E
353
353
354 test that phase are displayed in log at debug level
354 test that phase are displayed in log at debug level
355
355
356 $ hg log --debug
356 $ hg log --debug
357 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
357 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
358 tag: tip
358 tag: tip
359 phase: secret
359 phase: secret
360 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
360 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
361 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
361 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
362 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
362 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
363 user: test
363 user: test
364 date: Thu Jan 01 00:00:00 1970 +0000
364 date: Thu Jan 01 00:00:00 1970 +0000
365 files+: C D E
365 files+: C D E
366 extra: branch=default
366 extra: branch=default
367 description:
367 description:
368 merge B' and E
368 merge B' and E
369
369
370
370
371 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
371 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
372 phase: draft
372 phase: draft
373 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
373 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
374 parent: -1:0000000000000000000000000000000000000000
374 parent: -1:0000000000000000000000000000000000000000
375 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
375 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
376 user: test
376 user: test
377 date: Thu Jan 01 00:00:00 1970 +0000
377 date: Thu Jan 01 00:00:00 1970 +0000
378 files+: B'
378 files+: B'
379 extra: branch=default
379 extra: branch=default
380 description:
380 description:
381 B'
381 B'
382
382
383
383
384 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
384 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
385 phase: secret
385 phase: secret
386 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
386 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
387 parent: -1:0000000000000000000000000000000000000000
387 parent: -1:0000000000000000000000000000000000000000
388 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
388 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
389 user: test
389 user: test
390 date: Thu Jan 01 00:00:00 1970 +0000
390 date: Thu Jan 01 00:00:00 1970 +0000
391 files+: H
391 files+: H
392 extra: branch=default
392 extra: branch=default
393 description:
393 description:
394 H
394 H
395
395
396
396
397 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
397 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
398 phase: secret
398 phase: secret
399 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
399 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
400 parent: -1:0000000000000000000000000000000000000000
400 parent: -1:0000000000000000000000000000000000000000
401 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
401 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
402 user: test
402 user: test
403 date: Thu Jan 01 00:00:00 1970 +0000
403 date: Thu Jan 01 00:00:00 1970 +0000
404 files+: E
404 files+: E
405 extra: branch=default
405 extra: branch=default
406 description:
406 description:
407 E
407 E
408
408
409
409
410 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
410 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
411 phase: draft
411 phase: draft
412 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
412 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
413 parent: -1:0000000000000000000000000000000000000000
413 parent: -1:0000000000000000000000000000000000000000
414 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
414 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
415 user: test
415 user: test
416 date: Thu Jan 01 00:00:00 1970 +0000
416 date: Thu Jan 01 00:00:00 1970 +0000
417 files+: D
417 files+: D
418 extra: branch=default
418 extra: branch=default
419 description:
419 description:
420 D
420 D
421
421
422
422
423 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
423 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
424 phase: draft
424 phase: draft
425 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
425 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
426 parent: -1:0000000000000000000000000000000000000000
426 parent: -1:0000000000000000000000000000000000000000
427 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
427 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
428 user: test
428 user: test
429 date: Thu Jan 01 00:00:00 1970 +0000
429 date: Thu Jan 01 00:00:00 1970 +0000
430 files+: C
430 files+: C
431 extra: branch=default
431 extra: branch=default
432 description:
432 description:
433 C
433 C
434
434
435
435
436 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
436 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
437 phase: public
437 phase: public
438 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
438 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
439 parent: -1:0000000000000000000000000000000000000000
439 parent: -1:0000000000000000000000000000000000000000
440 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
440 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
441 user: test
441 user: test
442 date: Thu Jan 01 00:00:00 1970 +0000
442 date: Thu Jan 01 00:00:00 1970 +0000
443 files+: B
443 files+: B
444 extra: branch=default
444 extra: branch=default
445 description:
445 description:
446 B
446 B
447
447
448
448
449 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
449 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
450 phase: public
450 phase: public
451 parent: -1:0000000000000000000000000000000000000000
451 parent: -1:0000000000000000000000000000000000000000
452 parent: -1:0000000000000000000000000000000000000000
452 parent: -1:0000000000000000000000000000000000000000
453 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
453 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
454 user: test
454 user: test
455 date: Thu Jan 01 00:00:00 1970 +0000
455 date: Thu Jan 01 00:00:00 1970 +0000
456 files+: A
456 files+: A
457 extra: branch=default
457 extra: branch=default
458 description:
458 description:
459 A
459 A
460
460
461
461
462
462
463
463
464 (Issue3707)
464 (Issue3707)
465 test invalid phase name
465 test invalid phase name
466
466
467 $ mkcommit I --config phases.new-commit='babar'
467 $ mkcommit I --config phases.new-commit='babar'
468 transaction abort!
468 transaction abort!
469 rollback completed
469 rollback completed
470 abort: phases.new-commit: not a valid phase name ('babar')
470 abort: phases.new-commit: not a valid phase name ('babar')
471 [255]
471 [255]
472 Test phase command
472 Test phase command
473 ===================
473 ===================
474
474
475 initial picture
475 initial picture
476
476
477 $ hg log -G --template "{rev} {phase} {desc}\n"
477 $ hg log -G --template "{rev} {phase} {desc}\n"
478 @ 7 secret merge B' and E
478 @ 7 secret merge B' and E
479 |\
479 |\
480 | o 6 draft B'
480 | o 6 draft B'
481 | |
481 | |
482 +---o 5 secret H
482 +---o 5 secret H
483 | |
483 | |
484 o | 4 secret E
484 o | 4 secret E
485 | |
485 | |
486 o | 3 draft D
486 o | 3 draft D
487 | |
487 | |
488 o | 2 draft C
488 o | 2 draft C
489 |/
489 |/
490 o 1 public B
490 o 1 public B
491 |
491 |
492 o 0 public A
492 o 0 public A
493
493
494
494
495 display changesets phase
495 display changesets phase
496
496
497 (mixing -r and plain rev specification)
497 (mixing -r and plain rev specification)
498
498
499 $ hg phase 1::4 -r 7
499 $ hg phase 1::4 -r 7
500 1: public
500 1: public
501 2: draft
501 2: draft
502 3: draft
502 3: draft
503 4: secret
503 4: secret
504 7: secret
504 7: secret
505
505
506
506
507 move changeset forward
507 move changeset forward
508
508
509 (with -r option)
509 (with -r option)
510
510
511 $ hg phase --public -r 2
511 $ hg phase --public -r 2
512 test-debug-phase: move rev 2: 1 -> 0
512 test-debug-phase: move rev 2: 1 -> 0
513 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
513 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
514 $ hg log -G --template "{rev} {phase} {desc}\n"
514 $ hg log -G --template "{rev} {phase} {desc}\n"
515 @ 7 secret merge B' and E
515 @ 7 secret merge B' and E
516 |\
516 |\
517 | o 6 draft B'
517 | o 6 draft B'
518 | |
518 | |
519 +---o 5 secret H
519 +---o 5 secret H
520 | |
520 | |
521 o | 4 secret E
521 o | 4 secret E
522 | |
522 | |
523 o | 3 draft D
523 o | 3 draft D
524 | |
524 | |
525 o | 2 public C
525 o | 2 public C
526 |/
526 |/
527 o 1 public B
527 o 1 public B
528 |
528 |
529 o 0 public A
529 o 0 public A
530
530
531
531
532 move changeset backward
532 move changeset backward
533
533
534 (without -r option)
534 (without -r option)
535
535
536 $ hg phase --draft --force 2
536 $ hg phase --draft --force 2
537 test-debug-phase: move rev 2: 0 -> 1
537 test-debug-phase: move rev 2: 0 -> 1
538 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: public -> draft
538 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: public -> draft
539 $ hg log -G --template "{rev} {phase} {desc}\n"
539 $ hg log -G --template "{rev} {phase} {desc}\n"
540 @ 7 secret merge B' and E
540 @ 7 secret merge B' and E
541 |\
541 |\
542 | o 6 draft B'
542 | o 6 draft B'
543 | |
543 | |
544 +---o 5 secret H
544 +---o 5 secret H
545 | |
545 | |
546 o | 4 secret E
546 o | 4 secret E
547 | |
547 | |
548 o | 3 draft D
548 o | 3 draft D
549 | |
549 | |
550 o | 2 draft C
550 o | 2 draft C
551 |/
551 |/
552 o 1 public B
552 o 1 public B
553 |
553 |
554 o 0 public A
554 o 0 public A
555
555
556
556
557 move changeset forward and backward
557 move changeset forward and backward
558
558
559 $ hg phase --draft --force 1::4
559 $ hg phase --draft --force 1::4
560 test-debug-phase: move rev 1: 0 -> 1
560 test-debug-phase: move rev 1: 0 -> 1
561 test-debug-phase: move rev 4: 2 -> 1
561 test-debug-phase: move rev 4: 2 -> 1
562 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: public -> draft
562 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: public -> draft
563 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
563 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
564 $ hg log -G --template "{rev} {phase} {desc}\n"
564 $ hg log -G --template "{rev} {phase} {desc}\n"
565 @ 7 secret merge B' and E
565 @ 7 secret merge B' and E
566 |\
566 |\
567 | o 6 draft B'
567 | o 6 draft B'
568 | |
568 | |
569 +---o 5 secret H
569 +---o 5 secret H
570 | |
570 | |
571 o | 4 draft E
571 o | 4 draft E
572 | |
572 | |
573 o | 3 draft D
573 o | 3 draft D
574 | |
574 | |
575 o | 2 draft C
575 o | 2 draft C
576 |/
576 |/
577 o 1 draft B
577 o 1 draft B
578 |
578 |
579 o 0 public A
579 o 0 public A
580
580
581 test partial failure
581 test partial failure
582
582
583 $ hg phase --public 7
583 $ hg phase --public 7
584 test-debug-phase: move rev 1: 1 -> 0
584 test-debug-phase: move rev 1: 1 -> 0
585 test-debug-phase: move rev 2: 1 -> 0
585 test-debug-phase: move rev 2: 1 -> 0
586 test-debug-phase: move rev 3: 1 -> 0
586 test-debug-phase: move rev 3: 1 -> 0
587 test-debug-phase: move rev 4: 1 -> 0
587 test-debug-phase: move rev 4: 1 -> 0
588 test-debug-phase: move rev 6: 1 -> 0
588 test-debug-phase: move rev 6: 1 -> 0
589 test-debug-phase: move rev 7: 2 -> 0
589 test-debug-phase: move rev 7: 2 -> 0
590 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
590 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
591 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
591 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
592 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: draft -> public
592 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: draft -> public
593 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: draft -> public
593 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: draft -> public
594 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: draft -> public
594 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: draft -> public
595 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> public
595 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> public
596 $ hg phase --draft '5 or 7'
596 $ hg phase --draft '5 or 7'
597 test-debug-phase: move rev 5: 2 -> 1
597 test-debug-phase: move rev 5: 2 -> 1
598 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: secret -> draft
598 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: secret -> draft
599 cannot move 1 changesets to a higher phase, use --force
599 cannot move 1 changesets to a higher phase, use --force
600 phase changed for 1 changesets
600 phase changed for 1 changesets
601 [1]
601 [1]
602 $ hg log -G --template "{rev} {phase} {desc}\n"
602 $ hg log -G --template "{rev} {phase} {desc}\n"
603 @ 7 public merge B' and E
603 @ 7 public merge B' and E
604 |\
604 |\
605 | o 6 public B'
605 | o 6 public B'
606 | |
606 | |
607 +---o 5 draft H
607 +---o 5 draft H
608 | |
608 | |
609 o | 4 public E
609 o | 4 public E
610 | |
610 | |
611 o | 3 public D
611 o | 3 public D
612 | |
612 | |
613 o | 2 public C
613 o | 2 public C
614 |/
614 |/
615 o 1 public B
615 o 1 public B
616 |
616 |
617 o 0 public A
617 o 0 public A
618
618
619
619
620 test complete failure
620 test complete failure
621
621
622 $ hg phase --draft 7
622 $ hg phase --draft 7
623 cannot move 1 changesets to a higher phase, use --force
623 cannot move 1 changesets to a higher phase, use --force
624 no phases changed
624 no phases changed
625 [1]
625 [1]
626
626
627 $ cd ..
627 $ cd ..
628
628
629 test hidden changeset are not cloned as public (issue3935)
629 test hidden changeset are not cloned as public (issue3935)
630
630
631 $ cd initialrepo
631 $ cd initialrepo
632
632
633 (enabling evolution)
633 (enabling evolution)
634 $ cat >> $HGRCPATH << EOF
634 $ cat >> $HGRCPATH << EOF
635 > [experimental]
635 > [experimental]
636 > evolution.createmarkers=True
636 > evolution.createmarkers=True
637 > EOF
637 > EOF
638
638
639 (making a changeset hidden; H in that case)
639 (making a changeset hidden; H in that case)
640 $ hg debugobsolete `hg id --debug -r 5`
640 $ hg debugobsolete `hg id --debug -r 5`
641 obsoleted 1 changesets
641 obsoleted 1 changesets
642
642
643 $ cd ..
643 $ cd ..
644 $ hg clone initialrepo clonewithobs
644 $ hg clone initialrepo clonewithobs
645 requesting all changes
645 requesting all changes
646 adding changesets
646 adding changesets
647 adding manifests
647 adding manifests
648 adding file changes
648 adding file changes
649 added 7 changesets with 6 changes to 6 files
649 added 7 changesets with 6 changes to 6 files
650 new changesets 4a2df7238c3b:17a481b3bccb
650 new changesets 4a2df7238c3b:17a481b3bccb
651 test-debug-phase: new rev 0: x -> 0
651 test-debug-phase: new rev 0: x -> 0
652 test-debug-phase: new rev 1: x -> 0
652 test-debug-phase: new rev 1: x -> 0
653 test-debug-phase: new rev 2: x -> 0
653 test-debug-phase: new rev 2: x -> 0
654 test-debug-phase: new rev 3: x -> 0
654 test-debug-phase: new rev 3: x -> 0
655 test-debug-phase: new rev 4: x -> 0
655 test-debug-phase: new rev 4: x -> 0
656 test-debug-phase: new rev 5: x -> 0
656 test-debug-phase: new rev 5: x -> 0
657 test-debug-phase: new rev 6: x -> 0
657 test-debug-phase: new rev 6: x -> 0
658 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
658 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
659 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
659 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
660 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
660 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
661 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
661 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
662 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> public
662 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> public
663 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
663 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
664 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> public
664 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> public
665 updating to branch default
665 updating to branch default
666 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
666 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
667 $ cd clonewithobs
667 $ cd clonewithobs
668 $ hg log -G --template "{rev} {phase} {desc}\n"
668 $ hg log -G --template "{rev} {phase} {desc}\n"
669 @ 6 public merge B' and E
669 @ 6 public merge B' and E
670 |\
670 |\
671 | o 5 public B'
671 | o 5 public B'
672 | |
672 | |
673 o | 4 public E
673 o | 4 public E
674 | |
674 | |
675 o | 3 public D
675 o | 3 public D
676 | |
676 | |
677 o | 2 public C
677 o | 2 public C
678 |/
678 |/
679 o 1 public B
679 o 1 public B
680 |
680 |
681 o 0 public A
681 o 0 public A
682
682
683
683
684 test verify repo containing hidden changesets, which should not abort just
684 test verify repo containing hidden changesets, which should not abort just
685 because repo.cancopy() is False
685 because repo.cancopy() is False
686
686
687 $ cd ../initialrepo
687 $ cd ../initialrepo
688 $ hg verify
688 $ hg verify
689 checking changesets
689 checking changesets
690 checking manifests
690 checking manifests
691 crosschecking files in changesets and manifests
691 crosschecking files in changesets and manifests
692 checking files
692 checking files
693 7 files, 8 changesets, 7 total revisions
693 7 files, 8 changesets, 7 total revisions
694
694
695 $ cd ..
695 $ cd ..
696
696
697 check whether HG_PENDING makes pending changes only in related
697 check whether HG_PENDING makes pending changes only in related
698 repositories visible to an external hook.
698 repositories visible to an external hook.
699
699
700 (emulate a transaction running concurrently by copied
700 (emulate a transaction running concurrently by copied
701 .hg/phaseroots.pending in subsequent test)
701 .hg/phaseroots.pending in subsequent test)
702
702
703 $ cat > $TESTTMP/savepending.sh <<EOF
703 $ cat > $TESTTMP/savepending.sh <<EOF
704 > cp .hg/store/phaseroots.pending .hg/store/phaseroots.pending.saved
704 > cp .hg/store/phaseroots.pending .hg/store/phaseroots.pending.saved
705 > exit 1 # to avoid changing phase for subsequent tests
705 > exit 1 # to avoid changing phase for subsequent tests
706 > EOF
706 > EOF
707 $ cd push-dest
707 $ cd push-dest
708 $ hg phase 6
708 $ hg phase 6
709 6: draft
709 6: draft
710 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" phase -f -s 6
710 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" phase -f -s 6
711 transaction abort!
711 transaction abort!
712 rollback completed
712 rollback completed
713 abort: pretxnclose hook exited with status 1
713 abort: pretxnclose hook exited with status 1
714 [255]
714 [255]
715 $ cp .hg/store/phaseroots.pending.saved .hg/store/phaseroots.pending
715 $ cp .hg/store/phaseroots.pending.saved .hg/store/phaseroots.pending
716
716
717 (check (in)visibility of phaseroot while transaction running in repo)
717 (check (in)visibility of phaseroot while transaction running in repo)
718
718
719 $ cat > $TESTTMP/checkpending.sh <<EOF
719 $ cat > $TESTTMP/checkpending.sh <<EOF
720 > echo '@initialrepo'
720 > echo '@initialrepo'
721 > hg -R "$TESTTMP/initialrepo" phase 7
721 > hg -R "$TESTTMP/initialrepo" phase 7
722 > echo '@push-dest'
722 > echo '@push-dest'
723 > hg -R "$TESTTMP/push-dest" phase 6
723 > hg -R "$TESTTMP/push-dest" phase 6
724 > exit 1 # to avoid changing phase for subsequent tests
724 > exit 1 # to avoid changing phase for subsequent tests
725 > EOF
725 > EOF
726 $ cd ../initialrepo
726 $ cd ../initialrepo
727 $ hg phase 7
727 $ hg phase 7
728 7: public
728 7: public
729 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" phase -f -s 7
729 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" phase -f -s 7
730 @initialrepo
730 @initialrepo
731 7: secret
731 7: secret
732 @push-dest
732 @push-dest
733 6: draft
733 6: draft
734 transaction abort!
734 transaction abort!
735 rollback completed
735 rollback completed
736 abort: pretxnclose hook exited with status 1
736 abort: pretxnclose hook exited with status 1
737 [255]
737 [255]
738
738
739 Check that pretxnclose-phase hook can control phase movement
739 Check that pretxnclose-phase hook can control phase movement
740
740
741 $ hg phase --force b3325c91a4d9 --secret
741 $ hg phase --force b3325c91a4d9 --secret
742 test-debug-phase: move rev 3: 0 -> 2
742 test-debug-phase: move rev 3: 0 -> 2
743 test-debug-phase: move rev 4: 0 -> 2
743 test-debug-phase: move rev 4: 0 -> 2
744 test-debug-phase: move rev 5: 1 -> 2
744 test-debug-phase: move rev 5: 1 -> 2
745 test-debug-phase: move rev 7: 0 -> 2
745 test-debug-phase: move rev 7: 0 -> 2
746 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: public -> secret
746 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: public -> secret
747 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: public -> secret
747 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: public -> secret
748 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: draft -> secret
748 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: draft -> secret
749 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: public -> secret
749 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: public -> secret
750 $ hg log -G -T phases
750 $ hg log -G -T phases
751 @ changeset: 7:17a481b3bccb
751 @ changeset: 7:17a481b3bccb
752 |\ tag: tip
752 |\ tag: tip
753 | | phase: secret
753 | | phase: secret
754 | | parent: 6:cf9fe039dfd6
754 | | parent: 6:cf9fe039dfd6
755 | | parent: 4:a603bfb5a83e
755 | | parent: 4:a603bfb5a83e
756 | | user: test
756 | | user: test
757 | | date: Thu Jan 01 00:00:00 1970 +0000
757 | | date: Thu Jan 01 00:00:00 1970 +0000
758 | | summary: merge B' and E
758 | | summary: merge B' and E
759 | |
759 | |
760 | o changeset: 6:cf9fe039dfd6
760 | o changeset: 6:cf9fe039dfd6
761 | | phase: public
761 | | phase: public
762 | | parent: 1:27547f69f254
762 | | parent: 1:27547f69f254
763 | | user: test
763 | | user: test
764 | | date: Thu Jan 01 00:00:00 1970 +0000
764 | | date: Thu Jan 01 00:00:00 1970 +0000
765 | | summary: B'
765 | | summary: B'
766 | |
766 | |
767 o | changeset: 4:a603bfb5a83e
767 o | changeset: 4:a603bfb5a83e
768 | | phase: secret
768 | | phase: secret
769 | | user: test
769 | | user: test
770 | | date: Thu Jan 01 00:00:00 1970 +0000
770 | | date: Thu Jan 01 00:00:00 1970 +0000
771 | | summary: E
771 | | summary: E
772 | |
772 | |
773 o | changeset: 3:b3325c91a4d9
773 o | changeset: 3:b3325c91a4d9
774 | | phase: secret
774 | | phase: secret
775 | | user: test
775 | | user: test
776 | | date: Thu Jan 01 00:00:00 1970 +0000
776 | | date: Thu Jan 01 00:00:00 1970 +0000
777 | | summary: D
777 | | summary: D
778 | |
778 | |
779 o | changeset: 2:f838bfaca5c7
779 o | changeset: 2:f838bfaca5c7
780 |/ phase: public
780 |/ phase: public
781 | user: test
781 | user: test
782 | date: Thu Jan 01 00:00:00 1970 +0000
782 | date: Thu Jan 01 00:00:00 1970 +0000
783 | summary: C
783 | summary: C
784 |
784 |
785 o changeset: 1:27547f69f254
785 o changeset: 1:27547f69f254
786 | phase: public
786 | phase: public
787 | user: test
787 | user: test
788 | date: Thu Jan 01 00:00:00 1970 +0000
788 | date: Thu Jan 01 00:00:00 1970 +0000
789 | summary: B
789 | summary: B
790 |
790 |
791 o changeset: 0:4a2df7238c3b
791 o changeset: 0:4a2df7238c3b
792 phase: public
792 phase: public
793 user: test
793 user: test
794 date: Thu Jan 01 00:00:00 1970 +0000
794 date: Thu Jan 01 00:00:00 1970 +0000
795 summary: A
795 summary: A
796
796
797
797
798 Install a hook that prevent b3325c91a4d9 to become public
798 Install a hook that prevent b3325c91a4d9 to become public
799
799
800 $ cat >> .hg/hgrc << EOF
800 $ cat >> .hg/hgrc << EOF
801 > [hooks]
801 > [hooks]
802 > pretxnclose-phase.nopublish_D = sh -c "(echo \$HG_NODE| grep -v b3325c91a4d9>/dev/null) || [ 'public' != \$HG_PHASE ]"
802 > pretxnclose-phase.nopublish_D = sh -c "(echo \$HG_NODE| grep -v b3325c91a4d9>/dev/null) || [ 'public' != \$HG_PHASE ]"
803 > EOF
803 > EOF
804
804
805 Try various actions. only the draft move should succeed
805 Try various actions. only the draft move should succeed
806
806
807 $ hg phase --public b3325c91a4d9
807 $ hg phase --public b3325c91a4d9
808 transaction abort!
808 transaction abort!
809 rollback completed
809 rollback completed
810 abort: pretxnclose-phase.nopublish_D hook exited with status 1
810 abort: pretxnclose-phase.nopublish_D hook exited with status 1
811 [255]
811 [255]
812 $ hg phase --public a603bfb5a83e
812 $ hg phase --public a603bfb5a83e
813 transaction abort!
813 transaction abort!
814 rollback completed
814 rollback completed
815 abort: pretxnclose-phase.nopublish_D hook exited with status 1
815 abort: pretxnclose-phase.nopublish_D hook exited with status 1
816 [255]
816 [255]
817 $ hg phase --draft 17a481b3bccb
817 $ hg phase --draft 17a481b3bccb
818 test-debug-phase: move rev 3: 2 -> 1
818 test-debug-phase: move rev 3: 2 -> 1
819 test-debug-phase: move rev 4: 2 -> 1
819 test-debug-phase: move rev 4: 2 -> 1
820 test-debug-phase: move rev 7: 2 -> 1
820 test-debug-phase: move rev 7: 2 -> 1
821 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: secret -> draft
821 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: secret -> draft
822 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
822 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
823 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> draft
823 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> draft
824 $ hg phase --public 17a481b3bccb
824 $ hg phase --public 17a481b3bccb
825 transaction abort!
825 transaction abort!
826 rollback completed
826 rollback completed
827 abort: pretxnclose-phase.nopublish_D hook exited with status 1
827 abort: pretxnclose-phase.nopublish_D hook exited with status 1
828 [255]
828 [255]
829
829
830 $ cd ..
830 $ cd ..
831
831
832 Test for the "internal" phase
832 Test for the "internal" phase
833 =============================
833 =============================
834
834
835 $ hg init internal-phase
835 Check we deny its usage on older repository
836
837 $ hg init no-internal-phase --config format.internal-phase=no
838 $ cd no-internal-phase
839 $ cat .hg/requires
840 dotencode
841 fncache
842 generaldelta
843 revlogv1
844 store
845 $ echo X > X
846 $ hg add X
847 $ hg status
848 A X
849 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit" 2>&1 | grep ProgrammingError
850 ** ProgrammingError: this repository does not support the internal phase
851 raise error.ProgrammingError(msg)
852 mercurial.error.ProgrammingError: this repository does not support the internal phase
853
854 $ cd ..
855
856 Check it works fine with repository that supports it.
857
858 $ hg init internal-phase --config format.internal-phase=yes
836 $ cd internal-phase
859 $ cd internal-phase
860 $ cat .hg/requires
861 dotencode
862 fncache
863 generaldelta
864 internal-phase
865 revlogv1
866 store
837 $ mkcommit A
867 $ mkcommit A
838 test-debug-phase: new rev 0: x -> 1
868 test-debug-phase: new rev 0: x -> 1
839 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
869 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
840
870
841 Commit an internal changesets
871 Commit an internal changesets
842
872
843 $ echo B > B
873 $ echo B > B
844 $ hg add B
874 $ hg add B
845 $ hg status
875 $ hg status
846 A B
876 A B
847 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit"
877 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit"
848 test-debug-phase: new rev 1: x -> 96
878 test-debug-phase: new rev 1: x -> 96
849 test-hook-close-phase: c01c42dffc7f81223397e99652a0703f83e1c5ea: -> internal
879 test-hook-close-phase: c01c42dffc7f81223397e99652a0703f83e1c5ea: -> internal
850
880
851 Usual visibility rules apply when working directory parents
881 Usual visibility rules apply when working directory parents
852
882
853 $ hg log -G -l 3
883 $ hg log -G -l 3
854 @ changeset: 1:c01c42dffc7f
884 @ changeset: 1:c01c42dffc7f
855 | tag: tip
885 | tag: tip
856 | user: test
886 | user: test
857 | date: Thu Jan 01 00:00:00 1970 +0000
887 | date: Thu Jan 01 00:00:00 1970 +0000
858 | summary: my test internal commit
888 | summary: my test internal commit
859 |
889 |
860 o changeset: 0:4a2df7238c3b
890 o changeset: 0:4a2df7238c3b
861 user: test
891 user: test
862 date: Thu Jan 01 00:00:00 1970 +0000
892 date: Thu Jan 01 00:00:00 1970 +0000
863 summary: A
893 summary: A
864
894
865
895
866 Commit is hidden as expected
896 Commit is hidden as expected
867
897
868 $ hg up 0
898 $ hg up 0
869 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
899 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
870 $ hg log -G
900 $ hg log -G
871 @ changeset: 0:4a2df7238c3b
901 @ changeset: 0:4a2df7238c3b
872 tag: tip
902 tag: tip
873 user: test
903 user: test
874 date: Thu Jan 01 00:00:00 1970 +0000
904 date: Thu Jan 01 00:00:00 1970 +0000
875 summary: A
905 summary: A
876
906
General Comments 0
You need to be logged in to leave comments. Login now