##// END OF EJS Templates
phases: add a function to compute heads from root
Pierre-Yves David -
r15649:ca7c4254 default
parent child Browse files
Show More
@@ -1,142 +1,169 b''
1 1 # Mercurial phases support code
2 2 #
3 3 # Copyright 2011 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
4 4 # Logilab SA <contact@logilab.fr>
5 5 # Augie Fackler <durin42@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 import errno
11 11 from node import nullid, bin, hex, short
12 12 from i18n import _
13 13
14 14 allphases = range(2)
15 15 trackedphases = allphases[1:]
16 16
17 17 def readroots(repo):
18 18 """Read phase roots from disk"""
19 19 roots = [set() for i in allphases]
20 20 roots[0].add(nullid)
21 21 try:
22 22 f = repo.sopener('phaseroots')
23 23 try:
24 24 for line in f:
25 25 phase, nh = line.strip().split()
26 26 roots[int(phase)].add(bin(nh))
27 27 finally:
28 28 f.close()
29 29 except IOError, inst:
30 30 if inst.errno != errno.ENOENT:
31 31 raise
32 32 return roots
33 33
34 34 def writeroots(repo):
35 35 """Write phase roots from disk"""
36 36 f = repo.sopener('phaseroots', 'w', atomictemp=True)
37 37 try:
38 38 for phase, roots in enumerate(repo._phaseroots):
39 39 for h in roots:
40 40 f.write('%i %s\n' % (phase, hex(h)))
41 41 repo._dirtyphases = False
42 42 finally:
43 43 f.close()
44 44
45 45 def filterunknown(repo, phaseroots=None):
46 46 """remove unknown nodes from the phase boundary
47 47
48 48 no data is lost as unknown node only old data for their descentants
49 49 """
50 50 if phaseroots is None:
51 51 phaseroots = repo._phaseroots
52 52 for phase, nodes in enumerate(phaseroots):
53 53 missing = [node for node in nodes if node not in repo]
54 54 if missing:
55 55 for mnode in missing:
56 56 msg = _('Removing unknown node %(n)s from %(p)i-phase boundary')
57 57 repo.ui.debug(msg, {'n': short(mnode), 'p': phase})
58 58 nodes.symmetric_difference_update(missing)
59 59 repo._dirtyphases = True
60 60
61 61 def advanceboundary(repo, targetphase, nodes):
62 62 """Add nodes to a phase changing other nodes phases if necessary.
63 63
64 64 This function move boundary *forward* this means that all nodes are set
65 65 in the target phase or kept in a *lower* phase.
66 66
67 67 Simplify boundary to contains phase roots only."""
68 68 for phase in xrange(targetphase + 1, len(allphases)):
69 69 # filter nodes that are not in a compatible phase already
70 70 # XXX rev phase cache might have been invalidated by a previous loop
71 71 # XXX we need to be smarter here
72 72 nodes = [n for n in nodes if repo[n].phase() >= phase]
73 73 if not nodes:
74 74 break # no roots to move anymore
75 75 roots = repo._phaseroots[phase]
76 76 olds = roots.copy()
77 77 ctxs = list(repo.set('roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
78 78 roots.clear()
79 79 roots.update(ctx.node() for ctx in ctxs)
80 80 if olds != roots:
81 81 # invalidate cache (we probably could be smarter here
82 82 if '_phaserev' in vars(repo):
83 83 del repo._phaserev
84 84 repo._dirtyphases = True
85 85
86 86 def retractboundary(repo, targetphase, nodes):
87 87 """Set nodes back to a phase changing other nodes phases if necessary.
88 88
89 89 This function move boundary *backward* this means that all nodes are set
90 90 in the target phase or kept in a *higher* phase.
91 91
92 92 Simplify boundary to contains phase roots only."""
93 93 currentroots = repo._phaseroots[targetphase]
94 94 newroots = [n for n in nodes if repo[n].phase() < targetphase]
95 95 if newroots:
96 96 currentroots.update(newroots)
97 97 ctxs = repo.set('roots(%ln::)', currentroots)
98 98 currentroots.intersection_update(ctx.node() for ctx in ctxs)
99 99 if '_phaserev' in vars(repo):
100 100 del repo._phaserev
101 101 repo._dirtyphases = True
102 102
103 103
104 104 def listphases(repo):
105 105 """List phases root for serialisation over pushkey"""
106 106 keys = {}
107 107 for phase in trackedphases:
108 108 for root in repo._phaseroots[phase]:
109 109 keys[hex(root)] = '%i' % phase
110 110 if repo.ui.configbool('phases', 'publish', True):
111 111 # Add an extra data to let remote know we are a publishing repo.
112 112 # Publishing repo can't just pretend they are old repo. When pushing to
113 113 # a publishing repo, the client still need to push phase boundary
114 114 #
115 115 # Push do not only push changeset. It also push phase data. New
116 116 # phase data may apply to common changeset which won't be push (as they
117 117 # are common). Here is a very simple example:
118 118 #
119 119 # 1) repo A push changeset X as draft to repo B
120 120 # 2) repo B make changeset X public
121 121 # 3) repo B push to repo A. X is not pushed but the data that X as now
122 122 # public should
123 123 #
124 124 # The server can't handle it on it's own as it has no idea of client
125 125 # phase data.
126 126 keys['publishing'] = 'True'
127 127 return keys
128 128
129 129 def pushphase(repo, nhex, oldphasestr, newphasestr):
130 130 """List phases root for serialisation over pushkey"""
131 131 lock = repo.lock()
132 132 try:
133 133 currentphase = repo[nhex].phase()
134 134 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
135 135 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
136 136 if currentphase == oldphase and newphase < oldphase:
137 137 advanceboundary(repo, newphase, [bin(nhex)])
138 138 return 1
139 139 else:
140 140 return 0
141 141 finally:
142 142 lock.release()
143
144 def analyzeremotephases(repo, subset, roots):
145 """Compute phases heads and root in a subset of node from root dict
146
147 * subset is heads of the subset
148 * roots is {<nodeid> => phase} mapping. key and value are string.
149
150 Accept unknown element input
151 """
152 # build list from dictionary
153 phaseroots = [[] for p in allphases]
154 for nhex, phase in roots.iteritems():
155 if nhex == 'publishing': # ignore data related to publish option
156 continue
157 node = bin(nhex)
158 phase = int(phase)
159 if node in repo:
160 phaseroots[phase].append(node)
161 # compute heads
162 phaseheads = [[] for p in allphases]
163 for phase in allphases[:-1]:
164 toproof = phaseroots[phase + 1]
165 revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))',
166 subset, toproof, toproof, subset)
167 phaseheads[phase].extend(c.node() for c in revset)
168 return phaseheads, phaseroots
169
General Comments 0
You need to be logged in to leave comments. Login now