Show More
@@ -149,77 +149,96 b' def findcommonoutgoing(repo, other, only' | |||||
149 |
|
149 | |||
150 | return og |
|
150 | return og | |
151 |
|
151 | |||
|
152 | def _branchmapsummary(repo, remote, outgoing): | |||
|
153 | """compute a summary of branch and heads status before and after push | |||
|
154 | ||||
|
155 | - oldmap: {'branch': [heads]} mapping for remote | |||
|
156 | - newmap: {'branch': [heads]} mapping for local | |||
|
157 | - unsynced: set of branch that have unsynced remote changes | |||
|
158 | - branches: set of all common branch pushed | |||
|
159 | - newbranches: list of plain new pushed branch | |||
|
160 | """ | |||
|
161 | cl = repo.changelog | |||
|
162 | ||||
|
163 | # A. Create set of branches involved in the push. | |||
|
164 | branches = set(repo[n].branch() for n in outgoing.missing) | |||
|
165 | remotemap = remote.branchmap() | |||
|
166 | newbranches = branches - set(remotemap) | |||
|
167 | branches.difference_update(newbranches) | |||
|
168 | ||||
|
169 | # B. Construct the initial oldmap and newmap dicts. | |||
|
170 | # They contain information about the remote heads before and | |||
|
171 | # after the push, respectively. | |||
|
172 | # Heads not found locally are not included in either dict, | |||
|
173 | # since they won't be affected by the push. | |||
|
174 | # unsynced contains all branches with incoming changesets. | |||
|
175 | oldmap = {} | |||
|
176 | newmap = {} | |||
|
177 | unsynced = set() | |||
|
178 | for branch in branches: | |||
|
179 | remotebrheads = remotemap[branch] | |||
|
180 | ||||
|
181 | prunedbrheads = [h for h in remotebrheads if h in cl.nodemap] | |||
|
182 | oldmap[branch] = prunedbrheads | |||
|
183 | newmap[branch] = list(prunedbrheads) | |||
|
184 | if len(remotebrheads) > len(prunedbrheads): | |||
|
185 | unsynced.add(branch) | |||
|
186 | ||||
|
187 | # C. Update newmap with outgoing changes. | |||
|
188 | # This will possibly add new heads and remove existing ones. | |||
|
189 | ctxgen = (repo[n] for n in outgoing.missing) | |||
|
190 | repo._updatebranchcache(newmap, ctxgen) | |||
|
191 | return oldmap, newmap, unsynced, branches, newbranches | |||
|
192 | ||||
|
193 | def _oldbranchmapsummary(repo, remoteheads, outgoing, inc=False): | |||
|
194 | """Compute branchmapsummary for repo without branchmap support""" | |||
|
195 | ||||
|
196 | cl = repo.changelog | |||
|
197 | # 1-4b. old servers: Check for new topological heads. | |||
|
198 | # Construct {old,new}map with branch = None (topological branch). | |||
|
199 | # (code based on _updatebranchcache) | |||
|
200 | oldheads = set(h for h in remoteheads if h in cl.nodemap) | |||
|
201 | # all nodes in outgoing.missing are children of either: | |||
|
202 | # - an element of oldheads | |||
|
203 | # - another element of outgoing.missing | |||
|
204 | # - nullrev | |||
|
205 | # This explains why the new head are very simple to compute. | |||
|
206 | r = repo.set('heads(%ln + %ln)', oldheads, outgoing.missing) | |||
|
207 | branches = set([None]) | |||
|
208 | newmap = {None: list(c.node() for c in r)} | |||
|
209 | oldmap = {None: oldheads} | |||
|
210 | unsynced = inc and branches or set() | |||
|
211 | return oldmap, newmap, unsynced, branches, set() | |||
|
212 | ||||
152 | def checkheads(repo, remote, outgoing, remoteheads, newbranch=False, inc=False): |
|
213 | def checkheads(repo, remote, outgoing, remoteheads, newbranch=False, inc=False): | |
153 | """Check that a push won't add any outgoing head |
|
214 | """Check that a push won't add any outgoing head | |
154 |
|
215 | |||
155 | raise Abort error and display ui message as needed. |
|
216 | raise Abort error and display ui message as needed. | |
156 | """ |
|
217 | """ | |
|
218 | # Check for each named branch if we're creating new remote heads. | |||
|
219 | # To be a remote head after push, node must be either: | |||
|
220 | # - unknown locally | |||
|
221 | # - a local outgoing head descended from update | |||
|
222 | # - a remote head that's known locally and not | |||
|
223 | # ancestral to an outgoing head | |||
157 | if remoteheads == [nullid]: |
|
224 | if remoteheads == [nullid]: | |
158 | # remote is empty, nothing to check. |
|
225 | # remote is empty, nothing to check. | |
159 | return |
|
226 | return | |
160 |
|
227 | |||
161 | cl = repo.changelog |
|
|||
162 | if remote.capable('branchmap'): |
|
228 | if remote.capable('branchmap'): | |
163 | # Check for each named branch if we're creating new remote heads. |
|
229 | bms = _branchmapsummary(repo, remote, outgoing) | |
164 | # To be a remote head after push, node must be either: |
|
230 | else: | |
165 | # - unknown locally |
|
231 | bms = _oldbranchmapsummary(repo, remoteheads, outgoing, inc) | |
166 | # - a local outgoing head descended from update |
|
232 | oldmap, newmap, unsynced, branches, newbranches = bms | |
167 | # - a remote head that's known locally and not |
|
233 | # 1. Check for new branches on the remote. | |
168 | # ancestral to an outgoing head |
|
234 | if newbranches and not newbranch: # new branch requires --new-branch | |
169 |
|
235 | branchnames = ', '.join(sorted(newbranches)) | ||
170 | # 1. Create set of branches involved in the push. |
|
236 | raise util.Abort(_("push creates new remote branches: %s!") | |
171 | branches = set(repo[n].branch() for n in outgoing.missing) |
|
237 | % branchnames, | |
172 |
|
238 | hint=_("use 'hg push --new-branch' to create" | ||
173 | # 2. Check for new branches on the remote. |
|
239 | " new remote branches")) | |
174 | remotemap = remote.branchmap() |
|
|||
175 | newbranches = branches - set(remotemap) |
|
|||
176 | if newbranches and not newbranch: # new branch requires --new-branch |
|
|||
177 | branchnames = ', '.join(sorted(newbranches)) |
|
|||
178 | raise util.Abort(_("push creates new remote branches: %s!") |
|
|||
179 | % branchnames, |
|
|||
180 | hint=_("use 'hg push --new-branch' to create" |
|
|||
181 | " new remote branches")) |
|
|||
182 | branches.difference_update(newbranches) |
|
|||
183 |
|
240 | |||
184 | # 3. Construct the initial oldmap and newmap dicts. |
|
241 | # 2. Check for new heads. | |
185 | # They contain information about the remote heads before and |
|
|||
186 | # after the push, respectively. |
|
|||
187 | # Heads not found locally are not included in either dict, |
|
|||
188 | # since they won't be affected by the push. |
|
|||
189 | # unsynced contains all branches with incoming changesets. |
|
|||
190 | oldmap = {} |
|
|||
191 | newmap = {} |
|
|||
192 | unsynced = set() |
|
|||
193 | for branch in branches: |
|
|||
194 | remotebrheads = remotemap[branch] |
|
|||
195 | prunedbrheads = [h for h in remotebrheads if h in cl.nodemap] |
|
|||
196 | oldmap[branch] = prunedbrheads |
|
|||
197 | newmap[branch] = list(prunedbrheads) |
|
|||
198 | if len(remotebrheads) > len(prunedbrheads): |
|
|||
199 | unsynced.add(branch) |
|
|||
200 |
|
||||
201 | # 4. Update newmap with outgoing changes. |
|
|||
202 | # This will possibly add new heads and remove existing ones. |
|
|||
203 | ctxgen = (repo[n] for n in outgoing.missing) |
|
|||
204 | repo._updatebranchcache(newmap, ctxgen) |
|
|||
205 |
|
||||
206 | else: |
|
|||
207 | # 1-4b. old servers: Check for new topological heads. |
|
|||
208 | # Construct {old,new}map with branch = None (topological branch). |
|
|||
209 | # (code based on _updatebranchcache) |
|
|||
210 | oldheads = set(h for h in remoteheads if h in cl.nodemap) |
|
|||
211 | # all nodes in outgoing.missing are children of either: |
|
|||
212 | # - an element of oldheads |
|
|||
213 | # - another element of outgoing.missing |
|
|||
214 | # - nullrev |
|
|||
215 | # This explains why the new head are very simple to compute. |
|
|||
216 | r = repo.set('heads(%ln + %ln)', oldheads, outgoing.missing) |
|
|||
217 | branches = set([None]) |
|
|||
218 | newmap = {None: list(c.node() for c in r)} |
|
|||
219 | oldmap = {None: oldheads} |
|
|||
220 | unsynced = inc and branches or set() |
|
|||
221 |
|
||||
222 | # 5. Check for new heads. |
|
|||
223 | # If there are more heads after the push than before, a suitable |
|
242 | # If there are more heads after the push than before, a suitable | |
224 | # error message, depending on unsynced status, is displayed. |
|
243 | # error message, depending on unsynced status, is displayed. | |
225 | error = None |
|
244 | error = None |
General Comments 0
You need to be logged in to leave comments.
Login now