[Pkg-bazaar-commits] ./bzr/unstable r453: - Split WorkingTree into its own file
Martin Pool
mbp at sourcefrog.net
Fri Apr 10 08:19:27 UTC 2009
------------------------------------------------------------
revno: 453
committer: Martin Pool <mbp at sourcefrog.net>
timestamp: Wed 2005-05-11 12:22:26 +1000
message:
- Split WorkingTree into its own file
- pychecker fixes
- statcache works in terms of just directories, not branches
- use statcache from workingtree when getting file SHA-1 so that
diffs are faster
added:
bzrlib/workingtree.py
modified:
TODO
bzrlib/branch.py
bzrlib/commands.py
bzrlib/statcache.py
bzrlib/tree.py
-------------- next part --------------
=== modified file 'TODO'
--- a/TODO 2005-05-11 01:09:41 +0000
+++ b/TODO 2005-05-11 02:22:26 +0000
@@ -225,7 +225,7 @@
- Hold the ElementTree in memory in the Inventory object and work
directly on that, rather than converting into Python objects every
- time it is read in. Probably still expose it through some kind of
+ time it is read in. Probably still exposoe it through some kind of
object interface though, but perhaps that should just be a proxy
for the elements.
@@ -246,6 +246,8 @@
urlgrabber's docs say they are working on batched downloads; we
could perhaps ride on that or just create a background thread (ew).
+* Should be a signature at the top of the cache file.
+
Large things
------------
=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py 2005-05-10 06:09:58 +0000
+++ b/bzrlib/branch.py 2005-05-11 02:22:26 +0000
@@ -24,7 +24,7 @@
import bzrlib
from inventory import Inventory
from trace import mutter, note
-from tree import Tree, EmptyTree, RevisionTree, WorkingTree
+from tree import Tree, EmptyTree, RevisionTree
from inventory import InventoryEntry, Inventory
from osutils import isdir, quotefn, isfile, uuid, sha_file, username, \
format_date, compact_date, pumpfile, user_email, rand_bytes, splitpath, \
@@ -785,6 +785,7 @@
def working_tree(self):
"""Return a `Tree` for the working copy."""
+ from workingtree import WorkingTree
return WorkingTree(self.base, self.read_working_inventory())
=== modified file 'bzrlib/commands.py'
--- a/bzrlib/commands.py 2005-05-11 00:56:49 +0000
+++ b/bzrlib/commands.py 2005-05-11 02:22:26 +0000
@@ -23,7 +23,7 @@
from bzrlib.trace import mutter, note, log_error
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
-from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
+from bzrlib.tree import RevisionTree, EmptyTree, Tree
from bzrlib.revision import Revision
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
format_date
=== modified file 'bzrlib/statcache.py'
--- a/bzrlib/statcache.py 2005-05-10 06:54:12 +0000
+++ b/bzrlib/statcache.py 2005-05-11 02:22:26 +0000
@@ -82,10 +82,11 @@
fs.st_ctime, fs.st_ino, fs.st_dev)
-def _write_cache(branch, entry_iter, dangerfiles):
+def _write_cache(basedir, entry_iter, dangerfiles):
from atomicfile import AtomicFile
-
- outf = AtomicFile(branch.controlfilename('stat-cache'), 'wb', 'utf-8')
+
+ cachefn = os.path.join(basedir, '.bzr', 'stat-cache')
+ outf = AtomicFile(cachefn, 'wb', 'utf-8')
try:
for entry in entry_iter:
if entry[0] in dangerfiles:
@@ -100,11 +101,14 @@
outf.abort()
-def load_cache(branch):
+def load_cache(basedir):
+ import codecs
+
cache = {}
try:
- cachefile = branch.controlfile('stat-cache', 'r')
+ cachefn = os.path.join(basedir, '.bzr', 'stat-cache')
+ cachefile = codecs.open(cachefn, 'r', 'utf-8')
except IOError:
return cache
@@ -127,7 +131,7 @@
-def update_cache(branch, inv=None, flush=False):
+def update_cache(basedir, inv, flush=False):
"""Update and return the cache for the branch.
The returned cache may contain entries that have not been written
@@ -145,14 +149,12 @@
if flush:
cache = {}
else:
- cache = load_cache(branch)
- if inv == None:
- inv = branch.read_working_inventory()
- return _update_cache_from_list(branch, cache, _files_from_inventory(inv))
-
-
-
-def _update_cache_from_list(branch, cache, to_update):
+ cache = load_cache(basedir)
+ return _update_cache_from_list(basedir, cache, _files_from_inventory(inv))
+
+
+
+def _update_cache_from_list(basedir, cache, to_update):
"""Update and return the cache for given files.
cache -- Previously cached values to be validated.
@@ -171,7 +173,7 @@
now = int(time.time())
for file_id, path in to_update:
- fap = branch.abspath(path)
+ fap = os.path.join(basedir, path)
fp = fingerprint(fap, path)
cacheentry = cache.get(file_id)
@@ -204,6 +206,6 @@
if dirty:
mutter('updating on-disk statcache')
- _write_cache(branch, cache.itervalues(), dangerfiles)
+ _write_cache(basedir, cache.itervalues(), dangerfiles)
return cache
=== modified file 'bzrlib/tree.py'
--- a/bzrlib/tree.py 2005-04-15 07:53:59 +0000
+++ b/bzrlib/tree.py 2005-05-11 02:22:26 +0000
@@ -121,229 +121,11 @@
elif kind == 'file':
pumpfile(self.get_file(ie.file_id), file(fullpath, 'wb'))
else:
- bailout("don't know how to export {%s} of kind %r" % (fid, kind))
+ bailout("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
mutter(" export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
-class WorkingTree(Tree):
- """Working copy tree.
-
- The inventory is held in the `Branch` working-inventory, and the
- files are in a directory on disk.
-
- It is possible for a `WorkingTree` to have a filename which is
- not listed in the Inventory and vice versa.
- """
- def __init__(self, basedir, inv):
- self._inventory = inv
- self.basedir = basedir
- self.path2id = inv.path2id
-
- def __repr__(self):
- return "<%s of %s>" % (self.__class__.__name__,
- self.basedir)
-
- def abspath(self, filename):
- return os.path.join(self.basedir, filename)
-
- def has_filename(self, filename):
- return os.path.exists(self.abspath(filename))
-
- def get_file(self, file_id):
- return self.get_file_byname(self.id2path(file_id))
-
- def get_file_byname(self, filename):
- return file(self.abspath(filename), 'rb')
-
- def _get_store_filename(self, file_id):
- ## XXX: badly named; this isn't in the store at all
- return self.abspath(self.id2path(file_id))
-
- def has_id(self, file_id):
- # files that have been deleted are excluded
- if not self.inventory.has_id(file_id):
- return False
- return os.access(self.abspath(self.inventory.id2path(file_id)), os.F_OK)
-
- def get_file_size(self, file_id):
- return os.stat(self._get_store_filename(file_id))[ST_SIZE]
-
- def get_file_sha1(self, file_id):
- f = self.get_file(file_id)
- return sha_file(f)
-
-
- def file_class(self, filename):
- if self.path2id(filename):
- return 'V'
- elif self.is_ignored(filename):
- return 'I'
- else:
- return '?'
-
-
- def list_files(self):
- """Recursively list all files as (path, class, kind, id).
-
- Lists, but does not descend into unversioned directories.
-
- This does not include files that have been deleted in this
- tree.
-
- Skips the control directory.
- """
- inv = self.inventory
-
- def descend(from_dir_relpath, from_dir_id, dp):
- ls = os.listdir(dp)
- ls.sort()
- for f in ls:
- ## TODO: If we find a subdirectory with its own .bzr
- ## directory, then that is a separate tree and we
- ## should exclude it.
- if bzrlib.BZRDIR == f:
- continue
-
- # path within tree
- fp = appendpath(from_dir_relpath, f)
-
- # absolute path
- fap = appendpath(dp, f)
-
- f_ie = inv.get_child(from_dir_id, f)
- if f_ie:
- c = 'V'
- elif self.is_ignored(fp):
- c = 'I'
- else:
- c = '?'
-
- fk = file_kind(fap)
-
- if f_ie:
- if f_ie.kind != fk:
- bailout("file %r entered as kind %r id %r, now of kind %r"
- % (fap, f_ie.kind, f_ie.file_id, fk))
-
- yield fp, c, fk, (f_ie and f_ie.file_id)
-
- if fk != 'directory':
- continue
-
- if c != 'V':
- # don't descend unversioned directories
- continue
-
- for ff in descend(fp, f_ie.file_id, fap):
- yield ff
-
- for f in descend('', inv.root.file_id, self.basedir):
- yield f
-
-
-
- def unknowns(self):
- for subp in self.extras():
- if not self.is_ignored(subp):
- yield subp
-
-
- def extras(self):
- """Yield all unknown files in this WorkingTree.
-
- If there are any unknown directories then only the directory is
- returned, not all its children. But if there are unknown files
- under a versioned subdirectory, they are returned.
-
- Currently returned depth-first, sorted by name within directories.
- """
- ## TODO: Work from given directory downwards
-
- for path, dir_entry in self.inventory.directories():
- mutter("search for unknowns in %r" % path)
- dirabs = self.abspath(path)
- if not isdir(dirabs):
- # e.g. directory deleted
- continue
-
- fl = []
- for subf in os.listdir(dirabs):
- if (subf != '.bzr'
- and (subf not in dir_entry.children)):
- fl.append(subf)
-
- fl.sort()
- for subf in fl:
- subp = appendpath(path, subf)
- yield subp
-
-
- def ignored_files(self):
- """Yield list of PATH, IGNORE_PATTERN"""
- for subp in self.extras():
- pat = self.is_ignored(subp)
- if pat != None:
- yield subp, pat
-
-
- def get_ignore_list(self):
- """Return list of ignore patterns.
-
- Cached in the Tree object after the first call.
- """
- if hasattr(self, '_ignorelist'):
- return self._ignorelist
-
- l = bzrlib.DEFAULT_IGNORE[:]
- if self.has_filename(bzrlib.IGNORE_FILENAME):
- f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
- l.extend([line.rstrip("\n\r") for line in f.readlines()])
- self._ignorelist = l
- return l
-
-
- def is_ignored(self, filename):
- r"""Check whether the filename matches an ignore pattern.
-
- Patterns containing '/' or '\' need to match the whole path;
- others match against only the last component.
-
- If the file is ignored, returns the pattern which caused it to
- be ignored, otherwise None. So this can simply be used as a
- boolean if desired."""
-
- # TODO: Use '**' to match directories, and other extended
- # globbing stuff from cvs/rsync.
-
- # XXX: fnmatch is actually not quite what we want: it's only
- # approximately the same as real Unix fnmatch, and doesn't
- # treat dotfiles correctly and allows * to match /.
- # Eventually it should be replaced with something more
- # accurate.
-
- for pat in self.get_ignore_list():
- if '/' in pat or '\\' in pat:
-
- # as a special case, you can put ./ at the start of a
- # pattern; this is good to match in the top-level
- # only;
-
- if (pat[:2] == './') or (pat[:2] == '.\\'):
- newpat = pat[2:]
- else:
- newpat = pat
- if fnmatch.fnmatchcase(filename, newpat):
- return pat
- else:
- if fnmatch.fnmatchcase(splitpath(filename)[-1], pat):
- return pat
- return None
-
-
-
-
-
class RevisionTree(Tree):
"""Tree viewing a previous revision.
=== added file 'bzrlib/workingtree.py'
--- a/bzrlib/workingtree.py 1970-01-01 00:00:00 +0000
+++ b/bzrlib/workingtree.py 2005-05-11 02:22:26 +0000
@@ -0,0 +1,259 @@
+# Copyright (C) 2005 Canonical Ltd
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+import os
+
+import bzrlib.tree
+from errors import BzrCheckError
+from trace import mutter
+
+
+class WorkingTree(bzrlib.tree.Tree):
+ """Working copy tree.
+
+ The inventory is held in the `Branch` working-inventory, and the
+ files are in a directory on disk.
+
+ It is possible for a `WorkingTree` to have a filename which is
+ not listed in the Inventory and vice versa.
+ """
+ _statcache = None
+
+ def __init__(self, basedir, inv):
+ self._inventory = inv
+ self.basedir = basedir
+ self.path2id = inv.path2id
+
+ def __repr__(self):
+ return "<%s of %s>" % (self.__class__.__name__,
+ self.basedir)
+
+ def abspath(self, filename):
+ return os.path.join(self.basedir, filename)
+
+ def has_filename(self, filename):
+ return os.path.exists(self.abspath(filename))
+
+ def get_file(self, file_id):
+ return self.get_file_byname(self.id2path(file_id))
+
+ def get_file_byname(self, filename):
+ return file(self.abspath(filename), 'rb')
+
+ def _get_store_filename(self, file_id):
+ ## XXX: badly named; this isn't in the store at all
+ return self.abspath(self.id2path(file_id))
+
+ def has_id(self, file_id):
+ # files that have been deleted are excluded
+ if not self.inventory.has_id(file_id):
+ return False
+ import os
+ return os.access(self.abspath(self.inventory.id2path(file_id)), os.F_OK)
+
+ def get_file_size(self, file_id):
+ import os, stat
+ return os.stat(self._get_store_filename(file_id))[stat.ST_SIZE]
+
+
+ def get_file_sha1(self, file_id):
+ import statcache
+ if not self._statcache:
+ self._statcache = statcache.update_cache(self.basedir, self.inventory)
+
+ return self._statcache[file_id][statcache.SC_SHA1]
+
+
+ def file_class(self, filename):
+ if self.path2id(filename):
+ return 'V'
+ elif self.is_ignored(filename):
+ return 'I'
+ else:
+ return '?'
+
+
+ def list_files(self):
+ """Recursively list all files as (path, class, kind, id).
+
+ Lists, but does not descend into unversioned directories.
+
+ This does not include files that have been deleted in this
+ tree.
+
+ Skips the control directory.
+ """
+ from osutils import appendpath, file_kind
+ import os
+
+ inv = self.inventory
+
+ def descend(from_dir_relpath, from_dir_id, dp):
+ ls = os.listdir(dp)
+ ls.sort()
+ for f in ls:
+ ## TODO: If we find a subdirectory with its own .bzr
+ ## directory, then that is a separate tree and we
+ ## should exclude it.
+ if bzrlib.BZRDIR == f:
+ continue
+
+ # path within tree
+ fp = appendpath(from_dir_relpath, f)
+
+ # absolute path
+ fap = appendpath(dp, f)
+
+ f_ie = inv.get_child(from_dir_id, f)
+ if f_ie:
+ c = 'V'
+ elif self.is_ignored(fp):
+ c = 'I'
+ else:
+ c = '?'
+
+ fk = file_kind(fap)
+
+ if f_ie:
+ if f_ie.kind != fk:
+ raise BzrCheckError("file %r entered as kind %r id %r, "
+ "now of kind %r"
+ % (fap, f_ie.kind, f_ie.file_id, fk))
+
+ yield fp, c, fk, (f_ie and f_ie.file_id)
+
+ if fk != 'directory':
+ continue
+
+ if c != 'V':
+ # don't descend unversioned directories
+ continue
+
+ for ff in descend(fp, f_ie.file_id, fap):
+ yield ff
+
+ for f in descend('', inv.root.file_id, self.basedir):
+ yield f
+
+
+
+ def unknowns(self):
+ for subp in self.extras():
+ if not self.is_ignored(subp):
+ yield subp
+
+
+ def extras(self):
+ """Yield all unknown files in this WorkingTree.
+
+ If there are any unknown directories then only the directory is
+ returned, not all its children. But if there are unknown files
+ under a versioned subdirectory, they are returned.
+
+ Currently returned depth-first, sorted by name within directories.
+ """
+ ## TODO: Work from given directory downwards
+ from osutils import isdir, appendpath
+
+ for path, dir_entry in self.inventory.directories():
+ mutter("search for unknowns in %r" % path)
+ dirabs = self.abspath(path)
+ if not isdir(dirabs):
+ # e.g. directory deleted
+ continue
+
+ fl = []
+ for subf in os.listdir(dirabs):
+ if (subf != '.bzr'
+ and (subf not in dir_entry.children)):
+ fl.append(subf)
+
+ fl.sort()
+ for subf in fl:
+ subp = appendpath(path, subf)
+ yield subp
+
+
+ def ignored_files(self):
+ """Yield list of PATH, IGNORE_PATTERN"""
+ for subp in self.extras():
+ pat = self.is_ignored(subp)
+ if pat != None:
+ yield subp, pat
+
+
+ def get_ignore_list(self):
+ """Return list of ignore patterns.
+
+ Cached in the Tree object after the first call.
+ """
+ if hasattr(self, '_ignorelist'):
+ return self._ignorelist
+
+ l = bzrlib.DEFAULT_IGNORE[:]
+ if self.has_filename(bzrlib.IGNORE_FILENAME):
+ f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
+ l.extend([line.rstrip("\n\r") for line in f.readlines()])
+ self._ignorelist = l
+ return l
+
+
+ def is_ignored(self, filename):
+ r"""Check whether the filename matches an ignore pattern.
+
+ Patterns containing '/' or '\' need to match the whole path;
+ others match against only the last component.
+
+ If the file is ignored, returns the pattern which caused it to
+ be ignored, otherwise None. So this can simply be used as a
+ boolean if desired."""
+
+ # TODO: Use '**' to match directories, and other extended
+ # globbing stuff from cvs/rsync.
+
+ # XXX: fnmatch is actually not quite what we want: it's only
+ # approximately the same as real Unix fnmatch, and doesn't
+ # treat dotfiles correctly and allows * to match /.
+ # Eventually it should be replaced with something more
+ # accurate.
+
+ import fnmatch
+ from osutils import splitpath
+
+ for pat in self.get_ignore_list():
+ if '/' in pat or '\\' in pat:
+
+ # as a special case, you can put ./ at the start of a
+ # pattern; this is good to match in the top-level
+ # only;
+
+ if (pat[:2] == './') or (pat[:2] == '.\\'):
+ newpat = pat[2:]
+ else:
+ newpat = pat
+ if fnmatch.fnmatchcase(filename, newpat):
+ return pat
+ else:
+ if fnmatch.fnmatchcase(splitpath(filename)[-1], pat):
+ return pat
+ else:
+ return None
+
+
+
+
+
More information about the Pkg-bazaar-commits
mailing list