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 of the |
9 This software may be used and distributed according to the terms |
10 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 below, |
21 manipulated and communicated. The details of each phase is described |
22 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 permanent and |
24 Like bookmarks, phases are not stored in history and thus are not |
25 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, so they |
27 First, no changeset can be in two phases at once. Phases are ordered, |
28 can be considered from lowest to highest. The default, lowest phase is 'public' |
28 so they can be considered from lowest to highest. The default, lowest |
29 - this is the normal phase of existing changesets. A child changeset can not be |
29 phase is 'public' - this is the normal phase of existing changesets. A |
30 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 |
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 a |
44 Phase data is exchanged by pushkey on pull and push. Some servers have |
45 publish option set, we call such a server a "publishing server". Pushing a |
45 a publish option set, we call such a server a "publishing server". |
46 draft changeset to a publishing server changes the phase to public. |
46 Pushing a draft changeset to a publishing server changes the phase to |
|
47 public. |
47 |
48 |
48 A small list of fact/rules define the exchange of phase: |
49 A small list of fact/rules define the exchange of phase: |
49 |
50 |
50 * old client never changes server states |
51 * old client never changes server states |
51 * pull never changes server states |
52 * pull never changes server states |
52 * publish and old server changesets are seen as public by client |
53 * publish and old server changesets are seen as public by client |
53 * any secret changeset seen in another repository is lowered to at least draft |
54 * any secret changeset seen in another repository is lowered to at |
54 |
55 least draft |
55 Here is the final table summing up the 49 possible use cases of phase exchange: |
56 |
|
57 Here is the final table summing up the 49 possible use cases of phase |
|
58 exchange: |
56 |
59 |
57 server |
60 server |
58 old publish non-publish |
61 old publish non-publish |
59 N X N D P N D P |
62 N X N D P N D P |
60 old client |
63 old client |
77 A/B = final state on client / state on server |
80 A/B = final state on client / state on server |
78 |
81 |
79 * N = new/not present, |
82 * N = new/not present, |
80 * P = public, |
83 * P = public, |
81 * D = draft, |
84 * D = draft, |
82 * X = not tracked (i.e., the old client or server has no internal way of |
85 * X = not tracked (i.e., the old client or server has no internal |
83 recording the phase.) |
86 way of recording the phase.) |
84 |
87 |
85 passive = only pushes |
88 passive = only pushes |
86 |
89 |
87 |
90 |
88 A cell here can be read like this: |
91 A cell here can be read like this: |
89 |
92 |
90 "When a new client pushes a draft changeset (D) to a publishing server |
93 "When a new client pushes a draft changeset (D) to a publishing |
91 where it's not present (N), it's marked public on both sides (P/P)." |
94 server where it's not present (N), it's marked public on both |
|
95 sides (P/P)." |
92 |
96 |
93 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 |
94 - other people see it as public |
98 - other people see it as public |
95 - content is pushed as draft |
99 - content is pushed as draft |
96 |
100 |
258 self._updateroots(targetphase, currentroots) |
262 self._updateroots(targetphase, currentroots) |
259 |
263 |
260 def advanceboundary(repo, targetphase, nodes): |
264 def advanceboundary(repo, targetphase, nodes): |
261 """Add nodes to a phase changing other nodes phases if necessary. |
265 """Add nodes to a phase changing other nodes phases if necessary. |
262 |
266 |
263 This function move boundary *forward* this means that all nodes are set |
267 This function move boundary *forward* this means that all nodes |
264 in the target phase or kept in a *lower* phase. |
268 are set in the target phase or kept in a *lower* phase. |
265 |
269 |
266 Simplify boundary to contains phase roots only.""" |
270 Simplify boundary to contains phase roots only.""" |
267 phcache = repo._phasecache.copy() |
271 phcache = repo._phasecache.copy() |
268 phcache.advanceboundary(repo, targetphase, nodes) |
272 phcache.advanceboundary(repo, targetphase, nodes) |
269 repo._phasecache.replace(phcache) |
273 repo._phasecache.replace(phcache) |
270 |
274 |
271 def retractboundary(repo, targetphase, nodes): |
275 def retractboundary(repo, targetphase, nodes): |
272 """Set nodes back to a phase changing other nodes phases if necessary. |
276 """Set nodes back to a phase changing other nodes phases if |
273 |
277 necessary. |
274 This function move boundary *backward* this means that all nodes are set |
278 |
275 in the target phase or kept in a *higher* phase. |
279 This function move boundary *backward* this means that all nodes |
|
280 are set in the target phase or kept in a *higher* phase. |
276 |
281 |
277 Simplify boundary to contains phase roots only.""" |
282 Simplify boundary to contains phase roots only.""" |
278 phcache = repo._phasecache.copy() |
283 phcache = repo._phasecache.copy() |
279 phcache.retractboundary(repo, targetphase, nodes) |
284 phcache.retractboundary(repo, targetphase, nodes) |
280 repo._phasecache.replace(phcache) |
285 repo._phasecache.replace(phcache) |
285 value = '%i' % draft |
290 value = '%i' % draft |
286 for root in repo._phasecache.phaseroots[draft]: |
291 for root in repo._phasecache.phaseroots[draft]: |
287 keys[hex(root)] = value |
292 keys[hex(root)] = value |
288 |
293 |
289 if repo.ui.configbool('phases', 'publish', True): |
294 if repo.ui.configbool('phases', 'publish', True): |
290 # Add an extra data to let remote know we are a publishing repo. |
295 # Add an extra data to let remote know we are a publishing |
291 # Publishing repo can't just pretend they are old repo. When pushing to |
296 # repo. Publishing repo can't just pretend they are old repo. |
292 # a publishing repo, the client still need to push phase boundary |
297 # When pushing to a publishing repo, the client still need to |
|
298 # push phase boundary |
293 # |
299 # |
294 # Push do not only push changeset. It also push phase data. New |
300 # Push do not only push changeset. It also push phase data. |
295 # phase data may apply to common changeset which won't be push (as they |
301 # New phase data may apply to common changeset which won't be |
296 # are common). Here is a very simple example: |
302 # push (as they are common). Here is a very simple example: |
297 # |
303 # |
298 # 1) repo A push changeset X as draft to repo B |
304 # 1) repo A push changeset X as draft to repo B |
299 # 2) repo B make changeset X public |
305 # 2) repo B make changeset X public |
300 # 3) repo B push to repo A. X is not pushed but the data that X as now |
306 # 3) repo B push to repo A. X is not pushed but the data that |
301 # public should |
307 # X as now public should |
302 # |
308 # |
303 # The server can't handle it on it's own as it has no idea of client |
309 # The server can't handle it on it's own as it has no idea of |
304 # phase data. |
310 # client phase data. |
305 keys['publishing'] = 'True' |
311 keys['publishing'] = 'True' |
306 return keys |
312 return keys |
307 |
313 |
308 def pushphase(repo, nhex, oldphasestr, newphasestr): |
314 def pushphase(repo, nhex, oldphasestr, newphasestr): |
309 """List phases root for serialisation over pushkey""" |
315 """List phases root for serialisation over pushkey""" |