diff -r 0ce0cfee497f -r dad6404ccddb mercurial/bookmarks.py --- a/mercurial/bookmarks.py Thu Jan 07 20:02:47 2016 -0800 +++ b/mercurial/bookmarks.py Wed Nov 11 21:18:02 2015 -0500 @@ -44,16 +44,15 @@ class bmstore(dict): """Storage for bookmarks. - This object should do all bookmark reads and writes, so that it's - fairly simple to replace the storage underlying bookmarks without - having to clone the logic surrounding bookmarks. + This object should do all bookmark-related reads and writes, so + that it's fairly simple to replace the storage underlying + bookmarks without having to clone the logic surrounding + bookmarks. This type also should manage the active bookmark, if + any. This particular bmstore implementation stores bookmarks as {hash}\s{name}\n (the same format as localtags) in .hg/bookmarks. The mapping is stored as {name: nodeid}. - - This class does NOT handle the "active" bookmark state at this - time. """ def __init__(self, repo): @@ -79,6 +78,20 @@ if inst.errno != errno.ENOENT: raise self._clean = True + self._active = _readactive(repo, self) + self._aclean = True + + @property + def active(self): + return self._active + + @active.setter + def active(self, mark): + if mark is not None and mark not in self: + raise AssertionError('bookmark %s does not exist!' % mark) + + self._active = mark + self._aclean = False def __setitem__(self, *args, **kwargs): self._clean = False @@ -107,6 +120,9 @@ ''' msg = 'bm.write() is deprecated, use bm.recordchange(transaction)' self._repo.ui.deprecwarn(msg, '3.7') + # TODO: writing the active bookmark should probably also use a + # transaction. + self._writeactive() if self._clean: return repo = self._repo @@ -128,8 +144,10 @@ def _writerepo(self, repo): """Factored out for extensibility""" - if repo._activebookmark not in self: - deactivate(repo) + rbm = repo._bookmarks + if rbm.active not in self: + rbm.active = None + rbm._writeactive() wlock = repo.wlock() try: @@ -146,12 +164,33 @@ finally: wlock.release() + def _writeactive(self): + if self._aclean: + return + wlock = self._repo.wlock() + try: + if self._active is not None: + f = self._repo.vfs('bookmarks.current', 'w', atomictemp=True) + try: + f.write(encoding.fromlocal(self._active)) + finally: + f.close() + else: + try: + self._repo.vfs.unlink('bookmarks.current') + except OSError as inst: + if inst.errno != errno.ENOENT: + raise + finally: + wlock.release() + self._aclean = True + def _write(self, fp): for name, node in self.iteritems(): fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name))) self._clean = True -def readactive(repo): +def _readactive(repo, marks): """ Get the active bookmark. We can have an active bookmark that updates itself as we commit. This function returns the name of that bookmark. @@ -172,7 +211,7 @@ # static-http which only tries to load the file when we try # to read from it. mark = encoding.tolocal((file.readlines() or [''])[0]) - if mark == '' or mark not in repo._bookmarks: + if mark == '' or mark not in marks: mark = None except IOError as inst: if inst.errno != errno.ENOENT: @@ -188,35 +227,15 @@ follow new commits that are made. The name is recorded in .hg/bookmarks.current """ - if mark not in repo._bookmarks: - raise AssertionError('bookmark %s does not exist!' % mark) - - active = repo._activebookmark - if active == mark: - return - - wlock = repo.wlock() - try: - file = repo.vfs('bookmarks.current', 'w', atomictemp=True) - file.write(encoding.fromlocal(mark)) - file.close() - finally: - wlock.release() - repo._activebookmark = mark + repo._bookmarks.active = mark + repo._bookmarks._writeactive() def deactivate(repo): """ Unset the active bookmark in this repository. """ - wlock = repo.wlock() - try: - repo.vfs.unlink('bookmarks.current') - repo._activebookmark = None - except OSError as inst: - if inst.errno != errno.ENOENT: - raise - finally: - wlock.release() + repo._bookmarks.active = None + repo._bookmarks._writeactive() def isactivewdirparent(repo): """ @@ -266,7 +285,7 @@ deletefrom = parents marks = repo._bookmarks update = False - active = repo._activebookmark + active = marks.active if not active: return False