##// END OF EJS Templates
discovery: Fix error print mentioning a 'None' branch...
Pierre-Yves David -
r15292:7fa47124 stable
parent child Browse files
Show More
@@ -1,191 +1,192 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
10 import util, setdiscovery, treediscovery
11
11
12 def findcommonincoming(repo, remote, heads=None, force=False):
12 def findcommonincoming(repo, remote, heads=None, force=False):
13 """Return a tuple (common, anyincoming, heads) used to identify the common
13 """Return a tuple (common, anyincoming, heads) used to identify the common
14 subset of nodes between repo and remote.
14 subset of nodes between repo and remote.
15
15
16 "common" is a list of (at least) the heads of the common subset.
16 "common" is a list of (at least) the heads of the common subset.
17 "anyincoming" is testable as a boolean indicating if any nodes are missing
17 "anyincoming" is testable as a boolean indicating if any nodes are missing
18 locally. If remote does not support getbundle, this actually is a list of
18 locally. If remote does not support getbundle, this actually is a list of
19 roots of the nodes that would be incoming, to be supplied to
19 roots of the nodes that would be incoming, to be supplied to
20 changegroupsubset. No code except for pull should be relying on this fact
20 changegroupsubset. No code except for pull should be relying on this fact
21 any longer.
21 any longer.
22 "heads" is either the supplied heads, or else the remote's heads.
22 "heads" is either the supplied heads, or else the remote's heads.
23
23
24 If you pass heads and they are all known locally, the reponse lists justs
24 If you pass heads and they are all known locally, the reponse lists justs
25 these heads in "common" and in "heads".
25 these heads in "common" and in "heads".
26
26
27 Please use findcommonoutgoing to compute the set of outgoing nodes to give
27 Please use findcommonoutgoing to compute the set of outgoing nodes to give
28 extensions a good hook into outgoing.
28 extensions a good hook into outgoing.
29 """
29 """
30
30
31 if not remote.capable('getbundle'):
31 if not remote.capable('getbundle'):
32 return treediscovery.findcommonincoming(repo, remote, heads, force)
32 return treediscovery.findcommonincoming(repo, remote, heads, force)
33
33
34 if heads:
34 if heads:
35 allknown = True
35 allknown = True
36 nm = repo.changelog.nodemap
36 nm = repo.changelog.nodemap
37 for h in heads:
37 for h in heads:
38 if nm.get(h) is None:
38 if nm.get(h) is None:
39 allknown = False
39 allknown = False
40 break
40 break
41 if allknown:
41 if allknown:
42 return (heads, False, heads)
42 return (heads, False, heads)
43
43
44 res = setdiscovery.findcommonheads(repo.ui, repo, remote,
44 res = setdiscovery.findcommonheads(repo.ui, repo, remote,
45 abortwhenunrelated=not force)
45 abortwhenunrelated=not force)
46 common, anyinc, srvheads = res
46 common, anyinc, srvheads = res
47 return (list(common), anyinc, heads or list(srvheads))
47 return (list(common), anyinc, heads or list(srvheads))
48
48
49 def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
49 def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
50 '''Return a tuple (common, anyoutgoing, heads) used to identify the set
50 '''Return a tuple (common, anyoutgoing, heads) used to identify the set
51 of nodes present in repo but not in other.
51 of nodes present in repo but not in other.
52
52
53 If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
53 If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
54 are included. If you already know the local repo's heads, passing them in
54 are included. If you already know the local repo's heads, passing them in
55 onlyheads is faster than letting them be recomputed here.
55 onlyheads is faster than letting them be recomputed here.
56
56
57 If commoninc is given, it must the the result of a prior call to
57 If commoninc is given, it must the the result of a prior call to
58 findcommonincoming(repo, other, force) to avoid recomputing it here.
58 findcommonincoming(repo, other, force) to avoid recomputing it here.
59
59
60 The returned tuple is meant to be passed to changelog.findmissing.'''
60 The returned tuple is meant to be passed to changelog.findmissing.'''
61 common, _any, _hds = commoninc or findcommonincoming(repo, other, force=force)
61 common, _any, _hds = commoninc or findcommonincoming(repo, other, force=force)
62 return (common, onlyheads or repo.heads())
62 return (common, onlyheads or repo.heads())
63
63
64 def prepush(repo, remote, force, revs, newbranch):
64 def prepush(repo, remote, force, revs, newbranch):
65 '''Analyze the local and remote repositories and determine which
65 '''Analyze the local and remote repositories and determine which
66 changesets need to be pushed to the remote. Return value depends
66 changesets need to be pushed to the remote. Return value depends
67 on circumstances:
67 on circumstances:
68
68
69 If we are not going to push anything, return a tuple (None,
69 If we are not going to push anything, return a tuple (None,
70 outgoing) where outgoing is 0 if there are no outgoing
70 outgoing) where outgoing is 0 if there are no outgoing
71 changesets and 1 if there are, but we refuse to push them
71 changesets and 1 if there are, but we refuse to push them
72 (e.g. would create new remote heads).
72 (e.g. would create new remote heads).
73
73
74 Otherwise, return a tuple (changegroup, remoteheads), where
74 Otherwise, return a tuple (changegroup, remoteheads), where
75 changegroup is a readable file-like object whose read() returns
75 changegroup is a readable file-like object whose read() returns
76 successive changegroup chunks ready to be sent over the wire and
76 successive changegroup chunks ready to be sent over the wire and
77 remoteheads is the list of remote heads.'''
77 remoteheads is the list of remote heads.'''
78 commoninc = findcommonincoming(repo, remote, force=force)
78 commoninc = findcommonincoming(repo, remote, force=force)
79 common, revs = findcommonoutgoing(repo, remote, onlyheads=revs,
79 common, revs = findcommonoutgoing(repo, remote, onlyheads=revs,
80 commoninc=commoninc, force=force)
80 commoninc=commoninc, force=force)
81 _common, inc, remoteheads = commoninc
81 _common, inc, remoteheads = commoninc
82
82
83 cl = repo.changelog
83 cl = repo.changelog
84 outg = cl.findmissing(common, revs)
84 outg = cl.findmissing(common, revs)
85
85
86 if not outg:
86 if not outg:
87 repo.ui.status(_("no changes found\n"))
87 repo.ui.status(_("no changes found\n"))
88 return None, 1
88 return None, 1
89
89
90 if not force and remoteheads != [nullid]:
90 if not force and remoteheads != [nullid]:
91 if remote.capable('branchmap'):
91 if remote.capable('branchmap'):
92 # Check for each named branch if we're creating new remote heads.
92 # Check for each named branch if we're creating new remote heads.
93 # To be a remote head after push, node must be either:
93 # To be a remote head after push, node must be either:
94 # - unknown locally
94 # - unknown locally
95 # - a local outgoing head descended from update
95 # - a local outgoing head descended from update
96 # - a remote head that's known locally and not
96 # - a remote head that's known locally and not
97 # ancestral to an outgoing head
97 # ancestral to an outgoing head
98
98
99 # 1. Create set of branches involved in the push.
99 # 1. Create set of branches involved in the push.
100 branches = set(repo[n].branch() for n in outg)
100 branches = set(repo[n].branch() for n in outg)
101
101
102 # 2. Check for new branches on the remote.
102 # 2. Check for new branches on the remote.
103 remotemap = remote.branchmap()
103 remotemap = remote.branchmap()
104 newbranches = branches - set(remotemap)
104 newbranches = branches - set(remotemap)
105 if newbranches and not newbranch: # new branch requires --new-branch
105 if newbranches and not newbranch: # new branch requires --new-branch
106 branchnames = ', '.join(sorted(newbranches))
106 branchnames = ', '.join(sorted(newbranches))
107 raise util.Abort(_("push creates new remote branches: %s!")
107 raise util.Abort(_("push creates new remote branches: %s!")
108 % branchnames,
108 % branchnames,
109 hint=_("use 'hg push --new-branch' to create"
109 hint=_("use 'hg push --new-branch' to create"
110 " new remote branches"))
110 " new remote branches"))
111 branches.difference_update(newbranches)
111 branches.difference_update(newbranches)
112
112
113 # 3. Construct the initial oldmap and newmap dicts.
113 # 3. Construct the initial oldmap and newmap dicts.
114 # They contain information about the remote heads before and
114 # They contain information about the remote heads before and
115 # after the push, respectively.
115 # after the push, respectively.
116 # Heads not found locally are not included in either dict,
116 # Heads not found locally are not included in either dict,
117 # since they won't be affected by the push.
117 # since they won't be affected by the push.
118 # unsynced contains all branches with incoming changesets.
118 # unsynced contains all branches with incoming changesets.
119 oldmap = {}
119 oldmap = {}
120 newmap = {}
120 newmap = {}
121 unsynced = set()
121 unsynced = set()
122 for branch in branches:
122 for branch in branches:
123 remotebrheads = remotemap[branch]
123 remotebrheads = remotemap[branch]
124 prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
124 prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
125 oldmap[branch] = prunedbrheads
125 oldmap[branch] = prunedbrheads
126 newmap[branch] = list(prunedbrheads)
126 newmap[branch] = list(prunedbrheads)
127 if len(remotebrheads) > len(prunedbrheads):
127 if len(remotebrheads) > len(prunedbrheads):
128 unsynced.add(branch)
128 unsynced.add(branch)
129
129
130 # 4. Update newmap with outgoing changes.
130 # 4. Update newmap with outgoing changes.
131 # This will possibly add new heads and remove existing ones.
131 # This will possibly add new heads and remove existing ones.
132 ctxgen = (repo[n] for n in outg)
132 ctxgen = (repo[n] for n in outg)
133 repo._updatebranchcache(newmap, ctxgen)
133 repo._updatebranchcache(newmap, ctxgen)
134
134
135 else:
135 else:
136 # 1-4b. old servers: Check for new topological heads.
136 # 1-4b. old servers: Check for new topological heads.
137 # Construct {old,new}map with branch = None (topological branch).
137 # Construct {old,new}map with branch = None (topological branch).
138 # (code based on _updatebranchcache)
138 # (code based on _updatebranchcache)
139 oldheads = set(h for h in remoteheads if h in cl.nodemap)
139 oldheads = set(h for h in remoteheads if h in cl.nodemap)
140 newheads = oldheads.union(outg)
140 newheads = oldheads.union(outg)
141 if len(newheads) > 1:
141 if len(newheads) > 1:
142 for latest in reversed(outg):
142 for latest in reversed(outg):
143 if latest not in newheads:
143 if latest not in newheads:
144 continue
144 continue
145 minhrev = min(cl.rev(h) for h in newheads)
145 minhrev = min(cl.rev(h) for h in newheads)
146 reachable = cl.reachable(latest, cl.node(minhrev))
146 reachable = cl.reachable(latest, cl.node(minhrev))
147 reachable.remove(latest)
147 reachable.remove(latest)
148 newheads.difference_update(reachable)
148 newheads.difference_update(reachable)
149 branches = set([None])
149 branches = set([None])
150 newmap = {None: newheads}
150 newmap = {None: newheads}
151 oldmap = {None: oldheads}
151 oldmap = {None: oldheads}
152 unsynced = inc and branches or set()
152 unsynced = inc and branches or set()
153
153
154 # 5. Check for new heads.
154 # 5. Check for new heads.
155 # If there are more heads after the push than before, a suitable
155 # If there are more heads after the push than before, a suitable
156 # error message, depending on unsynced status, is displayed.
156 # error message, depending on unsynced status, is displayed.
157 error = None
157 error = None
158 for branch in branches:
158 for branch in branches:
159 newhs = set(newmap[branch])
159 newhs = set(newmap[branch])
160 oldhs = set(oldmap[branch])
160 oldhs = set(oldmap[branch])
161 if len(newhs) > len(oldhs):
161 if len(newhs) > len(oldhs):
162 dhs = list(newhs - oldhs)
162 dhs = list(newhs - oldhs)
163 if error is None:
163 if error is None:
164 if branch != 'default':
164 if branch not in ('default', None):
165 error = _("push creates new remote head %s "
165 error = _("push creates new remote head %s "
166 "on branch '%s'!") % (short(dhs[0]), branch)
166 "on branch '%s'!") % (short(dhs[0]), branch)
167 else:
167 else:
168 error = _("push creates new remote head %s!"
168 error = _("push creates new remote head %s!"
169 ) % short(dhs[0])
169 ) % short(dhs[0])
170 if branch in unsynced:
170 if branch in unsynced:
171 hint = _("you should pull and merge or "
171 hint = _("you should pull and merge or "
172 "use push -f to force")
172 "use push -f to force")
173 else:
173 else:
174 hint = _("did you forget to merge? "
174 hint = _("did you forget to merge? "
175 "use push -f to force")
175 "use push -f to force")
176 repo.ui.note("new remote heads on branch '%s'\n" % branch)
176 if branch is not None:
177 repo.ui.note("new remote heads on branch '%s'\n" % branch)
177 for h in dhs:
178 for h in dhs:
178 repo.ui.note("new remote head %s\n" % short(h))
179 repo.ui.note("new remote head %s\n" % short(h))
179 if error:
180 if error:
180 raise util.Abort(error, hint=hint)
181 raise util.Abort(error, hint=hint)
181
182
182 # 6. Check for unsynced changes on involved branches.
183 # 6. Check for unsynced changes on involved branches.
183 if unsynced:
184 if unsynced:
184 repo.ui.warn(_("note: unsynced remote changes!\n"))
185 repo.ui.warn(_("note: unsynced remote changes!\n"))
185
186
186 if revs is None:
187 if revs is None:
187 # use the fast path, no race possible on push
188 # use the fast path, no race possible on push
188 cg = repo._changegroup(outg, 'push')
189 cg = repo._changegroup(outg, 'push')
189 else:
190 else:
190 cg = repo.getbundle('push', heads=revs, common=common)
191 cg = repo.getbundle('push', heads=revs, common=common)
191 return cg, remoteheads
192 return cg, remoteheads
General Comments 0
You need to be logged in to leave comments. Login now