##// END OF EJS Templates
discovery: if a push would create a new head, mention the bookmark name if any
Stephen Lee -
r21580:32601b0b default
parent child Browse files
Show More
@@ -1,362 +1,366 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, bookmarks
11 11 import branchmap
12 12
13 13 def findcommonincoming(repo, remote, heads=None, force=False):
14 14 """Return a tuple (common, anyincoming, heads) used to identify the common
15 15 subset of nodes between repo and remote.
16 16
17 17 "common" is a list of (at least) the heads of the common subset.
18 18 "anyincoming" is testable as a boolean indicating if any nodes are missing
19 19 locally. If remote does not support getbundle, this actually is a list of
20 20 roots of the nodes that would be incoming, to be supplied to
21 21 changegroupsubset. No code except for pull should be relying on this fact
22 22 any longer.
23 23 "heads" is either the supplied heads, or else the remote's heads.
24 24
25 25 If you pass heads and they are all known locally, the response lists just
26 26 these heads in "common" and in "heads".
27 27
28 28 Please use findcommonoutgoing to compute the set of outgoing nodes to give
29 29 extensions a good hook into outgoing.
30 30 """
31 31
32 32 if not remote.capable('getbundle'):
33 33 return treediscovery.findcommonincoming(repo, remote, heads, force)
34 34
35 35 if heads:
36 36 allknown = True
37 37 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
38 38 for h in heads:
39 39 if not knownnode(h):
40 40 allknown = False
41 41 break
42 42 if allknown:
43 43 return (heads, False, heads)
44 44
45 45 res = setdiscovery.findcommonheads(repo.ui, repo, remote,
46 46 abortwhenunrelated=not force)
47 47 common, anyinc, srvheads = res
48 48 return (list(common), anyinc, heads or list(srvheads))
49 49
50 50 class outgoing(object):
51 51 '''Represents the set of nodes present in a local repo but not in a
52 52 (possibly) remote one.
53 53
54 54 Members:
55 55
56 56 missing is a list of all nodes present in local but not in remote.
57 57 common is a list of all nodes shared between the two repos.
58 58 excluded is the list of missing changeset that shouldn't be sent remotely.
59 59 missingheads is the list of heads of missing.
60 60 commonheads is the list of heads of common.
61 61
62 62 The sets are computed on demand from the heads, unless provided upfront
63 63 by discovery.'''
64 64
65 65 def __init__(self, revlog, commonheads, missingheads):
66 66 self.commonheads = commonheads
67 67 self.missingheads = missingheads
68 68 self._revlog = revlog
69 69 self._common = None
70 70 self._missing = None
71 71 self.excluded = []
72 72
73 73 def _computecommonmissing(self):
74 74 sets = self._revlog.findcommonmissing(self.commonheads,
75 75 self.missingheads)
76 76 self._common, self._missing = sets
77 77
78 78 @util.propertycache
79 79 def common(self):
80 80 if self._common is None:
81 81 self._computecommonmissing()
82 82 return self._common
83 83
84 84 @util.propertycache
85 85 def missing(self):
86 86 if self._missing is None:
87 87 self._computecommonmissing()
88 88 return self._missing
89 89
90 90 def findcommonoutgoing(repo, other, onlyheads=None, force=False,
91 91 commoninc=None, portable=False):
92 92 '''Return an outgoing instance to identify the nodes present in repo but
93 93 not in other.
94 94
95 95 If onlyheads is given, only nodes ancestral to nodes in onlyheads
96 96 (inclusive) are included. If you already know the local repo's heads,
97 97 passing them in onlyheads is faster than letting them be recomputed here.
98 98
99 99 If commoninc is given, it must be the result of a prior call to
100 100 findcommonincoming(repo, other, force) to avoid recomputing it here.
101 101
102 102 If portable is given, compute more conservative common and missingheads,
103 103 to make bundles created from the instance more portable.'''
104 104 # declare an empty outgoing object to be filled later
105 105 og = outgoing(repo.changelog, None, None)
106 106
107 107 # get common set if not provided
108 108 if commoninc is None:
109 109 commoninc = findcommonincoming(repo, other, force=force)
110 110 og.commonheads, _any, _hds = commoninc
111 111
112 112 # compute outgoing
113 113 mayexclude = (repo._phasecache.phaseroots[phases.secret] or repo.obsstore)
114 114 if not mayexclude:
115 115 og.missingheads = onlyheads or repo.heads()
116 116 elif onlyheads is None:
117 117 # use visible heads as it should be cached
118 118 og.missingheads = repo.filtered("served").heads()
119 119 og.excluded = [ctx.node() for ctx in repo.set('secret() or extinct()')]
120 120 else:
121 121 # compute common, missing and exclude secret stuff
122 122 sets = repo.changelog.findcommonmissing(og.commonheads, onlyheads)
123 123 og._common, allmissing = sets
124 124 og._missing = missing = []
125 125 og.excluded = excluded = []
126 126 for node in allmissing:
127 127 ctx = repo[node]
128 128 if ctx.phase() >= phases.secret or ctx.extinct():
129 129 excluded.append(node)
130 130 else:
131 131 missing.append(node)
132 132 if len(missing) == len(allmissing):
133 133 missingheads = onlyheads
134 134 else: # update missing heads
135 135 missingheads = phases.newheads(repo, onlyheads, excluded)
136 136 og.missingheads = missingheads
137 137 if portable:
138 138 # recompute common and missingheads as if -r<rev> had been given for
139 139 # each head of missing, and --base <rev> for each head of the proper
140 140 # ancestors of missing
141 141 og._computecommonmissing()
142 142 cl = repo.changelog
143 143 missingrevs = set(cl.rev(n) for n in og._missing)
144 144 og._common = set(cl.ancestors(missingrevs)) - missingrevs
145 145 commonheads = set(og.commonheads)
146 146 og.missingheads = [h for h in og.missingheads if h not in commonheads]
147 147
148 148 return og
149 149
150 150 def _headssummary(repo, remote, outgoing):
151 151 """compute a summary of branch and heads status before and after push
152 152
153 153 return {'branch': ([remoteheads], [newheads], [unsyncedheads])} mapping
154 154
155 155 - branch: the branch name
156 156 - remoteheads: the list of remote heads known locally
157 157 None if the branch is new
158 158 - newheads: the new remote heads (known locally) with outgoing pushed
159 159 - unsyncedheads: the list of remote heads unknown locally.
160 160 """
161 161 cl = repo.changelog
162 162 headssum = {}
163 163 # A. Create set of branches involved in the push.
164 164 branches = set(repo[n].branch() for n in outgoing.missing)
165 165 remotemap = remote.branchmap()
166 166 newbranches = branches - set(remotemap)
167 167 branches.difference_update(newbranches)
168 168
169 169 # A. register remote heads
170 170 remotebranches = set()
171 171 for branch, heads in remote.branchmap().iteritems():
172 172 remotebranches.add(branch)
173 173 known = []
174 174 unsynced = []
175 175 knownnode = cl.hasnode # do not use nodemap until it is filtered
176 176 for h in heads:
177 177 if knownnode(h):
178 178 known.append(h)
179 179 else:
180 180 unsynced.append(h)
181 181 headssum[branch] = (known, list(known), unsynced)
182 182 # B. add new branch data
183 183 missingctx = list(repo[n] for n in outgoing.missing)
184 184 touchedbranches = set()
185 185 for ctx in missingctx:
186 186 branch = ctx.branch()
187 187 touchedbranches.add(branch)
188 188 if branch not in headssum:
189 189 headssum[branch] = (None, [], [])
190 190
191 191 # C drop data about untouched branches:
192 192 for branch in remotebranches - touchedbranches:
193 193 del headssum[branch]
194 194
195 195 # D. Update newmap with outgoing changes.
196 196 # This will possibly add new heads and remove existing ones.
197 197 newmap = branchmap.branchcache((branch, heads[1])
198 198 for branch, heads in headssum.iteritems()
199 199 if heads[0] is not None)
200 200 newmap.update(repo, (ctx.rev() for ctx in missingctx))
201 201 for branch, newheads in newmap.iteritems():
202 202 headssum[branch][1][:] = newheads
203 203 return headssum
204 204
205 205 def _oldheadssummary(repo, remoteheads, outgoing, inc=False):
206 206 """Compute branchmapsummary for repo without branchmap support"""
207 207
208 208 # 1-4b. old servers: Check for new topological heads.
209 209 # Construct {old,new}map with branch = None (topological branch).
210 210 # (code based on update)
211 211 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
212 212 oldheads = set(h for h in remoteheads if knownnode(h))
213 213 # all nodes in outgoing.missing are children of either:
214 214 # - an element of oldheads
215 215 # - another element of outgoing.missing
216 216 # - nullrev
217 217 # This explains why the new head are very simple to compute.
218 218 r = repo.set('heads(%ln + %ln)', oldheads, outgoing.missing)
219 219 newheads = list(c.node() for c in r)
220 220 unsynced = inc and set([None]) or set()
221 221 return {None: (oldheads, newheads, unsynced)}
222 222
223 223 def checkheads(repo, remote, outgoing, remoteheads, newbranch=False, inc=False,
224 224 newbookmarks=[]):
225 225 """Check that a push won't add any outgoing head
226 226
227 227 raise Abort error and display ui message as needed.
228 228 """
229 229 # Check for each named branch if we're creating new remote heads.
230 230 # To be a remote head after push, node must be either:
231 231 # - unknown locally
232 232 # - a local outgoing head descended from update
233 233 # - a remote head that's known locally and not
234 234 # ancestral to an outgoing head
235 235 if remoteheads == [nullid]:
236 236 # remote is empty, nothing to check.
237 237 return
238 238
239 239 if remote.capable('branchmap'):
240 240 headssum = _headssummary(repo, remote, outgoing)
241 241 else:
242 242 headssum = _oldheadssummary(repo, remoteheads, outgoing, inc)
243 243 newbranches = [branch for branch, heads in headssum.iteritems()
244 244 if heads[0] is None]
245 245 # 1. Check for new branches on the remote.
246 246 if newbranches and not newbranch: # new branch requires --new-branch
247 247 branchnames = ', '.join(sorted(newbranches))
248 248 raise util.Abort(_("push creates new remote branches: %s!")
249 249 % branchnames,
250 250 hint=_("use 'hg push --new-branch' to create"
251 251 " new remote branches"))
252 252
253 253 # 2. Compute newly pushed bookmarks. We don't warn about bookmarked heads.
254 254 localbookmarks = repo._bookmarks
255 255 remotebookmarks = remote.listkeys('bookmarks')
256 256 bookmarkedheads = set()
257 257 for bm in localbookmarks:
258 258 rnode = remotebookmarks.get(bm)
259 259 if rnode and rnode in repo:
260 260 lctx, rctx = repo[bm], repo[rnode]
261 261 if bookmarks.validdest(repo, rctx, lctx):
262 262 bookmarkedheads.add(lctx.node())
263 263 else:
264 264 if bm in newbookmarks:
265 265 bookmarkedheads.add(repo[bm].node())
266 266
267 267 # 3. Check for new heads.
268 268 # If there are more heads after the push than before, a suitable
269 269 # error message, depending on unsynced status, is displayed.
270 270 error = None
271 271 allmissing = set(outgoing.missing)
272 272 allfuturecommon = set(c.node() for c in repo.set('%ld', outgoing.common))
273 273 allfuturecommon.update(allmissing)
274 274 for branch, heads in sorted(headssum.iteritems()):
275 275 remoteheads, newheads, unsyncedheads = heads
276 276 candidate_newhs = set(newheads)
277 277 # add unsynced data
278 278 if remoteheads is None:
279 279 oldhs = set()
280 280 else:
281 281 oldhs = set(remoteheads)
282 282 oldhs.update(unsyncedheads)
283 283 candidate_newhs.update(unsyncedheads)
284 284 dhs = None # delta heads, the new heads on branch
285 285 discardedheads = set()
286 286 if repo.obsstore:
287 287 # remove future heads which are actually obsoleted by another
288 288 # pushed element:
289 289 #
290 290 # XXX as above, There are several cases this case does not handle
291 291 # XXX properly
292 292 #
293 293 # (1) if <nh> is public, it won't be affected by obsolete marker
294 294 # and a new is created
295 295 #
296 296 # (2) if the new heads have ancestors which are not obsolete and
297 297 # not ancestors of any other heads we will have a new head too.
298 298 #
299 299 # These two cases will be easy to handle for known changeset but
300 300 # much more tricky for unsynced changes.
301 301 newhs = set()
302 302 for nh in candidate_newhs:
303 303 if nh in repo and repo[nh].phase() <= phases.public:
304 304 newhs.add(nh)
305 305 else:
306 306 for suc in obsolete.allsuccessors(repo.obsstore, [nh]):
307 307 if suc != nh and suc in allfuturecommon:
308 308 discardedheads.add(nh)
309 309 break
310 310 else:
311 311 newhs.add(nh)
312 312 else:
313 313 newhs = candidate_newhs
314 314 unsynced = sorted(h for h in unsyncedheads if h not in discardedheads)
315 315 if unsynced:
316 316 if len(unsynced) <= 4 or repo.ui.verbose:
317 317 heads = ' '.join(short(h) for h in unsynced)
318 318 else:
319 319 heads = (' '.join(short(h) for h in unsynced[:4]) +
320 320 ' ' + _("and %s others") % (len(unsynced) - 4))
321 321 if branch is None:
322 322 repo.ui.status(_("remote has heads that are "
323 323 "not known locally: %s\n") % heads)
324 324 else:
325 325 repo.ui.status(_("remote has heads on branch '%s' that are "
326 326 "not known locally: %s\n") % (branch, heads))
327 327 if remoteheads is None:
328 328 if len(newhs) > 1:
329 329 dhs = list(newhs)
330 330 if error is None:
331 331 error = (_("push creates new branch '%s' "
332 332 "with multiple heads") % (branch))
333 333 hint = _("merge or"
334 334 " see \"hg help push\" for details about"
335 335 " pushing new heads")
336 336 elif len(newhs) > len(oldhs):
337 337 # remove bookmarked or existing remote heads from the new heads list
338 338 dhs = sorted(newhs - bookmarkedheads - oldhs)
339 339 if dhs:
340 340 if error is None:
341 341 if branch not in ('default', None):
342 342 error = _("push creates new remote head %s "
343 343 "on branch '%s'!") % (short(dhs[0]), branch)
344 elif repo[dhs[0]].bookmarks():
345 error = _("push creates new remote head %s "
346 "with bookmark '%s'!") % (
347 short(dhs[0]), repo[dhs[0]].bookmarks()[0])
344 348 else:
345 349 error = _("push creates new remote head %s!"
346 350 ) % short(dhs[0])
347 351 if unsyncedheads:
348 352 hint = _("pull and merge or"
349 353 " see \"hg help push\" for details about"
350 354 " pushing new heads")
351 355 else:
352 356 hint = _("merge or"
353 357 " see \"hg help push\" for details about"
354 358 " pushing new heads")
355 359 if branch is None:
356 360 repo.ui.note(_("new remote heads:\n"))
357 361 else:
358 362 repo.ui.note(_("new remote heads on branch '%s':\n") % branch)
359 363 for h in dhs:
360 364 repo.ui.note((" %s\n") % short(h))
361 365 if error:
362 366 raise util.Abort(error, hint=hint)
@@ -1,446 +1,446 b''
1 1 $ "$TESTDIR/hghave" serve || exit 80
2 2
3 3 $ cat << EOF >> $HGRCPATH
4 4 > [ui]
5 5 > logtemplate={rev}:{node|short} {desc|firstline}
6 6 > [phases]
7 7 > publish=False
8 8 > [extensions]
9 9 > EOF
10 10 $ cat > obs.py << EOF
11 11 > import mercurial.obsolete
12 12 > mercurial.obsolete._enabled = True
13 13 > EOF
14 14 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
15 15
16 16 initialize
17 17
18 18 $ hg init a
19 19 $ cd a
20 20 $ echo 'test' > test
21 21 $ hg commit -Am'test'
22 22 adding test
23 23
24 24 set bookmarks
25 25
26 26 $ hg bookmark X
27 27 $ hg bookmark Y
28 28 $ hg bookmark Z
29 29
30 30 import bookmark by name
31 31
32 32 $ hg init ../b
33 33 $ cd ../b
34 34 $ hg book Y
35 35 $ hg book
36 36 * Y -1:000000000000
37 37 $ hg pull ../a
38 38 pulling from ../a
39 39 requesting all changes
40 40 adding changesets
41 41 adding manifests
42 42 adding file changes
43 43 added 1 changesets with 1 changes to 1 files
44 44 adding remote bookmark X
45 45 updating bookmark Y
46 46 adding remote bookmark Z
47 47 (run 'hg update' to get a working copy)
48 48 $ hg bookmarks
49 49 X 0:4e3505fd9583
50 50 * Y 0:4e3505fd9583
51 51 Z 0:4e3505fd9583
52 52 $ hg debugpushkey ../a namespaces
53 53 bookmarks
54 54 namespaces
55 55 obsolete
56 56 phases
57 57 $ hg debugpushkey ../a bookmarks
58 58 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
59 59 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
60 60 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
61 61 $ hg pull -B X ../a
62 62 pulling from ../a
63 63 no changes found
64 64 importing bookmark X
65 65 $ hg bookmark
66 66 X 0:4e3505fd9583
67 67 * Y 0:4e3505fd9583
68 68 Z 0:4e3505fd9583
69 69
70 70 export bookmark by name
71 71
72 72 $ hg bookmark W
73 73 $ hg bookmark foo
74 74 $ hg bookmark foobar
75 75 $ hg push -B W ../a
76 76 pushing to ../a
77 77 searching for changes
78 78 no changes found
79 79 exporting bookmark W
80 80 [1]
81 81 $ hg -R ../a bookmarks
82 82 W -1:000000000000
83 83 X 0:4e3505fd9583
84 84 Y 0:4e3505fd9583
85 85 * Z 0:4e3505fd9583
86 86
87 87 delete a remote bookmark
88 88
89 89 $ hg book -d W
90 90 $ hg push -B W ../a
91 91 pushing to ../a
92 92 searching for changes
93 93 no changes found
94 94 deleting remote bookmark W
95 95 [1]
96 96
97 97 push/pull name that doesn't exist
98 98
99 99 $ hg push -B badname ../a
100 100 pushing to ../a
101 101 searching for changes
102 102 no changes found
103 103 bookmark badname does not exist on the local or remote repository!
104 104 [2]
105 105 $ hg pull -B anotherbadname ../a
106 106 pulling from ../a
107 107 abort: remote bookmark anotherbadname not found!
108 108 [255]
109 109
110 110 divergent bookmarks
111 111
112 112 $ cd ../a
113 113 $ echo c1 > f1
114 114 $ hg ci -Am1
115 115 adding f1
116 116 $ hg book -f @
117 117 $ hg book -f X
118 118 $ hg book
119 119 @ 1:0d2164f0ce0d
120 120 * X 1:0d2164f0ce0d
121 121 Y 0:4e3505fd9583
122 122 Z 1:0d2164f0ce0d
123 123
124 124 $ cd ../b
125 125 $ hg up
126 126 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
127 127 updating bookmark foobar
128 128 $ echo c2 > f2
129 129 $ hg ci -Am2
130 130 adding f2
131 131 $ hg book -if @
132 132 $ hg book -if X
133 133 $ hg book
134 134 @ 1:9b140be10808
135 135 X 1:9b140be10808
136 136 Y 0:4e3505fd9583
137 137 Z 0:4e3505fd9583
138 138 foo -1:000000000000
139 139 * foobar 1:9b140be10808
140 140
141 141 $ hg pull --config paths.foo=../a foo
142 142 pulling from $TESTTMP/a (glob)
143 143 searching for changes
144 144 adding changesets
145 145 adding manifests
146 146 adding file changes
147 147 added 1 changesets with 1 changes to 1 files (+1 heads)
148 148 divergent bookmark @ stored as @foo
149 149 divergent bookmark X stored as X@foo
150 150 updating bookmark Z
151 151 (run 'hg heads' to see heads, 'hg merge' to merge)
152 152 $ hg book
153 153 @ 1:9b140be10808
154 154 @foo 2:0d2164f0ce0d
155 155 X 1:9b140be10808
156 156 X@foo 2:0d2164f0ce0d
157 157 Y 0:4e3505fd9583
158 158 Z 2:0d2164f0ce0d
159 159 foo -1:000000000000
160 160 * foobar 1:9b140be10808
161 161 $ hg push -f ../a
162 162 pushing to ../a
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 (+1 heads)
168 168 $ hg -R ../a book
169 169 @ 1:0d2164f0ce0d
170 170 * X 1:0d2164f0ce0d
171 171 Y 0:4e3505fd9583
172 172 Z 1:0d2164f0ce0d
173 173
174 174 revsets should not ignore divergent bookmarks
175 175
176 176 $ hg bookmark -fr 1 Z
177 177 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
178 178 0:4e3505fd9583 Y
179 179 1:9b140be10808 @ X Z foobar
180 180 2:0d2164f0ce0d @foo X@foo
181 181 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
182 182 2:0d2164f0ce0d @foo X@foo
183 183 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
184 184 2:0d2164f0ce0d @foo X@foo
185 185
186 186 update a remote bookmark from a non-head to a head
187 187
188 188 $ hg up -q Y
189 189 $ echo c3 > f2
190 190 $ hg ci -Am3
191 191 adding f2
192 192 created new head
193 193 $ hg push ../a
194 194 pushing to ../a
195 195 searching for changes
196 196 adding changesets
197 197 adding manifests
198 198 adding file changes
199 199 added 1 changesets with 1 changes to 1 files (+1 heads)
200 200 updating bookmark Y
201 201 $ hg -R ../a book
202 202 @ 1:0d2164f0ce0d
203 203 * X 1:0d2164f0ce0d
204 204 Y 3:f6fc62dde3c0
205 205 Z 1:0d2164f0ce0d
206 206
207 207 update a bookmark in the middle of a client pulling changes
208 208
209 209 $ cd ..
210 210 $ hg clone -q a pull-race
211 211 $ hg clone -q pull-race pull-race2
212 212 $ cd pull-race
213 213 $ hg up -q Y
214 214 $ echo c4 > f2
215 215 $ hg ci -Am4
216 216 $ echo c5 > f3
217 217 $ cat <<EOF > .hg/hgrc
218 218 > [hooks]
219 219 > outgoing.makecommit = hg ci -Am5; echo committed in pull-race
220 220 > EOF
221 221 $ cd ../pull-race2
222 222 $ hg pull
223 223 pulling from $TESTTMP/pull-race (glob)
224 224 searching for changes
225 225 adding changesets
226 226 adding f3
227 227 committed in pull-race
228 228 adding manifests
229 229 adding file changes
230 230 added 1 changesets with 1 changes to 1 files
231 231 updating bookmark Y
232 232 (run 'hg update' to get a working copy)
233 233 $ hg book
234 234 * @ 1:0d2164f0ce0d
235 235 X 1:0d2164f0ce0d
236 236 Y 4:b0a5eff05604
237 237 Z 1:0d2164f0ce0d
238 238 $ cd ../b
239 239
240 240 diverging a remote bookmark fails
241 241
242 242 $ hg up -q 4e3505fd9583
243 243 $ echo c4 > f2
244 244 $ hg ci -Am4
245 245 adding f2
246 246 created new head
247 247 $ echo c5 > f2
248 248 $ hg ci -Am5
249 249 $ hg log -G
250 250 @ 5:c922c0139ca0 5
251 251 |
252 252 o 4:4efff6d98829 4
253 253 |
254 254 | o 3:f6fc62dde3c0 3
255 255 |/
256 256 | o 2:0d2164f0ce0d 1
257 257 |/
258 258 | o 1:9b140be10808 2
259 259 |/
260 260 o 0:4e3505fd9583 test
261 261
262 262
263 263 $ hg book -f Y
264 264
265 265 $ cat <<EOF > ../a/.hg/hgrc
266 266 > [web]
267 267 > push_ssl = false
268 268 > allow_push = *
269 269 > EOF
270 270
271 271 $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid
272 272 $ cat ../hg2.pid >> $DAEMON_PIDS
273 273
274 274 $ hg push http://localhost:$HGPORT2/
275 275 pushing to http://localhost:$HGPORT2/
276 276 searching for changes
277 abort: push creates new remote head c922c0139ca0!
277 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
278 278 (merge or see "hg help push" for details about pushing new heads)
279 279 [255]
280 280 $ hg -R ../a book
281 281 @ 1:0d2164f0ce0d
282 282 * X 1:0d2164f0ce0d
283 283 Y 3:f6fc62dde3c0
284 284 Z 1:0d2164f0ce0d
285 285
286 286
287 287 Unrelated marker does not alter the decision
288 288
289 289 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
290 290 $ hg push http://localhost:$HGPORT2/
291 291 pushing to http://localhost:$HGPORT2/
292 292 searching for changes
293 abort: push creates new remote head c922c0139ca0!
293 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
294 294 (merge or see "hg help push" for details about pushing new heads)
295 295 [255]
296 296 $ hg -R ../a book
297 297 @ 1:0d2164f0ce0d
298 298 * X 1:0d2164f0ce0d
299 299 Y 3:f6fc62dde3c0
300 300 Z 1:0d2164f0ce0d
301 301
302 302 Update to a successor works
303 303
304 304 $ hg id --debug -r 3
305 305 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
306 306 $ hg id --debug -r 4
307 307 4efff6d98829d9c824c621afd6e3f01865f5439f
308 308 $ hg id --debug -r 5
309 309 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
310 310 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
311 311 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
312 312 $ hg push http://localhost:$HGPORT2/
313 313 pushing to http://localhost:$HGPORT2/
314 314 searching for changes
315 315 remote: adding changesets
316 316 remote: adding manifests
317 317 remote: adding file changes
318 318 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
319 319 updating bookmark Y
320 320 $ hg -R ../a book
321 321 @ 1:0d2164f0ce0d
322 322 * X 1:0d2164f0ce0d
323 323 Y 5:c922c0139ca0
324 324 Z 1:0d2164f0ce0d
325 325
326 326 hgweb
327 327
328 328 $ cat <<EOF > .hg/hgrc
329 329 > [web]
330 330 > push_ssl = false
331 331 > allow_push = *
332 332 > EOF
333 333
334 334 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
335 335 $ cat ../hg.pid >> $DAEMON_PIDS
336 336 $ cd ../a
337 337
338 338 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
339 339 bookmarks
340 340 namespaces
341 341 obsolete
342 342 phases
343 343 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
344 344 @ 9b140be1080824d768c5a4691a564088eede71f9
345 345 X 9b140be1080824d768c5a4691a564088eede71f9
346 346 Y c922c0139ca03858f655e4a2af4dd02796a63969
347 347 Z 9b140be1080824d768c5a4691a564088eede71f9
348 348 foo 0000000000000000000000000000000000000000
349 349 foobar 9b140be1080824d768c5a4691a564088eede71f9
350 350 $ hg out -B http://localhost:$HGPORT/
351 351 comparing with http://localhost:$HGPORT/
352 352 searching for changed bookmarks
353 353 no changed bookmarks found
354 354 [1]
355 355 $ hg push -B Z http://localhost:$HGPORT/
356 356 pushing to http://localhost:$HGPORT/
357 357 searching for changes
358 358 no changes found
359 359 exporting bookmark Z
360 360 [1]
361 361 $ hg book -d Z
362 362 $ hg in -B http://localhost:$HGPORT/
363 363 comparing with http://localhost:$HGPORT/
364 364 searching for changed bookmarks
365 365 Z 0d2164f0ce0d
366 366 foo 000000000000
367 367 foobar 9b140be10808
368 368 $ hg pull -B Z http://localhost:$HGPORT/
369 369 pulling from http://localhost:$HGPORT/
370 370 no changes found
371 371 divergent bookmark @ stored as @1
372 372 divergent bookmark X stored as X@1
373 373 adding remote bookmark Z
374 374 adding remote bookmark foo
375 375 adding remote bookmark foobar
376 376 importing bookmark Z
377 377 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
378 378 requesting all changes
379 379 adding changesets
380 380 adding manifests
381 381 adding file changes
382 382 added 5 changesets with 5 changes to 3 files (+2 heads)
383 383 updating to bookmark @
384 384 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
385 385 $ hg -R cloned-bookmarks bookmarks
386 386 * @ 1:9b140be10808
387 387 X 1:9b140be10808
388 388 Y 4:c922c0139ca0
389 389 Z 2:0d2164f0ce0d
390 390 foo -1:000000000000
391 391 foobar 1:9b140be10808
392 392
393 393 $ cd ..
394 394
395 395 Pushing a bookmark should only push the changes required by that
396 396 bookmark, not all outgoing changes:
397 397 $ hg clone http://localhost:$HGPORT/ addmarks
398 398 requesting all changes
399 399 adding changesets
400 400 adding manifests
401 401 adding file changes
402 402 added 5 changesets with 5 changes to 3 files (+2 heads)
403 403 updating to bookmark @
404 404 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 405 $ cd addmarks
406 406 $ echo foo > foo
407 407 $ hg add foo
408 408 $ hg commit -m 'add foo'
409 409 $ echo bar > bar
410 410 $ hg add bar
411 411 $ hg commit -m 'add bar'
412 412 $ hg co "tip^"
413 413 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
414 414 (leaving bookmark @)
415 415 $ hg book add-foo
416 416 $ hg book -r tip add-bar
417 417 Note: this push *must* push only a single changeset, as that's the point
418 418 of this test.
419 419 $ hg push -B add-foo --traceback
420 420 pushing to http://localhost:$HGPORT/
421 421 searching for changes
422 422 remote: adding changesets
423 423 remote: adding manifests
424 424 remote: adding file changes
425 425 remote: added 1 changesets with 1 changes to 1 files
426 426 exporting bookmark add-foo
427 427
428 428 pushing a new bookmark on a new head does not require -f if -B is specified
429 429
430 430 $ hg up -q X
431 431 $ hg book W
432 432 $ echo c5 > f2
433 433 $ hg ci -Am5
434 434 created new head
435 435 $ hg push -B W
436 436 pushing to http://localhost:$HGPORT/
437 437 searching for changes
438 438 remote: adding changesets
439 439 remote: adding manifests
440 440 remote: adding file changes
441 441 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
442 442 exporting bookmark W
443 443 $ hg -R ../b id -r W
444 444 cc978a373a53 tip W
445 445
446 446 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now