##// END OF EJS Templates
destroyed: invalidate phraserevs cache in all case (issue3858)...
Pierre-Yves David -
r18983:31bcc511 default
parent child Browse files
Show More
@@ -1,400 +1,408 b''
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 import errno
104 104 from node import nullid, nullrev, bin, hex, short
105 105 from i18n import _
106 106 import util, error
107 107
108 108 allphases = public, draft, secret = range(3)
109 109 trackedphases = allphases[1:]
110 110 phasenames = ['public', 'draft', 'secret']
111 111
112 112 def _readroots(repo, phasedefaults=None):
113 113 """Read phase roots from disk
114 114
115 115 phasedefaults is a list of fn(repo, roots) callable, which are
116 116 executed if the phase roots file does not exist. When phases are
117 117 being initialized on an existing repository, this could be used to
118 118 set selected changesets phase to something else than public.
119 119
120 120 Return (roots, dirty) where dirty is true if roots differ from
121 121 what is being stored.
122 122 """
123 123 repo = repo.unfiltered()
124 124 dirty = False
125 125 roots = [set() for i in allphases]
126 126 try:
127 127 f = repo.sopener('phaseroots')
128 128 try:
129 129 for line in f:
130 130 phase, nh = line.split()
131 131 roots[int(phase)].add(bin(nh))
132 132 finally:
133 133 f.close()
134 134 except IOError, inst:
135 135 if inst.errno != errno.ENOENT:
136 136 raise
137 137 if phasedefaults:
138 138 for f in phasedefaults:
139 139 roots = f(repo, roots)
140 140 dirty = True
141 141 return roots, dirty
142 142
143 143 class phasecache(object):
144 144 def __init__(self, repo, phasedefaults, _load=True):
145 145 if _load:
146 146 # Cheap trick to allow shallow-copy without copy module
147 147 self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
148 148 self._phaserevs = None
149 149 self.filterunknown(repo)
150 150 self.opener = repo.sopener
151 151
152 152 def copy(self):
153 153 # Shallow copy meant to ensure isolation in
154 154 # advance/retractboundary(), nothing more.
155 155 ph = phasecache(None, None, _load=False)
156 156 ph.phaseroots = self.phaseroots[:]
157 157 ph.dirty = self.dirty
158 158 ph.opener = self.opener
159 159 ph._phaserevs = self._phaserevs
160 160 return ph
161 161
162 162 def replace(self, phcache):
163 163 for a in 'phaseroots dirty opener _phaserevs'.split():
164 164 setattr(self, a, getattr(phcache, a))
165 165
166 166 def getphaserevs(self, repo, rebuild=False):
167 167 if rebuild or self._phaserevs is None:
168 168 repo = repo.unfiltered()
169 169 revs = [public] * len(repo.changelog)
170 170 for phase in trackedphases:
171 171 roots = map(repo.changelog.rev, self.phaseroots[phase])
172 172 if roots:
173 173 for rev in roots:
174 174 revs[rev] = phase
175 175 for rev in repo.changelog.descendants(roots):
176 176 revs[rev] = phase
177 177 self._phaserevs = revs
178 178 return self._phaserevs
179 179
180 180 def phase(self, repo, rev):
181 181 # We need a repo argument here to be able to build _phaserevs
182 182 # if necessary. The repository instance is not stored in
183 183 # phasecache to avoid reference cycles. The changelog instance
184 184 # is not stored because it is a filecache() property and can
185 185 # be replaced without us being notified.
186 186 if rev == nullrev:
187 187 return public
188 188 if self._phaserevs is None or rev >= len(self._phaserevs):
189 189 self._phaserevs = self.getphaserevs(repo, rebuild=True)
190 190 return self._phaserevs[rev]
191 191
192 192 def write(self):
193 193 if not self.dirty:
194 194 return
195 195 f = self.opener('phaseroots', 'w', atomictemp=True)
196 196 try:
197 197 for phase, roots in enumerate(self.phaseroots):
198 198 for h in roots:
199 199 f.write('%i %s\n' % (phase, hex(h)))
200 200 finally:
201 201 f.close()
202 202 self.dirty = False
203 203
204 204 def _updateroots(self, phase, newroots):
205 205 self.phaseroots[phase] = newroots
206 206 self._phaserevs = None
207 207 self.dirty = True
208 208
209 209 def advanceboundary(self, repo, targetphase, nodes):
210 210 # Be careful to preserve shallow-copied values: do not update
211 211 # phaseroots values, replace them.
212 212
213 213 repo = repo.unfiltered()
214 214 delroots = [] # set of root deleted by this path
215 215 for phase in xrange(targetphase + 1, len(allphases)):
216 216 # filter nodes that are not in a compatible phase already
217 217 nodes = [n for n in nodes
218 218 if self.phase(repo, repo[n].rev()) >= phase]
219 219 if not nodes:
220 220 break # no roots to move anymore
221 221 olds = self.phaseroots[phase]
222 222 roots = set(ctx.node() for ctx in repo.set(
223 223 'roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
224 224 if olds != roots:
225 225 self._updateroots(phase, roots)
226 226 # some roots may need to be declared for lower phases
227 227 delroots.extend(olds - roots)
228 228 # declare deleted root in the target phase
229 229 if targetphase != 0:
230 230 self.retractboundary(repo, targetphase, delroots)
231 231 repo.invalidatevolatilesets()
232 232
233 233 def retractboundary(self, repo, targetphase, nodes):
234 234 # Be careful to preserve shallow-copied values: do not update
235 235 # phaseroots values, replace them.
236 236
237 237 repo = repo.unfiltered()
238 238 currentroots = self.phaseroots[targetphase]
239 239 newroots = [n for n in nodes
240 240 if self.phase(repo, repo[n].rev()) < targetphase]
241 241 if newroots:
242 242 if nullid in newroots:
243 243 raise util.Abort(_('cannot change null revision phase'))
244 244 currentroots = currentroots.copy()
245 245 currentroots.update(newroots)
246 246 ctxs = repo.set('roots(%ln::)', currentroots)
247 247 currentroots.intersection_update(ctx.node() for ctx in ctxs)
248 248 self._updateroots(targetphase, currentroots)
249 249 repo.invalidatevolatilesets()
250 250
251 251 def filterunknown(self, repo):
252 252 """remove unknown nodes from the phase boundary
253 253
254 254 Nothing is lost as unknown nodes only hold data for their descendants.
255 255 """
256 256 filtered = False
257 257 nodemap = repo.changelog.nodemap # to filter unknown nodes
258 258 for phase, nodes in enumerate(self.phaseroots):
259 259 missing = [node for node in nodes if node not in nodemap]
260 260 if missing:
261 261 for mnode in missing:
262 262 repo.ui.debug(
263 263 'removing unknown node %s from %i-phase boundary\n'
264 264 % (short(mnode), phase))
265 265 nodes.symmetric_difference_update(missing)
266 266 filtered = True
267 267 if filtered:
268 268 self.dirty = True
269 self._phaserevs = None
269 # filterunknown is called by repo.destroyed, we may have no changes in
270 # root but phaserevs contents is certainly invalide (or at least we
271 # have not proper way to check that. related to issue 3858.
272 #
273 # The other caller is __init__ that have no _phaserevs initialized
274 # anyway. If this change we should consider adding a dedicated
275 # "destroyed" function to phasecache or a proper cache key mechanisme
276 # (see branchmap one)
277 self._phaserevs = None
270 278
271 279 def advanceboundary(repo, targetphase, nodes):
272 280 """Add nodes to a phase changing other nodes phases if necessary.
273 281
274 282 This function move boundary *forward* this means that all nodes
275 283 are set in the target phase or kept in a *lower* phase.
276 284
277 285 Simplify boundary to contains phase roots only."""
278 286 phcache = repo._phasecache.copy()
279 287 phcache.advanceboundary(repo, targetphase, nodes)
280 288 repo._phasecache.replace(phcache)
281 289
282 290 def retractboundary(repo, targetphase, nodes):
283 291 """Set nodes back to a phase changing other nodes phases if
284 292 necessary.
285 293
286 294 This function move boundary *backward* this means that all nodes
287 295 are set in the target phase or kept in a *higher* phase.
288 296
289 297 Simplify boundary to contains phase roots only."""
290 298 phcache = repo._phasecache.copy()
291 299 phcache.retractboundary(repo, targetphase, nodes)
292 300 repo._phasecache.replace(phcache)
293 301
294 302 def listphases(repo):
295 303 """List phases root for serialization over pushkey"""
296 304 keys = {}
297 305 value = '%i' % draft
298 306 for root in repo._phasecache.phaseroots[draft]:
299 307 keys[hex(root)] = value
300 308
301 309 if repo.ui.configbool('phases', 'publish', True):
302 310 # Add an extra data to let remote know we are a publishing
303 311 # repo. Publishing repo can't just pretend they are old repo.
304 312 # When pushing to a publishing repo, the client still need to
305 313 # push phase boundary
306 314 #
307 315 # Push do not only push changeset. It also push phase data.
308 316 # New phase data may apply to common changeset which won't be
309 317 # push (as they are common). Here is a very simple example:
310 318 #
311 319 # 1) repo A push changeset X as draft to repo B
312 320 # 2) repo B make changeset X public
313 321 # 3) repo B push to repo A. X is not pushed but the data that
314 322 # X as now public should
315 323 #
316 324 # The server can't handle it on it's own as it has no idea of
317 325 # client phase data.
318 326 keys['publishing'] = 'True'
319 327 return keys
320 328
321 329 def pushphase(repo, nhex, oldphasestr, newphasestr):
322 330 """List phases root for serialization over pushkey"""
323 331 repo = repo.unfiltered()
324 332 lock = repo.lock()
325 333 try:
326 334 currentphase = repo[nhex].phase()
327 335 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
328 336 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
329 337 if currentphase == oldphase and newphase < oldphase:
330 338 advanceboundary(repo, newphase, [bin(nhex)])
331 339 return 1
332 340 elif currentphase == newphase:
333 341 # raced, but got correct result
334 342 return 1
335 343 else:
336 344 return 0
337 345 finally:
338 346 lock.release()
339 347
340 348 def analyzeremotephases(repo, subset, roots):
341 349 """Compute phases heads and root in a subset of node from root dict
342 350
343 351 * subset is heads of the subset
344 352 * roots is {<nodeid> => phase} mapping. key and value are string.
345 353
346 354 Accept unknown element input
347 355 """
348 356 repo = repo.unfiltered()
349 357 # build list from dictionary
350 358 draftroots = []
351 359 nodemap = repo.changelog.nodemap # to filter unknown nodes
352 360 for nhex, phase in roots.iteritems():
353 361 if nhex == 'publishing': # ignore data related to publish option
354 362 continue
355 363 node = bin(nhex)
356 364 phase = int(phase)
357 365 if phase == 0:
358 366 if node != nullid:
359 367 repo.ui.warn(_('ignoring inconsistent public root'
360 368 ' from remote: %s\n') % nhex)
361 369 elif phase == 1:
362 370 if node in nodemap:
363 371 draftroots.append(node)
364 372 else:
365 373 repo.ui.warn(_('ignoring unexpected root from remote: %i %s\n')
366 374 % (phase, nhex))
367 375 # compute heads
368 376 publicheads = newheads(repo, subset, draftroots)
369 377 return publicheads, draftroots
370 378
371 379 def newheads(repo, heads, roots):
372 380 """compute new head of a subset minus another
373 381
374 382 * `heads`: define the first subset
375 383 * `roots`: define the second we subtract from the first"""
376 384 repo = repo.unfiltered()
377 385 revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))',
378 386 heads, roots, roots, heads)
379 387 return [c.node() for c in revset]
380 388
381 389
382 390 def newcommitphase(ui):
383 391 """helper to get the target phase of new commit
384 392
385 393 Handle all possible values for the phases.new-commit options.
386 394
387 395 """
388 396 v = ui.config('phases', 'new-commit', draft)
389 397 try:
390 398 return phasenames.index(v)
391 399 except ValueError:
392 400 try:
393 401 return int(v)
394 402 except ValueError:
395 403 msg = _("phases.new-commit: not a valid phase name ('%s')")
396 404 raise error.ConfigError(msg % v)
397 405
398 406 def hassecret(repo):
399 407 """utility function that check if a repo have any secret changeset."""
400 408 return bool(repo._phasecache.phaseroots[2])
@@ -1,387 +1,496 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > graphlog=
4 4 > rebase=
5 5 > mq=
6 6 >
7 7 > [phases]
8 8 > publish=False
9 9 >
10 10 > [alias]
11 11 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
12 12 > theads = heads --template "{rev}: '{desc}' {branches}\n"
13 13 > EOF
14 14
15 15 $ hg init a
16 16 $ cd a
17 17
18 18 $ echo a > a
19 19 $ hg ci -Am A
20 20 adding a
21 21
22 22 $ hg branch branch1
23 23 marked working directory as branch branch1
24 24 (branches are permanent and global, did you want a bookmark?)
25 25 $ hg ci -m 'branch1'
26 26
27 27 $ echo b > b
28 28 $ hg ci -Am B
29 29 adding b
30 30
31 31 $ hg up -q 0
32 32
33 33 $ hg branch branch2
34 34 marked working directory as branch branch2
35 35 (branches are permanent and global, did you want a bookmark?)
36 36 $ hg ci -m 'branch2'
37 37
38 38 $ echo c > C
39 39 $ hg ci -Am C
40 40 adding C
41 41
42 42 $ hg up -q 2
43 43
44 44 $ hg branch -f branch2
45 45 marked working directory as branch branch2
46 46 (branches are permanent and global, did you want a bookmark?)
47 47 $ echo d > d
48 48 $ hg ci -Am D
49 49 adding d
50 50 created new head
51 51
52 52 $ echo e > e
53 53 $ hg ci -Am E
54 54 adding e
55 55
56 56 $ hg update default
57 57 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
58 58
59 59 $ hg branch branch3
60 60 marked working directory as branch branch3
61 61 (branches are permanent and global, did you want a bookmark?)
62 62 $ hg ci -m 'branch3'
63 63
64 64 $ echo f > f
65 65 $ hg ci -Am F
66 66 adding f
67 67
68 68 $ cd ..
69 69
70 70
71 71 Rebase part of branch2 (5-6) onto branch3 (8):
72 72
73 73 $ hg clone -q -u . a a1
74 74 $ cd a1
75 75
76 76 $ hg tglog
77 77 @ 8: 'F' branch3
78 78 |
79 79 o 7: 'branch3' branch3
80 80 |
81 81 | o 6: 'E' branch2
82 82 | |
83 83 | o 5: 'D' branch2
84 84 | |
85 85 | | o 4: 'C' branch2
86 86 | | |
87 87 +---o 3: 'branch2' branch2
88 88 | |
89 89 | o 2: 'B' branch1
90 90 | |
91 91 | o 1: 'branch1' branch1
92 92 |/
93 93 o 0: 'A'
94 94
95 95 $ hg branches
96 96 branch3 8:4666b71e8e32
97 97 branch2 6:5097051d331d
98 98 branch1 2:0a03079c47fd (inactive)
99 99 default 0:1994f17a630e (inactive)
100 100
101 101 $ hg theads
102 102 8: 'F' branch3
103 103 6: 'E' branch2
104 104 4: 'C' branch2
105 105 2: 'B' branch1
106 106 0: 'A'
107 107
108 108 $ hg rebase -s 5 -d 8
109 109 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
110 110
111 111 $ hg branches
112 112 branch3 8:466cdfb14b62
113 113 branch2 4:e4fdb121d036
114 114 branch1 2:0a03079c47fd
115 115 default 0:1994f17a630e (inactive)
116 116
117 117 $ hg theads
118 118 8: 'E' branch3
119 119 4: 'C' branch2
120 120 2: 'B' branch1
121 121 0: 'A'
122 122
123 123 $ hg tglog
124 124 @ 8: 'E' branch3
125 125 |
126 126 o 7: 'D' branch3
127 127 |
128 128 o 6: 'F' branch3
129 129 |
130 130 o 5: 'branch3' branch3
131 131 |
132 132 | o 4: 'C' branch2
133 133 | |
134 134 | o 3: 'branch2' branch2
135 135 |/
136 136 | o 2: 'B' branch1
137 137 | |
138 138 | o 1: 'branch1' branch1
139 139 |/
140 140 o 0: 'A'
141 141
142 142 $ cd ..
143 143
144 144
145 145 Rebase head of branch3 (8) onto branch2 (6):
146 146
147 147 $ hg clone -q -u . a a2
148 148 $ cd a2
149 149
150 150 $ hg tglog
151 151 @ 8: 'F' branch3
152 152 |
153 153 o 7: 'branch3' branch3
154 154 |
155 155 | o 6: 'E' branch2
156 156 | |
157 157 | o 5: 'D' branch2
158 158 | |
159 159 | | o 4: 'C' branch2
160 160 | | |
161 161 +---o 3: 'branch2' branch2
162 162 | |
163 163 | o 2: 'B' branch1
164 164 | |
165 165 | o 1: 'branch1' branch1
166 166 |/
167 167 o 0: 'A'
168 168
169 169 $ hg rebase -s 8 -d 6
170 170 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
171 171
172 172 $ hg branches
173 173 branch2 8:6b4bdc1b5ac0
174 174 branch3 7:653b9feb4616
175 175 branch1 2:0a03079c47fd (inactive)
176 176 default 0:1994f17a630e (inactive)
177 177
178 178 $ hg theads
179 179 8: 'F' branch2
180 180 7: 'branch3' branch3
181 181 4: 'C' branch2
182 182 2: 'B' branch1
183 183 0: 'A'
184 184
185 185 $ hg tglog
186 186 @ 8: 'F' branch2
187 187 |
188 188 | o 7: 'branch3' branch3
189 189 | |
190 190 o | 6: 'E' branch2
191 191 | |
192 192 o | 5: 'D' branch2
193 193 | |
194 194 | | o 4: 'C' branch2
195 195 | | |
196 196 | | o 3: 'branch2' branch2
197 197 | |/
198 198 o | 2: 'B' branch1
199 199 | |
200 200 o | 1: 'branch1' branch1
201 201 |/
202 202 o 0: 'A'
203 203
204 204 $ hg verify -q
205 205
206 206 $ cd ..
207 207
208 208
209 209 Rebase entire branch3 (7-8) onto branch2 (6):
210 210
211 211 $ hg clone -q -u . a a3
212 212 $ cd a3
213 213
214 214 $ hg tglog
215 215 @ 8: 'F' branch3
216 216 |
217 217 o 7: 'branch3' branch3
218 218 |
219 219 | o 6: 'E' branch2
220 220 | |
221 221 | o 5: 'D' branch2
222 222 | |
223 223 | | o 4: 'C' branch2
224 224 | | |
225 225 +---o 3: 'branch2' branch2
226 226 | |
227 227 | o 2: 'B' branch1
228 228 | |
229 229 | o 1: 'branch1' branch1
230 230 |/
231 231 o 0: 'A'
232 232
233 233 $ hg rebase -s 7 -d 6
234 234 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
235 235
236 236 $ hg branches
237 237 branch2 7:6b4bdc1b5ac0
238 238 branch1 2:0a03079c47fd (inactive)
239 239 default 0:1994f17a630e (inactive)
240 240
241 241 $ hg theads
242 242 7: 'F' branch2
243 243 4: 'C' branch2
244 244 2: 'B' branch1
245 245 0: 'A'
246 246
247 247 $ hg tglog
248 248 @ 7: 'F' branch2
249 249 |
250 250 o 6: 'E' branch2
251 251 |
252 252 o 5: 'D' branch2
253 253 |
254 254 | o 4: 'C' branch2
255 255 | |
256 256 | o 3: 'branch2' branch2
257 257 | |
258 258 o | 2: 'B' branch1
259 259 | |
260 260 o | 1: 'branch1' branch1
261 261 |/
262 262 o 0: 'A'
263 263
264 264 $ hg verify -q
265 265
266 266 Stripping multiple branches in one go bypasses the fast-case code to
267 267 update the branch cache.
268 268
269 269 $ hg strip 2
270 270 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
271 271 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
272 272
273 273 $ hg tglog
274 274 o 3: 'C' branch2
275 275 |
276 276 o 2: 'branch2' branch2
277 277 |
278 278 | @ 1: 'branch1' branch1
279 279 |/
280 280 o 0: 'A'
281 281
282 282
283 283 $ hg branches
284 284 branch2 3:e4fdb121d036
285 285 branch1 1:63379ac49655
286 286 default 0:1994f17a630e (inactive)
287 287
288 288 $ hg theads
289 289 3: 'C' branch2
290 290 1: 'branch1' branch1
291 291 0: 'A'
292 292
293 293 Fast path branchcache code should not be invoked if branches stripped is not
294 294 the same as branches remaining.
295 295
296 296 $ hg init b
297 297 $ cd b
298 298
299 299 $ hg branch branch1
300 300 marked working directory as branch branch1
301 301 (branches are permanent and global, did you want a bookmark?)
302 302 $ hg ci -m 'branch1'
303 303
304 304 $ hg branch branch2
305 305 marked working directory as branch branch2
306 306 (branches are permanent and global, did you want a bookmark?)
307 307 $ hg ci -m 'branch2'
308 308
309 309 $ hg branch -f branch1
310 310 marked working directory as branch branch1
311 311 (branches are permanent and global, did you want a bookmark?)
312 312
313 313 $ echo a > A
314 314 $ hg ci -Am A
315 315 adding A
316 316 created new head
317 317
318 318 $ hg tglog
319 319 @ 2: 'A' branch1
320 320 |
321 321 o 1: 'branch2' branch2
322 322 |
323 323 o 0: 'branch1' branch1
324 324
325 325
326 326 $ hg theads
327 327 2: 'A' branch1
328 328 1: 'branch2' branch2
329 329
330 330 $ hg strip 2
331 331 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
332 332 saved backup bundle to $TESTTMP/a3/b/.hg/strip-backup/*-backup.hg (glob)
333 333
334 334 $ hg theads
335 335 1: 'branch2' branch2
336 336 0: 'branch1' branch1
337 337
338 338
339 339 Make sure requesting to strip a revision already stripped does not confuse things.
340 340 Try both orders.
341 341
342 342 $ cd ..
343 343
344 344 $ hg init c
345 345 $ cd c
346 346
347 347 $ echo a > a
348 348 $ hg ci -Am A
349 349 adding a
350 350 $ echo b > b
351 351 $ hg ci -Am B
352 352 adding b
353 353 $ echo c > c
354 354 $ hg ci -Am C
355 355 adding c
356 356 $ echo d > d
357 357 $ hg ci -Am D
358 358 adding d
359 359 $ echo e > e
360 360 $ hg ci -Am E
361 361 adding e
362 362
363 363 $ hg tglog
364 364 @ 4: 'E'
365 365 |
366 366 o 3: 'D'
367 367 |
368 368 o 2: 'C'
369 369 |
370 370 o 1: 'B'
371 371 |
372 372 o 0: 'A'
373 373
374 374
375 375 $ hg strip 3 4
376 376 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
377 377 saved backup bundle to $TESTTMP/a3/c/.hg/strip-backup/*-backup.hg (glob)
378 378
379 379 $ hg theads
380 380 2: 'C'
381 381
382 382 $ hg strip 2 1
383 383 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
384 384 saved backup bundle to $TESTTMP/a3/c/.hg/strip-backup/*-backup.hg (glob)
385 385
386 386 $ hg theads
387 387 0: 'A'
388
389 Make sure rebase does not break for phase/filter related reason
390 ----------------------------------------------------------------
391 (issue3858)
392
393 $ cd ..
394
395 $ cat >> $HGRCPATH << EOF
396 > [ui]
397 > logtemplate={rev} {desc} {phase}\n
398 > EOF
399 $ cat $HGRCPATH
400 [ui]
401 slash = True
402 interactive = False
403 [defaults]
404 backout = -d "0 0"
405 commit = -d "0 0"
406 tag = -d "0 0"
407 [extensions]
408 graphlog=
409 rebase=
410 mq=
411
412 [phases]
413 publish=False
414
415 [alias]
416 tglog = log -G --template "{rev}: '{desc}' {branches}\n"
417 theads = heads --template "{rev}: '{desc}' {branches}\n"
418 [ui]
419 logtemplate={rev} {desc} {phase}\n
420
421
422 $ hg init c4
423 $ cd c4
424
425 $ echo a > a
426 $ hg ci -Am A
427 adding a
428 $ echo b > b
429 $ hg ci -Am B
430 adding b
431 $ hg up 0
432 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
433 $ echo c > c
434 $ hg ci -Am C
435 adding c
436 created new head
437 $ hg up 1
438 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
439 $ hg merge
440 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
441 (branch merge, don't forget to commit)
442 $ hg ci -m d
443 $ hg up 2
444 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
445 $ echo e > e
446 $ hg ci -Am E
447 adding e
448 created new head
449 $ hg merge 3
450 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
451 (branch merge, don't forget to commit)
452 $ hg ci -m F
453 $ hg up 3
454 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
455 $ echo g > g
456 $ hg ci -Am G
457 adding g
458 created new head
459 $ echo h > h
460 $ hg ci -Am H
461 adding h
462 $ hg up 5
463 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
464 $ echo i > i
465 $ hg ci -Am I
466 adding i
467
468 Turn most changeset public
469
470 $ hg ph -p 7
471
472 $ hg heads
473 8 I draft
474 7 H public
475 $ hg log -G
476 @ 8 I draft
477 |
478 | o 7 H public
479 | |
480 | o 6 G public
481 | |
482 o | 5 F draft
483 |\|
484 o | 4 E draft
485 | |
486 | o 3 d public
487 |/|
488 o | 2 C public
489 | |
490 | o 1 B public
491 |/
492 o 0 A public
493
494
495 $ hg rebase --dest 7 --source 5
496 saved backup bundle to $TESTTMP/a3/c4/.hg/strip-backup/*-backup.hg (glob)
General Comments 0
You need to be logged in to leave comments. Login now