##// END OF EJS Templates
bookmarks: reuse @number bookmark, if it refers changeset referred remotely...
FUJIWARA Katsunori -
r24355:ca4b8968 default
parent child Browse files
Show More
@@ -1,474 +1,478 b''
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 def _diverge(ui, b, path, localmarks):
365 def _diverge(ui, b, path, localmarks, remotenode):
366 366 '''Return appropriate diverged bookmark for specified ``path``
367 367
368 368 This returns None, if it is failed to assign any divergent
369 369 bookmark name.
370
371 This reuses already existing one with "@number" suffix, if it
372 refers ``remotenode``.
370 373 '''
371 374 if b == '@':
372 375 b = ''
373 376 # try to use an @pathalias suffix
374 377 # if an @pathalias already exists, we overwrite (update) it
375 378 if path.startswith("file:"):
376 379 path = util.url(path).path
377 380 for p, u in ui.configitems("paths"):
378 381 if u.startswith("file:"):
379 382 u = util.url(u).path
380 383 if path == u:
381 384 return '%s@%s' % (b, p)
382 385
383 386 # assign a unique "@number" suffix newly
384 387 for x in range(1, 100):
385 388 n = '%s@%d' % (b, x)
386 if n not in localmarks:
389 if n not in localmarks or localmarks[n] == remotenode:
387 390 return n
388 391
389 392 return None
390 393
391 394 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
392 395 ui.debug("checking for updated bookmarks\n")
393 396 localmarks = repo._bookmarks
394 397 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
395 398 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
396 399
397 400 status = ui.status
398 401 warn = ui.warn
399 402 if ui.configbool('ui', 'quietbookmarkmove', False):
400 403 status = warn = ui.debug
401 404
402 405 explicit = set(explicit)
403 406 changed = []
404 407 for b, scid, dcid in addsrc:
405 408 if scid in repo: # add remote bookmarks for changes we already have
406 409 changed.append((b, bin(scid), status,
407 410 _("adding remote bookmark %s\n") % (b)))
408 411 for b, scid, dcid in advsrc:
409 412 changed.append((b, bin(scid), status,
410 413 _("updating bookmark %s\n") % (b)))
411 414 # remove normal movement from explicit set
412 415 explicit.difference_update(d[0] for d in changed)
413 416
414 417 for b, scid, dcid in diverge:
415 418 if b in explicit:
416 419 explicit.discard(b)
417 420 changed.append((b, bin(scid), status,
418 421 _("importing bookmark %s\n") % (b)))
419 422 else:
420 db = _diverge(ui, b, path, localmarks)
423 snode = bin(scid)
424 db = _diverge(ui, b, path, localmarks, snode)
421 425 if db:
422 changed.append((db, bin(scid), warn,
426 changed.append((db, snode, warn,
423 427 _("divergent bookmark %s stored as %s\n") %
424 428 (b, db)))
425 429 else:
426 430 warn(_("warning: failed to assign numbered name "
427 431 "to divergent bookmark %s\n") % (b))
428 432 for b, scid, dcid in adddst + advdst:
429 433 if b in explicit:
430 434 explicit.discard(b)
431 435 changed.append((b, bin(scid), status,
432 436 _("importing bookmark %s\n") % (b)))
433 437
434 438 if changed:
435 439 tr = trfunc()
436 440 for b, node, writer, msg in sorted(changed):
437 441 localmarks[b] = node
438 442 writer(msg)
439 443 localmarks.recordchange(tr)
440 444
441 445 def diff(ui, dst, src):
442 446 ui.status(_("searching for changed bookmarks\n"))
443 447
444 448 smarks = src.listkeys('bookmarks')
445 449 dmarks = dst.listkeys('bookmarks')
446 450
447 451 diff = sorted(set(smarks) - set(dmarks))
448 452 for k in diff:
449 453 if ui.debugflag:
450 454 mark = smarks[k]
451 455 else:
452 456 mark = smarks[k][:12]
453 457 ui.write(" %-25s %s\n" % (k, mark))
454 458
455 459 if len(diff) <= 0:
456 460 ui.status(_("no changed bookmarks found\n"))
457 461 return 1
458 462 return 0
459 463
460 464 def validdest(repo, old, new):
461 465 """Is the new bookmark destination a valid update from the old one"""
462 466 repo = repo.unfiltered()
463 467 if old == new:
464 468 # Old == new -> nothing to update.
465 469 return False
466 470 elif not old:
467 471 # old is nullrev, anything is valid.
468 472 # (new != nullrev has been excluded by the previous check)
469 473 return True
470 474 elif repo.obsstore:
471 475 return new.node() in obsolete.foreground(repo, [old.node()])
472 476 else:
473 477 # still an independent clause as it is lazier (and therefore faster)
474 478 return old.descendant(new)
@@ -1,583 +1,601 b''
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 167
168 168 (test that too many divergence of bookmark)
169 169
170 170 $ cat > $TESTTMP/seq.py <<EOF
171 171 > import sys
172 172 > for i in xrange(*[int(a) for a in sys.argv[1:]]):
173 173 > print i
174 174 > EOF
175 175 $ python $TESTTMP/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
176 176 $ hg pull ../a
177 177 pulling from ../a
178 178 searching for changes
179 179 no changes found
180 180 warning: failed to assign numbered name to divergent bookmark X
181 181 divergent bookmark @ stored as @1
182 182 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
183 183 X 1:9b140be10808
184 184 X@foo 2:0d2164f0ce0d
185
186 (test that remotely diverged bookmarks are reused if they aren't changed)
187
188 $ hg bookmarks | grep '^ @'
189 @ 1:9b140be10808
190 @1 2:0d2164f0ce0d
191 @foo 2:0d2164f0ce0d
192 $ hg pull ../a
193 pulling from ../a
194 searching for changes
195 no changes found
196 warning: failed to assign numbered name to divergent bookmark X
197 divergent bookmark @ stored as @1
198 $ hg bookmarks | grep '^ @'
199 @ 1:9b140be10808
200 @1 2:0d2164f0ce0d
201 @foo 2:0d2164f0ce0d
202
185 203 $ python $TESTTMP/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
186 204 $ hg bookmarks -d "@1"
187 205
188 206 $ hg push -f ../a
189 207 pushing to ../a
190 208 searching for changes
191 209 adding changesets
192 210 adding manifests
193 211 adding file changes
194 212 added 1 changesets with 1 changes to 1 files (+1 heads)
195 213 $ hg -R ../a book
196 214 @ 1:0d2164f0ce0d
197 215 * X 1:0d2164f0ce0d
198 216 Y 0:4e3505fd9583
199 217 Z 1:0d2164f0ce0d
200 218
201 219 explicit pull should overwrite the local version (issue4439)
202 220
203 221 $ hg pull --config paths.foo=../a foo -B X
204 222 pulling from $TESTTMP/a (glob)
205 223 no changes found
206 224 divergent bookmark @ stored as @foo
207 225 importing bookmark X
208 226
209 227 reinstall state for further testing:
210 228
211 229 $ hg book -fr 9b140be10808 X
212 230
213 231 revsets should not ignore divergent bookmarks
214 232
215 233 $ hg bookmark -fr 1 Z
216 234 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
217 235 0:4e3505fd9583 Y
218 236 1:9b140be10808 @ X Z foobar
219 237 2:0d2164f0ce0d @foo X@foo
220 238 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
221 239 2:0d2164f0ce0d @foo X@foo
222 240 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
223 241 2:0d2164f0ce0d @foo X@foo
224 242
225 243 update a remote bookmark from a non-head to a head
226 244
227 245 $ hg up -q Y
228 246 $ echo c3 > f2
229 247 $ hg ci -Am3
230 248 adding f2
231 249 created new head
232 250 $ hg push ../a
233 251 pushing to ../a
234 252 searching for changes
235 253 adding changesets
236 254 adding manifests
237 255 adding file changes
238 256 added 1 changesets with 1 changes to 1 files (+1 heads)
239 257 updating bookmark Y
240 258 $ hg -R ../a book
241 259 @ 1:0d2164f0ce0d
242 260 * X 1:0d2164f0ce0d
243 261 Y 3:f6fc62dde3c0
244 262 Z 1:0d2164f0ce0d
245 263
246 264 update a bookmark in the middle of a client pulling changes
247 265
248 266 $ cd ..
249 267 $ hg clone -q a pull-race
250 268 $ hg clone -q pull-race pull-race2
251 269 $ cd pull-race
252 270 $ hg up -q Y
253 271 $ echo c4 > f2
254 272 $ hg ci -Am4
255 273 $ echo c5 > f3
256 274 $ cat <<EOF > .hg/hgrc
257 275 > [hooks]
258 276 > outgoing.makecommit = hg ci -Am5; echo committed in pull-race
259 277 > EOF
260 278 $ cd ../pull-race2
261 279 $ hg pull
262 280 pulling from $TESTTMP/pull-race (glob)
263 281 searching for changes
264 282 adding changesets
265 283 adding f3
266 284 committed in pull-race
267 285 adding manifests
268 286 adding file changes
269 287 added 1 changesets with 1 changes to 1 files
270 288 updating bookmark Y
271 289 (run 'hg update' to get a working copy)
272 290 $ hg book
273 291 * @ 1:0d2164f0ce0d
274 292 X 1:0d2164f0ce0d
275 293 Y 4:b0a5eff05604
276 294 Z 1:0d2164f0ce0d
277 295 $ cd ../b
278 296
279 297 diverging a remote bookmark fails
280 298
281 299 $ hg up -q 4e3505fd9583
282 300 $ echo c4 > f2
283 301 $ hg ci -Am4
284 302 adding f2
285 303 created new head
286 304 $ echo c5 > f2
287 305 $ hg ci -Am5
288 306 $ hg log -G
289 307 @ 5:c922c0139ca0 5
290 308 |
291 309 o 4:4efff6d98829 4
292 310 |
293 311 | o 3:f6fc62dde3c0 3
294 312 |/
295 313 | o 2:0d2164f0ce0d 1
296 314 |/
297 315 | o 1:9b140be10808 2
298 316 |/
299 317 o 0:4e3505fd9583 test
300 318
301 319
302 320 $ hg book -f Y
303 321
304 322 $ cat <<EOF > ../a/.hg/hgrc
305 323 > [web]
306 324 > push_ssl = false
307 325 > allow_push = *
308 326 > EOF
309 327
310 328 $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid
311 329 $ cat ../hg2.pid >> $DAEMON_PIDS
312 330
313 331 $ hg push http://localhost:$HGPORT2/
314 332 pushing to http://localhost:$HGPORT2/
315 333 searching for changes
316 334 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
317 335 (merge or see "hg help push" for details about pushing new heads)
318 336 [255]
319 337 $ hg -R ../a book
320 338 @ 1:0d2164f0ce0d
321 339 * X 1:0d2164f0ce0d
322 340 Y 3:f6fc62dde3c0
323 341 Z 1:0d2164f0ce0d
324 342
325 343
326 344 Unrelated marker does not alter the decision
327 345
328 346 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
329 347 $ hg push http://localhost:$HGPORT2/
330 348 pushing to http://localhost:$HGPORT2/
331 349 searching for changes
332 350 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
333 351 (merge or see "hg help push" for details about pushing new heads)
334 352 [255]
335 353 $ hg -R ../a book
336 354 @ 1:0d2164f0ce0d
337 355 * X 1:0d2164f0ce0d
338 356 Y 3:f6fc62dde3c0
339 357 Z 1:0d2164f0ce0d
340 358
341 359 Update to a successor works
342 360
343 361 $ hg id --debug -r 3
344 362 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
345 363 $ hg id --debug -r 4
346 364 4efff6d98829d9c824c621afd6e3f01865f5439f
347 365 $ hg id --debug -r 5
348 366 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
349 367 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
350 368 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
351 369 $ hg push http://localhost:$HGPORT2/
352 370 pushing to http://localhost:$HGPORT2/
353 371 searching for changes
354 372 remote: adding changesets
355 373 remote: adding manifests
356 374 remote: adding file changes
357 375 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
358 376 updating bookmark Y
359 377 $ hg -R ../a book
360 378 @ 1:0d2164f0ce0d
361 379 * X 1:0d2164f0ce0d
362 380 Y 5:c922c0139ca0
363 381 Z 1:0d2164f0ce0d
364 382
365 383 hgweb
366 384
367 385 $ cat <<EOF > .hg/hgrc
368 386 > [web]
369 387 > push_ssl = false
370 388 > allow_push = *
371 389 > EOF
372 390
373 391 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
374 392 $ cat ../hg.pid >> $DAEMON_PIDS
375 393 $ cd ../a
376 394
377 395 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
378 396 bookmarks
379 397 namespaces
380 398 obsolete
381 399 phases
382 400 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
383 401 @ 9b140be1080824d768c5a4691a564088eede71f9
384 402 X 9b140be1080824d768c5a4691a564088eede71f9
385 403 Y c922c0139ca03858f655e4a2af4dd02796a63969
386 404 Z 9b140be1080824d768c5a4691a564088eede71f9
387 405 foo 0000000000000000000000000000000000000000
388 406 foobar 9b140be1080824d768c5a4691a564088eede71f9
389 407 $ hg out -B http://localhost:$HGPORT/
390 408 comparing with http://localhost:$HGPORT/
391 409 searching for changed bookmarks
392 410 no changed bookmarks found
393 411 [1]
394 412 $ hg push -B Z http://localhost:$HGPORT/
395 413 pushing to http://localhost:$HGPORT/
396 414 searching for changes
397 415 no changes found
398 416 updating bookmark Z
399 417 [1]
400 418 $ hg book -d Z
401 419 $ hg in -B http://localhost:$HGPORT/
402 420 comparing with http://localhost:$HGPORT/
403 421 searching for changed bookmarks
404 422 Z 0d2164f0ce0d
405 423 foo 000000000000
406 424 foobar 9b140be10808
407 425 $ hg pull -B Z http://localhost:$HGPORT/
408 426 pulling from http://localhost:$HGPORT/
409 427 no changes found
410 428 divergent bookmark @ stored as @1
411 429 divergent bookmark X stored as X@1
412 430 adding remote bookmark Z
413 431 adding remote bookmark foo
414 432 adding remote bookmark foobar
415 433 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
416 434 requesting all changes
417 435 adding changesets
418 436 adding manifests
419 437 adding file changes
420 438 added 5 changesets with 5 changes to 3 files (+2 heads)
421 439 updating to bookmark @
422 440 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
423 441 $ hg -R cloned-bookmarks bookmarks
424 442 * @ 1:9b140be10808
425 443 X 1:9b140be10808
426 444 Y 4:c922c0139ca0
427 445 Z 2:0d2164f0ce0d
428 446 foo -1:000000000000
429 447 foobar 1:9b140be10808
430 448
431 449 $ cd ..
432 450
433 451 Pushing a bookmark should only push the changes required by that
434 452 bookmark, not all outgoing changes:
435 453 $ hg clone http://localhost:$HGPORT/ addmarks
436 454 requesting all changes
437 455 adding changesets
438 456 adding manifests
439 457 adding file changes
440 458 added 5 changesets with 5 changes to 3 files (+2 heads)
441 459 updating to bookmark @
442 460 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 461 $ cd addmarks
444 462 $ echo foo > foo
445 463 $ hg add foo
446 464 $ hg commit -m 'add foo'
447 465 $ echo bar > bar
448 466 $ hg add bar
449 467 $ hg commit -m 'add bar'
450 468 $ hg co "tip^"
451 469 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
452 470 (leaving bookmark @)
453 471 $ hg book add-foo
454 472 $ hg book -r tip add-bar
455 473 Note: this push *must* push only a single changeset, as that's the point
456 474 of this test.
457 475 $ hg push -B add-foo --traceback
458 476 pushing to http://localhost:$HGPORT/
459 477 searching for changes
460 478 remote: adding changesets
461 479 remote: adding manifests
462 480 remote: adding file changes
463 481 remote: added 1 changesets with 1 changes to 1 files
464 482 exporting bookmark add-foo
465 483
466 484 pushing a new bookmark on a new head does not require -f if -B is specified
467 485
468 486 $ hg up -q X
469 487 $ hg book W
470 488 $ echo c5 > f2
471 489 $ hg ci -Am5
472 490 created new head
473 491 $ hg push -B W
474 492 pushing to http://localhost:$HGPORT/
475 493 searching for changes
476 494 remote: adding changesets
477 495 remote: adding manifests
478 496 remote: adding file changes
479 497 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
480 498 exporting bookmark W
481 499 $ hg -R ../b id -r W
482 500 cc978a373a53 tip W
483 501
484 502 $ cd ..
485 503
486 504 pushing an unchanged bookmark should result in no changes
487 505
488 506 $ hg init unchanged-a
489 507 $ hg init unchanged-b
490 508 $ cd unchanged-a
491 509 $ echo initial > foo
492 510 $ hg commit -A -m initial
493 511 adding foo
494 512 $ hg bookmark @
495 513 $ hg push -B @ ../unchanged-b
496 514 pushing to ../unchanged-b
497 515 searching for changes
498 516 adding changesets
499 517 adding manifests
500 518 adding file changes
501 519 added 1 changesets with 1 changes to 1 files
502 520 exporting bookmark @
503 521
504 522 $ hg push -B @ ../unchanged-b
505 523 pushing to ../unchanged-b
506 524 searching for changes
507 525 no changes found
508 526 [1]
509 527
510 528
511 529 Check hook preventing push (issue4455)
512 530 ======================================
513 531
514 532 $ hg bookmarks
515 533 * @ 0:55482a6fb4b1
516 534 $ hg log -G
517 535 @ 0:55482a6fb4b1 initial
518 536
519 537 $ hg init ../issue4455-dest
520 538 $ hg push ../issue4455-dest # changesets only
521 539 pushing to ../issue4455-dest
522 540 searching for changes
523 541 adding changesets
524 542 adding manifests
525 543 adding file changes
526 544 added 1 changesets with 1 changes to 1 files
527 545 $ cat >> .hg/hgrc << EOF
528 546 > [paths]
529 547 > local=../issue4455-dest/
530 548 > ssh=ssh://user@dummy/issue4455-dest
531 549 > http=http://localhost:$HGPORT/
532 550 > [ui]
533 551 > ssh=python "$TESTDIR/dummyssh"
534 552 > EOF
535 553 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
536 554 > [hooks]
537 555 > prepushkey=false
538 556 > [web]
539 557 > push_ssl = false
540 558 > allow_push = *
541 559 > EOF
542 560 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
543 561 $ hg -R ../issue4455-dest serve -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
544 562 $ cat ../issue4455.pid >> $DAEMON_PIDS
545 563
546 564 Local push
547 565 ----------
548 566
549 567 $ hg push -B @ local
550 568 pushing to $TESTTMP/issue4455-dest (glob)
551 569 searching for changes
552 570 no changes found
553 571 pushkey-abort: prepushkey hook exited with status 1
554 572 exporting bookmark @ failed!
555 573 [1]
556 574 $ hg -R ../issue4455-dest/ bookmarks
557 575 no bookmarks set
558 576
559 577 Using ssh
560 578 ---------
561 579
562 580 $ hg push -B @ ssh
563 581 pushing to ssh://user@dummy/issue4455-dest
564 582 searching for changes
565 583 no changes found
566 584 remote: pushkey-abort: prepushkey hook exited with status 1
567 585 exporting bookmark @ failed!
568 586 [1]
569 587 $ hg -R ../issue4455-dest/ bookmarks
570 588 no bookmarks set
571 589
572 590 Using http
573 591 ----------
574 592
575 593 $ hg push -B @ http
576 594 pushing to http://localhost:$HGPORT/
577 595 searching for changes
578 596 no changes found
579 597 remote: pushkey-abort: prepushkey hook exited with status 1
580 598 exporting bookmark @ failed!
581 599 [1]
582 600 $ hg -R ../issue4455-dest/ bookmarks
583 601 no bookmarks set
General Comments 0
You need to be logged in to leave comments. Login now