mercurial/bookmarks.py
changeset 17922 7f5dab94e48c
parent 17918 2eb5f4d1c235
child 18008 cf91b36f368c
--- a/mercurial/bookmarks.py	Fri Nov 09 14:49:30 2012 -0800
+++ b/mercurial/bookmarks.py	Wed Nov 07 16:21:39 2012 -0600
@@ -10,32 +10,72 @@
 from mercurial import encoding, error, util, obsolete
 import errno, os
 
-def read(repo):
-    '''Parse .hg/bookmarks file and return a dictionary
+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 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 "current" bookmark state at this
+    time.
+    """
 
-    Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
-    in the .hg/bookmarks file.
-    Read the file and return a (name=>nodeid) dictionary
-    '''
-    bookmarks = {}
-    try:
-        for line in repo.opener('bookmarks'):
-            line = line.strip()
-            if not line:
-                continue
-            if ' ' not in line:
-                repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
-                continue
-            sha, refspec = line.split(' ', 1)
-            refspec = encoding.tolocal(refspec)
+    def __init__(self, repo):
+        dict.__init__(self)
+        self._repo = repo
+        try:
+            for line in repo.vfs('bookmarks'):
+                line = line.strip()
+                if not line:
+                    continue
+                if ' ' not in line:
+                    repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
+                                 % line)
+                    continue
+                sha, refspec = line.split(' ', 1)
+                refspec = encoding.tolocal(refspec)
+                try:
+                    self[refspec] = repo.changelog.lookup(sha)
+                except LookupError:
+                    pass
+        except IOError, inst:
+            if inst.errno != errno.ENOENT:
+                raise
+
+    def write(self):
+        '''Write bookmarks
+
+        Write the given bookmark => hash dictionary to the .hg/bookmarks file
+        in a format equal to those of localtags.
+
+        We also store a backup of the previous state in undo.bookmarks that
+        can be copied back on rollback.
+        '''
+        repo = self._repo
+        if repo._bookmarkcurrent not in self:
+            setcurrent(repo, None)
+
+        wlock = repo.wlock()
+        try:
+
+            file = repo.vfs('bookmarks', 'w', atomictemp=True)
+            for name, node in self.iteritems():
+                file.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
+            file.close()
+
+            # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
             try:
-                bookmarks[refspec] = repo.changelog.lookup(sha)
-            except LookupError:
+                os.utime(repo.sjoin('00changelog.i'), None)
+            except OSError:
                 pass
-    except IOError, inst:
-        if inst.errno != errno.ENOENT:
-            raise
-    return bookmarks
+
+        finally:
+            wlock.release()
 
 def readcurrent(repo):
     '''Get the current bookmark
@@ -60,37 +100,6 @@
         file.close()
     return mark
 
-def write(repo):
-    '''Write bookmarks
-
-    Write the given bookmark => hash dictionary to the .hg/bookmarks file
-    in a format equal to those of localtags.
-
-    We also store a backup of the previous state in undo.bookmarks that
-    can be copied back on rollback.
-    '''
-    refs = repo._bookmarks
-
-    if repo._bookmarkcurrent not in refs:
-        setcurrent(repo, None)
-
-    wlock = repo.wlock()
-    try:
-
-        file = repo.opener('bookmarks', 'w', atomictemp=True)
-        for refspec, node in refs.iteritems():
-            file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
-        file.close()
-
-        # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
-        try:
-            os.utime(repo.sjoin('00changelog.i'), None)
-        except OSError:
-            pass
-
-    finally:
-        wlock.release()
-
 def setcurrent(repo, mark):
     '''Set the name of the bookmark that we are currently on
 
@@ -152,7 +161,7 @@
             if mark != cur:
                 del marks[mark]
     if update:
-        repo._writebookmarks(marks)
+        marks.write()
     return update
 
 def listbookmarks(repo):
@@ -179,7 +188,7 @@
             if new not in repo:
                 return False
             marks[key] = repo[new].node()
-        write(repo)
+        marks.write()
         return True
     finally:
         w.release()
@@ -188,16 +197,17 @@
     ui.debug("checking for updated bookmarks\n")
     rb = remote.listkeys('bookmarks')
     changed = False
+    localmarks = repo._bookmarks
     for k in rb.keys():
-        if k in repo._bookmarks:
-            nr, nl = rb[k], repo._bookmarks[k]
+        if k in localmarks:
+            nr, nl = rb[k], localmarks[k]
             if nr in repo:
                 cr = repo[nr]
                 cl = repo[nl]
                 if cl.rev() >= cr.rev():
                     continue
                 if validdest(repo, cl, cr):
-                    repo._bookmarks[k] = cr.node()
+                    localmarks[k] = cr.node()
                     changed = True
                     ui.status(_("updating bookmark %s\n") % k)
                 else:
@@ -208,7 +218,7 @@
                     # find a unique @ suffix
                     for x in range(1, 100):
                         n = '%s@%d' % (kd, x)
-                        if n not in repo._bookmarks:
+                        if n not in localmarks:
                             break
                     # try to use an @pathalias suffix
                     # if an @pathalias already exists, we overwrite (update) it
@@ -216,17 +226,17 @@
                         if path == u:
                             n = '%s@%s' % (kd, p)
 
-                    repo._bookmarks[n] = cr.node()
+                    localmarks[n] = cr.node()
                     changed = True
                     ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
         elif rb[k] in repo:
             # add remote bookmarks for changes we already have
-            repo._bookmarks[k] = repo[rb[k]].node()
+            localmarks[k] = repo[rb[k]].node()
             changed = True
             ui.status(_("adding remote bookmark %s\n") % k)
 
     if changed:
-        write(repo)
+        localmarks.write()
 
 def diff(ui, dst, src):
     ui.status(_("searching for changed bookmarks\n"))