--- 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"))