changeset 4265:94bb953b43e5

Merge with crew
author Matt Mackall <mpm@selenic.com>
date Fri, 23 Mar 2007 01:04:21 -0500
parents cd7b36b7869c (diff) bda63383d529 (current diff)
children f38f90a177dc
files mercurial/commands.py
diffstat 13 files changed, 144 insertions(+), 268 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/appendfile.py	Wed Mar 21 14:06:25 2007 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-# appendfile.py - special classes to make repo updates atomic
-#
-# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
-#
-# This software may be used and distributed according to the terms
-# of the GNU General Public License, incorporated herein by reference.
-
-import cStringIO, changelog, errno, manifest, os, tempfile, util
-
-# writes to metadata files are ordered.  reads: changelog, manifest,
-# normal files.  writes: normal files, manifest, changelog.
-
-# manifest contains pointers to offsets in normal files.  changelog
-# contains pointers to offsets in manifest.  if reader reads old
-# changelog while manifest or normal files are written, it has no
-# pointers into new parts of those files that are maybe not consistent
-# yet, so will not read them.
-
-# localrepo.addchangegroup thinks it writes changelog first, then
-# manifest, then normal files (this is order they are available, and
-# needed for computing linkrev fields), but uses appendfile to hide
-# updates from readers.  data not written to manifest or changelog
-# until all normal files updated.  write manifest first, then
-# changelog.
-
-# with this write ordering, readers cannot see inconsistent view of
-# repo during update.
-
-class appendfile(object):
-    '''implement enough of file protocol to append to revlog file.
-    appended data is written to temp file.  reads and seeks span real
-    file and temp file.  readers cannot see appended data until
-    writedata called.'''
-
-    def __init__(self, fp, tmpname):
-        if tmpname:
-            self.tmpname = tmpname
-            self.tmpfp = util.posixfile(self.tmpname, 'ab+')
-        else:
-            fd, self.tmpname = tempfile.mkstemp(prefix="hg-appendfile-")
-            os.close(fd)
-            self.tmpfp = util.posixfile(self.tmpname, 'ab+')
-        self.realfp = fp
-        self.offset = fp.tell()
-        # real file is not written by anyone else. cache its size so
-        # seek and read can be fast.
-        self.realsize = util.fstat(fp).st_size
-        self.name = fp.name
-
-    def end(self):
-        self.tmpfp.flush() # make sure the stat is correct
-        return self.realsize + util.fstat(self.tmpfp).st_size
-
-    def tell(self):
-        return self.offset
-
-    def flush(self):
-        self.tmpfp.flush()
-
-    def close(self):
-        self.realfp.close()
-        self.tmpfp.close()
-
-    def seek(self, offset, whence=0):
-        '''virtual file offset spans real file and temp file.'''
-        if whence == 0:
-            self.offset = offset
-        elif whence == 1:
-            self.offset += offset
-        elif whence == 2:
-            self.offset = self.end() + offset
-
-        if self.offset < self.realsize:
-            self.realfp.seek(self.offset)
-        else:
-            self.tmpfp.seek(self.offset - self.realsize)
-
-    def read(self, count=-1):
-        '''only trick here is reads that span real file and temp file.'''
-        fp = cStringIO.StringIO()
-        old_offset = self.offset
-        if self.offset < self.realsize:
-            s = self.realfp.read(count)
-            fp.write(s)
-            self.offset += len(s)
-            if count > 0:
-                count -= len(s)
-        if count != 0:
-            if old_offset != self.offset:
-                self.tmpfp.seek(self.offset - self.realsize)
-            s = self.tmpfp.read(count)
-            fp.write(s)
-            self.offset += len(s)
-        return fp.getvalue()
-
-    def write(self, s):
-        '''append to temp file.'''
-        self.tmpfp.seek(0, 2)
-        self.tmpfp.write(s)
-        # all writes are appends, so offset must go to end of file.
-        self.offset = self.realsize + self.tmpfp.tell()
-
-class appendopener(object):
-    '''special opener for files that only read or append.'''
-
-    def __init__(self, opener):
-        self.realopener = opener
-        # key: file name, value: appendfile name
-        self.tmpnames = {}
-
-    def __call__(self, name, mode='r'):
-        '''open file.'''
-
-        assert mode in 'ra+'
-        try:
-            realfp = self.realopener(name, 'r')
-        except IOError, err:
-            if err.errno != errno.ENOENT: raise
-            realfp = self.realopener(name, 'w+')
-        tmpname = self.tmpnames.get(name)
-        fp = appendfile(realfp, tmpname)
-        if tmpname is None:
-            self.tmpnames[name] = fp.tmpname
-        return fp
-
-    def writedata(self):
-        '''copy data from temp files to real files.'''
-        # write .d file before .i file.
-        tmpnames = self.tmpnames.items()
-        tmpnames.sort()
-        for name, tmpname in tmpnames:
-            ifp = open(tmpname, 'rb')
-            ofp = self.realopener(name, 'a')
-            for chunk in util.filechunkiter(ifp):
-                ofp.write(chunk)
-            ifp.close()
-            os.unlink(tmpname)
-            del self.tmpnames[name]
-            ofp.close()
-
-    def cleanup(self):
-        '''delete temp files (this discards unwritten data!)'''
-        for tmpname in self.tmpnames.values():
-            os.unlink(tmpname)
-
-# files for changelog and manifest are in different appendopeners, so
-# not mixed up together.
-
-class appendchangelog(changelog.changelog, appendopener):
-    def __init__(self, opener, version):
-        appendopener.__init__(self, opener)
-        changelog.changelog.__init__(self, self, version)
-    def checkinlinesize(self, fp, tr):
-        return
-
-class appendmanifest(manifest.manifest, appendopener):
-    def __init__(self, opener, version):
-        appendopener.__init__(self, opener)
-        manifest.manifest.__init__(self, self, version)
-    def checkinlinesize(self, fp, tr):
-        return
--- a/mercurial/bundlerepo.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/bundlerepo.py	Fri Mar 23 01:04:21 2007 -0500
@@ -17,7 +17,7 @@
 import localrepo, changelog, manifest, filelog, revlog
 
 class bundlerevlog(revlog.revlog):
-    def __init__(self, opener, indexfile, datafile, bundlefile,
+    def __init__(self, opener, indexfile, bundlefile,
                  linkmapper=None):
         # How it works:
         # to retrieve a revision, we need to know the offset of
@@ -28,7 +28,7 @@
         # len(index[r]). If the tuple is bigger than 7, it is a bundle
         # (it is bigger since we store the node to which the delta is)
         #
-        revlog.revlog.__init__(self, opener, indexfile, datafile)
+        revlog.revlog.__init__(self, opener, indexfile)
         self.bundlefile = bundlefile
         self.basemap = {}
         def chunkpositer():
@@ -140,20 +140,19 @@
 class bundlechangelog(bundlerevlog, changelog.changelog):
     def __init__(self, opener, bundlefile):
         changelog.changelog.__init__(self, opener)
-        bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
-                              bundlefile)
+        bundlerevlog.__init__(self, opener, self.indexfile, bundlefile)
 
 class bundlemanifest(bundlerevlog, manifest.manifest):
     def __init__(self, opener, bundlefile, linkmapper):
         manifest.manifest.__init__(self, opener)
-        bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
-                              bundlefile, linkmapper)
+        bundlerevlog.__init__(self, opener, self.indexfile, bundlefile,
+                              linkmapper)
 
 class bundlefilelog(bundlerevlog, filelog.filelog):
     def __init__(self, opener, path, bundlefile, linkmapper):
         filelog.filelog.__init__(self, opener, path)
-        bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
-                              bundlefile, linkmapper)
+        bundlerevlog.__init__(self, opener, self.indexfile, bundlefile,
+                              linkmapper)
 
 class bundlerepository(localrepo.localrepository):
     def __init__(self, ui, path, bundlename):
--- a/mercurial/changelog.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/changelog.py	Fri Mar 23 01:04:21 2007 -0500
@@ -26,10 +26,88 @@
 def _string_unescape(text):
     return text.decode('string_escape')
 
+class appender:
+    '''the changelog index must be update last on disk, so we use this class
+    to delay writes to it'''
+    def __init__(self, fp, buf):
+        self.data = buf
+        self.fp = fp
+        self.offset = fp.tell()
+        self.size = util.fstat(fp).st_size
+
+    def end(self):
+        return self.size + len("".join(self.data))
+    def tell(self):
+        return self.offset
+    def flush(self):
+        pass
+    def close(self):
+        close(self.fp)
+
+    def seek(self, offset, whence=0):
+        '''virtual file offset spans real file and data'''
+        if whence == 0:
+            self.offset = offset
+        elif whence == 1:
+            self.offset += offset
+        elif whence == 2:
+            self.offset = self.end() + offset
+        if self.offset < self.size:
+            self.fp.seek(self.offset)
+
+    def read(self, count=-1):
+        '''only trick here is reads that span real file and data'''
+        ret = ""
+        old_offset = self.offset
+        if self.offset < self.size:
+            s = self.fp.read(count)
+            ret = s
+            self.offset += len(s)
+            if count > 0:
+                count -= len(s)
+        if count != 0:
+            doff = self.offset - self.size
+            self.data.insert(0, "".join(self.data))
+            del self.data[1:]
+            s = self.data[0][doff:doff+count]
+            self.offset += len(s)
+            ret += s
+        return ret
+
+    def write(self, s):
+        self.data.append(s)
+        self.offset += len(s)
+
 class changelog(revlog):
-    def __init__(self, opener, defversion=REVLOGV0):
-        revlog.__init__(self, opener, "00changelog.i", "00changelog.d",
-                        defversion)
+    def __init__(self, opener):
+        revlog.__init__(self, opener, "00changelog.i")
+
+    def delayupdate(self):
+        "delay visibility of index updates to other readers"
+        self._realopener = self.opener
+        self.opener = self._appendopener
+        self._delaybuf = []
+
+    def finalize(self, tr):
+        "finalize index updates"
+        self.opener = self._realopener
+        if self._delaybuf:
+            fp = self.opener(self.indexfile, 'a')
+            fp.write("".join(self._delaybuf))
+            fp.close()
+            del self._delaybuf
+        self.checkinlinesize(tr)
+
+    def _appendopener(self, name, mode='r'):
+        fp = self._realopener(name, mode)
+        if not name == self.indexfile:
+            return fp
+        return appender(fp, self._delaybuf)
+
+    def checkinlinesize(self, tr, fp=None):
+        if self.opener == self._appendopener:
+            return
+        return revlog.checkinlinesize(self, tr, fp)
 
     def decode_extra(self, text):
         extra = {}
--- a/mercurial/commands.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/commands.py	Fri Mar 23 01:04:21 2007 -0500
@@ -666,7 +666,7 @@
 
 def debugancestor(ui, index, rev1, rev2):
     """find the ancestor revision of two revisions in a given index"""
-    r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
+    r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
     a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
     ui.write("%d:%s\n" % (r.rev(a), hex(a)))
 
@@ -794,9 +794,8 @@
         ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
 
 def debugdata(ui, file_, rev):
-    """dump the contents of an data file revision"""
-    r = revlog.revlog(util.opener(os.getcwd(), audit=False),
-                      file_[:-2] + ".i", file_, 0)
+    """dump the contents of a data file revision"""
+    r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
     try:
         ui.write(r.revision(r.lookup(rev)))
     except KeyError:
@@ -816,7 +815,7 @@
 
 def debugindex(ui, file_):
     """dump the contents of an index file"""
-    r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
+    r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
     ui.write("   rev    offset  length   base linkrev" +
              " nodeid       p1           p2\n")
     for i in xrange(r.count()):
@@ -828,7 +827,7 @@
 
 def debugindexdot(ui, file_):
     """dump an index DAG as a .dot file"""
-    r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
+    r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
     ui.write("digraph G {\n")
     for i in xrange(r.count()):
         node = r.node(i)
--- a/mercurial/filelog.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/filelog.py	Fri Mar 23 01:04:21 2007 -0500
@@ -9,11 +9,9 @@
 import os
 
 class filelog(revlog):
-    def __init__(self, opener, path, defversion=REVLOG_DEFAULT_VERSION):
+    def __init__(self, opener, path):
         revlog.__init__(self, opener,
-                        "/".join(("data", self.encodedir(path + ".i"))),
-                        "/".join(("data", self.encodedir(path + ".d"))),
-                        defversion)
+                        "/".join(("data", self.encodedir(path + ".i"))))
 
     # This avoids a collision between a file named foo and a dir named
     # foo.i or foo.d
--- a/mercurial/hgweb/hgweb_mod.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/hgweb/hgweb_mod.py	Fri Mar 23 01:04:21 2007 -0500
@@ -1018,7 +1018,7 @@
     def do_capabilities(self, req):
         caps = ['lookup', 'changegroupsubset']
         if self.configbool('server', 'uncompressed'):
-            caps.append('stream=%d' % self.repo.revlogversion)
+            caps.append('stream=%d' % self.repo.changelog.version)
         # XXX: make configurable and/or share code with do_unbundle:
         unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
         if unbundleversions:
--- a/mercurial/localrepo.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/localrepo.py	Fri Mar 23 01:04:21 2007 -0500
@@ -7,7 +7,7 @@
 
 from node import *
 from i18n import _
-import repo, appendfile, changegroup
+import repo, changegroup
 import changelog, dirstate, filelog, manifest, context
 import re, lock, transaction, tempfile, stat, mdiff, errno, ui
 import os, revlog, time, util
@@ -88,34 +88,14 @@
         except IOError:
             pass
 
-        v = self.ui.configrevlog()
-        self.revlogversion = int(v.get('format', revlog.REVLOG_DEFAULT_FORMAT))
-        self.revlogv1 = self.revlogversion != revlog.REVLOGV0
-        fl = v.get('flags', None)
-        flags = 0
-        if fl != None:
-            for x in fl.split():
-                flags |= revlog.flagstr(x)
-        elif self.revlogv1:
-            flags = revlog.REVLOG_DEFAULT_FLAGS
-
-        v = self.revlogversion | flags
-        self.manifest = manifest.manifest(self.sopener, v)
-        self.changelog = changelog.changelog(self.sopener, v)
+        self.changelog = changelog.changelog(self.sopener)
+        self.sopener.defversion = self.changelog.version
+        self.manifest = manifest.manifest(self.sopener)
 
         fallback = self.ui.config('ui', 'fallbackencoding')
         if fallback:
             util._fallbackencoding = fallback
 
-        # the changelog might not have the inline index flag
-        # on.  If the format of the changelog is the same as found in
-        # .hgrc, apply any flags found in the .hgrc as well.
-        # Otherwise, just version from the changelog
-        v = self.changelog.version
-        if v == self.revlogversion:
-            v |= flags
-        self.revlogversion = v
-
         self.tagscache = None
         self.branchcache = None
         self.nodetagscache = None
@@ -493,7 +473,7 @@
     def file(self, f):
         if f[0] == '/':
             f = f[1:]
-        return filelog.filelog(self.sopener, f, self.revlogversion)
+        return filelog.filelog(self.sopener, f)
 
     def changectx(self, changeid=None):
         return context.changectx(self, changeid)
@@ -1802,55 +1782,45 @@
 
         # write changelog data to temp files so concurrent readers will not see
         # inconsistent view
-        cl = None
-        try:
-            cl = appendfile.appendchangelog(self.sopener,
-                                            self.changelog.version)
+        cl = self.changelog
+        cl.delayupdate()
+        oldheads = len(cl.heads())
 
-            oldheads = len(cl.heads())
+        # pull off the changeset group
+        self.ui.status(_("adding changesets\n"))
+        cor = cl.count() - 1
+        chunkiter = changegroup.chunkiter(source)
+        if cl.addgroup(chunkiter, csmap, tr, 1) is None:
+            raise util.Abort(_("received changelog group is empty"))
+        cnr = cl.count() - 1
+        changesets = cnr - cor
 
-            # pull off the changeset group
-            self.ui.status(_("adding changesets\n"))
-            cor = cl.count() - 1
-            chunkiter = changegroup.chunkiter(source)
-            if cl.addgroup(chunkiter, csmap, tr, 1) is None:
-                raise util.Abort(_("received changelog group is empty"))
-            cnr = cl.count() - 1
-            changesets = cnr - cor
+        # pull off the manifest group
+        self.ui.status(_("adding manifests\n"))
+        chunkiter = changegroup.chunkiter(source)
+        # no need to check for empty manifest group here:
+        # if the result of the merge of 1 and 2 is the same in 3 and 4,
+        # no new manifest will be created and the manifest group will
+        # be empty during the pull
+        self.manifest.addgroup(chunkiter, revmap, tr)
 
-            # pull off the manifest group
-            self.ui.status(_("adding manifests\n"))
+        # process the files
+        self.ui.status(_("adding file changes\n"))
+        while 1:
+            f = changegroup.getchunk(source)
+            if not f:
+                break
+            self.ui.debug(_("adding %s revisions\n") % f)
+            fl = self.file(f)
+            o = fl.count()
             chunkiter = changegroup.chunkiter(source)
-            # no need to check for empty manifest group here:
-            # if the result of the merge of 1 and 2 is the same in 3 and 4,
-            # no new manifest will be created and the manifest group will
-            # be empty during the pull
-            self.manifest.addgroup(chunkiter, revmap, tr)
-
-            # process the files
-            self.ui.status(_("adding file changes\n"))
-            while 1:
-                f = changegroup.getchunk(source)
-                if not f:
-                    break
-                self.ui.debug(_("adding %s revisions\n") % f)
-                fl = self.file(f)
-                o = fl.count()
-                chunkiter = changegroup.chunkiter(source)
-                if fl.addgroup(chunkiter, revmap, tr) is None:
-                    raise util.Abort(_("received file revlog group is empty"))
-                revisions += fl.count() - o
-                files += 1
-
-            cl.writedata()
-        finally:
-            if cl:
-                cl.cleanup()
+            if fl.addgroup(chunkiter, revmap, tr) is None:
+                raise util.Abort(_("received file revlog group is empty"))
+            revisions += fl.count() - o
+            files += 1
 
         # make changelog see real files again
-        self.changelog = changelog.changelog(self.sopener,
-                                             self.changelog.version)
-        self.changelog.checkinlinesize(tr)
+        cl.finalize(tr)
 
         newheads = len(self.changelog.heads())
         heads = ""
--- a/mercurial/manifest.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/manifest.py	Fri Mar 23 01:04:21 2007 -0500
@@ -35,11 +35,10 @@
         return manifestdict(dict.copy(self), dict.copy(self._flags))
 
 class manifest(revlog):
-    def __init__(self, opener, defversion=REVLOGV0):
+    def __init__(self, opener):
         self.mapcache = None
         self.listcache = None
-        revlog.__init__(self, opener, "00manifest.i", "00manifest.d",
-                        defversion)
+        revlog.__init__(self, opener, "00manifest.i")
 
     def parselines(self, lines):
         for l in lines.splitlines(1):
--- a/mercurial/revlog.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/revlog.py	Fri Mar 23 01:04:21 2007 -0500
@@ -311,8 +311,7 @@
     remove data, and can use some simple techniques to avoid the need
     for locking while reading.
     """
-    def __init__(self, opener, indexfile, datafile,
-                 defversion=REVLOG_DEFAULT_VERSION):
+    def __init__(self, opener, indexfile):
         """
         create a revlog object
 
@@ -320,13 +319,15 @@
         and can be used to implement COW semantics or the like.
         """
         self.indexfile = indexfile
-        self.datafile = datafile
+        self.datafile = indexfile[:-2] + ".d"
         self.opener = opener
 
         self.indexstat = None
         self.cache = None
         self.chunkcache = None
-        self.defversion = defversion
+        self.defversion=REVLOG_DEFAULT_VERSION
+        if hasattr(opener, "defversion"):
+            self.defversion = opener.defversion
         self.load()
 
     def load(self):
--- a/mercurial/sshserver.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/sshserver.py	Fri Mar 23 01:04:21 2007 -0500
@@ -73,7 +73,7 @@
 
         caps = ['unbundle', 'lookup', 'changegroupsubset']
         if self.ui.configbool('server', 'uncompressed'):
-            caps.append('stream=%d' % self.repo.revlogversion)
+            caps.append('stream=%d' % self.repo.changelog.version)
         self.respond("capabilities: %s\n" % (' '.join(caps),))
 
     def do_lock(self):
--- a/mercurial/statichttprepo.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/statichttprepo.py	Fri Mar 23 01:04:21 2007 -0500
@@ -32,7 +32,6 @@
     def __init__(self, ui, path):
         self._url = path
         self.ui = ui
-        self.revlogversion = 0
 
         self.path = (path + "/.hg")
         self.opener = opener(self.path)
--- a/mercurial/ui.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/ui.py	Fri Mar 23 01:04:21 2007 -0500
@@ -325,12 +325,6 @@
                 result.append(os.path.expanduser(value))
         return result
 
-    def configrevlog(self):
-        result = {}
-        for key, value in self.configitems("revlog"):
-            result[key.lower()] = value
-        return result
-
     def username(self):
         """Return default username to be used in commits.
 
--- a/mercurial/verify.py	Wed Mar 21 14:06:25 2007 -0700
+++ b/mercurial/verify.py	Fri Mar 23 01:04:21 2007 -0500
@@ -39,8 +39,8 @@
         elif revlogv1:
             warn(_("warning: `%s' uses revlog format 0") % name)
 
-    revlogv1 = repo.revlogversion != revlog.REVLOGV0
-    if repo.ui.verbose or revlogv1 != repo.revlogv1:
+    revlogv1 = repo.changelog.version != revlog.REVLOGV0
+    if repo.ui.verbose or not revlogv1:
         repo.ui.status(_("repository uses revlog format %d\n") %
                        (revlogv1 and 1 or 0))