##// END OF EJS Templates
bookmarks: prevent divergent bookmark from being updated unexpectedly...
FUJIWARA Katsunori -
r24353:3f6bf9f2 default
parent child Browse files
Show More
@@ -1,463 +1,474
1 1 # Mercurial bookmark support code
2 2 #
3 3 # Copyright 2008 David Soria Parra <dsp@php.net>
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 import os
9 9 from mercurial.i18n import _
10 10 from mercurial.node import hex, bin
11 11 from mercurial import encoding, error, util, obsolete, lock as lockmod
12 12 import errno
13 13
14 14 class bmstore(dict):
15 15 """Storage for bookmarks.
16 16
17 17 This object should do all bookmark reads and writes, so that it's
18 18 fairly simple to replace the storage underlying bookmarks without
19 19 having to clone the logic surrounding bookmarks.
20 20
21 21 This particular bmstore implementation stores bookmarks as
22 22 {hash}\s{name}\n (the same format as localtags) in
23 23 .hg/bookmarks. The mapping is stored as {name: nodeid}.
24 24
25 25 This class does NOT handle the "current" bookmark state at this
26 26 time.
27 27 """
28 28
29 29 def __init__(self, repo):
30 30 dict.__init__(self)
31 31 self._repo = repo
32 32 try:
33 33 bkfile = self.getbkfile(repo)
34 34 for line in bkfile:
35 35 line = line.strip()
36 36 if not line:
37 37 continue
38 38 if ' ' not in line:
39 39 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
40 40 % line)
41 41 continue
42 42 sha, refspec = line.split(' ', 1)
43 43 refspec = encoding.tolocal(refspec)
44 44 try:
45 45 self[refspec] = repo.changelog.lookup(sha)
46 46 except LookupError:
47 47 pass
48 48 except IOError, inst:
49 49 if inst.errno != errno.ENOENT:
50 50 raise
51 51
52 52 def getbkfile(self, repo):
53 53 bkfile = None
54 54 if 'HG_PENDING' in os.environ:
55 55 try:
56 56 bkfile = repo.vfs('bookmarks.pending')
57 57 except IOError, inst:
58 58 if inst.errno != errno.ENOENT:
59 59 raise
60 60 if bkfile is None:
61 61 bkfile = repo.vfs('bookmarks')
62 62 return bkfile
63 63
64 64 def recordchange(self, tr):
65 65 """record that bookmarks have been changed in a transaction
66 66
67 67 The transaction is then responsible for updating the file content."""
68 68 tr.addfilegenerator('bookmarks', ('bookmarks',), self._write,
69 69 location='plain')
70 70 tr.hookargs['bookmark_moved'] = '1'
71 71
72 72 def write(self):
73 73 '''Write bookmarks
74 74
75 75 Write the given bookmark => hash dictionary to the .hg/bookmarks file
76 76 in a format equal to those of localtags.
77 77
78 78 We also store a backup of the previous state in undo.bookmarks that
79 79 can be copied back on rollback.
80 80 '''
81 81 repo = self._repo
82 82 self._writerepo(repo)
83 83
84 84 def _writerepo(self, repo):
85 85 """Factored out for extensibility"""
86 86 if repo._bookmarkcurrent not in self:
87 87 unsetcurrent(repo)
88 88
89 89 wlock = repo.wlock()
90 90 try:
91 91
92 92 file = repo.vfs('bookmarks', 'w', atomictemp=True)
93 93 self._write(file)
94 94 file.close()
95 95
96 96 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
97 97 try:
98 98 repo.svfs.utime('00changelog.i', None)
99 99 except OSError:
100 100 pass
101 101
102 102 finally:
103 103 wlock.release()
104 104
105 105 def _write(self, fp):
106 106 for name, node in self.iteritems():
107 107 fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
108 108
109 109 def readcurrent(repo):
110 110 '''Get the current bookmark
111 111
112 112 If we use gittish branches we have a current bookmark that
113 113 we are on. This function returns the name of the bookmark. It
114 114 is stored in .hg/bookmarks.current
115 115 '''
116 116 mark = None
117 117 try:
118 118 file = repo.vfs('bookmarks.current')
119 119 except IOError, inst:
120 120 if inst.errno != errno.ENOENT:
121 121 raise
122 122 return None
123 123 try:
124 124 # No readline() in osutil.posixfile, reading everything is cheap
125 125 mark = encoding.tolocal((file.readlines() or [''])[0])
126 126 if mark == '' or mark not in repo._bookmarks:
127 127 mark = None
128 128 finally:
129 129 file.close()
130 130 return mark
131 131
132 132 def setcurrent(repo, mark):
133 133 '''Set the name of the bookmark that we are currently on
134 134
135 135 Set the name of the bookmark that we are on (hg update <bookmark>).
136 136 The name is recorded in .hg/bookmarks.current
137 137 '''
138 138 if mark not in repo._bookmarks:
139 139 raise AssertionError('bookmark %s does not exist!' % mark)
140 140
141 141 current = repo._bookmarkcurrent
142 142 if current == mark:
143 143 return
144 144
145 145 wlock = repo.wlock()
146 146 try:
147 147 file = repo.vfs('bookmarks.current', 'w', atomictemp=True)
148 148 file.write(encoding.fromlocal(mark))
149 149 file.close()
150 150 finally:
151 151 wlock.release()
152 152 repo._bookmarkcurrent = mark
153 153
154 154 def unsetcurrent(repo):
155 155 wlock = repo.wlock()
156 156 try:
157 157 try:
158 158 repo.vfs.unlink('bookmarks.current')
159 159 repo._bookmarkcurrent = None
160 160 except OSError, inst:
161 161 if inst.errno != errno.ENOENT:
162 162 raise
163 163 finally:
164 164 wlock.release()
165 165
166 166 def iscurrent(repo, mark=None, parents=None):
167 167 '''Tell whether the current bookmark is also active
168 168
169 169 I.e., the bookmark listed in .hg/bookmarks.current also points to a
170 170 parent of the working directory.
171 171 '''
172 172 if not mark:
173 173 mark = repo._bookmarkcurrent
174 174 if not parents:
175 175 parents = [p.node() for p in repo[None].parents()]
176 176 marks = repo._bookmarks
177 177 return (mark in marks and marks[mark] in parents)
178 178
179 179 def updatecurrentbookmark(repo, oldnode, curbranch):
180 180 try:
181 181 return update(repo, oldnode, repo.branchtip(curbranch))
182 182 except error.RepoLookupError:
183 183 if curbranch == "default": # no default branch!
184 184 return update(repo, oldnode, repo.lookup("tip"))
185 185 else:
186 186 raise util.Abort(_("branch %s not found") % curbranch)
187 187
188 188 def deletedivergent(repo, deletefrom, bm):
189 189 '''Delete divergent versions of bm on nodes in deletefrom.
190 190
191 191 Return True if at least one bookmark was deleted, False otherwise.'''
192 192 deleted = False
193 193 marks = repo._bookmarks
194 194 divergent = [b for b in marks if b.split('@', 1)[0] == bm.split('@', 1)[0]]
195 195 for mark in divergent:
196 196 if mark == '@' or '@' not in mark:
197 197 # can't be divergent by definition
198 198 continue
199 199 if mark and marks[mark] in deletefrom:
200 200 if mark != bm:
201 201 del marks[mark]
202 202 deleted = True
203 203 return deleted
204 204
205 205 def calculateupdate(ui, repo, checkout):
206 206 '''Return a tuple (targetrev, movemarkfrom) indicating the rev to
207 207 check out and where to move the active bookmark from, if needed.'''
208 208 movemarkfrom = None
209 209 if checkout is None:
210 210 curmark = repo._bookmarkcurrent
211 211 if iscurrent(repo):
212 212 movemarkfrom = repo['.'].node()
213 213 elif curmark:
214 214 ui.status(_("updating to active bookmark %s\n") % curmark)
215 215 checkout = curmark
216 216 return (checkout, movemarkfrom)
217 217
218 218 def update(repo, parents, node):
219 219 deletefrom = parents
220 220 marks = repo._bookmarks
221 221 update = False
222 222 cur = repo._bookmarkcurrent
223 223 if not cur:
224 224 return False
225 225
226 226 if marks[cur] in parents:
227 227 new = repo[node]
228 228 divs = [repo[b] for b in marks
229 229 if b.split('@', 1)[0] == cur.split('@', 1)[0]]
230 230 anc = repo.changelog.ancestors([new.rev()])
231 231 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
232 232 if validdest(repo, repo[marks[cur]], new):
233 233 marks[cur] = new.node()
234 234 update = True
235 235
236 236 if deletedivergent(repo, deletefrom, cur):
237 237 update = True
238 238
239 239 if update:
240 240 marks.write()
241 241 return update
242 242
243 243 def listbookmarks(repo):
244 244 # We may try to list bookmarks on a repo type that does not
245 245 # support it (e.g., statichttprepository).
246 246 marks = getattr(repo, '_bookmarks', {})
247 247
248 248 d = {}
249 249 hasnode = repo.changelog.hasnode
250 250 for k, v in marks.iteritems():
251 251 # don't expose local divergent bookmarks
252 252 if hasnode(v) and ('@' not in k or k.endswith('@')):
253 253 d[k] = hex(v)
254 254 return d
255 255
256 256 def pushbookmark(repo, key, old, new):
257 257 w = l = tr = None
258 258 try:
259 259 w = repo.wlock()
260 260 l = repo.lock()
261 261 tr = repo.transaction('bookmarks')
262 262 marks = repo._bookmarks
263 263 existing = hex(marks.get(key, ''))
264 264 if existing != old and existing != new:
265 265 return False
266 266 if new == '':
267 267 del marks[key]
268 268 else:
269 269 if new not in repo:
270 270 return False
271 271 marks[key] = repo[new].node()
272 272 marks.recordchange(tr)
273 273 tr.close()
274 274 return True
275 275 finally:
276 276 lockmod.release(tr, l, w)
277 277
278 278 def compare(repo, srcmarks, dstmarks,
279 279 srchex=None, dsthex=None, targets=None):
280 280 '''Compare bookmarks between srcmarks and dstmarks
281 281
282 282 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
283 283 differ, invalid)", each are list of bookmarks below:
284 284
285 285 :addsrc: added on src side (removed on dst side, perhaps)
286 286 :adddst: added on dst side (removed on src side, perhaps)
287 287 :advsrc: advanced on src side
288 288 :advdst: advanced on dst side
289 289 :diverge: diverge
290 290 :differ: changed, but changeset referred on src is unknown on dst
291 291 :invalid: unknown on both side
292 292 :same: same on both side
293 293
294 294 Each elements of lists in result tuple is tuple "(bookmark name,
295 295 changeset ID on source side, changeset ID on destination
296 296 side)". Each changeset IDs are 40 hexadecimal digit string or
297 297 None.
298 298
299 299 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
300 300 "invalid" list may be unknown for repo.
301 301
302 302 This function expects that "srcmarks" and "dstmarks" return
303 303 changeset ID in 40 hexadecimal digit string for specified
304 304 bookmark. If not so (e.g. bmstore "repo._bookmarks" returning
305 305 binary value), "srchex" or "dsthex" should be specified to convert
306 306 into such form.
307 307
308 308 If "targets" is specified, only bookmarks listed in it are
309 309 examined.
310 310 '''
311 311 if not srchex:
312 312 srchex = lambda x: x
313 313 if not dsthex:
314 314 dsthex = lambda x: x
315 315
316 316 if targets:
317 317 bset = set(targets)
318 318 else:
319 319 srcmarkset = set(srcmarks)
320 320 dstmarkset = set(dstmarks)
321 321 bset = srcmarkset | dstmarkset
322 322
323 323 results = ([], [], [], [], [], [], [], [])
324 324 addsrc = results[0].append
325 325 adddst = results[1].append
326 326 advsrc = results[2].append
327 327 advdst = results[3].append
328 328 diverge = results[4].append
329 329 differ = results[5].append
330 330 invalid = results[6].append
331 331 same = results[7].append
332 332
333 333 for b in sorted(bset):
334 334 if b not in srcmarks:
335 335 if b in dstmarks:
336 336 adddst((b, None, dsthex(dstmarks[b])))
337 337 else:
338 338 invalid((b, None, None))
339 339 elif b not in dstmarks:
340 340 addsrc((b, srchex(srcmarks[b]), None))
341 341 else:
342 342 scid = srchex(srcmarks[b])
343 343 dcid = dsthex(dstmarks[b])
344 344 if scid == dcid:
345 345 same((b, scid, dcid))
346 346 elif scid in repo and dcid in repo:
347 347 sctx = repo[scid]
348 348 dctx = repo[dcid]
349 349 if sctx.rev() < dctx.rev():
350 350 if validdest(repo, sctx, dctx):
351 351 advdst((b, scid, dcid))
352 352 else:
353 353 diverge((b, scid, dcid))
354 354 else:
355 355 if validdest(repo, dctx, sctx):
356 356 advsrc((b, scid, dcid))
357 357 else:
358 358 diverge((b, scid, dcid))
359 359 else:
360 360 # it is too expensive to examine in detail, in this case
361 361 differ((b, scid, dcid))
362 362
363 363 return results
364 364
365 365 def _diverge(ui, b, path, localmarks):
366 '''Return appropriate diverged bookmark for specified ``path``
367
368 This returns None, if it is failed to assign any divergent
369 bookmark name.
370 '''
366 371 if b == '@':
367 372 b = ''
368 373 # find a unique @ suffix
369 374 for x in range(1, 100):
370 375 n = '%s@%d' % (b, x)
371 376 if n not in localmarks:
372 377 break
378 else:
379 n = None
373 380 # try to use an @pathalias suffix
374 381 # if an @pathalias already exists, we overwrite (update) it
375 382 if path.startswith("file:"):
376 383 path = util.url(path).path
377 384 for p, u in ui.configitems("paths"):
378 385 if u.startswith("file:"):
379 386 u = util.url(u).path
380 387 if path == u:
381 388 n = '%s@%s' % (b, p)
382 389 return n
383 390
384 391 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
385 392 ui.debug("checking for updated bookmarks\n")
386 393 localmarks = repo._bookmarks
387 394 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
388 395 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
389 396
390 397 status = ui.status
391 398 warn = ui.warn
392 399 if ui.configbool('ui', 'quietbookmarkmove', False):
393 400 status = warn = ui.debug
394 401
395 402 explicit = set(explicit)
396 403 changed = []
397 404 for b, scid, dcid in addsrc:
398 405 if scid in repo: # add remote bookmarks for changes we already have
399 406 changed.append((b, bin(scid), status,
400 407 _("adding remote bookmark %s\n") % (b)))
401 408 for b, scid, dcid in advsrc:
402 409 changed.append((b, bin(scid), status,
403 410 _("updating bookmark %s\n") % (b)))
404 411 # remove normal movement from explicit set
405 412 explicit.difference_update(d[0] for d in changed)
406 413
407 414 for b, scid, dcid in diverge:
408 415 if b in explicit:
409 416 explicit.discard(b)
410 417 changed.append((b, bin(scid), status,
411 418 _("importing bookmark %s\n") % (b)))
412 419 else:
413 420 db = _diverge(ui, b, path, localmarks)
421 if db:
414 422 changed.append((db, bin(scid), warn,
415 _("divergent bookmark %s stored as %s\n")
416 % (b, db)))
423 _("divergent bookmark %s stored as %s\n") %
424 (b, db)))
425 else:
426 warn(_("warning: failed to assign numbered name "
427 "to divergent bookmark %s\n") % (b))
417 428 for b, scid, dcid in adddst + advdst:
418 429 if b in explicit:
419 430 explicit.discard(b)
420 431 changed.append((b, bin(scid), status,
421 432 _("importing bookmark %s\n") % (b)))
422 433
423 434 if changed:
424 435 tr = trfunc()
425 436 for b, node, writer, msg in sorted(changed):
426 437 localmarks[b] = node
427 438 writer(msg)
428 439 localmarks.recordchange(tr)
429 440
430 441 def diff(ui, dst, src):
431 442 ui.status(_("searching for changed bookmarks\n"))
432 443
433 444 smarks = src.listkeys('bookmarks')
434 445 dmarks = dst.listkeys('bookmarks')
435 446
436 447 diff = sorted(set(smarks) - set(dmarks))
437 448 for k in diff:
438 449 if ui.debugflag:
439 450 mark = smarks[k]
440 451 else:
441 452 mark = smarks[k][:12]
442 453 ui.write(" %-25s %s\n" % (k, mark))
443 454
444 455 if len(diff) <= 0:
445 456 ui.status(_("no changed bookmarks found\n"))
446 457 return 1
447 458 return 0
448 459
449 460 def validdest(repo, old, new):
450 461 """Is the new bookmark destination a valid update from the old one"""
451 462 repo = repo.unfiltered()
452 463 if old == new:
453 464 # Old == new -> nothing to update.
454 465 return False
455 466 elif not old:
456 467 # old is nullrev, anything is valid.
457 468 # (new != nullrev has been excluded by the previous check)
458 469 return True
459 470 elif repo.obsstore:
460 471 return new.node() in obsolete.foreground(repo, [old.node()])
461 472 else:
462 473 # still an independent clause as it is lazier (and therefore faster)
463 474 return old.descendant(new)
@@ -1,562 +1,583
1 1 #require serve
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 > [experimental]
9 9 > evolution=createmarkers,exchange
10 10 > EOF
11 11
12 12 initialize
13 13
14 14 $ hg init a
15 15 $ cd a
16 16 $ echo 'test' > test
17 17 $ hg commit -Am'test'
18 18 adding test
19 19
20 20 set bookmarks
21 21
22 22 $ hg bookmark X
23 23 $ hg bookmark Y
24 24 $ hg bookmark Z
25 25
26 26 import bookmark by name
27 27
28 28 $ hg init ../b
29 29 $ cd ../b
30 30 $ hg book Y
31 31 $ hg book
32 32 * Y -1:000000000000
33 33 $ hg pull ../a
34 34 pulling from ../a
35 35 requesting all changes
36 36 adding changesets
37 37 adding manifests
38 38 adding file changes
39 39 added 1 changesets with 1 changes to 1 files
40 40 adding remote bookmark X
41 41 updating bookmark Y
42 42 adding remote bookmark Z
43 43 (run 'hg update' to get a working copy)
44 44 $ hg bookmarks
45 45 X 0:4e3505fd9583
46 46 * Y 0:4e3505fd9583
47 47 Z 0:4e3505fd9583
48 48 $ hg debugpushkey ../a namespaces
49 49 bookmarks
50 50 namespaces
51 51 obsolete
52 52 phases
53 53 $ hg debugpushkey ../a bookmarks
54 54 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
55 55 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
56 56 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
57 57
58 58 delete the bookmark to re-pull it
59 59
60 60 $ hg book -d X
61 61 $ hg pull -B X ../a
62 62 pulling from ../a
63 63 no changes found
64 64 adding remote bookmark X
65 65
66 66 finally no-op pull
67 67
68 68 $ hg pull -B X ../a
69 69 pulling from ../a
70 70 no changes found
71 71 $ hg bookmark
72 72 X 0:4e3505fd9583
73 73 * Y 0:4e3505fd9583
74 74 Z 0:4e3505fd9583
75 75
76 76 export bookmark by name
77 77
78 78 $ hg bookmark W
79 79 $ hg bookmark foo
80 80 $ hg bookmark foobar
81 81 $ hg push -B W ../a
82 82 pushing to ../a
83 83 searching for changes
84 84 no changes found
85 85 exporting bookmark W
86 86 [1]
87 87 $ hg -R ../a bookmarks
88 88 W -1:000000000000
89 89 X 0:4e3505fd9583
90 90 Y 0:4e3505fd9583
91 91 * Z 0:4e3505fd9583
92 92
93 93 delete a remote bookmark
94 94
95 95 $ hg book -d W
96 96 $ hg push -B W ../a
97 97 pushing to ../a
98 98 searching for changes
99 99 no changes found
100 100 deleting remote bookmark W
101 101 [1]
102 102
103 103 push/pull name that doesn't exist
104 104
105 105 $ hg push -B badname ../a
106 106 pushing to ../a
107 107 searching for changes
108 108 bookmark badname does not exist on the local or remote repository!
109 109 no changes found
110 110 [2]
111 111 $ hg pull -B anotherbadname ../a
112 112 pulling from ../a
113 113 abort: remote bookmark anotherbadname not found!
114 114 [255]
115 115
116 116 divergent bookmarks
117 117
118 118 $ cd ../a
119 119 $ echo c1 > f1
120 120 $ hg ci -Am1
121 121 adding f1
122 122 $ hg book -f @
123 123 $ hg book -f X
124 124 $ hg book
125 125 @ 1:0d2164f0ce0d
126 126 * X 1:0d2164f0ce0d
127 127 Y 0:4e3505fd9583
128 128 Z 1:0d2164f0ce0d
129 129
130 130 $ cd ../b
131 131 $ hg up
132 132 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
133 133 updating bookmark foobar
134 134 $ echo c2 > f2
135 135 $ hg ci -Am2
136 136 adding f2
137 137 $ hg book -if @
138 138 $ hg book -if X
139 139 $ hg book
140 140 @ 1:9b140be10808
141 141 X 1:9b140be10808
142 142 Y 0:4e3505fd9583
143 143 Z 0:4e3505fd9583
144 144 foo -1:000000000000
145 145 * foobar 1:9b140be10808
146 146
147 147 $ hg pull --config paths.foo=../a foo
148 148 pulling from $TESTTMP/a (glob)
149 149 searching for changes
150 150 adding changesets
151 151 adding manifests
152 152 adding file changes
153 153 added 1 changesets with 1 changes to 1 files (+1 heads)
154 154 divergent bookmark @ stored as @foo
155 155 divergent bookmark X stored as X@foo
156 156 updating bookmark Z
157 157 (run 'hg heads' to see heads, 'hg merge' to merge)
158 158 $ hg book
159 159 @ 1:9b140be10808
160 160 @foo 2:0d2164f0ce0d
161 161 X 1:9b140be10808
162 162 X@foo 2:0d2164f0ce0d
163 163 Y 0:4e3505fd9583
164 164 Z 2:0d2164f0ce0d
165 165 foo -1:000000000000
166 166 * foobar 1:9b140be10808
167
168 (test that too many divergence of bookmark)
169
170 $ cat > $TESTTMP/seq.py <<EOF
171 > import sys
172 > for i in xrange(*[int(a) for a in sys.argv[1:]]):
173 > print i
174 > EOF
175 $ python $TESTTMP/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
176 $ hg pull ../a
177 pulling from ../a
178 searching for changes
179 no changes found
180 warning: failed to assign numbered name to divergent bookmark X
181 divergent bookmark @ stored as @1
182 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
183 X 1:9b140be10808
184 X@foo 2:0d2164f0ce0d
185 $ python $TESTTMP/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
186 $ hg bookmarks -d "@1"
187
167 188 $ hg push -f ../a
168 189 pushing to ../a
169 190 searching for changes
170 191 adding changesets
171 192 adding manifests
172 193 adding file changes
173 194 added 1 changesets with 1 changes to 1 files (+1 heads)
174 195 $ hg -R ../a book
175 196 @ 1:0d2164f0ce0d
176 197 * X 1:0d2164f0ce0d
177 198 Y 0:4e3505fd9583
178 199 Z 1:0d2164f0ce0d
179 200
180 201 explicit pull should overwrite the local version (issue4439)
181 202
182 203 $ hg pull --config paths.foo=../a foo -B X
183 204 pulling from $TESTTMP/a (glob)
184 205 no changes found
185 206 divergent bookmark @ stored as @foo
186 207 importing bookmark X
187 208
188 209 reinstall state for further testing:
189 210
190 211 $ hg book -fr 9b140be10808 X
191 212
192 213 revsets should not ignore divergent bookmarks
193 214
194 215 $ hg bookmark -fr 1 Z
195 216 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
196 217 0:4e3505fd9583 Y
197 218 1:9b140be10808 @ X Z foobar
198 219 2:0d2164f0ce0d @foo X@foo
199 220 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
200 221 2:0d2164f0ce0d @foo X@foo
201 222 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
202 223 2:0d2164f0ce0d @foo X@foo
203 224
204 225 update a remote bookmark from a non-head to a head
205 226
206 227 $ hg up -q Y
207 228 $ echo c3 > f2
208 229 $ hg ci -Am3
209 230 adding f2
210 231 created new head
211 232 $ hg push ../a
212 233 pushing to ../a
213 234 searching for changes
214 235 adding changesets
215 236 adding manifests
216 237 adding file changes
217 238 added 1 changesets with 1 changes to 1 files (+1 heads)
218 239 updating bookmark Y
219 240 $ hg -R ../a book
220 241 @ 1:0d2164f0ce0d
221 242 * X 1:0d2164f0ce0d
222 243 Y 3:f6fc62dde3c0
223 244 Z 1:0d2164f0ce0d
224 245
225 246 update a bookmark in the middle of a client pulling changes
226 247
227 248 $ cd ..
228 249 $ hg clone -q a pull-race
229 250 $ hg clone -q pull-race pull-race2
230 251 $ cd pull-race
231 252 $ hg up -q Y
232 253 $ echo c4 > f2
233 254 $ hg ci -Am4
234 255 $ echo c5 > f3
235 256 $ cat <<EOF > .hg/hgrc
236 257 > [hooks]
237 258 > outgoing.makecommit = hg ci -Am5; echo committed in pull-race
238 259 > EOF
239 260 $ cd ../pull-race2
240 261 $ hg pull
241 262 pulling from $TESTTMP/pull-race (glob)
242 263 searching for changes
243 264 adding changesets
244 265 adding f3
245 266 committed in pull-race
246 267 adding manifests
247 268 adding file changes
248 269 added 1 changesets with 1 changes to 1 files
249 270 updating bookmark Y
250 271 (run 'hg update' to get a working copy)
251 272 $ hg book
252 273 * @ 1:0d2164f0ce0d
253 274 X 1:0d2164f0ce0d
254 275 Y 4:b0a5eff05604
255 276 Z 1:0d2164f0ce0d
256 277 $ cd ../b
257 278
258 279 diverging a remote bookmark fails
259 280
260 281 $ hg up -q 4e3505fd9583
261 282 $ echo c4 > f2
262 283 $ hg ci -Am4
263 284 adding f2
264 285 created new head
265 286 $ echo c5 > f2
266 287 $ hg ci -Am5
267 288 $ hg log -G
268 289 @ 5:c922c0139ca0 5
269 290 |
270 291 o 4:4efff6d98829 4
271 292 |
272 293 | o 3:f6fc62dde3c0 3
273 294 |/
274 295 | o 2:0d2164f0ce0d 1
275 296 |/
276 297 | o 1:9b140be10808 2
277 298 |/
278 299 o 0:4e3505fd9583 test
279 300
280 301
281 302 $ hg book -f Y
282 303
283 304 $ cat <<EOF > ../a/.hg/hgrc
284 305 > [web]
285 306 > push_ssl = false
286 307 > allow_push = *
287 308 > EOF
288 309
289 310 $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid
290 311 $ cat ../hg2.pid >> $DAEMON_PIDS
291 312
292 313 $ hg push http://localhost:$HGPORT2/
293 314 pushing to http://localhost:$HGPORT2/
294 315 searching for changes
295 316 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
296 317 (merge or see "hg help push" for details about pushing new heads)
297 318 [255]
298 319 $ hg -R ../a book
299 320 @ 1:0d2164f0ce0d
300 321 * X 1:0d2164f0ce0d
301 322 Y 3:f6fc62dde3c0
302 323 Z 1:0d2164f0ce0d
303 324
304 325
305 326 Unrelated marker does not alter the decision
306 327
307 328 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
308 329 $ hg push http://localhost:$HGPORT2/
309 330 pushing to http://localhost:$HGPORT2/
310 331 searching for changes
311 332 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
312 333 (merge or see "hg help push" for details about pushing new heads)
313 334 [255]
314 335 $ hg -R ../a book
315 336 @ 1:0d2164f0ce0d
316 337 * X 1:0d2164f0ce0d
317 338 Y 3:f6fc62dde3c0
318 339 Z 1:0d2164f0ce0d
319 340
320 341 Update to a successor works
321 342
322 343 $ hg id --debug -r 3
323 344 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
324 345 $ hg id --debug -r 4
325 346 4efff6d98829d9c824c621afd6e3f01865f5439f
326 347 $ hg id --debug -r 5
327 348 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
328 349 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
329 350 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
330 351 $ hg push http://localhost:$HGPORT2/
331 352 pushing to http://localhost:$HGPORT2/
332 353 searching for changes
333 354 remote: adding changesets
334 355 remote: adding manifests
335 356 remote: adding file changes
336 357 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
337 358 updating bookmark Y
338 359 $ hg -R ../a book
339 360 @ 1:0d2164f0ce0d
340 361 * X 1:0d2164f0ce0d
341 362 Y 5:c922c0139ca0
342 363 Z 1:0d2164f0ce0d
343 364
344 365 hgweb
345 366
346 367 $ cat <<EOF > .hg/hgrc
347 368 > [web]
348 369 > push_ssl = false
349 370 > allow_push = *
350 371 > EOF
351 372
352 373 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
353 374 $ cat ../hg.pid >> $DAEMON_PIDS
354 375 $ cd ../a
355 376
356 377 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
357 378 bookmarks
358 379 namespaces
359 380 obsolete
360 381 phases
361 382 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
362 383 @ 9b140be1080824d768c5a4691a564088eede71f9
363 384 X 9b140be1080824d768c5a4691a564088eede71f9
364 385 Y c922c0139ca03858f655e4a2af4dd02796a63969
365 386 Z 9b140be1080824d768c5a4691a564088eede71f9
366 387 foo 0000000000000000000000000000000000000000
367 388 foobar 9b140be1080824d768c5a4691a564088eede71f9
368 389 $ hg out -B http://localhost:$HGPORT/
369 390 comparing with http://localhost:$HGPORT/
370 391 searching for changed bookmarks
371 392 no changed bookmarks found
372 393 [1]
373 394 $ hg push -B Z http://localhost:$HGPORT/
374 395 pushing to http://localhost:$HGPORT/
375 396 searching for changes
376 397 no changes found
377 398 updating bookmark Z
378 399 [1]
379 400 $ hg book -d Z
380 401 $ hg in -B http://localhost:$HGPORT/
381 402 comparing with http://localhost:$HGPORT/
382 403 searching for changed bookmarks
383 404 Z 0d2164f0ce0d
384 405 foo 000000000000
385 406 foobar 9b140be10808
386 407 $ hg pull -B Z http://localhost:$HGPORT/
387 408 pulling from http://localhost:$HGPORT/
388 409 no changes found
389 410 divergent bookmark @ stored as @1
390 411 divergent bookmark X stored as X@1
391 412 adding remote bookmark Z
392 413 adding remote bookmark foo
393 414 adding remote bookmark foobar
394 415 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
395 416 requesting all changes
396 417 adding changesets
397 418 adding manifests
398 419 adding file changes
399 420 added 5 changesets with 5 changes to 3 files (+2 heads)
400 421 updating to bookmark @
401 422 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
402 423 $ hg -R cloned-bookmarks bookmarks
403 424 * @ 1:9b140be10808
404 425 X 1:9b140be10808
405 426 Y 4:c922c0139ca0
406 427 Z 2:0d2164f0ce0d
407 428 foo -1:000000000000
408 429 foobar 1:9b140be10808
409 430
410 431 $ cd ..
411 432
412 433 Pushing a bookmark should only push the changes required by that
413 434 bookmark, not all outgoing changes:
414 435 $ hg clone http://localhost:$HGPORT/ addmarks
415 436 requesting all changes
416 437 adding changesets
417 438 adding manifests
418 439 adding file changes
419 440 added 5 changesets with 5 changes to 3 files (+2 heads)
420 441 updating to bookmark @
421 442 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
422 443 $ cd addmarks
423 444 $ echo foo > foo
424 445 $ hg add foo
425 446 $ hg commit -m 'add foo'
426 447 $ echo bar > bar
427 448 $ hg add bar
428 449 $ hg commit -m 'add bar'
429 450 $ hg co "tip^"
430 451 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
431 452 (leaving bookmark @)
432 453 $ hg book add-foo
433 454 $ hg book -r tip add-bar
434 455 Note: this push *must* push only a single changeset, as that's the point
435 456 of this test.
436 457 $ hg push -B add-foo --traceback
437 458 pushing to http://localhost:$HGPORT/
438 459 searching for changes
439 460 remote: adding changesets
440 461 remote: adding manifests
441 462 remote: adding file changes
442 463 remote: added 1 changesets with 1 changes to 1 files
443 464 exporting bookmark add-foo
444 465
445 466 pushing a new bookmark on a new head does not require -f if -B is specified
446 467
447 468 $ hg up -q X
448 469 $ hg book W
449 470 $ echo c5 > f2
450 471 $ hg ci -Am5
451 472 created new head
452 473 $ hg push -B W
453 474 pushing to http://localhost:$HGPORT/
454 475 searching for changes
455 476 remote: adding changesets
456 477 remote: adding manifests
457 478 remote: adding file changes
458 479 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
459 480 exporting bookmark W
460 481 $ hg -R ../b id -r W
461 482 cc978a373a53 tip W
462 483
463 484 $ cd ..
464 485
465 486 pushing an unchanged bookmark should result in no changes
466 487
467 488 $ hg init unchanged-a
468 489 $ hg init unchanged-b
469 490 $ cd unchanged-a
470 491 $ echo initial > foo
471 492 $ hg commit -A -m initial
472 493 adding foo
473 494 $ hg bookmark @
474 495 $ hg push -B @ ../unchanged-b
475 496 pushing to ../unchanged-b
476 497 searching for changes
477 498 adding changesets
478 499 adding manifests
479 500 adding file changes
480 501 added 1 changesets with 1 changes to 1 files
481 502 exporting bookmark @
482 503
483 504 $ hg push -B @ ../unchanged-b
484 505 pushing to ../unchanged-b
485 506 searching for changes
486 507 no changes found
487 508 [1]
488 509
489 510
490 511 Check hook preventing push (issue4455)
491 512 ======================================
492 513
493 514 $ hg bookmarks
494 515 * @ 0:55482a6fb4b1
495 516 $ hg log -G
496 517 @ 0:55482a6fb4b1 initial
497 518
498 519 $ hg init ../issue4455-dest
499 520 $ hg push ../issue4455-dest # changesets only
500 521 pushing to ../issue4455-dest
501 522 searching for changes
502 523 adding changesets
503 524 adding manifests
504 525 adding file changes
505 526 added 1 changesets with 1 changes to 1 files
506 527 $ cat >> .hg/hgrc << EOF
507 528 > [paths]
508 529 > local=../issue4455-dest/
509 530 > ssh=ssh://user@dummy/issue4455-dest
510 531 > http=http://localhost:$HGPORT/
511 532 > [ui]
512 533 > ssh=python "$TESTDIR/dummyssh"
513 534 > EOF
514 535 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
515 536 > [hooks]
516 537 > prepushkey=false
517 538 > [web]
518 539 > push_ssl = false
519 540 > allow_push = *
520 541 > EOF
521 542 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
522 543 $ hg -R ../issue4455-dest serve -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
523 544 $ cat ../issue4455.pid >> $DAEMON_PIDS
524 545
525 546 Local push
526 547 ----------
527 548
528 549 $ hg push -B @ local
529 550 pushing to $TESTTMP/issue4455-dest (glob)
530 551 searching for changes
531 552 no changes found
532 553 pushkey-abort: prepushkey hook exited with status 1
533 554 exporting bookmark @ failed!
534 555 [1]
535 556 $ hg -R ../issue4455-dest/ bookmarks
536 557 no bookmarks set
537 558
538 559 Using ssh
539 560 ---------
540 561
541 562 $ hg push -B @ ssh
542 563 pushing to ssh://user@dummy/issue4455-dest
543 564 searching for changes
544 565 no changes found
545 566 remote: pushkey-abort: prepushkey hook exited with status 1
546 567 exporting bookmark @ failed!
547 568 [1]
548 569 $ hg -R ../issue4455-dest/ bookmarks
549 570 no bookmarks set
550 571
551 572 Using http
552 573 ----------
553 574
554 575 $ hg push -B @ http
555 576 pushing to http://localhost:$HGPORT/
556 577 searching for changes
557 578 no changes found
558 579 remote: pushkey-abort: prepushkey hook exited with status 1
559 580 exporting bookmark @ failed!
560 581 [1]
561 582 $ hg -R ../issue4455-dest/ bookmarks
562 583 no bookmarks set
General Comments 0
You need to be logged in to leave comments. Login now