##// END OF EJS Templates
phases: add killswitch for native implementation
Laurent Charignon -
r24520:34e8bfc2 default
parent child Browse files
Show More
@@ -1,458 +1,462 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 import os
103 import os
104 import errno
104 import errno
105 from node import nullid, nullrev, bin, hex, short
105 from node import nullid, nullrev, bin, hex, short
106 from i18n import _
106 from i18n import _
107 import util, error
107 import util, error
108
108
109 allphases = public, draft, secret = range(3)
109 allphases = public, draft, secret = range(3)
110 trackedphases = allphases[1:]
110 trackedphases = allphases[1:]
111 phasenames = ['public', 'draft', 'secret']
111 phasenames = ['public', 'draft', 'secret']
112
112
113 def _readroots(repo, phasedefaults=None):
113 def _readroots(repo, phasedefaults=None):
114 """Read phase roots from disk
114 """Read phase roots from disk
115
115
116 phasedefaults is a list of fn(repo, roots) callable, which are
116 phasedefaults is a list of fn(repo, roots) callable, which are
117 executed if the phase roots file does not exist. When phases are
117 executed if the phase roots file does not exist. When phases are
118 being initialized on an existing repository, this could be used to
118 being initialized on an existing repository, this could be used to
119 set selected changesets phase to something else than public.
119 set selected changesets phase to something else than public.
120
120
121 Return (roots, dirty) where dirty is true if roots differ from
121 Return (roots, dirty) where dirty is true if roots differ from
122 what is being stored.
122 what is being stored.
123 """
123 """
124 repo = repo.unfiltered()
124 repo = repo.unfiltered()
125 dirty = False
125 dirty = False
126 roots = [set() for i in allphases]
126 roots = [set() for i in allphases]
127 try:
127 try:
128 f = None
128 f = None
129 if 'HG_PENDING' in os.environ:
129 if 'HG_PENDING' in os.environ:
130 try:
130 try:
131 f = repo.svfs('phaseroots.pending')
131 f = repo.svfs('phaseroots.pending')
132 except IOError, inst:
132 except IOError, inst:
133 if inst.errno != errno.ENOENT:
133 if inst.errno != errno.ENOENT:
134 raise
134 raise
135 if f is None:
135 if f is None:
136 f = repo.svfs('phaseroots')
136 f = repo.svfs('phaseroots')
137 try:
137 try:
138 for line in f:
138 for line in f:
139 phase, nh = line.split()
139 phase, nh = line.split()
140 roots[int(phase)].add(bin(nh))
140 roots[int(phase)].add(bin(nh))
141 finally:
141 finally:
142 f.close()
142 f.close()
143 except IOError, inst:
143 except IOError, inst:
144 if inst.errno != errno.ENOENT:
144 if inst.errno != errno.ENOENT:
145 raise
145 raise
146 if phasedefaults:
146 if phasedefaults:
147 for f in phasedefaults:
147 for f in phasedefaults:
148 roots = f(repo, roots)
148 roots = f(repo, roots)
149 dirty = True
149 dirty = True
150 return roots, dirty
150 return roots, dirty
151
151
152 class phasecache(object):
152 class phasecache(object):
153 def __init__(self, repo, phasedefaults, _load=True):
153 def __init__(self, repo, phasedefaults, _load=True):
154 if _load:
154 if _load:
155 # Cheap trick to allow shallow-copy without copy module
155 # Cheap trick to allow shallow-copy without copy module
156 self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
156 self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
157 self._phaserevs = None
157 self._phaserevs = None
158 self.filterunknown(repo)
158 self.filterunknown(repo)
159 self.opener = repo.svfs
159 self.opener = repo.svfs
160
160
161 def copy(self):
161 def copy(self):
162 # Shallow copy meant to ensure isolation in
162 # Shallow copy meant to ensure isolation in
163 # advance/retractboundary(), nothing more.
163 # advance/retractboundary(), nothing more.
164 ph = self.__class__(None, None, _load=False)
164 ph = self.__class__(None, None, _load=False)
165 ph.phaseroots = self.phaseroots[:]
165 ph.phaseroots = self.phaseroots[:]
166 ph.dirty = self.dirty
166 ph.dirty = self.dirty
167 ph.opener = self.opener
167 ph.opener = self.opener
168 ph._phaserevs = self._phaserevs
168 ph._phaserevs = self._phaserevs
169 return ph
169 return ph
170
170
171 def replace(self, phcache):
171 def replace(self, phcache):
172 for a in 'phaseroots dirty opener _phaserevs'.split():
172 for a in 'phaseroots dirty opener _phaserevs'.split():
173 setattr(self, a, getattr(phcache, a))
173 setattr(self, a, getattr(phcache, a))
174
174
175 def getphaserevsnative(self, repo):
175 def getphaserevsnative(self, repo):
176 repo = repo.unfiltered()
176 repo = repo.unfiltered()
177 nativeroots = []
177 nativeroots = []
178 for phase in trackedphases:
178 for phase in trackedphases:
179 nativeroots.append(map(repo.changelog.rev, self.phaseroots[phase]))
179 nativeroots.append(map(repo.changelog.rev, self.phaseroots[phase]))
180 return repo.changelog.computephases(nativeroots)
180 return repo.changelog.computephases(nativeroots)
181
181
182 def computephaserevspure(self, repo):
182 def computephaserevspure(self, repo):
183 repo = repo.unfiltered()
183 repo = repo.unfiltered()
184 revs = [public] * len(repo.changelog)
184 revs = [public] * len(repo.changelog)
185 self._phaserevs = revs
185 self._phaserevs = revs
186 self._populatephaseroots(repo)
186 self._populatephaseroots(repo)
187 for phase in trackedphases:
187 for phase in trackedphases:
188 roots = map(repo.changelog.rev, self.phaseroots[phase])
188 roots = map(repo.changelog.rev, self.phaseroots[phase])
189 if roots:
189 if roots:
190 for rev in roots:
190 for rev in roots:
191 revs[rev] = phase
191 revs[rev] = phase
192 for rev in repo.changelog.descendants(roots):
192 for rev in repo.changelog.descendants(roots):
193 revs[rev] = phase
193 revs[rev] = phase
194
194
195 def getphaserevs(self, repo):
195 def getphaserevs(self, repo):
196 if self._phaserevs is None:
196 if self._phaserevs is None:
197 try:
197 try:
198 self._phaserevs = self.getphaserevsnative(repo)
198 if repo.ui.configbool('experimental',
199 'nativephaseskillswitch'):
200 self.computephaserevspure(repo)
201 else:
202 self._phaserevs = self.getphaserevsnative(repo)
199 except AttributeError:
203 except AttributeError:
200 self.computephaserevspure(repo)
204 self.computephaserevspure(repo)
201 return self._phaserevs
205 return self._phaserevs
202
206
203 def invalidate(self):
207 def invalidate(self):
204 self._phaserevs = None
208 self._phaserevs = None
205
209
206 def _populatephaseroots(self, repo):
210 def _populatephaseroots(self, repo):
207 """Fills the _phaserevs cache with phases for the roots.
211 """Fills the _phaserevs cache with phases for the roots.
208 """
212 """
209 cl = repo.changelog
213 cl = repo.changelog
210 phaserevs = self._phaserevs
214 phaserevs = self._phaserevs
211 for phase in trackedphases:
215 for phase in trackedphases:
212 roots = map(cl.rev, self.phaseroots[phase])
216 roots = map(cl.rev, self.phaseroots[phase])
213 for root in roots:
217 for root in roots:
214 phaserevs[root] = phase
218 phaserevs[root] = phase
215
219
216 def phase(self, repo, rev):
220 def phase(self, repo, rev):
217 # We need a repo argument here to be able to build _phaserevs
221 # We need a repo argument here to be able to build _phaserevs
218 # if necessary. The repository instance is not stored in
222 # if necessary. The repository instance is not stored in
219 # phasecache to avoid reference cycles. The changelog instance
223 # phasecache to avoid reference cycles. The changelog instance
220 # is not stored because it is a filecache() property and can
224 # is not stored because it is a filecache() property and can
221 # be replaced without us being notified.
225 # be replaced without us being notified.
222 if rev == nullrev:
226 if rev == nullrev:
223 return public
227 return public
224 if rev < nullrev:
228 if rev < nullrev:
225 raise ValueError(_('cannot lookup negative revision'))
229 raise ValueError(_('cannot lookup negative revision'))
226 if self._phaserevs is None or rev >= len(self._phaserevs):
230 if self._phaserevs is None or rev >= len(self._phaserevs):
227 self.invalidate()
231 self.invalidate()
228 self._phaserevs = self.getphaserevs(repo)
232 self._phaserevs = self.getphaserevs(repo)
229 return self._phaserevs[rev]
233 return self._phaserevs[rev]
230
234
231 def write(self):
235 def write(self):
232 if not self.dirty:
236 if not self.dirty:
233 return
237 return
234 f = self.opener('phaseroots', 'w', atomictemp=True)
238 f = self.opener('phaseroots', 'w', atomictemp=True)
235 try:
239 try:
236 self._write(f)
240 self._write(f)
237 finally:
241 finally:
238 f.close()
242 f.close()
239
243
240 def _write(self, fp):
244 def _write(self, fp):
241 for phase, roots in enumerate(self.phaseroots):
245 for phase, roots in enumerate(self.phaseroots):
242 for h in roots:
246 for h in roots:
243 fp.write('%i %s\n' % (phase, hex(h)))
247 fp.write('%i %s\n' % (phase, hex(h)))
244 self.dirty = False
248 self.dirty = False
245
249
246 def _updateroots(self, phase, newroots, tr):
250 def _updateroots(self, phase, newroots, tr):
247 self.phaseroots[phase] = newroots
251 self.phaseroots[phase] = newroots
248 self.invalidate()
252 self.invalidate()
249 self.dirty = True
253 self.dirty = True
250
254
251 tr.addfilegenerator('phase', ('phaseroots',), self._write)
255 tr.addfilegenerator('phase', ('phaseroots',), self._write)
252 tr.hookargs['phases_moved'] = '1'
256 tr.hookargs['phases_moved'] = '1'
253
257
254 def advanceboundary(self, repo, tr, targetphase, nodes):
258 def advanceboundary(self, repo, tr, targetphase, nodes):
255 # Be careful to preserve shallow-copied values: do not update
259 # Be careful to preserve shallow-copied values: do not update
256 # phaseroots values, replace them.
260 # phaseroots values, replace them.
257
261
258 repo = repo.unfiltered()
262 repo = repo.unfiltered()
259 delroots = [] # set of root deleted by this path
263 delroots = [] # set of root deleted by this path
260 for phase in xrange(targetphase + 1, len(allphases)):
264 for phase in xrange(targetphase + 1, len(allphases)):
261 # filter nodes that are not in a compatible phase already
265 # filter nodes that are not in a compatible phase already
262 nodes = [n for n in nodes
266 nodes = [n for n in nodes
263 if self.phase(repo, repo[n].rev()) >= phase]
267 if self.phase(repo, repo[n].rev()) >= phase]
264 if not nodes:
268 if not nodes:
265 break # no roots to move anymore
269 break # no roots to move anymore
266 olds = self.phaseroots[phase]
270 olds = self.phaseroots[phase]
267 roots = set(ctx.node() for ctx in repo.set(
271 roots = set(ctx.node() for ctx in repo.set(
268 'roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
272 'roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
269 if olds != roots:
273 if olds != roots:
270 self._updateroots(phase, roots, tr)
274 self._updateroots(phase, roots, tr)
271 # some roots may need to be declared for lower phases
275 # some roots may need to be declared for lower phases
272 delroots.extend(olds - roots)
276 delroots.extend(olds - roots)
273 # declare deleted root in the target phase
277 # declare deleted root in the target phase
274 if targetphase != 0:
278 if targetphase != 0:
275 self.retractboundary(repo, tr, targetphase, delroots)
279 self.retractboundary(repo, tr, targetphase, delroots)
276 repo.invalidatevolatilesets()
280 repo.invalidatevolatilesets()
277
281
278 def retractboundary(self, repo, tr, targetphase, nodes):
282 def retractboundary(self, repo, tr, targetphase, nodes):
279 # Be careful to preserve shallow-copied values: do not update
283 # Be careful to preserve shallow-copied values: do not update
280 # phaseroots values, replace them.
284 # phaseroots values, replace them.
281
285
282 repo = repo.unfiltered()
286 repo = repo.unfiltered()
283 currentroots = self.phaseroots[targetphase]
287 currentroots = self.phaseroots[targetphase]
284 newroots = [n for n in nodes
288 newroots = [n for n in nodes
285 if self.phase(repo, repo[n].rev()) < targetphase]
289 if self.phase(repo, repo[n].rev()) < targetphase]
286 if newroots:
290 if newroots:
287 if nullid in newroots:
291 if nullid in newroots:
288 raise util.Abort(_('cannot change null revision phase'))
292 raise util.Abort(_('cannot change null revision phase'))
289 currentroots = currentroots.copy()
293 currentroots = currentroots.copy()
290 currentroots.update(newroots)
294 currentroots.update(newroots)
291 ctxs = repo.set('roots(%ln::)', currentroots)
295 ctxs = repo.set('roots(%ln::)', currentroots)
292 currentroots.intersection_update(ctx.node() for ctx in ctxs)
296 currentroots.intersection_update(ctx.node() for ctx in ctxs)
293 self._updateroots(targetphase, currentroots, tr)
297 self._updateroots(targetphase, currentroots, tr)
294 repo.invalidatevolatilesets()
298 repo.invalidatevolatilesets()
295
299
296 def filterunknown(self, repo):
300 def filterunknown(self, repo):
297 """remove unknown nodes from the phase boundary
301 """remove unknown nodes from the phase boundary
298
302
299 Nothing is lost as unknown nodes only hold data for their descendants.
303 Nothing is lost as unknown nodes only hold data for their descendants.
300 """
304 """
301 filtered = False
305 filtered = False
302 nodemap = repo.changelog.nodemap # to filter unknown nodes
306 nodemap = repo.changelog.nodemap # to filter unknown nodes
303 for phase, nodes in enumerate(self.phaseroots):
307 for phase, nodes in enumerate(self.phaseroots):
304 missing = sorted(node for node in nodes if node not in nodemap)
308 missing = sorted(node for node in nodes if node not in nodemap)
305 if missing:
309 if missing:
306 for mnode in missing:
310 for mnode in missing:
307 repo.ui.debug(
311 repo.ui.debug(
308 'removing unknown node %s from %i-phase boundary\n'
312 'removing unknown node %s from %i-phase boundary\n'
309 % (short(mnode), phase))
313 % (short(mnode), phase))
310 nodes.symmetric_difference_update(missing)
314 nodes.symmetric_difference_update(missing)
311 filtered = True
315 filtered = True
312 if filtered:
316 if filtered:
313 self.dirty = True
317 self.dirty = True
314 # filterunknown is called by repo.destroyed, we may have no changes in
318 # filterunknown is called by repo.destroyed, we may have no changes in
315 # root but phaserevs contents is certainly invalid (or at least we
319 # root but phaserevs contents is certainly invalid (or at least we
316 # have not proper way to check that). related to issue 3858.
320 # have not proper way to check that). related to issue 3858.
317 #
321 #
318 # The other caller is __init__ that have no _phaserevs initialized
322 # The other caller is __init__ that have no _phaserevs initialized
319 # anyway. If this change we should consider adding a dedicated
323 # anyway. If this change we should consider adding a dedicated
320 # "destroyed" function to phasecache or a proper cache key mechanism
324 # "destroyed" function to phasecache or a proper cache key mechanism
321 # (see branchmap one)
325 # (see branchmap one)
322 self.invalidate()
326 self.invalidate()
323
327
324 def advanceboundary(repo, tr, targetphase, nodes):
328 def advanceboundary(repo, tr, targetphase, nodes):
325 """Add nodes to a phase changing other nodes phases if necessary.
329 """Add nodes to a phase changing other nodes phases if necessary.
326
330
327 This function move boundary *forward* this means that all nodes
331 This function move boundary *forward* this means that all nodes
328 are set in the target phase or kept in a *lower* phase.
332 are set in the target phase or kept in a *lower* phase.
329
333
330 Simplify boundary to contains phase roots only."""
334 Simplify boundary to contains phase roots only."""
331 phcache = repo._phasecache.copy()
335 phcache = repo._phasecache.copy()
332 phcache.advanceboundary(repo, tr, targetphase, nodes)
336 phcache.advanceboundary(repo, tr, targetphase, nodes)
333 repo._phasecache.replace(phcache)
337 repo._phasecache.replace(phcache)
334
338
335 def retractboundary(repo, tr, targetphase, nodes):
339 def retractboundary(repo, tr, targetphase, nodes):
336 """Set nodes back to a phase changing other nodes phases if
340 """Set nodes back to a phase changing other nodes phases if
337 necessary.
341 necessary.
338
342
339 This function move boundary *backward* this means that all nodes
343 This function move boundary *backward* this means that all nodes
340 are set in the target phase or kept in a *higher* phase.
344 are set in the target phase or kept in a *higher* phase.
341
345
342 Simplify boundary to contains phase roots only."""
346 Simplify boundary to contains phase roots only."""
343 phcache = repo._phasecache.copy()
347 phcache = repo._phasecache.copy()
344 phcache.retractboundary(repo, tr, targetphase, nodes)
348 phcache.retractboundary(repo, tr, targetphase, nodes)
345 repo._phasecache.replace(phcache)
349 repo._phasecache.replace(phcache)
346
350
347 def listphases(repo):
351 def listphases(repo):
348 """List phases root for serialization over pushkey"""
352 """List phases root for serialization over pushkey"""
349 keys = {}
353 keys = {}
350 value = '%i' % draft
354 value = '%i' % draft
351 for root in repo._phasecache.phaseroots[draft]:
355 for root in repo._phasecache.phaseroots[draft]:
352 keys[hex(root)] = value
356 keys[hex(root)] = value
353
357
354 if repo.ui.configbool('phases', 'publish', True):
358 if repo.ui.configbool('phases', 'publish', True):
355 # Add an extra data to let remote know we are a publishing
359 # Add an extra data to let remote know we are a publishing
356 # repo. Publishing repo can't just pretend they are old repo.
360 # repo. Publishing repo can't just pretend they are old repo.
357 # When pushing to a publishing repo, the client still need to
361 # When pushing to a publishing repo, the client still need to
358 # push phase boundary
362 # push phase boundary
359 #
363 #
360 # Push do not only push changeset. It also push phase data.
364 # Push do not only push changeset. It also push phase data.
361 # New phase data may apply to common changeset which won't be
365 # New phase data may apply to common changeset which won't be
362 # push (as they are common). Here is a very simple example:
366 # push (as they are common). Here is a very simple example:
363 #
367 #
364 # 1) repo A push changeset X as draft to repo B
368 # 1) repo A push changeset X as draft to repo B
365 # 2) repo B make changeset X public
369 # 2) repo B make changeset X public
366 # 3) repo B push to repo A. X is not pushed but the data that
370 # 3) repo B push to repo A. X is not pushed but the data that
367 # X as now public should
371 # X as now public should
368 #
372 #
369 # The server can't handle it on it's own as it has no idea of
373 # The server can't handle it on it's own as it has no idea of
370 # client phase data.
374 # client phase data.
371 keys['publishing'] = 'True'
375 keys['publishing'] = 'True'
372 return keys
376 return keys
373
377
374 def pushphase(repo, nhex, oldphasestr, newphasestr):
378 def pushphase(repo, nhex, oldphasestr, newphasestr):
375 """List phases root for serialization over pushkey"""
379 """List phases root for serialization over pushkey"""
376 repo = repo.unfiltered()
380 repo = repo.unfiltered()
377 tr = None
381 tr = None
378 lock = repo.lock()
382 lock = repo.lock()
379 try:
383 try:
380 currentphase = repo[nhex].phase()
384 currentphase = repo[nhex].phase()
381 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
385 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
382 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
386 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
383 if currentphase == oldphase and newphase < oldphase:
387 if currentphase == oldphase and newphase < oldphase:
384 tr = repo.transaction('pushkey-phase')
388 tr = repo.transaction('pushkey-phase')
385 advanceboundary(repo, tr, newphase, [bin(nhex)])
389 advanceboundary(repo, tr, newphase, [bin(nhex)])
386 tr.close()
390 tr.close()
387 return 1
391 return 1
388 elif currentphase == newphase:
392 elif currentphase == newphase:
389 # raced, but got correct result
393 # raced, but got correct result
390 return 1
394 return 1
391 else:
395 else:
392 return 0
396 return 0
393 finally:
397 finally:
394 if tr:
398 if tr:
395 tr.release()
399 tr.release()
396 lock.release()
400 lock.release()
397
401
398 def analyzeremotephases(repo, subset, roots):
402 def analyzeremotephases(repo, subset, roots):
399 """Compute phases heads and root in a subset of node from root dict
403 """Compute phases heads and root in a subset of node from root dict
400
404
401 * subset is heads of the subset
405 * subset is heads of the subset
402 * roots is {<nodeid> => phase} mapping. key and value are string.
406 * roots is {<nodeid> => phase} mapping. key and value are string.
403
407
404 Accept unknown element input
408 Accept unknown element input
405 """
409 """
406 repo = repo.unfiltered()
410 repo = repo.unfiltered()
407 # build list from dictionary
411 # build list from dictionary
408 draftroots = []
412 draftroots = []
409 nodemap = repo.changelog.nodemap # to filter unknown nodes
413 nodemap = repo.changelog.nodemap # to filter unknown nodes
410 for nhex, phase in roots.iteritems():
414 for nhex, phase in roots.iteritems():
411 if nhex == 'publishing': # ignore data related to publish option
415 if nhex == 'publishing': # ignore data related to publish option
412 continue
416 continue
413 node = bin(nhex)
417 node = bin(nhex)
414 phase = int(phase)
418 phase = int(phase)
415 if phase == 0:
419 if phase == 0:
416 if node != nullid:
420 if node != nullid:
417 repo.ui.warn(_('ignoring inconsistent public root'
421 repo.ui.warn(_('ignoring inconsistent public root'
418 ' from remote: %s\n') % nhex)
422 ' from remote: %s\n') % nhex)
419 elif phase == 1:
423 elif phase == 1:
420 if node in nodemap:
424 if node in nodemap:
421 draftroots.append(node)
425 draftroots.append(node)
422 else:
426 else:
423 repo.ui.warn(_('ignoring unexpected root from remote: %i %s\n')
427 repo.ui.warn(_('ignoring unexpected root from remote: %i %s\n')
424 % (phase, nhex))
428 % (phase, nhex))
425 # compute heads
429 # compute heads
426 publicheads = newheads(repo, subset, draftroots)
430 publicheads = newheads(repo, subset, draftroots)
427 return publicheads, draftroots
431 return publicheads, draftroots
428
432
429 def newheads(repo, heads, roots):
433 def newheads(repo, heads, roots):
430 """compute new head of a subset minus another
434 """compute new head of a subset minus another
431
435
432 * `heads`: define the first subset
436 * `heads`: define the first subset
433 * `roots`: define the second we subtract from the first"""
437 * `roots`: define the second we subtract from the first"""
434 repo = repo.unfiltered()
438 repo = repo.unfiltered()
435 revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))',
439 revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))',
436 heads, roots, roots, heads)
440 heads, roots, roots, heads)
437 return [c.node() for c in revset]
441 return [c.node() for c in revset]
438
442
439
443
440 def newcommitphase(ui):
444 def newcommitphase(ui):
441 """helper to get the target phase of new commit
445 """helper to get the target phase of new commit
442
446
443 Handle all possible values for the phases.new-commit options.
447 Handle all possible values for the phases.new-commit options.
444
448
445 """
449 """
446 v = ui.config('phases', 'new-commit', draft)
450 v = ui.config('phases', 'new-commit', draft)
447 try:
451 try:
448 return phasenames.index(v)
452 return phasenames.index(v)
449 except ValueError:
453 except ValueError:
450 try:
454 try:
451 return int(v)
455 return int(v)
452 except ValueError:
456 except ValueError:
453 msg = _("phases.new-commit: not a valid phase name ('%s')")
457 msg = _("phases.new-commit: not a valid phase name ('%s')")
454 raise error.ConfigError(msg % v)
458 raise error.ConfigError(msg % v)
455
459
456 def hassecret(repo):
460 def hassecret(repo):
457 """utility function that check if a repo have any secret changeset."""
461 """utility function that check if a repo have any secret changeset."""
458 return bool(repo._phasecache.phaseroots[2])
462 return bool(repo._phasecache.phaseroots[2])
@@ -1,561 +1,569 b''
1 $ hglog() { hg log --template "{rev} {phaseidx} {desc}\n" $*; }
1 $ hglog() { hg log --template "{rev} {phaseidx} {desc}\n" $*; }
2 $ mkcommit() {
2 $ mkcommit() {
3 > echo "$1" > "$1"
3 > echo "$1" > "$1"
4 > hg add "$1"
4 > hg add "$1"
5 > message="$1"
5 > message="$1"
6 > shift
6 > shift
7 > hg ci -m "$message" $*
7 > hg ci -m "$message" $*
8 > }
8 > }
9
9
10 $ hg init initialrepo
10 $ hg init initialrepo
11 $ cd initialrepo
11 $ cd initialrepo
12
12
13 Cannot change null revision phase
13 Cannot change null revision phase
14
14
15 $ hg phase --force --secret null
15 $ hg phase --force --secret null
16 abort: cannot change null revision phase
16 abort: cannot change null revision phase
17 [255]
17 [255]
18 $ hg phase null
18 $ hg phase null
19 -1: public
19 -1: public
20
20
21 $ mkcommit A
21 $ mkcommit A
22
22
23 New commit are draft by default
23 New commit are draft by default
24
24
25 $ hglog
25 $ hglog
26 0 1 A
26 0 1 A
27
27
28 Following commit are draft too
28 Following commit are draft too
29
29
30 $ mkcommit B
30 $ mkcommit B
31
31
32 $ hglog
32 $ hglog
33 1 1 B
33 1 1 B
34 0 1 A
34 0 1 A
35
35
36 Draft commit are properly created over public one:
36 Draft commit are properly created over public one:
37
37
38 $ hg phase --public .
38 $ hg phase --public .
39 $ hglog
39 $ hglog
40 1 0 B
40 1 0 B
41 0 0 A
41 0 0 A
42
42
43 $ mkcommit C
43 $ mkcommit C
44 $ mkcommit D
44 $ mkcommit D
45
45
46 $ hglog
46 $ hglog
47 3 1 D
47 3 1 D
48 2 1 C
48 2 1 C
49 1 0 B
49 1 0 B
50 0 0 A
50 0 0 A
51
51
52 Test creating changeset as secret
52 Test creating changeset as secret
53
53
54 $ mkcommit E --config phases.new-commit='secret'
54 $ mkcommit E --config phases.new-commit='secret'
55 $ hglog
55 $ hglog
56 4 2 E
56 4 2 E
57 3 1 D
57 3 1 D
58 2 1 C
58 2 1 C
59 1 0 B
59 1 0 B
60 0 0 A
60 0 0 A
61
61
62 Test the secret property is inherited
62 Test the secret property is inherited
63
63
64 $ mkcommit H
64 $ mkcommit H
65 $ hglog
65 $ hglog
66 5 2 H
66 5 2 H
67 4 2 E
67 4 2 E
68 3 1 D
68 3 1 D
69 2 1 C
69 2 1 C
70 1 0 B
70 1 0 B
71 0 0 A
71 0 0 A
72
72
73 Even on merge
73 Even on merge
74
74
75 $ hg up -q 1
75 $ hg up -q 1
76 $ mkcommit "B'"
76 $ mkcommit "B'"
77 created new head
77 created new head
78 $ hglog
78 $ hglog
79 6 1 B'
79 6 1 B'
80 5 2 H
80 5 2 H
81 4 2 E
81 4 2 E
82 3 1 D
82 3 1 D
83 2 1 C
83 2 1 C
84 1 0 B
84 1 0 B
85 0 0 A
85 0 0 A
86 $ hg merge 4 # E
86 $ hg merge 4 # E
87 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 (branch merge, don't forget to commit)
88 (branch merge, don't forget to commit)
89 $ hg ci -m "merge B' and E"
89 $ hg ci -m "merge B' and E"
90 $ hglog
90 $ hglog
91 7 2 merge B' and E
91 7 2 merge B' and E
92 6 1 B'
92 6 1 B'
93 5 2 H
93 5 2 H
94 4 2 E
94 4 2 E
95 3 1 D
95 3 1 D
96 2 1 C
96 2 1 C
97 1 0 B
97 1 0 B
98 0 0 A
98 0 0 A
99
99
100 Test secret changeset are not pushed
100 Test secret changeset are not pushed
101
101
102 $ hg init ../push-dest
102 $ hg init ../push-dest
103 $ cat > ../push-dest/.hg/hgrc << EOF
103 $ cat > ../push-dest/.hg/hgrc << EOF
104 > [phases]
104 > [phases]
105 > publish=False
105 > publish=False
106 > EOF
106 > EOF
107 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
107 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
108 comparing with ../push-dest
108 comparing with ../push-dest
109 searching for changes
109 searching for changes
110 0 public A
110 0 public A
111 1 public B
111 1 public B
112 2 draft C
112 2 draft C
113 3 draft D
113 3 draft D
114 6 draft B'
114 6 draft B'
115 $ hg outgoing -r 'branch(default)' ../push-dest --template='{rev} {phase} {desc|firstline}\n'
115 $ hg outgoing -r 'branch(default)' ../push-dest --template='{rev} {phase} {desc|firstline}\n'
116 comparing with ../push-dest
116 comparing with ../push-dest
117 searching for changes
117 searching for changes
118 0 public A
118 0 public A
119 1 public B
119 1 public B
120 2 draft C
120 2 draft C
121 3 draft D
121 3 draft D
122 6 draft B'
122 6 draft B'
123
123
124 $ hg push ../push-dest -f # force because we push multiple heads
124 $ hg push ../push-dest -f # force because we push multiple heads
125 pushing to ../push-dest
125 pushing to ../push-dest
126 searching for changes
126 searching for changes
127 adding changesets
127 adding changesets
128 adding manifests
128 adding manifests
129 adding file changes
129 adding file changes
130 added 5 changesets with 5 changes to 5 files (+1 heads)
130 added 5 changesets with 5 changes to 5 files (+1 heads)
131 $ hglog
131 $ hglog
132 7 2 merge B' and E
132 7 2 merge B' and E
133 6 1 B'
133 6 1 B'
134 5 2 H
134 5 2 H
135 4 2 E
135 4 2 E
136 3 1 D
136 3 1 D
137 2 1 C
137 2 1 C
138 1 0 B
138 1 0 B
139 0 0 A
139 0 0 A
140 $ cd ../push-dest
140 $ cd ../push-dest
141 $ hglog
141 $ hglog
142 4 1 B'
142 4 1 B'
143 3 1 D
143 3 1 D
144 2 1 C
144 2 1 C
145 1 0 B
145 1 0 B
146 0 0 A
146 0 0 A
147
147
148 (Issue3303)
148 (Issue3303)
149 Check that remote secret changeset are ignore when checking creation of remote heads
149 Check that remote secret changeset are ignore when checking creation of remote heads
150
150
151 We add a secret head into the push destination. This secret head shadows a
151 We add a secret head into the push destination. This secret head shadows a
152 visible shared between the initial repo and the push destination.
152 visible shared between the initial repo and the push destination.
153
153
154 $ hg up -q 4 # B'
154 $ hg up -q 4 # B'
155 $ mkcommit Z --config phases.new-commit=secret
155 $ mkcommit Z --config phases.new-commit=secret
156 $ hg phase .
156 $ hg phase .
157 5: secret
157 5: secret
158
158
159 We now try to push a new public changeset that descend from the common public
159 We now try to push a new public changeset that descend from the common public
160 head shadowed by the remote secret head.
160 head shadowed by the remote secret head.
161
161
162 $ cd ../initialrepo
162 $ cd ../initialrepo
163 $ hg up -q 6 #B'
163 $ hg up -q 6 #B'
164 $ mkcommit I
164 $ mkcommit I
165 created new head
165 created new head
166 $ hg push ../push-dest
166 $ hg push ../push-dest
167 pushing to ../push-dest
167 pushing to ../push-dest
168 searching for changes
168 searching for changes
169 adding changesets
169 adding changesets
170 adding manifests
170 adding manifests
171 adding file changes
171 adding file changes
172 added 1 changesets with 1 changes to 1 files (+1 heads)
172 added 1 changesets with 1 changes to 1 files (+1 heads)
173
173
174 :note: The "(+1 heads)" is wrong as we do not had any visible head
174 :note: The "(+1 heads)" is wrong as we do not had any visible head
175
175
176 check that branch cache with "served" filter are properly computed and stored
176 check that branch cache with "served" filter are properly computed and stored
177
177
178 $ ls ../push-dest/.hg/cache/branch2*
178 $ ls ../push-dest/.hg/cache/branch2*
179 ../push-dest/.hg/cache/branch2-served
179 ../push-dest/.hg/cache/branch2-served
180 $ cat ../push-dest/.hg/cache/branch2-served
180 $ cat ../push-dest/.hg/cache/branch2-served
181 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
181 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
182 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
182 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
183 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
183 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
184 $ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n' #update visible cache too
184 $ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n' #update visible cache too
185 6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
185 6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
186 5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
186 5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
187 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
187 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
188 $ ls ../push-dest/.hg/cache/branch2*
188 $ ls ../push-dest/.hg/cache/branch2*
189 ../push-dest/.hg/cache/branch2-served
189 ../push-dest/.hg/cache/branch2-served
190 ../push-dest/.hg/cache/branch2-visible
190 ../push-dest/.hg/cache/branch2-visible
191 $ cat ../push-dest/.hg/cache/branch2-served
191 $ cat ../push-dest/.hg/cache/branch2-served
192 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
192 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
193 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
193 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
194 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
194 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
195 $ cat ../push-dest/.hg/cache/branch2-visible
195 $ cat ../push-dest/.hg/cache/branch2-visible
196 6d6770faffce199f1fddd1cf87f6f026138cf061 6
196 6d6770faffce199f1fddd1cf87f6f026138cf061 6
197 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
197 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
198 2713879da13d6eea1ff22b442a5a87cb31a7ce6a o default
198 2713879da13d6eea1ff22b442a5a87cb31a7ce6a o default
199 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
199 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
200
200
201
201
202 Restore condition prior extra insertion.
202 Restore condition prior extra insertion.
203 $ hg -q --config extensions.mq= strip .
203 $ hg -q --config extensions.mq= strip .
204 $ hg up -q 7
204 $ hg up -q 7
205 $ cd ..
205 $ cd ..
206
206
207 Test secret changeset are not pull
207 Test secret changeset are not pull
208
208
209 $ hg init pull-dest
209 $ hg init pull-dest
210 $ cd pull-dest
210 $ cd pull-dest
211 $ hg pull ../initialrepo
211 $ hg pull ../initialrepo
212 pulling from ../initialrepo
212 pulling from ../initialrepo
213 requesting all changes
213 requesting all changes
214 adding changesets
214 adding changesets
215 adding manifests
215 adding manifests
216 adding file changes
216 adding file changes
217 added 5 changesets with 5 changes to 5 files (+1 heads)
217 added 5 changesets with 5 changes to 5 files (+1 heads)
218 (run 'hg heads' to see heads, 'hg merge' to merge)
218 (run 'hg heads' to see heads, 'hg merge' to merge)
219 $ hglog
219 $ hglog
220 4 0 B'
220 4 0 B'
221 3 0 D
221 3 0 D
222 2 0 C
222 2 0 C
223 1 0 B
223 1 0 B
224 0 0 A
224 0 0 A
225 $ cd ..
225 $ cd ..
226
226
227 But secret can still be bundled explicitly
227 But secret can still be bundled explicitly
228
228
229 $ cd initialrepo
229 $ cd initialrepo
230 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
230 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
231 4 changesets found
231 4 changesets found
232 $ cd ..
232 $ cd ..
233
233
234 Test secret changeset are not cloned
234 Test secret changeset are not cloned
235 (during local clone)
235 (during local clone)
236
236
237 $ hg clone -qU initialrepo clone-dest
237 $ hg clone -qU initialrepo clone-dest
238 $ hglog -R clone-dest
238 $ hglog -R clone-dest
239 4 0 B'
239 4 0 B'
240 3 0 D
240 3 0 D
241 2 0 C
241 2 0 C
242 1 0 B
242 1 0 B
243 0 0 A
243 0 0 A
244
244
245 Test revset
245 Test revset
246
246
247 $ cd initialrepo
247 $ cd initialrepo
248 $ hglog -r 'public()'
248 $ hglog -r 'public()'
249 0 0 A
249 0 0 A
250 1 0 B
250 1 0 B
251 $ hglog -r 'draft()'
251 $ hglog -r 'draft()'
252 2 1 C
252 2 1 C
253 3 1 D
253 3 1 D
254 6 1 B'
254 6 1 B'
255 $ hglog -r 'secret()'
255 $ hglog -r 'secret()'
256 4 2 E
256 4 2 E
257 5 2 H
257 5 2 H
258 7 2 merge B' and E
258 7 2 merge B' and E
259
259
260 test that phase are displayed in log at debug level
260 test that phase are displayed in log at debug level
261
261
262 $ hg log --debug
262 $ hg log --debug
263 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
263 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
264 tag: tip
264 tag: tip
265 phase: secret
265 phase: secret
266 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
266 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
267 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
267 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
268 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
268 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
269 user: test
269 user: test
270 date: Thu Jan 01 00:00:00 1970 +0000
270 date: Thu Jan 01 00:00:00 1970 +0000
271 files+: C D E
271 files+: C D E
272 extra: branch=default
272 extra: branch=default
273 description:
273 description:
274 merge B' and E
274 merge B' and E
275
275
276
276
277 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
277 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
278 phase: draft
278 phase: draft
279 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
279 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
280 parent: -1:0000000000000000000000000000000000000000
280 parent: -1:0000000000000000000000000000000000000000
281 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
281 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
282 user: test
282 user: test
283 date: Thu Jan 01 00:00:00 1970 +0000
283 date: Thu Jan 01 00:00:00 1970 +0000
284 files+: B'
284 files+: B'
285 extra: branch=default
285 extra: branch=default
286 description:
286 description:
287 B'
287 B'
288
288
289
289
290 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
290 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
291 phase: secret
291 phase: secret
292 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
292 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
293 parent: -1:0000000000000000000000000000000000000000
293 parent: -1:0000000000000000000000000000000000000000
294 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
294 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
295 user: test
295 user: test
296 date: Thu Jan 01 00:00:00 1970 +0000
296 date: Thu Jan 01 00:00:00 1970 +0000
297 files+: H
297 files+: H
298 extra: branch=default
298 extra: branch=default
299 description:
299 description:
300 H
300 H
301
301
302
302
303 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
303 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
304 phase: secret
304 phase: secret
305 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
305 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
306 parent: -1:0000000000000000000000000000000000000000
306 parent: -1:0000000000000000000000000000000000000000
307 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
307 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
308 user: test
308 user: test
309 date: Thu Jan 01 00:00:00 1970 +0000
309 date: Thu Jan 01 00:00:00 1970 +0000
310 files+: E
310 files+: E
311 extra: branch=default
311 extra: branch=default
312 description:
312 description:
313 E
313 E
314
314
315
315
316 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
316 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
317 phase: draft
317 phase: draft
318 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
318 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
319 parent: -1:0000000000000000000000000000000000000000
319 parent: -1:0000000000000000000000000000000000000000
320 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
320 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
321 user: test
321 user: test
322 date: Thu Jan 01 00:00:00 1970 +0000
322 date: Thu Jan 01 00:00:00 1970 +0000
323 files+: D
323 files+: D
324 extra: branch=default
324 extra: branch=default
325 description:
325 description:
326 D
326 D
327
327
328
328
329 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
329 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
330 phase: draft
330 phase: draft
331 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
331 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
332 parent: -1:0000000000000000000000000000000000000000
332 parent: -1:0000000000000000000000000000000000000000
333 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
333 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
334 user: test
334 user: test
335 date: Thu Jan 01 00:00:00 1970 +0000
335 date: Thu Jan 01 00:00:00 1970 +0000
336 files+: C
336 files+: C
337 extra: branch=default
337 extra: branch=default
338 description:
338 description:
339 C
339 C
340
340
341
341
342 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
342 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
343 phase: public
343 phase: public
344 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
344 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
345 parent: -1:0000000000000000000000000000000000000000
345 parent: -1:0000000000000000000000000000000000000000
346 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
346 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
347 user: test
347 user: test
348 date: Thu Jan 01 00:00:00 1970 +0000
348 date: Thu Jan 01 00:00:00 1970 +0000
349 files+: B
349 files+: B
350 extra: branch=default
350 extra: branch=default
351 description:
351 description:
352 B
352 B
353
353
354
354
355 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
355 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
356 phase: public
356 phase: public
357 parent: -1:0000000000000000000000000000000000000000
357 parent: -1:0000000000000000000000000000000000000000
358 parent: -1:0000000000000000000000000000000000000000
358 parent: -1:0000000000000000000000000000000000000000
359 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
359 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
360 user: test
360 user: test
361 date: Thu Jan 01 00:00:00 1970 +0000
361 date: Thu Jan 01 00:00:00 1970 +0000
362 files+: A
362 files+: A
363 extra: branch=default
363 extra: branch=default
364 description:
364 description:
365 A
365 A
366
366
367
367
368
368
369
369
370 (Issue3707)
370 (Issue3707)
371 test invalid phase name
371 test invalid phase name
372
372
373 $ mkcommit I --config phases.new-commit='babar'
373 $ mkcommit I --config phases.new-commit='babar'
374 transaction abort!
374 transaction abort!
375 rollback completed
375 rollback completed
376 abort: phases.new-commit: not a valid phase name ('babar')
376 abort: phases.new-commit: not a valid phase name ('babar')
377 [255]
377 [255]
378 Test phase command
378 Test phase command
379 ===================
379 ===================
380
380
381 initial picture
381 initial picture
382
382
383 $ hg log -G --template "{rev} {phase} {desc}\n"
383 $ hg log -G --template "{rev} {phase} {desc}\n"
384 @ 7 secret merge B' and E
384 @ 7 secret merge B' and E
385 |\
385 |\
386 | o 6 draft B'
386 | o 6 draft B'
387 | |
387 | |
388 +---o 5 secret H
388 +---o 5 secret H
389 | |
389 | |
390 o | 4 secret E
390 o | 4 secret E
391 | |
391 | |
392 o | 3 draft D
392 o | 3 draft D
393 | |
393 | |
394 o | 2 draft C
394 o | 2 draft C
395 |/
395 |/
396 o 1 public B
396 o 1 public B
397 |
397 |
398 o 0 public A
398 o 0 public A
399
399
400
400
401 display changesets phase
401 display changesets phase
402
402
403 (mixing -r and plain rev specification)
403 (mixing -r and plain rev specification)
404
404
405 $ hg phase 1::4 -r 7
405 $ hg phase 1::4 -r 7
406 1: public
406 1: public
407 2: draft
407 2: draft
408 3: draft
408 3: draft
409 4: secret
409 4: secret
410 7: secret
410 7: secret
411
411
412
412
413 move changeset forward
413 move changeset forward
414
414
415 (with -r option)
415 (with -r option)
416
416
417 $ hg phase --public -r 2
417 $ hg phase --public -r 2
418 $ hg log -G --template "{rev} {phase} {desc}\n"
418 $ hg log -G --template "{rev} {phase} {desc}\n"
419 @ 7 secret merge B' and E
419 @ 7 secret merge B' and E
420 |\
420 |\
421 | o 6 draft B'
421 | o 6 draft B'
422 | |
422 | |
423 +---o 5 secret H
423 +---o 5 secret H
424 | |
424 | |
425 o | 4 secret E
425 o | 4 secret E
426 | |
426 | |
427 o | 3 draft D
427 o | 3 draft D
428 | |
428 | |
429 o | 2 public C
429 o | 2 public C
430 |/
430 |/
431 o 1 public B
431 o 1 public B
432 |
432 |
433 o 0 public A
433 o 0 public A
434
434
435
435
436 move changeset backward
436 move changeset backward
437
437
438 (without -r option)
438 (without -r option)
439
439
440 $ hg phase --draft --force 2
440 $ hg phase --draft --force 2
441 $ hg log -G --template "{rev} {phase} {desc}\n"
441 $ hg log -G --template "{rev} {phase} {desc}\n"
442 @ 7 secret merge B' and E
442 @ 7 secret merge B' and E
443 |\
443 |\
444 | o 6 draft B'
444 | o 6 draft B'
445 | |
445 | |
446 +---o 5 secret H
446 +---o 5 secret H
447 | |
447 | |
448 o | 4 secret E
448 o | 4 secret E
449 | |
449 | |
450 o | 3 draft D
450 o | 3 draft D
451 | |
451 | |
452 o | 2 draft C
452 o | 2 draft C
453 |/
453 |/
454 o 1 public B
454 o 1 public B
455 |
455 |
456 o 0 public A
456 o 0 public A
457
457
458
458
459 move changeset forward and backward
459 move changeset forward and backward and test kill switch
460
460
461 $ cat <<EOF >> $HGRCPATH
462 > [experimental]
463 > nativephaseskillswitch = true
464 > EOF
461 $ hg phase --draft --force 1::4
465 $ hg phase --draft --force 1::4
462 $ hg log -G --template "{rev} {phase} {desc}\n"
466 $ hg log -G --template "{rev} {phase} {desc}\n"
463 @ 7 secret merge B' and E
467 @ 7 secret merge B' and E
464 |\
468 |\
465 | o 6 draft B'
469 | o 6 draft B'
466 | |
470 | |
467 +---o 5 secret H
471 +---o 5 secret H
468 | |
472 | |
469 o | 4 draft E
473 o | 4 draft E
470 | |
474 | |
471 o | 3 draft D
475 o | 3 draft D
472 | |
476 | |
473 o | 2 draft C
477 o | 2 draft C
474 |/
478 |/
475 o 1 draft B
479 o 1 draft B
476 |
480 |
477 o 0 public A
481 o 0 public A
478
482
479 test partial failure
483 test partial failure
480
484
485 $ cat <<EOF >> $HGRCPATH
486 > [experimental]
487 > nativephaseskillswitch = false
488 > EOF
481 $ hg phase --public 7
489 $ hg phase --public 7
482 $ hg phase --draft '5 or 7'
490 $ hg phase --draft '5 or 7'
483 cannot move 1 changesets to a higher phase, use --force
491 cannot move 1 changesets to a higher phase, use --force
484 phase changed for 1 changesets
492 phase changed for 1 changesets
485 [1]
493 [1]
486 $ hg log -G --template "{rev} {phase} {desc}\n"
494 $ hg log -G --template "{rev} {phase} {desc}\n"
487 @ 7 public merge B' and E
495 @ 7 public merge B' and E
488 |\
496 |\
489 | o 6 public B'
497 | o 6 public B'
490 | |
498 | |
491 +---o 5 draft H
499 +---o 5 draft H
492 | |
500 | |
493 o | 4 public E
501 o | 4 public E
494 | |
502 | |
495 o | 3 public D
503 o | 3 public D
496 | |
504 | |
497 o | 2 public C
505 o | 2 public C
498 |/
506 |/
499 o 1 public B
507 o 1 public B
500 |
508 |
501 o 0 public A
509 o 0 public A
502
510
503
511
504 test complete failure
512 test complete failure
505
513
506 $ hg phase --draft 7
514 $ hg phase --draft 7
507 cannot move 1 changesets to a higher phase, use --force
515 cannot move 1 changesets to a higher phase, use --force
508 no phases changed
516 no phases changed
509 [1]
517 [1]
510
518
511 $ cd ..
519 $ cd ..
512
520
513 test hidden changeset are not cloned as public (issue3935)
521 test hidden changeset are not cloned as public (issue3935)
514
522
515 $ cd initialrepo
523 $ cd initialrepo
516
524
517 (enabling evolution)
525 (enabling evolution)
518 $ cat >> $HGRCPATH << EOF
526 $ cat >> $HGRCPATH << EOF
519 > [experimental]
527 > [experimental]
520 > evolution=createmarkers
528 > evolution=createmarkers
521 > EOF
529 > EOF
522
530
523 (making a changeset hidden; H in that case)
531 (making a changeset hidden; H in that case)
524 $ hg debugobsolete `hg id --debug -r 5`
532 $ hg debugobsolete `hg id --debug -r 5`
525
533
526 $ cd ..
534 $ cd ..
527 $ hg clone initialrepo clonewithobs
535 $ hg clone initialrepo clonewithobs
528 requesting all changes
536 requesting all changes
529 adding changesets
537 adding changesets
530 adding manifests
538 adding manifests
531 adding file changes
539 adding file changes
532 added 7 changesets with 6 changes to 6 files
540 added 7 changesets with 6 changes to 6 files
533 updating to branch default
541 updating to branch default
534 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
542 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
535 $ cd clonewithobs
543 $ cd clonewithobs
536 $ hg log -G --template "{rev} {phase} {desc}\n"
544 $ hg log -G --template "{rev} {phase} {desc}\n"
537 @ 6 public merge B' and E
545 @ 6 public merge B' and E
538 |\
546 |\
539 | o 5 public B'
547 | o 5 public B'
540 | |
548 | |
541 o | 4 public E
549 o | 4 public E
542 | |
550 | |
543 o | 3 public D
551 o | 3 public D
544 | |
552 | |
545 o | 2 public C
553 o | 2 public C
546 |/
554 |/
547 o 1 public B
555 o 1 public B
548 |
556 |
549 o 0 public A
557 o 0 public A
550
558
551
559
552 test verify repo containing hidden changesets, which should not abort just
560 test verify repo containing hidden changesets, which should not abort just
553 because repo.cancopy() is False
561 because repo.cancopy() is False
554
562
555 $ cd ../initialrepo
563 $ cd ../initialrepo
556 $ hg verify
564 $ hg verify
557 checking changesets
565 checking changesets
558 checking manifests
566 checking manifests
559 crosschecking files in changesets and manifests
567 crosschecking files in changesets and manifests
560 checking files
568 checking files
561 7 files, 8 changesets, 7 total revisions
569 7 files, 8 changesets, 7 total revisions
General Comments 0
You need to be logged in to leave comments. Login now