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