##// END OF EJS Templates
bookmarks: remove unused updatecurrentbookmark function (API)...
Ryan McElroy -
r24962:eecd4836 default
parent child Browse files
Show More
@@ -1,558 +1,549 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 from mercurial import encoding, error, util, obsolete, lock as lockmod
11 from mercurial import encoding, 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._activebookmark not in self:
87 87 deactivate(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 readactive(repo):
110 110 """
111 111 Get the active bookmark. We can have an active bookmark that updates
112 112 itself as we commit. This function returns the name of that bookmark.
113 113 It is stored in .hg/bookmarks.current
114 114 """
115 115 mark = None
116 116 try:
117 117 file = repo.vfs('bookmarks.current')
118 118 except IOError, inst:
119 119 if inst.errno != errno.ENOENT:
120 120 raise
121 121 return None
122 122 try:
123 123 # No readline() in osutil.posixfile, reading everything is cheap
124 124 mark = encoding.tolocal((file.readlines() or [''])[0])
125 125 if mark == '' or mark not in repo._bookmarks:
126 126 mark = None
127 127 finally:
128 128 file.close()
129 129 return mark
130 130
131 131 def activate(repo, mark):
132 132 """
133 133 Set the given bookmark to be 'active', meaning that this bookmark will
134 134 follow new commits that are made.
135 135 The name is recorded in .hg/bookmarks.current
136 136 """
137 137 if mark not in repo._bookmarks:
138 138 raise AssertionError('bookmark %s does not exist!' % mark)
139 139
140 140 current = repo._activebookmark
141 141 if current == mark:
142 142 return
143 143
144 144 wlock = repo.wlock()
145 145 try:
146 146 file = repo.vfs('bookmarks.current', 'w', atomictemp=True)
147 147 file.write(encoding.fromlocal(mark))
148 148 file.close()
149 149 finally:
150 150 wlock.release()
151 151 repo._activebookmark = mark
152 152
153 153 def deactivate(repo):
154 154 """
155 155 Unset the active bookmark in this reposiotry.
156 156 """
157 157 wlock = repo.wlock()
158 158 try:
159 159 try:
160 160 repo.vfs.unlink('bookmarks.current')
161 161 repo._activebookmark = None
162 162 except OSError, inst:
163 163 if inst.errno != errno.ENOENT:
164 164 raise
165 165 finally:
166 166 wlock.release()
167 167
168 168 def iscurrent(repo, mark=None, parents=None):
169 169 '''Tell whether the current bookmark is also active
170 170
171 171 I.e., the bookmark listed in .hg/bookmarks.current also points to a
172 172 parent of the working directory.
173 173 '''
174 174 if not mark:
175 175 mark = repo._activebookmark
176 176 if not parents:
177 177 parents = [p.node() for p in repo[None].parents()]
178 178 marks = repo._bookmarks
179 179 return (mark in marks and marks[mark] in parents)
180 180
181 def updatecurrentbookmark(repo, oldnode, curbranch):
182 try:
183 return update(repo, oldnode, repo.branchtip(curbranch))
184 except error.RepoLookupError:
185 if curbranch == "default": # no default branch!
186 return update(repo, oldnode, repo.lookup("tip"))
187 else:
188 raise util.Abort(_("branch %s not found") % curbranch)
189
190 181 def deletedivergent(repo, deletefrom, bm):
191 182 '''Delete divergent versions of bm on nodes in deletefrom.
192 183
193 184 Return True if at least one bookmark was deleted, False otherwise.'''
194 185 deleted = False
195 186 marks = repo._bookmarks
196 187 divergent = [b for b in marks if b.split('@', 1)[0] == bm.split('@', 1)[0]]
197 188 for mark in divergent:
198 189 if mark == '@' or '@' not in mark:
199 190 # can't be divergent by definition
200 191 continue
201 192 if mark and marks[mark] in deletefrom:
202 193 if mark != bm:
203 194 del marks[mark]
204 195 deleted = True
205 196 return deleted
206 197
207 198 def calculateupdate(ui, repo, checkout):
208 199 '''Return a tuple (targetrev, movemarkfrom) indicating the rev to
209 200 check out and where to move the active bookmark from, if needed.'''
210 201 movemarkfrom = None
211 202 if checkout is None:
212 203 curmark = repo._activebookmark
213 204 if iscurrent(repo):
214 205 movemarkfrom = repo['.'].node()
215 206 elif curmark:
216 207 ui.status(_("updating to active bookmark %s\n") % curmark)
217 208 checkout = curmark
218 209 return (checkout, movemarkfrom)
219 210
220 211 def update(repo, parents, node):
221 212 deletefrom = parents
222 213 marks = repo._bookmarks
223 214 update = False
224 215 cur = repo._activebookmark
225 216 if not cur:
226 217 return False
227 218
228 219 if marks[cur] in parents:
229 220 new = repo[node]
230 221 divs = [repo[b] for b in marks
231 222 if b.split('@', 1)[0] == cur.split('@', 1)[0]]
232 223 anc = repo.changelog.ancestors([new.rev()])
233 224 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
234 225 if validdest(repo, repo[marks[cur]], new):
235 226 marks[cur] = new.node()
236 227 update = True
237 228
238 229 if deletedivergent(repo, deletefrom, cur):
239 230 update = True
240 231
241 232 if update:
242 233 marks.write()
243 234 return update
244 235
245 236 def listbookmarks(repo):
246 237 # We may try to list bookmarks on a repo type that does not
247 238 # support it (e.g., statichttprepository).
248 239 marks = getattr(repo, '_bookmarks', {})
249 240
250 241 d = {}
251 242 hasnode = repo.changelog.hasnode
252 243 for k, v in marks.iteritems():
253 244 # don't expose local divergent bookmarks
254 245 if hasnode(v) and ('@' not in k or k.endswith('@')):
255 246 d[k] = hex(v)
256 247 return d
257 248
258 249 def pushbookmark(repo, key, old, new):
259 250 w = l = tr = None
260 251 try:
261 252 w = repo.wlock()
262 253 l = repo.lock()
263 254 tr = repo.transaction('bookmarks')
264 255 marks = repo._bookmarks
265 256 existing = hex(marks.get(key, ''))
266 257 if existing != old and existing != new:
267 258 return False
268 259 if new == '':
269 260 del marks[key]
270 261 else:
271 262 if new not in repo:
272 263 return False
273 264 marks[key] = repo[new].node()
274 265 marks.recordchange(tr)
275 266 tr.close()
276 267 return True
277 268 finally:
278 269 lockmod.release(tr, l, w)
279 270
280 271 def compare(repo, srcmarks, dstmarks,
281 272 srchex=None, dsthex=None, targets=None):
282 273 '''Compare bookmarks between srcmarks and dstmarks
283 274
284 275 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
285 276 differ, invalid)", each are list of bookmarks below:
286 277
287 278 :addsrc: added on src side (removed on dst side, perhaps)
288 279 :adddst: added on dst side (removed on src side, perhaps)
289 280 :advsrc: advanced on src side
290 281 :advdst: advanced on dst side
291 282 :diverge: diverge
292 283 :differ: changed, but changeset referred on src is unknown on dst
293 284 :invalid: unknown on both side
294 285 :same: same on both side
295 286
296 287 Each elements of lists in result tuple is tuple "(bookmark name,
297 288 changeset ID on source side, changeset ID on destination
298 289 side)". Each changeset IDs are 40 hexadecimal digit string or
299 290 None.
300 291
301 292 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
302 293 "invalid" list may be unknown for repo.
303 294
304 295 This function expects that "srcmarks" and "dstmarks" return
305 296 changeset ID in 40 hexadecimal digit string for specified
306 297 bookmark. If not so (e.g. bmstore "repo._bookmarks" returning
307 298 binary value), "srchex" or "dsthex" should be specified to convert
308 299 into such form.
309 300
310 301 If "targets" is specified, only bookmarks listed in it are
311 302 examined.
312 303 '''
313 304 if not srchex:
314 305 srchex = lambda x: x
315 306 if not dsthex:
316 307 dsthex = lambda x: x
317 308
318 309 if targets:
319 310 bset = set(targets)
320 311 else:
321 312 srcmarkset = set(srcmarks)
322 313 dstmarkset = set(dstmarks)
323 314 bset = srcmarkset | dstmarkset
324 315
325 316 results = ([], [], [], [], [], [], [], [])
326 317 addsrc = results[0].append
327 318 adddst = results[1].append
328 319 advsrc = results[2].append
329 320 advdst = results[3].append
330 321 diverge = results[4].append
331 322 differ = results[5].append
332 323 invalid = results[6].append
333 324 same = results[7].append
334 325
335 326 for b in sorted(bset):
336 327 if b not in srcmarks:
337 328 if b in dstmarks:
338 329 adddst((b, None, dsthex(dstmarks[b])))
339 330 else:
340 331 invalid((b, None, None))
341 332 elif b not in dstmarks:
342 333 addsrc((b, srchex(srcmarks[b]), None))
343 334 else:
344 335 scid = srchex(srcmarks[b])
345 336 dcid = dsthex(dstmarks[b])
346 337 if scid == dcid:
347 338 same((b, scid, dcid))
348 339 elif scid in repo and dcid in repo:
349 340 sctx = repo[scid]
350 341 dctx = repo[dcid]
351 342 if sctx.rev() < dctx.rev():
352 343 if validdest(repo, sctx, dctx):
353 344 advdst((b, scid, dcid))
354 345 else:
355 346 diverge((b, scid, dcid))
356 347 else:
357 348 if validdest(repo, dctx, sctx):
358 349 advsrc((b, scid, dcid))
359 350 else:
360 351 diverge((b, scid, dcid))
361 352 else:
362 353 # it is too expensive to examine in detail, in this case
363 354 differ((b, scid, dcid))
364 355
365 356 return results
366 357
367 358 def _diverge(ui, b, path, localmarks, remotenode):
368 359 '''Return appropriate diverged bookmark for specified ``path``
369 360
370 361 This returns None, if it is failed to assign any divergent
371 362 bookmark name.
372 363
373 364 This reuses already existing one with "@number" suffix, if it
374 365 refers ``remotenode``.
375 366 '''
376 367 if b == '@':
377 368 b = ''
378 369 # try to use an @pathalias suffix
379 370 # if an @pathalias already exists, we overwrite (update) it
380 371 if path.startswith("file:"):
381 372 path = util.url(path).path
382 373 for p, u in ui.configitems("paths"):
383 374 if u.startswith("file:"):
384 375 u = util.url(u).path
385 376 if path == u:
386 377 return '%s@%s' % (b, p)
387 378
388 379 # assign a unique "@number" suffix newly
389 380 for x in range(1, 100):
390 381 n = '%s@%d' % (b, x)
391 382 if n not in localmarks or localmarks[n] == remotenode:
392 383 return n
393 384
394 385 return None
395 386
396 387 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
397 388 ui.debug("checking for updated bookmarks\n")
398 389 localmarks = repo._bookmarks
399 390 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
400 391 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
401 392
402 393 status = ui.status
403 394 warn = ui.warn
404 395 if ui.configbool('ui', 'quietbookmarkmove', False):
405 396 status = warn = ui.debug
406 397
407 398 explicit = set(explicit)
408 399 changed = []
409 400 for b, scid, dcid in addsrc:
410 401 if scid in repo: # add remote bookmarks for changes we already have
411 402 changed.append((b, bin(scid), status,
412 403 _("adding remote bookmark %s\n") % (b)))
413 404 for b, scid, dcid in advsrc:
414 405 changed.append((b, bin(scid), status,
415 406 _("updating bookmark %s\n") % (b)))
416 407 # remove normal movement from explicit set
417 408 explicit.difference_update(d[0] for d in changed)
418 409
419 410 for b, scid, dcid in diverge:
420 411 if b in explicit:
421 412 explicit.discard(b)
422 413 changed.append((b, bin(scid), status,
423 414 _("importing bookmark %s\n") % (b)))
424 415 else:
425 416 snode = bin(scid)
426 417 db = _diverge(ui, b, path, localmarks, snode)
427 418 if db:
428 419 changed.append((db, snode, warn,
429 420 _("divergent bookmark %s stored as %s\n") %
430 421 (b, db)))
431 422 else:
432 423 warn(_("warning: failed to assign numbered name "
433 424 "to divergent bookmark %s\n") % (b))
434 425 for b, scid, dcid in adddst + advdst:
435 426 if b in explicit:
436 427 explicit.discard(b)
437 428 changed.append((b, bin(scid), status,
438 429 _("importing bookmark %s\n") % (b)))
439 430
440 431 if changed:
441 432 tr = trfunc()
442 433 for b, node, writer, msg in sorted(changed):
443 434 localmarks[b] = node
444 435 writer(msg)
445 436 localmarks.recordchange(tr)
446 437
447 438 def incoming(ui, repo, other):
448 439 '''Show bookmarks incoming from other to repo
449 440 '''
450 441 ui.status(_("searching for changed bookmarks\n"))
451 442
452 443 r = compare(repo, other.listkeys('bookmarks'), repo._bookmarks,
453 444 dsthex=hex)
454 445 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
455 446
456 447 incomings = []
457 448 if ui.debugflag:
458 449 getid = lambda id: id
459 450 else:
460 451 getid = lambda id: id[:12]
461 452 if ui.verbose:
462 453 def add(b, id, st):
463 454 incomings.append(" %-25s %s %s\n" % (b, getid(id), st))
464 455 else:
465 456 def add(b, id, st):
466 457 incomings.append(" %-25s %s\n" % (b, getid(id)))
467 458 for b, scid, dcid in addsrc:
468 459 # i18n: "added" refers to a bookmark
469 460 add(b, scid, _('added'))
470 461 for b, scid, dcid in advsrc:
471 462 # i18n: "advanced" refers to a bookmark
472 463 add(b, scid, _('advanced'))
473 464 for b, scid, dcid in diverge:
474 465 # i18n: "diverged" refers to a bookmark
475 466 add(b, scid, _('diverged'))
476 467 for b, scid, dcid in differ:
477 468 # i18n: "changed" refers to a bookmark
478 469 add(b, scid, _('changed'))
479 470
480 471 if not incomings:
481 472 ui.status(_("no changed bookmarks found\n"))
482 473 return 1
483 474
484 475 for s in sorted(incomings):
485 476 ui.write(s)
486 477
487 478 return 0
488 479
489 480 def outgoing(ui, repo, other):
490 481 '''Show bookmarks outgoing from repo to other
491 482 '''
492 483 ui.status(_("searching for changed bookmarks\n"))
493 484
494 485 r = compare(repo, repo._bookmarks, other.listkeys('bookmarks'),
495 486 srchex=hex)
496 487 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
497 488
498 489 outgoings = []
499 490 if ui.debugflag:
500 491 getid = lambda id: id
501 492 else:
502 493 getid = lambda id: id[:12]
503 494 if ui.verbose:
504 495 def add(b, id, st):
505 496 outgoings.append(" %-25s %s %s\n" % (b, getid(id), st))
506 497 else:
507 498 def add(b, id, st):
508 499 outgoings.append(" %-25s %s\n" % (b, getid(id)))
509 500 for b, scid, dcid in addsrc:
510 501 # i18n: "added refers to a bookmark
511 502 add(b, scid, _('added'))
512 503 for b, scid, dcid in adddst:
513 504 # i18n: "deleted" refers to a bookmark
514 505 add(b, ' ' * 40, _('deleted'))
515 506 for b, scid, dcid in advsrc:
516 507 # i18n: "advanced" refers to a bookmark
517 508 add(b, scid, _('advanced'))
518 509 for b, scid, dcid in diverge:
519 510 # i18n: "diverged" refers to a bookmark
520 511 add(b, scid, _('diverged'))
521 512 for b, scid, dcid in differ:
522 513 # i18n: "changed" refers to a bookmark
523 514 add(b, scid, _('changed'))
524 515
525 516 if not outgoings:
526 517 ui.status(_("no changed bookmarks found\n"))
527 518 return 1
528 519
529 520 for s in sorted(outgoings):
530 521 ui.write(s)
531 522
532 523 return 0
533 524
534 525 def summary(repo, other):
535 526 '''Compare bookmarks between repo and other for "hg summary" output
536 527
537 528 This returns "(# of incoming, # of outgoing)" tuple.
538 529 '''
539 530 r = compare(repo, other.listkeys('bookmarks'), repo._bookmarks,
540 531 dsthex=hex)
541 532 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
542 533 return (len(addsrc), len(adddst))
543 534
544 535 def validdest(repo, old, new):
545 536 """Is the new bookmark destination a valid update from the old one"""
546 537 repo = repo.unfiltered()
547 538 if old == new:
548 539 # Old == new -> nothing to update.
549 540 return False
550 541 elif not old:
551 542 # old is nullrev, anything is valid.
552 543 # (new != nullrev has been excluded by the previous check)
553 544 return True
554 545 elif repo.obsstore:
555 546 return new.node() in obsolete.foreground(repo, [old.node()])
556 547 else:
557 548 # still an independent clause as it is lazier (and therefore faster)
558 549 return old.descendant(new)
General Comments 0
You need to be logged in to leave comments. Login now