##// END OF EJS Templates
checkheads: check successors for new heads in both missing and common...
Pierre-Yves David -
r17548:eaa5fcc5 default
parent child Browse files
Show More
@@ -1,372 +1,374 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, obsolete
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 response lists just
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,
90 90 commoninc=None, portable=False):
91 91 '''Return an outgoing instance to identify the nodes present in repo but
92 92 not in other.
93 93
94 94 If onlyheads is given, only nodes ancestral to nodes in onlyheads
95 95 (inclusive) are included. If you already know the local repo's heads,
96 96 passing them in onlyheads is faster than letting them be recomputed here.
97 97
98 98 If commoninc is given, it must be the result of a prior call to
99 99 findcommonincoming(repo, other, force) to avoid recomputing it here.
100 100
101 101 If portable is given, compute more conservative common and missingheads,
102 102 to make bundles created from the instance more portable.'''
103 103 # declare an empty outgoing object to be filled later
104 104 og = outgoing(repo.changelog, None, None)
105 105
106 106 # get common set if not provided
107 107 if commoninc is None:
108 108 commoninc = findcommonincoming(repo, other, force=force)
109 109 og.commonheads, _any, _hds = commoninc
110 110
111 111 # compute outgoing
112 112 mayexclude = (repo._phasecache.phaseroots[phases.secret] or repo.obsstore)
113 113 if not mayexclude:
114 114 og.missingheads = onlyheads or repo.heads()
115 115 elif onlyheads is None:
116 116 # use visible heads as it should be cached
117 117 og.missingheads = visibleheads(repo)
118 118 og.excluded = [ctx.node() for ctx in repo.set('secret() or extinct()')]
119 119 else:
120 120 # compute common, missing and exclude secret stuff
121 121 sets = repo.changelog.findcommonmissing(og.commonheads, onlyheads)
122 122 og._common, allmissing = sets
123 123 og._missing = missing = []
124 124 og.excluded = excluded = []
125 125 for node in allmissing:
126 126 ctx = repo[node]
127 127 if ctx.phase() >= phases.secret or ctx.extinct():
128 128 excluded.append(node)
129 129 else:
130 130 missing.append(node)
131 131 if len(missing) == len(allmissing):
132 132 missingheads = onlyheads
133 133 else: # update missing heads
134 134 missingheads = phases.newheads(repo, onlyheads, excluded)
135 135 og.missingheads = missingheads
136 136 if portable:
137 137 # recompute common and missingheads as if -r<rev> had been given for
138 138 # each head of missing, and --base <rev> for each head of the proper
139 139 # ancestors of missing
140 140 og._computecommonmissing()
141 141 cl = repo.changelog
142 142 missingrevs = set(cl.rev(n) for n in og._missing)
143 143 og._common = set(cl.ancestors(missingrevs)) - missingrevs
144 144 commonheads = set(og.commonheads)
145 145 og.missingheads = [h for h in og.missingheads if h not in commonheads]
146 146
147 147 return og
148 148
149 149 def _headssummary(repo, remote, outgoing):
150 150 """compute a summary of branch and heads status before and after push
151 151
152 152 return {'branch': ([remoteheads], [newheads], [unsyncedheads])} mapping
153 153
154 154 - branch: the branch name
155 155 - remoteheads: the list of remote heads known locally
156 156 None is the branch is new
157 157 - newheads: the new remote heads (known locally) with outgoing pushed
158 158 - unsyncedheads: the list of remote heads unknown locally.
159 159 """
160 160 cl = repo.changelog
161 161 headssum = {}
162 162 # A. Create set of branches involved in the push.
163 163 branches = set(repo[n].branch() for n in outgoing.missing)
164 164 remotemap = remote.branchmap()
165 165 newbranches = branches - set(remotemap)
166 166 branches.difference_update(newbranches)
167 167
168 168 # A. register remote heads
169 169 remotebranches = set()
170 170 for branch, heads in remote.branchmap().iteritems():
171 171 remotebranches.add(branch)
172 172 known = []
173 173 unsynced = []
174 174 for h in heads:
175 175 if h in cl.nodemap:
176 176 known.append(h)
177 177 else:
178 178 unsynced.append(h)
179 179 headssum[branch] = (known, list(known), unsynced)
180 180 # B. add new branch data
181 181 missingctx = list(repo[n] for n in outgoing.missing)
182 182 touchedbranches = set()
183 183 for ctx in missingctx:
184 184 branch = ctx.branch()
185 185 touchedbranches.add(branch)
186 186 if branch not in headssum:
187 187 headssum[branch] = (None, [], [])
188 188
189 189 # C drop data about untouched branches:
190 190 for branch in remotebranches - touchedbranches:
191 191 del headssum[branch]
192 192
193 193 # D. Update newmap with outgoing changes.
194 194 # This will possibly add new heads and remove existing ones.
195 195 newmap = dict((branch, heads[1]) for branch, heads in headssum.iteritems()
196 196 if heads[0] is not None)
197 197 repo._updatebranchcache(newmap, missingctx)
198 198 for branch, newheads in newmap.iteritems():
199 199 headssum[branch][1][:] = newheads
200 200 return headssum
201 201
202 202 def _oldheadssummary(repo, remoteheads, outgoing, inc=False):
203 203 """Compute branchmapsummary for repo without branchmap support"""
204 204
205 205 cl = repo.changelog
206 206 # 1-4b. old servers: Check for new topological heads.
207 207 # Construct {old,new}map with branch = None (topological branch).
208 208 # (code based on _updatebranchcache)
209 209 oldheads = set(h for h in remoteheads if h in cl.nodemap)
210 210 # all nodes in outgoing.missing are children of either:
211 211 # - an element of oldheads
212 212 # - another element of outgoing.missing
213 213 # - nullrev
214 214 # This explains why the new head are very simple to compute.
215 215 r = repo.set('heads(%ln + %ln)', oldheads, outgoing.missing)
216 216 newheads = list(c.node() for c in r)
217 217 unsynced = inc and set([None]) or set()
218 218 return {None: (oldheads, newheads, unsynced)}
219 219
220 220 def checkheads(repo, remote, outgoing, remoteheads, newbranch=False, inc=False):
221 221 """Check that a push won't add any outgoing head
222 222
223 223 raise Abort error and display ui message as needed.
224 224 """
225 225 # Check for each named branch if we're creating new remote heads.
226 226 # To be a remote head after push, node must be either:
227 227 # - unknown locally
228 228 # - a local outgoing head descended from update
229 229 # - a remote head that's known locally and not
230 230 # ancestral to an outgoing head
231 231 if remoteheads == [nullid]:
232 232 # remote is empty, nothing to check.
233 233 return
234 234
235 235 if remote.capable('branchmap'):
236 236 headssum = _headssummary(repo, remote, outgoing)
237 237 else:
238 238 headssum = _oldheadssummary(repo, remoteheads, outgoing, inc)
239 239 newbranches = [branch for branch, heads in headssum.iteritems()
240 240 if heads[0] is None]
241 241 # 1. Check for new branches on the remote.
242 242 if newbranches and not newbranch: # new branch requires --new-branch
243 243 branchnames = ', '.join(sorted(newbranches))
244 244 raise util.Abort(_("push creates new remote branches: %s!")
245 245 % branchnames,
246 246 hint=_("use 'hg push --new-branch' to create"
247 247 " new remote branches"))
248 248
249 249 # 2 compute newly pushed bookmarks. We
250 250 # we don't warned about bookmarked heads.
251 251 localbookmarks = repo._bookmarks
252 252 remotebookmarks = remote.listkeys('bookmarks')
253 253 bookmarkedheads = set()
254 254 for bm in localbookmarks:
255 255 rnode = remotebookmarks.get(bm)
256 256 if rnode and rnode in repo:
257 257 lctx, rctx = repo[bm], repo[rnode]
258 258 if rctx == lctx.ancestor(rctx):
259 259 bookmarkedheads.add(lctx.node())
260 260
261 261 # 3. Check for new heads.
262 262 # If there are more heads after the push than before, a suitable
263 263 # error message, depending on unsynced status, is displayed.
264 264 error = None
265 265 unsynced = False
266 266 allmissing = set(outgoing.missing)
267 allfuturecommon = set(c.node() for c in repo.set('%ld', outgoing.common))
268 allfuturecommon.update(allmissing)
267 269 for branch, heads in headssum.iteritems():
268 270 if heads[0] is None:
269 271 # Maybe we should abort if we push more that one head
270 272 # for new branches ?
271 273 continue
272 274 if heads[2]:
273 275 unsynced = True
274 276 oldhs = set(heads[0])
275 277 candidate_newhs = set(heads[1])
276 278 # add unsynced data
277 279 oldhs.update(heads[2])
278 280 candidate_newhs.update(heads[2])
279 281 dhs = None
280 282 if repo.obsstore:
281 283 # remove future heads which are actually obsolete by another
282 284 # pushed element:
283 285 #
284 286 # XXX There is several case this case does not handle properly
285 287 #
286 288 # (1) if <nh> is public, it won't be affected by obsolete marker
287 289 # and a new is created
288 290 #
289 291 # (2) if the new heads have ancestors which are not obsolete and
290 292 # not ancestors of any other heads we will have a new head too.
291 293 #
292 294 # This two case will be easy to handle for know changeset but much
293 295 # more tricky for unsynced changes.
294 296 newhs = set()
295 297 for nh in candidate_newhs:
296 if repo[nh].phase() <= phases.public:
298 if nh in repo and repo[nh].phase() <= phases.public:
297 299 newhs.add(nh)
298 300 else:
299 301 for suc in obsolete.anysuccessors(repo.obsstore, nh):
300 if suc != nh and suc in allmissing:
302 if suc != nh and suc in allfuturecommon:
301 303 break
302 304 else:
303 305 newhs.add(nh)
304 306 else:
305 307 newhs = candidate_newhs
306 308 if len(newhs) > len(oldhs):
307 309 # strip updates to existing remote heads from the new heads list
308 310 dhs = list(newhs - bookmarkedheads - oldhs)
309 311 if dhs:
310 312 if error is None:
311 313 if branch not in ('default', None):
312 314 error = _("push creates new remote head %s "
313 315 "on branch '%s'!") % (short(dhs[0]), branch)
314 316 else:
315 317 error = _("push creates new remote head %s!"
316 318 ) % short(dhs[0])
317 319 if heads[2]: # unsynced
318 320 hint = _("you should pull and merge or "
319 321 "use push -f to force")
320 322 else:
321 323 hint = _("did you forget to merge? "
322 324 "use push -f to force")
323 325 if branch is not None:
324 326 repo.ui.note(_("new remote heads on branch '%s'\n") % branch)
325 327 for h in dhs:
326 328 repo.ui.note(_("new remote head %s\n") % short(h))
327 329 if error:
328 330 raise util.Abort(error, hint=hint)
329 331
330 332 # 6. Check for unsynced changes on involved branches.
331 333 if unsynced:
332 334 repo.ui.warn(_("note: unsynced remote changes!\n"))
333 335
334 336 def visibleheads(repo):
335 337 """return the set of visible head of this repo"""
336 338 # XXX we want a cache on this
337 339 sroots = repo._phasecache.phaseroots[phases.secret]
338 340 if sroots or repo.obsstore:
339 341 # XXX very slow revset. storing heads or secret "boundary"
340 342 # would help.
341 343 revset = repo.set('heads(not (%ln:: + extinct()))', sroots)
342 344
343 345 vheads = [ctx.node() for ctx in revset]
344 346 if not vheads:
345 347 vheads.append(nullid)
346 348 else:
347 349 vheads = repo.heads()
348 350 return vheads
349 351
350 352
351 353 def visiblebranchmap(repo):
352 354 """return a branchmap for the visible set"""
353 355 # XXX Recomputing this data on the fly is very slow. We should build a
354 356 # XXX cached version while computing the standard branchmap version.
355 357 sroots = repo._phasecache.phaseroots[phases.secret]
356 358 if sroots or repo.obsstore:
357 359 vbranchmap = {}
358 360 for branch, nodes in repo.branchmap().iteritems():
359 361 # search for secret heads.
360 362 for n in nodes:
361 363 if repo[n].phase() >= phases.secret:
362 364 nodes = None
363 365 break
364 366 # if secret heads were found we must compute them again
365 367 if nodes is None:
366 368 s = repo.set('heads(branch(%s) - secret() - extinct())',
367 369 branch)
368 370 nodes = [c.node() for c in s]
369 371 vbranchmap[branch] = nodes
370 372 else:
371 373 vbranchmap = repo.branchmap()
372 374 return vbranchmap
@@ -1,298 +1,298 b''
1 1 $ "$TESTDIR/hghave" serve || exit 80
2 2
3 3 initialize
4 4
5 5 $ hg init a
6 6 $ cd a
7 7 $ echo 'test' > test
8 8 $ hg commit -Am'test'
9 9 adding test
10 10
11 11 set bookmarks
12 12
13 13 $ hg bookmark X
14 14 $ hg bookmark Y
15 15 $ hg bookmark Z
16 16
17 17 import bookmark by name
18 18
19 19 $ hg init ../b
20 20 $ cd ../b
21 21 $ hg book Y
22 22 $ hg book
23 23 * Y -1:000000000000
24 24 $ hg pull ../a
25 25 pulling from ../a
26 26 requesting all changes
27 27 adding changesets
28 28 adding manifests
29 29 adding file changes
30 30 added 1 changesets with 1 changes to 1 files
31 31 updating bookmark Y
32 32 adding remote bookmark X
33 33 adding remote bookmark Z
34 34 (run 'hg update' to get a working copy)
35 35 $ hg bookmarks
36 36 X 0:4e3505fd9583
37 37 Y 0:4e3505fd9583
38 38 Z 0:4e3505fd9583
39 39 $ hg debugpushkey ../a namespaces
40 40 bookmarks
41 41 phases
42 42 namespaces
43 43 $ hg debugpushkey ../a bookmarks
44 44 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
45 45 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
46 46 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
47 47 $ hg pull -B X ../a
48 48 pulling from ../a
49 49 no changes found
50 50 importing bookmark X
51 51 $ hg bookmark
52 52 X 0:4e3505fd9583
53 53 Y 0:4e3505fd9583
54 54 Z 0:4e3505fd9583
55 55
56 56 export bookmark by name
57 57
58 58 $ hg bookmark W
59 59 $ hg bookmark foo
60 60 $ hg bookmark foobar
61 61 $ hg push -B W ../a
62 62 pushing to ../a
63 63 searching for changes
64 64 no changes found
65 65 exporting bookmark W
66 66 [1]
67 67 $ hg -R ../a bookmarks
68 68 W -1:000000000000
69 69 X 0:4e3505fd9583
70 70 Y 0:4e3505fd9583
71 71 * Z 0:4e3505fd9583
72 72
73 73 delete a remote bookmark
74 74
75 75 $ hg book -d W
76 76 $ hg push -B W ../a
77 77 pushing to ../a
78 78 searching for changes
79 79 no changes found
80 80 deleting remote bookmark W
81 81 [1]
82 82
83 83 push/pull name that doesn't exist
84 84
85 85 $ hg push -B badname ../a
86 86 pushing to ../a
87 87 searching for changes
88 88 no changes found
89 89 bookmark badname does not exist on the local or remote repository!
90 90 [2]
91 91 $ hg pull -B anotherbadname ../a
92 92 pulling from ../a
93 93 abort: remote bookmark anotherbadname not found!
94 94 [255]
95 95
96 96 divergent bookmarks
97 97
98 98 $ cd ../a
99 99 $ echo c1 > f1
100 100 $ hg ci -Am1
101 101 adding f1
102 102 $ hg book -f X
103 103 $ hg book
104 104 * X 1:0d2164f0ce0d
105 105 Y 0:4e3505fd9583
106 106 Z 1:0d2164f0ce0d
107 107
108 108 $ cd ../b
109 109 $ hg up
110 110 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 111 updating bookmark foobar
112 112 $ echo c2 > f2
113 113 $ hg ci -Am2
114 114 adding f2
115 115 $ hg book -f X
116 116 $ hg book
117 117 * X 1:9b140be10808
118 118 Y 0:4e3505fd9583
119 119 Z 0:4e3505fd9583
120 120 foo -1:000000000000
121 121 foobar 1:9b140be10808
122 122
123 123 $ hg pull --config paths.foo=../a foo
124 124 pulling from $TESTTMP/a (glob)
125 125 searching for changes
126 126 adding changesets
127 127 adding manifests
128 128 adding file changes
129 129 added 1 changesets with 1 changes to 1 files (+1 heads)
130 130 divergent bookmark X stored as X@foo
131 131 updating bookmark Z
132 132 (run 'hg heads' to see heads, 'hg merge' to merge)
133 133 $ hg book
134 134 * X 1:9b140be10808
135 135 X@foo 2:0d2164f0ce0d
136 136 Y 0:4e3505fd9583
137 137 Z 2:0d2164f0ce0d
138 138 foo -1:000000000000
139 139 foobar 1:9b140be10808
140 140 $ hg push -f ../a
141 141 pushing to ../a
142 142 searching for changes
143 143 adding changesets
144 144 adding manifests
145 145 adding file changes
146 146 added 1 changesets with 1 changes to 1 files (+1 heads)
147 147 $ hg -R ../a book
148 148 * X 1:0d2164f0ce0d
149 149 Y 0:4e3505fd9583
150 150 Z 1:0d2164f0ce0d
151 151
152 152 update a remote bookmark from a non-head to a head
153 153
154 154 $ hg up -q Y
155 155 $ echo c3 > f2
156 156 $ hg ci -Am3
157 157 adding f2
158 158 created new head
159 159 $ hg push ../a
160 160 pushing to ../a
161 161 searching for changes
162 162 adding changesets
163 163 adding manifests
164 164 adding file changes
165 165 added 1 changesets with 1 changes to 1 files (+1 heads)
166 166 updating bookmark Y
167 167 $ hg -R ../a book
168 168 * X 1:0d2164f0ce0d
169 169 Y 3:f6fc62dde3c0
170 170 Z 1:0d2164f0ce0d
171 171
172 172 diverging a remote bookmark fails
173 173
174 174 $ hg up -q 4e3505fd9583
175 175 $ echo c4 > f2
176 176 $ hg ci -Am4
177 177 adding f2
178 178 created new head
179 179 $ hg book -f Y
180 180
181 181 $ cat <<EOF > ../a/.hg/hgrc
182 182 > [web]
183 183 > push_ssl = false
184 184 > allow_push = *
185 185 > EOF
186 186
187 187 $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid
188 188 $ cat ../hg2.pid >> $DAEMON_PIDS
189 189
190 190 $ hg push http://localhost:$HGPORT2/
191 191 pushing to http://localhost:$HGPORT2/
192 192 searching for changes
193 193 abort: push creates new remote head 4efff6d98829!
194 194 (did you forget to merge? use push -f to force)
195 195 [255]
196 196 $ hg -R ../a book
197 197 * X 1:0d2164f0ce0d
198 198 Y 3:f6fc62dde3c0
199 199 Z 1:0d2164f0ce0d
200 200
201 201 hgweb
202 202
203 203 $ cat <<EOF > .hg/hgrc
204 204 > [web]
205 205 > push_ssl = false
206 206 > allow_push = *
207 207 > EOF
208 208
209 209 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
210 210 $ cat ../hg.pid >> $DAEMON_PIDS
211 211 $ cd ../a
212 212
213 213 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
214 214 bookmarks
215 215 phases
216 216 namespaces
217 217 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
218 218 Y 4efff6d98829d9c824c621afd6e3f01865f5439f
219 219 foobar 9b140be1080824d768c5a4691a564088eede71f9
220 220 Z 0d2164f0ce0d8f1d6f94351eba04b794909be66c
221 221 foo 0000000000000000000000000000000000000000
222 222 X 9b140be1080824d768c5a4691a564088eede71f9
223 223 $ hg out -B http://localhost:$HGPORT/
224 224 comparing with http://localhost:$HGPORT/
225 225 searching for changed bookmarks
226 226 no changed bookmarks found
227 227 [1]
228 228 $ hg push -B Z http://localhost:$HGPORT/
229 229 pushing to http://localhost:$HGPORT/
230 230 searching for changes
231 231 no changes found
232 232 exporting bookmark Z
233 233 [1]
234 234 $ hg book -d Z
235 235 $ hg in -B http://localhost:$HGPORT/
236 236 comparing with http://localhost:$HGPORT/
237 237 searching for changed bookmarks
238 238 Z 0d2164f0ce0d
239 239 foo 000000000000
240 240 foobar 9b140be10808
241 241 $ hg pull -B Z http://localhost:$HGPORT/
242 242 pulling from http://localhost:$HGPORT/
243 243 no changes found
244 244 adding remote bookmark foobar
245 245 adding remote bookmark Z
246 246 adding remote bookmark foo
247 247 divergent bookmark X stored as X@1
248 248 importing bookmark Z
249 249 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
250 250 requesting all changes
251 251 adding changesets
252 252 adding manifests
253 253 adding file changes
254 254 added 5 changesets with 5 changes to 3 files (+3 heads)
255 255 updating to branch default
256 256 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 257 $ hg -R cloned-bookmarks bookmarks
258 258 X 1:9b140be10808
259 259 Y 4:4efff6d98829
260 260 Z 2:0d2164f0ce0d
261 261 foo -1:000000000000
262 262 foobar 1:9b140be10808
263 263
264 264 $ cd ..
265 265
266 266 Pushing a bookmark should only push the changes required by that
267 267 bookmark, not all outgoing changes:
268 268 $ hg clone http://localhost:$HGPORT/ addmarks
269 269 requesting all changes
270 270 adding changesets
271 271 adding manifests
272 272 adding file changes
273 273 added 5 changesets with 5 changes to 3 files (+3 heads)
274 274 updating to branch default
275 275 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 276 $ cd addmarks
277 277 $ echo foo > foo
278 278 $ hg add foo
279 279 $ hg commit -m 'add foo'
280 280 $ echo bar > bar
281 281 $ hg add bar
282 282 $ hg commit -m 'add bar'
283 283 $ hg co "tip^"
284 284 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
285 285 $ hg book add-foo
286 286 $ hg book -r tip add-bar
287 287 Note: this push *must* push only a single changeset, as that's the point
288 288 of this test.
289 $ hg push -B add-foo
289 $ hg push -B add-foo --traceback
290 290 pushing to http://localhost:$HGPORT/
291 291 searching for changes
292 292 remote: adding changesets
293 293 remote: adding manifests
294 294 remote: adding file changes
295 295 remote: added 1 changesets with 1 changes to 1 files
296 296 exporting bookmark add-foo
297 297
298 298 $ cd ..
@@ -1,161 +1,273 b''
1 1 Check that obsolete properly strip heads
2 2 $ cat > obs.py << EOF
3 3 > import mercurial.obsolete
4 4 > mercurial.obsolete._enabled = True
5 5 > EOF
6 6 $ cat >> $HGRCPATH << EOF
7 7 > [phases]
8 8 > # public changeset are not obsolete
9 9 > publish=false
10 10 > [ui]
11 11 > logtemplate='{node|short} ({phase}) {desc|firstline}\n'
12 12 > [extensions]
13 13 > graphlog=
14 14 > EOF
15 15 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
16 16 $ mkcommit() {
17 17 > echo "$1" > "$1"
18 18 > hg add "$1"
19 19 > hg ci -m "add $1"
20 20 > }
21 21 $ getid() {
22 22 > hg id --debug -ir "desc('$1')"
23 23 > }
24 24
25 25
26 26 $ hg init remote
27 27 $ cd remote
28 28 $ mkcommit base
29 29 $ hg phase --public .
30 30 $ cd ..
31 31 $ cp -r remote base
32 32 $ hg clone remote local
33 33 updating to branch default
34 34 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 35 $ cd local
36 36
37 37 New head replaces old head
38 38 ==========================
39 39
40 40 setup
41 41
42 42 $ mkcommit old
43 43 $ hg push
44 44 pushing to $TESTTMP/remote
45 45 searching for changes
46 46 adding changesets
47 47 adding manifests
48 48 adding file changes
49 49 added 1 changesets with 1 changes to 1 files
50 50 $ hg up -q '.^'
51 51 $ mkcommit new
52 52 created new head
53 53 $ hg debugobsolete `getid old` `getid new`
54 54 $ hg glog --hidden
55 55 @ 71e3228bffe1 (draft) add new
56 56 |
57 57 | x c70b08862e08 (draft) add old
58 58 |/
59 59 o b4952fcf48cf (public) add base
60 60
61 61 $ cp -r ../remote ../backup1
62 62
63 63 old exists remotely as draft. It is obsoleted by new that we now push.
64 64 Push should not warn about creating new head
65 65
66 66 $ hg push
67 67 pushing to $TESTTMP/remote
68 68 searching for changes
69 69 adding changesets
70 70 adding manifests
71 71 adding file changes
72 72 added 1 changesets with 1 changes to 1 files (+1 heads)
73 73
74 74 old head is now public (public local version)
75 75 =============================================
76 76
77 77 setup
78 78
79 79 $ rm -fr ../remote
80 80 $ cp -r ../backup1 ../remote
81 81 $ hg -R ../remote phase --public c70b08862e08
82 82 $ hg pull -v
83 83 pulling from $TESTTMP/remote
84 84 searching for changes
85 85 no changes found
86 86 $ hg glog --hidden
87 87 @ 71e3228bffe1 (draft) add new
88 88 |
89 89 | o c70b08862e08 (public) add old
90 90 |/
91 91 o b4952fcf48cf (public) add base
92 92
93 93
94 94 Abort: old will still be an head because it's public.
95 95
96 96 $ hg push
97 97 pushing to $TESTTMP/remote
98 98 searching for changes
99 99 abort: push creates new remote head 71e3228bffe1!
100 100 (did you forget to merge? use push -f to force)
101 101 [255]
102 102
103 103 old head is now public (public remote version)
104 104 ==============================================
105 105
106 106 TODO: Not implemented yet.
107 107
108 108 # setup
109 109 #
110 110 # $ rm -fr ../remote
111 111 # $ cp -r ../backup1 ../remote
112 112 # $ hg -R ../remote phase --public c70b08862e08
113 113 # $ hg phase --draft --force c70b08862e08
114 114 # $ hg glog --hidden
115 115 # @ 71e3228bffe1 (draft) add new
116 116 # |
117 117 # | x c70b08862e08 (draft) add old
118 118 # |/
119 119 # o b4952fcf48cf (public) add base
120 120 #
121 121 #
122 122 #
123 123 # Abort: old will still be an head because it's public.
124 124 #
125 125 # $ hg push
126 126 # pushing to $TESTTMP/remote
127 127 # searching for changes
128 128 # abort: push creates new remote head 71e3228bffe1!
129 129 # (did you forget to merge? use push -f to force)
130 130 # [255]
131 131
132 132 old head is obsolete but replacement is not pushed
133 133 ==================================================
134 134
135 135 setup
136 136
137 137 $ rm -fr ../remote
138 138 $ cp -r ../backup1 ../remote
139 139 $ hg phase --draft --force '(0::) - 0'
140 140 $ hg up -q '.^'
141 141 $ mkcommit other
142 142 created new head
143 143 $ hg glog --hidden
144 144 @ d7d41ccbd4de (draft) add other
145 145 |
146 146 | o 71e3228bffe1 (draft) add new
147 147 |/
148 148 | x c70b08862e08 (draft) add old
149 149 |/
150 150 o b4952fcf48cf (public) add base
151 151
152 152
153 153 old exists remotely as draft. It is obsoleted by new but we don't push new.
154 154 Push should abort on new head
155 155
156 156 $ hg push -r 'desc("other")'
157 157 pushing to $TESTTMP/remote
158 158 searching for changes
159 159 abort: push creates new remote head d7d41ccbd4de!
160 160 (did you forget to merge? use push -f to force)
161 161 [255]
162
163
164
165 Both precursors and successors are already know remotely. Descendant adds heads
166 ===============================================================================
167
168 setup. (The obsolete marker is known locally only
169
170 $ cd ..
171 $ rm -rf local
172 $ hg clone remote local
173 updating to branch default
174 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 $ cd local
176 $ mkcommit old
177 old already tracked!
178 nothing changed
179 [1]
180 $ hg up -q '.^'
181 $ mkcommit new
182 created new head
183 $ hg push -f
184 pushing to $TESTTMP/remote
185 searching for changes
186 adding changesets
187 adding manifests
188 adding file changes
189 added 1 changesets with 1 changes to 1 files (+1 heads)
190 $ mkcommit desc1
191 $ hg up -q '.^'
192 $ mkcommit desc2
193 created new head
194 $ hg debugobsolete `getid old` `getid new`
195 $ hg glog --hidden
196 @ 5fe37041cc2b (draft) add desc2
197 |
198 | o a3ef1d111c5f (draft) add desc1
199 |/
200 o 71e3228bffe1 (draft) add new
201 |
202 | x c70b08862e08 (draft) add old
203 |/
204 o b4952fcf48cf (public) add base
205
206 $ hg glog --hidden -R ../remote
207 o 71e3228bffe1 (draft) add new
208 |
209 | o c70b08862e08 (draft) add old
210 |/
211 @ b4952fcf48cf (public) add base
212
213 $ cp -r ../remote ../backup2
214
215 Push should not warn about adding new heads. We create one, but we'll delete
216 one anyway.
217
218 $ hg push
219 pushing to $TESTTMP/remote
220 searching for changes
221 adding changesets
222 adding manifests
223 adding file changes
224 added 2 changesets with 2 changes to 2 files (+1 heads)
225
226
227 Remote head is unknown but obsoleted by a local changeset
228 =========================================================
229
230 setup
231
232 $ rm -fr ../remote
233 $ cp -r ../backup1 ../remote
234 $ cd ..
235 $ rm -rf local
236 $ hg clone remote local -r 0
237 adding changesets
238 adding manifests
239 adding file changes
240 added 1 changesets with 1 changes to 1 files
241 updating to branch default
242 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
243 $ cd local
244 $ mkcommit new
245 $ hg -R ../remote id --debug -r tip
246 c70b08862e0838ea6d7c59c85da2f1ed6c8d67da tip
247 $ hg id --debug -r tip
248 71e3228bffe1886550777233d6c97bb5a6b2a650 tip
249 $ hg debugobsolete c70b08862e0838ea6d7c59c85da2f1ed6c8d67da 71e3228bffe1886550777233d6c97bb5a6b2a650
250 $ hg glog --hidden
251 @ 71e3228bffe1 (draft) add new
252 |
253 o b4952fcf48cf (public) add base
254
255 $ hg glog --hidden -R ../remote
256 o c70b08862e08 (draft) add old
257 |
258 @ b4952fcf48cf (public) add base
259
260
261 Push should not complain about new heads.
262
263 It should not complain about "unsynced remote changes!" either but that's not
264 handled yet.
265
266 $ hg push --traceback
267 pushing to $TESTTMP/remote
268 searching for changes
269 note: unsynced remote changes!
270 adding changesets
271 adding manifests
272 adding file changes
273 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