deleted file mode 100644
--- a/mercurial/appendfile.py
+++ /dev/null
@@ -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
+++ b/mercurial/bundlerepo.py
@@ -17,7 +17,7 @@ import changegroup, util, os, struct, bz
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 @@ class bundlerevlog(revlog.revlog):
# 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 bundlerevlog(revlog.revlog):
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
+++ b/mercurial/changelog.py
@@ -26,10 +26,88 @@ def _string_escape(text):
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
+++ b/mercurial/commands.py
@@ -666,7 +666,7 @@ def copy(ui, repo, *pats, **opts):
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 @@ def debugstate(ui, repo):
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 debugdate(ui, date, range=None, **op
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 debugindex(ui, file_):
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
+++ b/mercurial/filelog.py
@@ -9,11 +9,9 @@ from revlog import *
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
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -1018,7 +1018,7 @@ class hgweb(object):
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
+++ b/mercurial/localrepo.py
@@ -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 @@ class localrepository(repo.repository):
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 @@ class localrepository(repo.repository):
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 @@ class localrepository(repo.repository):
# 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
+++ b/mercurial/manifest.py
@@ -35,11 +35,10 @@ class manifestdict(dict):
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
+++ b/mercurial/revlog.py
@@ -311,8 +311,7 @@ class revlog(object):
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 @@ class revlog(object):
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
+++ b/mercurial/sshserver.py
@@ -73,7 +73,7 @@ class sshserver(object):
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
+++ b/mercurial/statichttprepo.py
@@ -32,7 +32,6 @@ class statichttprepository(localrepo.loc
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
+++ b/mercurial/ui.py
@@ -325,12 +325,6 @@ class ui(object):
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
+++ b/mercurial/verify.py
@@ -39,8 +39,8 @@ def verify(repo):
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))