[PATCH 1 of 8] localrepo: move bookmarks to core

dsp at php.net dsp at php.net
Sun Jan 16 16:57:37 CST 2011


# HG changeset patch
# User David Soria Parra <dsp at php.net>
# Date 1295218241 -3600
# Node ID 365ba79250901d16cc59bc18780fab4d156a948e
# Parent  20a54bdf232832d3d6c4e4a43361dc84ec73dca0
localrepo: move bookmarks to core

diff --git a/hgext/bookmarks.py b/hgext/bookmarks.py
--- a/hgext/bookmarks.py
+++ b/hgext/bookmarks.py
@@ -261,7 +261,7 @@
                 if mark == '':
                     mark = None
                 file.close()
-            return mark
+            return encoding.tolocal(mark)
 
         def rollback(self, *args):
             if os.path.exists(self.join('undo.bookmarks')):
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -500,7 +500,140 @@
                 bheads = [b for b in bheads if b not in reachable]
             partial[branch] = bheads
 
+    def _bookmarkspull(self, remote):
+        self.ui.debug("checking for updated bookmarks\n")
+        rb = remote.listkeys('bookmarks')
+        changed = False
+        donotupdate = []
+        for k in rb.keys():
+            if k in self.bookmarks:
+                nr, nl = rb[k], self.bookmarks[k]
+                if nr in self:
+                    cr = self[nr]
+                    cl = self[nl]
+                    if cl.rev() >= cr.rev():
+                        continue
+                    if cr in cl.descendants():
+                        self.bookmarks[k] = cr.node()
+                        changed = True
+                        self.ui.status(_("updating bookmark %s\n") % k)
+                    else:
+                        self.ui.warn(_("not updating divergent"
+                                       " bookmark %s\n") % k)
+                    donotupdate.append(k)
+            else:
+                try:
+                    self.bookmarks[k] = self.changelog.lookup(rb[k])
+                    changed = True
+                    self.ui.status(_("importing bookmark %s\n") % k)
+                except:
+                    self.ui.warn(_("cannot import bookmark %s. referenced"
+                                   " revision is not in repository\n") % k)
+
+        k = self.bookmarkcurrent
+        if k not in donotupdate:
+            branch = self.dirstate.branch()
+            n = self[branch].node()
+            cl = self[k]
+            if cl.rev() and n in cl.descendants():
+                self.bookmarks[k] = n
+                changed = True
+
+        if changed:
+            self.writebookmarks()
+
+    def _bookmarkspush(self, remote):
+        self.ui.debug("checking for updated bookmarks\n")
+        rb = remote.listkeys('bookmarks')
+        for k in rb.keys():
+            if k in self.bookmarks:
+                nr, nl = rb[k], self.bookmarks[k]
+                if nr in self:
+                    cr = self[nr]
+                    cl = self[nl]
+                    if cl in cr.descendants():
+                        r = remote.pushkey('bookmarks', k, nr, nl)
+                        if r:
+                            self.ui.status(_("updating bookmark %s\n") % k)
+                        else:
+                            self.ui.warn(_('updating bookmark %s'
+                                           ' failed!\n') % k)
+
+    def _bookmarksupdate(self, node):
+        mark = self.bookmarkcurrent
+        if mark and self.bookmarks[mark] in self.dirstate.parents():
+            self.bookmarks[mark] = node
+            self.writebookmarks()
+
+    @propertycache
+    def bookmarks(self):
+        '''Parse .hg/bookmarks file and return a dictionary
+
+        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 = {}
+        if os.path.exists(self.join('bookmarks')):
+            for line in self.opener('bookmarks'):
+                sha, name = line.strip().split(' ', 1)
+                ref = encoding.tolocal(name)
+                bookmarks[ref] = self.changelog.lookup(sha)
+        return bookmarks
+
+    @propertycache
+    def bookmarkcurrent(self):
+        mark = None
+        if os.path.exists(self.join('bookmarks.current')):
+            file = self.opener('bookmarks.current')
+            # No readline() in posixfile_nt, reading everything is cheap
+            mark = encoding.tolocal((file.readlines() or [''])[0])
+            if mark == '':
+                mark = None
+            file.close()
+        return mark
+
+    def writebookmarks(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.
+        '''
+        refs = self.bookmarks
+        try:
+            bms = self.opener('bookmarks').read()
+        except IOError:
+            bms = ''
+        self.opener('undo.bookmarks', 'w').write(bms)
+
+        wlock = self.wlock()
+        try:
+            file = self.opener('bookmarks', 'w', atomictemp=True)
+            for ref, node in refs.iteritems():
+                file.write("%s %s\n" % (hex(node), encoding.fromlocal(ref)))
+            file.rename()
+
+            mark = self.bookmarkcurrent
+            if mark not in refs:
+                mark = ''
+            file = self.opener('bookmarks.current', 'w', atomictemp=True)
+            file.write(encoding.fromlocal(mark))
+            file.rename()
+            # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
+            try:
+                os.utime(self.sjoin('00changelog.i'), None)
+            except OSError:
+                pass
+
+        finally:
+            wlock.release()
+
     def lookup(self, key):
+        if key in self.bookmarks:
+            return self.bookmarks[key]
         if isinstance(key, int):
             return self.changelog.node(key)
         elif key == '.':
@@ -691,6 +824,8 @@
         try:
             wlock = self.wlock()
             lock = self.lock()
+            if os.path.exists(self.join('undo.bookmarks')):
+                util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
             if os.path.exists(self.sjoin("undo")):
                 try:
                     args = self.opener("undo.desc", "r").read().splitlines()
@@ -733,7 +868,7 @@
         self._branchcachetip = None
 
     def invalidate(self):
-        for a in ("changelog", "manifest"):
+        for a in ("changelog", "manifest", "_bookmarks", "_bookmarkcurrent"):
             if a in self.__dict__:
                 delattr(self, a)
         self.invalidatecaches()
@@ -977,6 +1112,9 @@
                         _('note: commit message saved in %s\n') % msgfn)
                 raise
 
+            # update bookmarks
+            self._bookmarksupdate(ret)
+
             # update dirstate and mergestate
             for f in changes[0] + changes[1]:
                 self.dirstate.normal(f)
@@ -1300,8 +1438,11 @@
                                        "other repository doesn't support "
                                        "changegroupsubset."))
                 cg = remote.changegroupsubset(fetch, heads, 'pull')
+
             return self.addchangegroup(cg, 'pull', remote.url(), lock=lock)
         finally:
+            if remote.capable('pushkey'):
+                self._bookmarkspull(remote)
             lock.release()
 
     def push(self, remote, force=False, revs=None, newbranch=False):
@@ -1341,10 +1482,12 @@
                     remote_heads = ['force']
                 # ssh: return remote's addchangegroup()
                 # http: return remote's addchangegroup() or 0 for error
-                return remote.unbundle(cg, remote_heads, 'push')
+                ret = remote.unbundle(cg, remote_heads, 'push')
             else:
                 # we return an integer indicating remote head count change
-                return remote.addchangegroup(cg, 'push', self.url(), lock=lock)
+                ret = remote.addchangegroup(cg, 'push', self.url(), lock=lock)
+
+            return ret
         finally:
             if lock is not None:
                 lock.release()


More information about the Mercurial-devel mailing list