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