##// END OF EJS Templates
discovery: don't compute allfuturecommon when it won't be used...
Michael O'Connor -
r24702:2b044925 default
parent child Browse files
Show More
@@ -1,376 +1,380 b''
1 # discovery.py - protocol changeset discovery functions
1 # discovery.py - protocol changeset discovery functions
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import nullid, short
8 from node import nullid, short
9 from i18n import _
9 from i18n import _
10 import util, setdiscovery, treediscovery, phases, obsolete, bookmarks
10 import util, setdiscovery, treediscovery, phases, obsolete, bookmarks
11 import branchmap
11 import branchmap
12
12
13 def findcommonincoming(repo, remote, heads=None, force=False):
13 def findcommonincoming(repo, remote, heads=None, force=False):
14 """Return a tuple (common, anyincoming, heads) used to identify the common
14 """Return a tuple (common, anyincoming, heads) used to identify the common
15 subset of nodes between repo and remote.
15 subset of nodes between repo and remote.
16
16
17 "common" is a list of (at least) the heads of the common subset.
17 "common" is a list of (at least) the heads of the common subset.
18 "anyincoming" is testable as a boolean indicating if any nodes are missing
18 "anyincoming" is testable as a boolean indicating if any nodes are missing
19 locally. If remote does not support getbundle, this actually is a list of
19 locally. If remote does not support getbundle, this actually is a list of
20 roots of the nodes that would be incoming, to be supplied to
20 roots of the nodes that would be incoming, to be supplied to
21 changegroupsubset. No code except for pull should be relying on this fact
21 changegroupsubset. No code except for pull should be relying on this fact
22 any longer.
22 any longer.
23 "heads" is either the supplied heads, or else the remote's heads.
23 "heads" is either the supplied heads, or else the remote's heads.
24
24
25 If you pass heads and they are all known locally, the response lists just
25 If you pass heads and they are all known locally, the response lists just
26 these heads in "common" and in "heads".
26 these heads in "common" and in "heads".
27
27
28 Please use findcommonoutgoing to compute the set of outgoing nodes to give
28 Please use findcommonoutgoing to compute the set of outgoing nodes to give
29 extensions a good hook into outgoing.
29 extensions a good hook into outgoing.
30 """
30 """
31
31
32 if not remote.capable('getbundle'):
32 if not remote.capable('getbundle'):
33 return treediscovery.findcommonincoming(repo, remote, heads, force)
33 return treediscovery.findcommonincoming(repo, remote, heads, force)
34
34
35 if heads:
35 if heads:
36 allknown = True
36 allknown = True
37 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
37 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
38 for h in heads:
38 for h in heads:
39 if not knownnode(h):
39 if not knownnode(h):
40 allknown = False
40 allknown = False
41 break
41 break
42 if allknown:
42 if allknown:
43 return (heads, False, heads)
43 return (heads, False, heads)
44
44
45 res = setdiscovery.findcommonheads(repo.ui, repo, remote,
45 res = setdiscovery.findcommonheads(repo.ui, repo, remote,
46 abortwhenunrelated=not force)
46 abortwhenunrelated=not force)
47 common, anyinc, srvheads = res
47 common, anyinc, srvheads = res
48 return (list(common), anyinc, heads or list(srvheads))
48 return (list(common), anyinc, heads or list(srvheads))
49
49
50 class outgoing(object):
50 class outgoing(object):
51 '''Represents the set of nodes present in a local repo but not in a
51 '''Represents the set of nodes present in a local repo but not in a
52 (possibly) remote one.
52 (possibly) remote one.
53
53
54 Members:
54 Members:
55
55
56 missing is a list of all nodes present in local but not in remote.
56 missing is a list of all nodes present in local but not in remote.
57 common is a list of all nodes shared between the two repos.
57 common is a list of all nodes shared between the two repos.
58 excluded is the list of missing changeset that shouldn't be sent remotely.
58 excluded is the list of missing changeset that shouldn't be sent remotely.
59 missingheads is the list of heads of missing.
59 missingheads is the list of heads of missing.
60 commonheads is the list of heads of common.
60 commonheads is the list of heads of common.
61
61
62 The sets are computed on demand from the heads, unless provided upfront
62 The sets are computed on demand from the heads, unless provided upfront
63 by discovery.'''
63 by discovery.'''
64
64
65 def __init__(self, revlog, commonheads, missingheads):
65 def __init__(self, revlog, commonheads, missingheads):
66 self.commonheads = commonheads
66 self.commonheads = commonheads
67 self.missingheads = missingheads
67 self.missingheads = missingheads
68 self._revlog = revlog
68 self._revlog = revlog
69 self._common = None
69 self._common = None
70 self._missing = None
70 self._missing = None
71 self.excluded = []
71 self.excluded = []
72
72
73 def _computecommonmissing(self):
73 def _computecommonmissing(self):
74 sets = self._revlog.findcommonmissing(self.commonheads,
74 sets = self._revlog.findcommonmissing(self.commonheads,
75 self.missingheads)
75 self.missingheads)
76 self._common, self._missing = sets
76 self._common, self._missing = sets
77
77
78 @util.propertycache
78 @util.propertycache
79 def common(self):
79 def common(self):
80 if self._common is None:
80 if self._common is None:
81 self._computecommonmissing()
81 self._computecommonmissing()
82 return self._common
82 return self._common
83
83
84 @util.propertycache
84 @util.propertycache
85 def missing(self):
85 def missing(self):
86 if self._missing is None:
86 if self._missing is None:
87 self._computecommonmissing()
87 self._computecommonmissing()
88 return self._missing
88 return self._missing
89
89
90 def findcommonoutgoing(repo, other, onlyheads=None, force=False,
90 def findcommonoutgoing(repo, other, onlyheads=None, force=False,
91 commoninc=None, portable=False):
91 commoninc=None, portable=False):
92 '''Return an outgoing instance to identify the nodes present in repo but
92 '''Return an outgoing instance to identify the nodes present in repo but
93 not in other.
93 not in other.
94
94
95 If onlyheads is given, only nodes ancestral to nodes in onlyheads
95 If onlyheads is given, only nodes ancestral to nodes in onlyheads
96 (inclusive) are included. If you already know the local repo's heads,
96 (inclusive) are included. If you already know the local repo's heads,
97 passing them in onlyheads is faster than letting them be recomputed here.
97 passing them in onlyheads is faster than letting them be recomputed here.
98
98
99 If commoninc is given, it must be the result of a prior call to
99 If commoninc is given, it must be the result of a prior call to
100 findcommonincoming(repo, other, force) to avoid recomputing it here.
100 findcommonincoming(repo, other, force) to avoid recomputing it here.
101
101
102 If portable is given, compute more conservative common and missingheads,
102 If portable is given, compute more conservative common and missingheads,
103 to make bundles created from the instance more portable.'''
103 to make bundles created from the instance more portable.'''
104 # declare an empty outgoing object to be filled later
104 # declare an empty outgoing object to be filled later
105 og = outgoing(repo.changelog, None, None)
105 og = outgoing(repo.changelog, None, None)
106
106
107 # get common set if not provided
107 # get common set if not provided
108 if commoninc is None:
108 if commoninc is None:
109 commoninc = findcommonincoming(repo, other, force=force)
109 commoninc = findcommonincoming(repo, other, force=force)
110 og.commonheads, _any, _hds = commoninc
110 og.commonheads, _any, _hds = commoninc
111
111
112 # compute outgoing
112 # compute outgoing
113 mayexclude = (repo._phasecache.phaseroots[phases.secret] or repo.obsstore)
113 mayexclude = (repo._phasecache.phaseroots[phases.secret] or repo.obsstore)
114 if not mayexclude:
114 if not mayexclude:
115 og.missingheads = onlyheads or repo.heads()
115 og.missingheads = onlyheads or repo.heads()
116 elif onlyheads is None:
116 elif onlyheads is None:
117 # use visible heads as it should be cached
117 # use visible heads as it should be cached
118 og.missingheads = repo.filtered("served").heads()
118 og.missingheads = repo.filtered("served").heads()
119 og.excluded = [ctx.node() for ctx in repo.set('secret() or extinct()')]
119 og.excluded = [ctx.node() for ctx in repo.set('secret() or extinct()')]
120 else:
120 else:
121 # compute common, missing and exclude secret stuff
121 # compute common, missing and exclude secret stuff
122 sets = repo.changelog.findcommonmissing(og.commonheads, onlyheads)
122 sets = repo.changelog.findcommonmissing(og.commonheads, onlyheads)
123 og._common, allmissing = sets
123 og._common, allmissing = sets
124 og._missing = missing = []
124 og._missing = missing = []
125 og.excluded = excluded = []
125 og.excluded = excluded = []
126 for node in allmissing:
126 for node in allmissing:
127 ctx = repo[node]
127 ctx = repo[node]
128 if ctx.phase() >= phases.secret or ctx.extinct():
128 if ctx.phase() >= phases.secret or ctx.extinct():
129 excluded.append(node)
129 excluded.append(node)
130 else:
130 else:
131 missing.append(node)
131 missing.append(node)
132 if len(missing) == len(allmissing):
132 if len(missing) == len(allmissing):
133 missingheads = onlyheads
133 missingheads = onlyheads
134 else: # update missing heads
134 else: # update missing heads
135 missingheads = phases.newheads(repo, onlyheads, excluded)
135 missingheads = phases.newheads(repo, onlyheads, excluded)
136 og.missingheads = missingheads
136 og.missingheads = missingheads
137 if portable:
137 if portable:
138 # recompute common and missingheads as if -r<rev> had been given for
138 # recompute common and missingheads as if -r<rev> had been given for
139 # each head of missing, and --base <rev> for each head of the proper
139 # each head of missing, and --base <rev> for each head of the proper
140 # ancestors of missing
140 # ancestors of missing
141 og._computecommonmissing()
141 og._computecommonmissing()
142 cl = repo.changelog
142 cl = repo.changelog
143 missingrevs = set(cl.rev(n) for n in og._missing)
143 missingrevs = set(cl.rev(n) for n in og._missing)
144 og._common = set(cl.ancestors(missingrevs)) - missingrevs
144 og._common = set(cl.ancestors(missingrevs)) - missingrevs
145 commonheads = set(og.commonheads)
145 commonheads = set(og.commonheads)
146 og.missingheads = [h for h in og.missingheads if h not in commonheads]
146 og.missingheads = [h for h in og.missingheads if h not in commonheads]
147
147
148 return og
148 return og
149
149
150 def _headssummary(repo, remote, outgoing):
150 def _headssummary(repo, remote, outgoing):
151 """compute a summary of branch and heads status before and after push
151 """compute a summary of branch and heads status before and after push
152
152
153 return {'branch': ([remoteheads], [newheads], [unsyncedheads])} mapping
153 return {'branch': ([remoteheads], [newheads], [unsyncedheads])} mapping
154
154
155 - branch: the branch name
155 - branch: the branch name
156 - remoteheads: the list of remote heads known locally
156 - remoteheads: the list of remote heads known locally
157 None if the branch is new
157 None if the branch is new
158 - newheads: the new remote heads (known locally) with outgoing pushed
158 - newheads: the new remote heads (known locally) with outgoing pushed
159 - unsyncedheads: the list of remote heads unknown locally.
159 - unsyncedheads: the list of remote heads unknown locally.
160 """
160 """
161 cl = repo.changelog
161 cl = repo.changelog
162 headssum = {}
162 headssum = {}
163 # A. Create set of branches involved in the push.
163 # A. Create set of branches involved in the push.
164 branches = set(repo[n].branch() for n in outgoing.missing)
164 branches = set(repo[n].branch() for n in outgoing.missing)
165 remotemap = remote.branchmap()
165 remotemap = remote.branchmap()
166 newbranches = branches - set(remotemap)
166 newbranches = branches - set(remotemap)
167 branches.difference_update(newbranches)
167 branches.difference_update(newbranches)
168
168
169 # A. register remote heads
169 # A. register remote heads
170 remotebranches = set()
170 remotebranches = set()
171 for branch, heads in remote.branchmap().iteritems():
171 for branch, heads in remote.branchmap().iteritems():
172 remotebranches.add(branch)
172 remotebranches.add(branch)
173 known = []
173 known = []
174 unsynced = []
174 unsynced = []
175 knownnode = cl.hasnode # do not use nodemap until it is filtered
175 knownnode = cl.hasnode # do not use nodemap until it is filtered
176 for h in heads:
176 for h in heads:
177 if knownnode(h):
177 if knownnode(h):
178 known.append(h)
178 known.append(h)
179 else:
179 else:
180 unsynced.append(h)
180 unsynced.append(h)
181 headssum[branch] = (known, list(known), unsynced)
181 headssum[branch] = (known, list(known), unsynced)
182 # B. add new branch data
182 # B. add new branch data
183 missingctx = list(repo[n] for n in outgoing.missing)
183 missingctx = list(repo[n] for n in outgoing.missing)
184 touchedbranches = set()
184 touchedbranches = set()
185 for ctx in missingctx:
185 for ctx in missingctx:
186 branch = ctx.branch()
186 branch = ctx.branch()
187 touchedbranches.add(branch)
187 touchedbranches.add(branch)
188 if branch not in headssum:
188 if branch not in headssum:
189 headssum[branch] = (None, [], [])
189 headssum[branch] = (None, [], [])
190
190
191 # C drop data about untouched branches:
191 # C drop data about untouched branches:
192 for branch in remotebranches - touchedbranches:
192 for branch in remotebranches - touchedbranches:
193 del headssum[branch]
193 del headssum[branch]
194
194
195 # D. Update newmap with outgoing changes.
195 # D. Update newmap with outgoing changes.
196 # This will possibly add new heads and remove existing ones.
196 # This will possibly add new heads and remove existing ones.
197 newmap = branchmap.branchcache((branch, heads[1])
197 newmap = branchmap.branchcache((branch, heads[1])
198 for branch, heads in headssum.iteritems()
198 for branch, heads in headssum.iteritems()
199 if heads[0] is not None)
199 if heads[0] is not None)
200 newmap.update(repo, (ctx.rev() for ctx in missingctx))
200 newmap.update(repo, (ctx.rev() for ctx in missingctx))
201 for branch, newheads in newmap.iteritems():
201 for branch, newheads in newmap.iteritems():
202 headssum[branch][1][:] = newheads
202 headssum[branch][1][:] = newheads
203 return headssum
203 return headssum
204
204
205 def _oldheadssummary(repo, remoteheads, outgoing, inc=False):
205 def _oldheadssummary(repo, remoteheads, outgoing, inc=False):
206 """Compute branchmapsummary for repo without branchmap support"""
206 """Compute branchmapsummary for repo without branchmap support"""
207
207
208 # 1-4b. old servers: Check for new topological heads.
208 # 1-4b. old servers: Check for new topological heads.
209 # Construct {old,new}map with branch = None (topological branch).
209 # Construct {old,new}map with branch = None (topological branch).
210 # (code based on update)
210 # (code based on update)
211 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
211 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
212 oldheads = set(h for h in remoteheads if knownnode(h))
212 oldheads = set(h for h in remoteheads if knownnode(h))
213 # all nodes in outgoing.missing are children of either:
213 # all nodes in outgoing.missing are children of either:
214 # - an element of oldheads
214 # - an element of oldheads
215 # - another element of outgoing.missing
215 # - another element of outgoing.missing
216 # - nullrev
216 # - nullrev
217 # This explains why the new head are very simple to compute.
217 # This explains why the new head are very simple to compute.
218 r = repo.set('heads(%ln + %ln)', oldheads, outgoing.missing)
218 r = repo.set('heads(%ln + %ln)', oldheads, outgoing.missing)
219 newheads = list(c.node() for c in r)
219 newheads = list(c.node() for c in r)
220 # set some unsynced head to issue the "unsynced changes" warning
220 # set some unsynced head to issue the "unsynced changes" warning
221 if inc:
221 if inc:
222 unsynced = set([None])
222 unsynced = set([None])
223 else:
223 else:
224 unsynced = set()
224 unsynced = set()
225 return {None: (oldheads, newheads, unsynced)}
225 return {None: (oldheads, newheads, unsynced)}
226
226
227 def checkheads(repo, remote, outgoing, remoteheads, newbranch=False, inc=False,
227 def checkheads(repo, remote, outgoing, remoteheads, newbranch=False, inc=False,
228 newbookmarks=[]):
228 newbookmarks=[]):
229 """Check that a push won't add any outgoing head
229 """Check that a push won't add any outgoing head
230
230
231 raise Abort error and display ui message as needed.
231 raise Abort error and display ui message as needed.
232 """
232 """
233 # Check for each named branch if we're creating new remote heads.
233 # Check for each named branch if we're creating new remote heads.
234 # To be a remote head after push, node must be either:
234 # To be a remote head after push, node must be either:
235 # - unknown locally
235 # - unknown locally
236 # - a local outgoing head descended from update
236 # - a local outgoing head descended from update
237 # - a remote head that's known locally and not
237 # - a remote head that's known locally and not
238 # ancestral to an outgoing head
238 # ancestral to an outgoing head
239 if remoteheads == [nullid]:
239 if remoteheads == [nullid]:
240 # remote is empty, nothing to check.
240 # remote is empty, nothing to check.
241 return
241 return
242
242
243 if remote.capable('branchmap'):
243 if remote.capable('branchmap'):
244 headssum = _headssummary(repo, remote, outgoing)
244 headssum = _headssummary(repo, remote, outgoing)
245 else:
245 else:
246 headssum = _oldheadssummary(repo, remoteheads, outgoing, inc)
246 headssum = _oldheadssummary(repo, remoteheads, outgoing, inc)
247 newbranches = [branch for branch, heads in headssum.iteritems()
247 newbranches = [branch for branch, heads in headssum.iteritems()
248 if heads[0] is None]
248 if heads[0] is None]
249 # 1. Check for new branches on the remote.
249 # 1. Check for new branches on the remote.
250 if newbranches and not newbranch: # new branch requires --new-branch
250 if newbranches and not newbranch: # new branch requires --new-branch
251 branchnames = ', '.join(sorted(newbranches))
251 branchnames = ', '.join(sorted(newbranches))
252 raise util.Abort(_("push creates new remote branches: %s!")
252 raise util.Abort(_("push creates new remote branches: %s!")
253 % branchnames,
253 % branchnames,
254 hint=_("use 'hg push --new-branch' to create"
254 hint=_("use 'hg push --new-branch' to create"
255 " new remote branches"))
255 " new remote branches"))
256
256
257 # 2. Compute newly pushed bookmarks. We don't warn about bookmarked heads.
257 # 2. Compute newly pushed bookmarks. We don't warn about bookmarked heads.
258 localbookmarks = repo._bookmarks
258 localbookmarks = repo._bookmarks
259 remotebookmarks = remote.listkeys('bookmarks')
259 remotebookmarks = remote.listkeys('bookmarks')
260 bookmarkedheads = set()
260 bookmarkedheads = set()
261 for bm in localbookmarks:
261 for bm in localbookmarks:
262 rnode = remotebookmarks.get(bm)
262 rnode = remotebookmarks.get(bm)
263 if rnode and rnode in repo:
263 if rnode and rnode in repo:
264 lctx, rctx = repo[bm], repo[rnode]
264 lctx, rctx = repo[bm], repo[rnode]
265 if bookmarks.validdest(repo, rctx, lctx):
265 if bookmarks.validdest(repo, rctx, lctx):
266 bookmarkedheads.add(lctx.node())
266 bookmarkedheads.add(lctx.node())
267 else:
267 else:
268 if bm in newbookmarks:
268 if bm in newbookmarks:
269 bookmarkedheads.add(repo[bm].node())
269 bookmarkedheads.add(repo[bm].node())
270
270
271 # 3. Check for new heads.
271 # 3. Check for new heads.
272 # If there are more heads after the push than before, a suitable
272 # If there are more heads after the push than before, a suitable
273 # error message, depending on unsynced status, is displayed.
273 # error message, depending on unsynced status, is displayed.
274 error = None
274 error = None
275 allmissing = set(outgoing.missing)
275 # If there is no obsstore, allfuturecommon won't be used, so no
276 allfuturecommon = set(c.node() for c in repo.set('%ld', outgoing.common))
276 # need to compute it.
277 allfuturecommon.update(allmissing)
277 if repo.obsstore:
278 allmissing = set(outgoing.missing)
279 cctx = repo.set('%ld', outgoing.common)
280 allfuturecommon = set(c.node() for c in cctx)
281 allfuturecommon.update(allmissing)
278 for branch, heads in sorted(headssum.iteritems()):
282 for branch, heads in sorted(headssum.iteritems()):
279 remoteheads, newheads, unsyncedheads = heads
283 remoteheads, newheads, unsyncedheads = heads
280 candidate_newhs = set(newheads)
284 candidate_newhs = set(newheads)
281 # add unsynced data
285 # add unsynced data
282 if remoteheads is None:
286 if remoteheads is None:
283 oldhs = set()
287 oldhs = set()
284 else:
288 else:
285 oldhs = set(remoteheads)
289 oldhs = set(remoteheads)
286 oldhs.update(unsyncedheads)
290 oldhs.update(unsyncedheads)
287 candidate_newhs.update(unsyncedheads)
291 candidate_newhs.update(unsyncedheads)
288 dhs = None # delta heads, the new heads on branch
292 dhs = None # delta heads, the new heads on branch
289 discardedheads = set()
293 discardedheads = set()
290 if repo.obsstore:
294 if repo.obsstore:
291 # remove future heads which are actually obsoleted by another
295 # remove future heads which are actually obsoleted by another
292 # pushed element:
296 # pushed element:
293 #
297 #
294 # XXX as above, There are several cases this case does not handle
298 # XXX as above, There are several cases this case does not handle
295 # XXX properly
299 # XXX properly
296 #
300 #
297 # (1) if <nh> is public, it won't be affected by obsolete marker
301 # (1) if <nh> is public, it won't be affected by obsolete marker
298 # and a new is created
302 # and a new is created
299 #
303 #
300 # (2) if the new heads have ancestors which are not obsolete and
304 # (2) if the new heads have ancestors which are not obsolete and
301 # not ancestors of any other heads we will have a new head too.
305 # not ancestors of any other heads we will have a new head too.
302 #
306 #
303 # These two cases will be easy to handle for known changeset but
307 # These two cases will be easy to handle for known changeset but
304 # much more tricky for unsynced changes.
308 # much more tricky for unsynced changes.
305 newhs = set()
309 newhs = set()
306 for nh in candidate_newhs:
310 for nh in candidate_newhs:
307 if nh in repo and repo[nh].phase() <= phases.public:
311 if nh in repo and repo[nh].phase() <= phases.public:
308 newhs.add(nh)
312 newhs.add(nh)
309 else:
313 else:
310 for suc in obsolete.allsuccessors(repo.obsstore, [nh]):
314 for suc in obsolete.allsuccessors(repo.obsstore, [nh]):
311 if suc != nh and suc in allfuturecommon:
315 if suc != nh and suc in allfuturecommon:
312 discardedheads.add(nh)
316 discardedheads.add(nh)
313 break
317 break
314 else:
318 else:
315 newhs.add(nh)
319 newhs.add(nh)
316 else:
320 else:
317 newhs = candidate_newhs
321 newhs = candidate_newhs
318 unsynced = sorted(h for h in unsyncedheads if h not in discardedheads)
322 unsynced = sorted(h for h in unsyncedheads if h not in discardedheads)
319 if unsynced:
323 if unsynced:
320 if None in unsynced:
324 if None in unsynced:
321 # old remote, no heads data
325 # old remote, no heads data
322 heads = None
326 heads = None
323 elif len(unsynced) <= 4 or repo.ui.verbose:
327 elif len(unsynced) <= 4 or repo.ui.verbose:
324 heads = ' '.join(short(h) for h in unsynced)
328 heads = ' '.join(short(h) for h in unsynced)
325 else:
329 else:
326 heads = (' '.join(short(h) for h in unsynced[:4]) +
330 heads = (' '.join(short(h) for h in unsynced[:4]) +
327 ' ' + _("and %s others") % (len(unsynced) - 4))
331 ' ' + _("and %s others") % (len(unsynced) - 4))
328 if heads is None:
332 if heads is None:
329 repo.ui.status(_("remote has heads that are "
333 repo.ui.status(_("remote has heads that are "
330 "not known locally\n"))
334 "not known locally\n"))
331 elif branch is None:
335 elif branch is None:
332 repo.ui.status(_("remote has heads that are "
336 repo.ui.status(_("remote has heads that are "
333 "not known locally: %s\n") % heads)
337 "not known locally: %s\n") % heads)
334 else:
338 else:
335 repo.ui.status(_("remote has heads on branch '%s' that are "
339 repo.ui.status(_("remote has heads on branch '%s' that are "
336 "not known locally: %s\n") % (branch, heads))
340 "not known locally: %s\n") % (branch, heads))
337 if remoteheads is None:
341 if remoteheads is None:
338 if len(newhs) > 1:
342 if len(newhs) > 1:
339 dhs = list(newhs)
343 dhs = list(newhs)
340 if error is None:
344 if error is None:
341 error = (_("push creates new branch '%s' "
345 error = (_("push creates new branch '%s' "
342 "with multiple heads") % (branch))
346 "with multiple heads") % (branch))
343 hint = _("merge or"
347 hint = _("merge or"
344 " see \"hg help push\" for details about"
348 " see \"hg help push\" for details about"
345 " pushing new heads")
349 " pushing new heads")
346 elif len(newhs) > len(oldhs):
350 elif len(newhs) > len(oldhs):
347 # remove bookmarked or existing remote heads from the new heads list
351 # remove bookmarked or existing remote heads from the new heads list
348 dhs = sorted(newhs - bookmarkedheads - oldhs)
352 dhs = sorted(newhs - bookmarkedheads - oldhs)
349 if dhs:
353 if dhs:
350 if error is None:
354 if error is None:
351 if branch not in ('default', None):
355 if branch not in ('default', None):
352 error = _("push creates new remote head %s "
356 error = _("push creates new remote head %s "
353 "on branch '%s'!") % (short(dhs[0]), branch)
357 "on branch '%s'!") % (short(dhs[0]), branch)
354 elif repo[dhs[0]].bookmarks():
358 elif repo[dhs[0]].bookmarks():
355 error = _("push creates new remote head %s "
359 error = _("push creates new remote head %s "
356 "with bookmark '%s'!") % (
360 "with bookmark '%s'!") % (
357 short(dhs[0]), repo[dhs[0]].bookmarks()[0])
361 short(dhs[0]), repo[dhs[0]].bookmarks()[0])
358 else:
362 else:
359 error = _("push creates new remote head %s!"
363 error = _("push creates new remote head %s!"
360 ) % short(dhs[0])
364 ) % short(dhs[0])
361 if unsyncedheads:
365 if unsyncedheads:
362 hint = _("pull and merge or"
366 hint = _("pull and merge or"
363 " see \"hg help push\" for details about"
367 " see \"hg help push\" for details about"
364 " pushing new heads")
368 " pushing new heads")
365 else:
369 else:
366 hint = _("merge or"
370 hint = _("merge or"
367 " see \"hg help push\" for details about"
371 " see \"hg help push\" for details about"
368 " pushing new heads")
372 " pushing new heads")
369 if branch is None:
373 if branch is None:
370 repo.ui.note(_("new remote heads:\n"))
374 repo.ui.note(_("new remote heads:\n"))
371 else:
375 else:
372 repo.ui.note(_("new remote heads on branch '%s':\n") % branch)
376 repo.ui.note(_("new remote heads on branch '%s':\n") % branch)
373 for h in dhs:
377 for h in dhs:
374 repo.ui.note((" %s\n") % short(h))
378 repo.ui.note((" %s\n") % short(h))
375 if error:
379 if error:
376 raise util.Abort(error, hint=hint)
380 raise util.Abort(error, hint=hint)
General Comments 0
You need to be logged in to leave comments. Login now