##// END OF EJS Templates
phases: properly register excluded changeset when revision are specified...
Pierre-Yves David -
r15951:bd84fc0b stable
parent child Browse files
Show More
@@ -1,239 +1,239 b''
1 1 # discovery.py - protocol changeset discovery functions
2 2 #
3 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import nullid, short
9 9 from i18n import _
10 10 import util, setdiscovery, treediscovery, phases
11 11
12 12 def findcommonincoming(repo, remote, heads=None, force=False):
13 13 """Return a tuple (common, anyincoming, heads) used to identify the common
14 14 subset of nodes between repo and remote.
15 15
16 16 "common" is a list of (at least) the heads of the common subset.
17 17 "anyincoming" is testable as a boolean indicating if any nodes are missing
18 18 locally. If remote does not support getbundle, this actually is a list of
19 19 roots of the nodes that would be incoming, to be supplied to
20 20 changegroupsubset. No code except for pull should be relying on this fact
21 21 any longer.
22 22 "heads" is either the supplied heads, or else the remote's heads.
23 23
24 24 If you pass heads and they are all known locally, the reponse lists justs
25 25 these heads in "common" and in "heads".
26 26
27 27 Please use findcommonoutgoing to compute the set of outgoing nodes to give
28 28 extensions a good hook into outgoing.
29 29 """
30 30
31 31 if not remote.capable('getbundle'):
32 32 return treediscovery.findcommonincoming(repo, remote, heads, force)
33 33
34 34 if heads:
35 35 allknown = True
36 36 nm = repo.changelog.nodemap
37 37 for h in heads:
38 38 if nm.get(h) is None:
39 39 allknown = False
40 40 break
41 41 if allknown:
42 42 return (heads, False, heads)
43 43
44 44 res = setdiscovery.findcommonheads(repo.ui, repo, remote,
45 45 abortwhenunrelated=not force)
46 46 common, anyinc, srvheads = res
47 47 return (list(common), anyinc, heads or list(srvheads))
48 48
49 49 class outgoing(object):
50 50 '''Represents the set of nodes present in a local repo but not in a
51 51 (possibly) remote one.
52 52
53 53 Members:
54 54
55 55 missing is a list of all nodes present in local but not in remote.
56 56 common is a list of all nodes shared between the two repos.
57 57 excluded is the list of missing changeset that shouldn't be sent remotely.
58 58 missingheads is the list of heads of missing.
59 59 commonheads is the list of heads of common.
60 60
61 61 The sets are computed on demand from the heads, unless provided upfront
62 62 by discovery.'''
63 63
64 64 def __init__(self, revlog, commonheads, missingheads):
65 65 self.commonheads = commonheads
66 66 self.missingheads = missingheads
67 67 self._revlog = revlog
68 68 self._common = None
69 69 self._missing = None
70 70 self.excluded = []
71 71
72 72 def _computecommonmissing(self):
73 73 sets = self._revlog.findcommonmissing(self.commonheads,
74 74 self.missingheads)
75 75 self._common, self._missing = sets
76 76
77 77 @util.propertycache
78 78 def common(self):
79 79 if self._common is None:
80 80 self._computecommonmissing()
81 81 return self._common
82 82
83 83 @util.propertycache
84 84 def missing(self):
85 85 if self._missing is None:
86 86 self._computecommonmissing()
87 87 return self._missing
88 88
89 89 def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
90 90 '''Return an outgoing instance to identify the nodes present in repo but
91 91 not in other.
92 92
93 93 If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
94 94 are included. If you already know the local repo's heads, passing them in
95 95 onlyheads is faster than letting them be recomputed here.
96 96
97 97 If commoninc is given, it must the the result of a prior call to
98 98 findcommonincoming(repo, other, force) to avoid recomputing it here.'''
99 99 # declare an empty outgoing object to be filled later
100 100 og = outgoing(repo.changelog, None, None)
101 101
102 102 # get common set if not provided
103 103 if commoninc is None:
104 104 commoninc = findcommonincoming(repo, other, force=force)
105 105 og.commonheads, _any, _hds = commoninc
106 106
107 107 # compute outgoing
108 108 if not repo._phaseroots[phases.secret]:
109 109 og.missingheads = onlyheads or repo.heads()
110 110 elif onlyheads is None:
111 111 # use visible heads as it should be cached
112 112 og.missingheads = phases.visibleheads(repo)
113 113 og.excluded = [ctx.node() for ctx in repo.set('secret()')]
114 114 else:
115 115 # compute common, missing and exclude secret stuff
116 116 sets = repo.changelog.findcommonmissing(og.commonheads, onlyheads)
117 117 og._common, allmissing = sets
118 118 og._missing = missing = []
119 og._excluded = excluded = []
119 og.excluded = excluded = []
120 120 for node in allmissing:
121 121 if repo[node].phase() >= phases.secret:
122 122 excluded.append(node)
123 123 else:
124 124 missing.append(node)
125 125 if excluded:
126 126 # update missing heads
127 127 rset = repo.set('heads(%ln)', missing)
128 128 missingheads = [ctx.node() for ctx in rset]
129 129 else:
130 130 missingheads = onlyheads
131 131 og.missingheads = missingheads
132 132
133 133 return og
134 134
135 135 def checkheads(repo, remote, outgoing, remoteheads, newbranch=False):
136 136 """Check that a push won't add any outgoing head
137 137
138 138 raise Abort error and display ui message as needed.
139 139 """
140 140 if remoteheads == [nullid]:
141 141 # remote is empty, nothing to check.
142 142 return
143 143
144 144 cl = repo.changelog
145 145 if remote.capable('branchmap'):
146 146 # Check for each named branch if we're creating new remote heads.
147 147 # To be a remote head after push, node must be either:
148 148 # - unknown locally
149 149 # - a local outgoing head descended from update
150 150 # - a remote head that's known locally and not
151 151 # ancestral to an outgoing head
152 152
153 153 # 1. Create set of branches involved in the push.
154 154 branches = set(repo[n].branch() for n in outgoing.missing)
155 155
156 156 # 2. Check for new branches on the remote.
157 157 remotemap = remote.branchmap()
158 158 newbranches = branches - set(remotemap)
159 159 if newbranches and not newbranch: # new branch requires --new-branch
160 160 branchnames = ', '.join(sorted(newbranches))
161 161 raise util.Abort(_("push creates new remote branches: %s!")
162 162 % branchnames,
163 163 hint=_("use 'hg push --new-branch' to create"
164 164 " new remote branches"))
165 165 branches.difference_update(newbranches)
166 166
167 167 # 3. Construct the initial oldmap and newmap dicts.
168 168 # They contain information about the remote heads before and
169 169 # after the push, respectively.
170 170 # Heads not found locally are not included in either dict,
171 171 # since they won't be affected by the push.
172 172 # unsynced contains all branches with incoming changesets.
173 173 oldmap = {}
174 174 newmap = {}
175 175 unsynced = set()
176 176 for branch in branches:
177 177 remotebrheads = remotemap[branch]
178 178 prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
179 179 oldmap[branch] = prunedbrheads
180 180 newmap[branch] = list(prunedbrheads)
181 181 if len(remotebrheads) > len(prunedbrheads):
182 182 unsynced.add(branch)
183 183
184 184 # 4. Update newmap with outgoing changes.
185 185 # This will possibly add new heads and remove existing ones.
186 186 ctxgen = (repo[n] for n in outgoing.missing)
187 187 repo._updatebranchcache(newmap, ctxgen)
188 188
189 189 else:
190 190 # 1-4b. old servers: Check for new topological heads.
191 191 # Construct {old,new}map with branch = None (topological branch).
192 192 # (code based on _updatebranchcache)
193 193 oldheads = set(h for h in remoteheads if h in cl.nodemap)
194 194 newheads = oldheads.union(outg)
195 195 if len(newheads) > 1:
196 196 for latest in reversed(outg):
197 197 if latest not in newheads:
198 198 continue
199 199 minhrev = min(cl.rev(h) for h in newheads)
200 200 reachable = cl.reachable(latest, cl.node(minhrev))
201 201 reachable.remove(latest)
202 202 newheads.difference_update(reachable)
203 203 branches = set([None])
204 204 newmap = {None: newheads}
205 205 oldmap = {None: oldheads}
206 206 unsynced = inc and branches or set()
207 207
208 208 # 5. Check for new heads.
209 209 # If there are more heads after the push than before, a suitable
210 210 # error message, depending on unsynced status, is displayed.
211 211 error = None
212 212 for branch in branches:
213 213 newhs = set(newmap[branch])
214 214 oldhs = set(oldmap[branch])
215 215 if len(newhs) > len(oldhs):
216 216 dhs = list(newhs - oldhs)
217 217 if error is None:
218 218 if branch not in ('default', None):
219 219 error = _("push creates new remote head %s "
220 220 "on branch '%s'!") % (short(dhs[0]), branch)
221 221 else:
222 222 error = _("push creates new remote head %s!"
223 223 ) % short(dhs[0])
224 224 if branch in unsynced:
225 225 hint = _("you should pull and merge or "
226 226 "use push -f to force")
227 227 else:
228 228 hint = _("did you forget to merge? "
229 229 "use push -f to force")
230 230 if branch is not None:
231 231 repo.ui.note(_("new remote heads on branch '%s'\n") % branch)
232 232 for h in dhs:
233 233 repo.ui.note(_("new remote head %s\n") % short(h))
234 234 if error:
235 235 raise util.Abort(error, hint=hint)
236 236
237 237 # 6. Check for unsynced changes on involved branches.
238 238 if unsynced:
239 239 repo.ui.warn(_("note: unsynced remote changes!\n"))
@@ -1,182 +1,182 b''
1 1 $ echo '[extensions]' >> $HGRCPATH
2 2 $ echo 'mq =' >> $HGRCPATH
3 3
4 4 $ hg init repo
5 5 $ cd repo
6 6
7 7 $ echo foo > foo
8 8 $ hg ci -qAm 'add a file'
9 9
10 10 $ hg qinit
11 11
12 12 $ hg qnew foo
13 13 $ echo foo >> foo
14 14 $ hg qrefresh -m 'append foo'
15 15
16 16 $ hg qnew bar
17 17 $ echo bar >> foo
18 18 $ hg qrefresh -m 'append bar'
19 19
20 20
21 21 try to commit on top of a patch
22 22
23 23 $ echo quux >> foo
24 24 $ hg ci -m 'append quux'
25 25 abort: cannot commit over an applied mq patch
26 26 [255]
27 27
28 28
29 29 cheat a bit...
30 30
31 31 $ mv .hg/patches .hg/patches2
32 32 $ hg ci -m 'append quux'
33 33 $ mv .hg/patches2 .hg/patches
34 34
35 35
36 36 qpop/qrefresh on the wrong revision
37 37
38 38 $ hg qpop
39 39 abort: popping would remove a revision not managed by this patch queue
40 40 [255]
41 41 $ hg qpop -n patches
42 42 using patch queue: $TESTTMP/repo/.hg/patches (glob)
43 43 abort: popping would remove a revision not managed by this patch queue
44 44 [255]
45 45 $ hg qrefresh
46 46 abort: working directory revision is not qtip
47 47 [255]
48 48
49 49 $ hg up -C qtip
50 50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 51 $ hg qpop
52 52 abort: popping would remove a revision not managed by this patch queue
53 53 [255]
54 54 $ hg qrefresh
55 55 abort: cannot refresh a revision with children
56 56 [255]
57 57 $ hg tip --template '{rev} {desc}\n'
58 58 3 append quux
59 59
60 60
61 61 qpush warning branchheads
62 62
63 63 $ cd ..
64 64 $ hg init branchy
65 65 $ cd branchy
66 66 $ echo q > q
67 67 $ hg add q
68 68 $ hg qnew -f qp
69 69 $ hg qpop
70 70 popping qp
71 71 patch queue now empty
72 72 $ echo a > a
73 73 $ hg ci -Ama
74 74 adding a
75 75 $ hg up null
76 76 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
77 77 $ hg branch b
78 78 marked working directory as branch b
79 79 (branches are permanent and global, did you want a bookmark?)
80 80 $ echo c > c
81 81 $ hg ci -Amc
82 82 adding c
83 83 $ hg merge default
84 84 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 85 (branch merge, don't forget to commit)
86 86 $ hg ci -mmerge
87 87 $ hg up default
88 88 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
89 89 $ hg log
90 90 changeset: 2:65309210bf4e
91 91 branch: b
92 92 tag: tip
93 93 parent: 1:707adb4c8ae1
94 94 parent: 0:cb9a9f314b8b
95 95 user: test
96 96 date: Thu Jan 01 00:00:00 1970 +0000
97 97 summary: merge
98 98
99 99 changeset: 1:707adb4c8ae1
100 100 branch: b
101 101 parent: -1:000000000000
102 102 user: test
103 103 date: Thu Jan 01 00:00:00 1970 +0000
104 104 summary: c
105 105
106 106 changeset: 0:cb9a9f314b8b
107 107 user: test
108 108 date: Thu Jan 01 00:00:00 1970 +0000
109 109 summary: a
110 110
111 111 $ hg qpush
112 112 applying qp
113 113 now at: qp
114 114
115 115 Testing applied patches, push and --force
116 116
117 117 $ cd ..
118 118 $ hg init forcepush
119 119 $ cd forcepush
120 120 $ echo a > a
121 121 $ hg ci -Am adda
122 122 adding a
123 123 $ echo a >> a
124 124 $ hg ci -m changea
125 125 $ hg up 0
126 126 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
127 127 $ hg branch branch
128 128 marked working directory as branch branch
129 129 (branches are permanent and global, did you want a bookmark?)
130 130 $ echo b > b
131 131 $ hg ci -Am addb
132 132 adding b
133 133 $ hg up 0
134 134 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
135 135 $ hg --cwd .. clone -r 0 forcepush forcepush2
136 136 adding changesets
137 137 adding manifests
138 138 adding file changes
139 139 added 1 changesets with 1 changes to 1 files
140 140 updating to branch default
141 141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 142 $ echo a >> a
143 143 $ hg qnew patch
144 144
145 145 Pushing applied patch with --rev without --force
146 146
147 147 $ hg push -r default ../forcepush2
148 148 pushing to ../forcepush2
149 149 abort: source has mq patches applied
150 150 [255]
151 151
152 152 Pushing applied patch with branchhash, without --force
153 153
154 154 $ hg push ../forcepush2#default
155 155 pushing to ../forcepush2
156 156 abort: source has mq patches applied
157 157 [255]
158 158
159 159 Pushing revs excluding applied patch
160 160
161 161 $ hg push --new-branch -r branch -r 2 ../forcepush2
162 162 pushing to ../forcepush2
163 163 searching for changes
164 164 adding changesets
165 165 adding manifests
166 166 adding file changes
167 167 added 1 changesets with 1 changes to 1 files
168 168
169 169 Pushing applied patch with --force
170 170
171 171 $ hg push --force -r default ../forcepush2
172 172 pushing to ../forcepush2
173 173 searching for changes
174 no changes found
174 no changes to push but 1 secret changesets
175 175 $ hg phase -d 'mq()'
176 176 $ hg push --force -r default ../forcepush2
177 177 pushing to ../forcepush2
178 178 searching for changes
179 179 adding changesets
180 180 adding manifests
181 181 adding file changes
182 182 added 1 changesets with 1 changes to 1 files (+1 heads)
General Comments 0
You need to be logged in to leave comments. Login now